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

View file

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

View file

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

View file

@ -33,7 +33,7 @@ export default {
},
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) {