[#341] Added UI for while loops in procedure editor.

This commit is contained in:
Fabio Manganiello 2024-09-15 02:06:23 +02:00
parent ab07fc0fa3
commit dfbbea93fd
Signed by untrusted user: blacklight
GPG key ID: D90FBA7F76362774
4 changed files with 141 additions and 39 deletions

View file

@ -29,7 +29,7 @@
v-on="componentsData[index].on" v-on="componentsData[index].on"
:collapsed="collapsedBlocks[index]" :collapsed="collapsedBlocks[index]"
:dragging="isDragging" :dragging="isDragging"
v-else-if="fors[index]" /> v-else-if="loops[index]" />
<BreakTile :active="isDragging" <BreakTile :active="isDragging"
:readOnly="readOnly" :readOnly="readOnly"
@ -86,8 +86,12 @@
<AddTile icon="fas fa-question" title="Add Else" @click="$emit('add-else')" /> <AddTile icon="fas fa-question" title="Add Else" @click="$emit('add-else')" />
</div> </div>
<div class="row item action add-for-container" v-if="visibleAddButtons.loop"> <div class="row item action add-for-container" v-if="visibleAddButtons.for">
<AddTile icon="fas fa-arrow-rotate-left" title="Add Loop" @click="addLoop" /> <AddTile icon="fas fa-arrow-rotate-left" title="Add For Loop" @click="addForLoop" />
</div>
<div class="row item action add-while-container" v-if="visibleAddButtons.while">
<AddTile icon="fas fa-arrow-rotate-left" title="Add While Loop" @click="addWhileLoop" />
</div> </div>
<div class="row item action add-break-container" v-if="visibleAddButtons.break"> <div class="row item action add-break-container" v-if="visibleAddButtons.break">
@ -265,9 +269,15 @@ export default {
data.props.indent = this.indent + 1 data.props.indent = this.indent + 1
} }
const loop = this.getFor(action) const forLoop = this.getFor(action)
if (loop) { if (forLoop) {
data.props.async = loop.async data.props.async = forLoop.async
data.props.type = 'for'
}
const whileLoop = this.getWhile(action)
if (whileLoop) {
data.props.type = 'while'
} }
return data return data
@ -338,6 +348,23 @@ export default {
}, {}) || {} }, {}) || {}
}, },
whiles() {
return this.newValue?.reduce?.((acc, action, index) => {
if (this.getWhile(action)) {
acc[index] = action
}
return acc
}, {}) || {}
},
loops() {
return [...Object.keys(this.fors), ...Object.keys(this.whiles)].reduce((acc, index) => {
acc[index] = this.newValue[index]
return acc
}, {})
},
hasChanges() { hasChanges() {
return this.newStringValue !== this.stringValue return this.newStringValue !== this.stringValue
}, },
@ -389,14 +416,17 @@ export default {
showAddButtons() { showAddButtons() {
return ( return (
this.newValue.length === 0 || !this.collapseAddButtons (this.indent === 0 && this.newValue.length === 0) || !this.collapseAddButtons
) )
}, },
showAddButtonsExpander() { showAddButtonsExpander() {
return ( return (
!this.readOnly && !this.readOnly &&
this.newValue?.length > 0 && (
this.newValue?.length > 0 ||
this.indent > 0
) &&
Object.entries(this.visibleAddButtons).filter( Object.entries(this.visibleAddButtons).filter(
([key, value]) => value && key != 'action' ([key, value]) => value && key != 'action'
).length > 1 ).length > 1
@ -431,6 +461,7 @@ export default {
this.conditions[index] || this.conditions[index] ||
this.elses[index] || this.elses[index] ||
this.fors[index] || this.fors[index] ||
this.whiles[index] ||
this.isAction(action) || this.isAction(action) ||
this.isReturn(action) || this.isReturn(action) ||
this.isBreak(action) || this.isBreak(action) ||
@ -448,7 +479,8 @@ export default {
action: this.allowAddButtons, action: this.allowAddButtons,
return: this.allowAddButtons, return: this.allowAddButtons,
condition: this.allowAddButtons, condition: this.allowAddButtons,
loop: this.allowAddButtons, for: this.allowAddButtons,
while: this.allowAddButtons,
else: ( else: (
this.allowAddButtons && this.allowAddButtons &&
this.parent && this.parent &&
@ -663,11 +695,16 @@ export default {
this.selectLastExprEditor() this.selectLastExprEditor()
}, },
addLoop() { addForLoop() {
this.newValue.push({ 'for _ in ${range(10)}': [] }) this.newValue.push({ 'for _ in ${range(10)}': [] })
this.selectLastExprEditor() this.selectLastExprEditor()
}, },
addWhileLoop() {
this.newValue.push({ 'while ${True}': [] })
this.selectLastExprEditor()
},
addBreak() { addBreak() {
this.newValue.push('break') this.newValue.push('break')
}, },

View file

@ -22,7 +22,7 @@
</template> </template>
<template #after> <template #after>
<EndBlockTile value="end for" <EndBlockTile :value="`end ${type}`"
icon="fas fa-arrow-rotate-right" icon="fas fa-arrow-rotate-right"
:active="active" :active="active"
:spacer-bottom="spacerBottom" :spacer-bottom="spacerBottom"
@ -64,6 +64,11 @@ export default {
required: true, required: true,
}, },
type: {
type: String,
required: true,
},
active: { active: {
type: Boolean, type: Boolean,
default: false, default: false,
@ -117,8 +122,36 @@ export default {
}, },
computed: { computed: {
changeHandler() {
if (this.type === 'for') {
return this.onForChange
}
if (this.type === 'while') {
return this.onWhileChange
}
return () => {}
},
isDragging() {
return this.dragging_ || this.dragging
},
key() {
return this.getKey(this.value)
},
loop() { loop() {
if (this.type === 'for') {
return this.getFor(this.key) return this.getFor(this.key)
}
if (this.type === 'while') {
return {condition: this.getWhile(this.key)}
}
return {}
}, },
loopTileConf() { loopTileConf() {
@ -129,10 +162,11 @@ export default {
readOnly: this.readOnly, readOnly: this.readOnly,
spacerBottom: this.spacerBottom, spacerBottom: this.spacerBottom,
spacerTop: this.spacerTop, spacerTop: this.spacerTop,
type: this.type,
}, },
on: { on: {
change: this.onLoopChange, change: this.changeHandler,
delete: (event) => this.$emit('delete', event), delete: (event) => this.$emit('delete', event),
drag: this.onDragStart, drag: this.onDragStart,
dragend: this.onDragEnd, dragend: this.onDragEnd,
@ -144,14 +178,6 @@ export default {
}, },
} }
}, },
isDragging() {
return this.dragging_ || this.dragging
},
key() {
return this.getKey(this.value)
},
}, },
methods: { methods: {
@ -163,7 +189,7 @@ export default {
this.$emit('input', { [this.key]: value }) this.$emit('input', { [this.key]: value })
}, },
onLoopChange(loop) { onForChange(loop) {
const iterable = loop?.iterable?.trim() const iterable = loop?.iterable?.trim()
const iterator = loop?.iterator?.trim() const iterator = loop?.iterator?.trim()
const async_ = loop?.async || false const async_ = loop?.async || false
@ -177,6 +203,16 @@ export default {
this.$emit('input', { [loop]: this.value[this.key] }) this.$emit('input', { [loop]: this.value[this.key] })
}, },
onWhileChange(condition) {
condition = condition?.trim()
if (!this.key || this.readOnly || !condition?.length) {
return
}
const loop = `while \${${condition}}`
this.$emit('input', { [loop]: this.value[this.key] })
},
onDragStart(event) { onDragStart(event) {
if (this.readOnly) { if (this.readOnly) {
return return

View file

@ -17,12 +17,17 @@
<span class="icon"> <span class="icon">
<i class="fas fa-arrow-rotate-left" /> <i class="fas fa-arrow-rotate-left" />
</span> </span>
<span class="name"> <span class="name" v-if="type === 'for'">
<span class="keyword">for<span v-if="async">k</span></span>&nbsp; <span class="keyword">for<span v-if="async">k</span></span>&nbsp;
<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>
<span class="name" v-else-if="type === 'while'">
<span class="keyword">while</span> [
<span class="code" v-text="condition" /> ]
</span>
</div> </div>
</Tile> </Tile>
@ -33,17 +38,23 @@
<LoopEditor :iterator="iterator" <LoopEditor :iterator="iterator"
:iterable="iterable" :iterable="iterable"
:async="async" :async="async"
ref="loopEditor"
@change="onLoopChange" @change="onLoopChange"
v-if="showLoopEditor"> v-if="showLoopEditor && type === 'for'">
Loop Loop
</LoopEditor> </LoopEditor>
<ExpressionEditor :value="condition"
@input.prevent.stop="onConditionChange"
v-else-if="showLoopEditor && type === 'while'">
Loop Condition
</ExpressionEditor>
</Modal> </Modal>
</div> </div>
</ListItem> </ListItem>
</template> </template>
<script> <script>
import ExpressionEditor from "./ExpressionEditor"
import ListItem from "./ListItem" import ListItem from "./ListItem"
import LoopEditor from "./LoopEditor" import LoopEditor from "./LoopEditor"
import Modal from "@/components/Modal" import Modal from "@/components/Modal"
@ -64,6 +75,7 @@ export default {
], ],
components: { components: {
ExpressionEditor,
LoopEditor, LoopEditor,
ListItem, ListItem,
Modal, Modal,
@ -71,16 +83,6 @@ export default {
}, },
props: { props: {
iterator: {
type: String,
required: true,
},
iterable: {
type: String,
required: true,
},
active: { active: {
type: Boolean, type: Boolean,
default: false, default: false,
@ -91,9 +93,16 @@ export default {
default: false, default: false,
}, },
isElse: { condition: {
type: Boolean, type: String,
default: false, },
iterator: {
type: String,
},
iterable: {
type: String,
}, },
readOnly: { readOnly: {
@ -110,6 +119,11 @@ export default {
type: Boolean, type: Boolean,
default: true, default: true,
}, },
type: {
type: String,
required: true,
},
}, },
computed: { computed: {
@ -154,6 +168,21 @@ export default {
}, },
methods: { methods: {
onConditionChange(event) {
this.showLoopEditor = false
if (this.readOnly) {
return
}
const condition = event.target.value?.trim()
if (!condition?.length) {
return
}
event.target.value = condition
this.$emit('change', condition)
},
onLoopChange(event) { onLoopChange(event) {
this.showLoopEditor = false this.showLoopEditor = false
if (this.readOnly) { if (this.readOnly) {

View file

@ -33,7 +33,7 @@ export default {
}, },
isActionsBlock(value) { isActionsBlock(value) {
return this.getCondition(value) || this.isElse(value) || this.getFor(value) return this.getCondition(value) || this.isElse(value) || this.getFor(value) || this.getWhile(value)
}, },
isBreak(value) { isBreak(value) {