Skip to content

Commit

Permalink
Rule/script/scene edit: Update tag input & Refactoring (#2083)
Browse files Browse the repository at this point in the history
- Refactor rule & scene settings into rule-general-settings Vue
component and use this component on rule-edit, scene-edit pages and
inside script-general-settings component.
- Replace semantic tag picker & custom tag input with accordion tag
input (inspired by #2078) and show number of tags.
- Refactor shared code from rules & scenes into mixin
(`rule-edit-mixin.js`).
- Fix Scene tag is not hidden from tag input.
- Show number of tags on page settings page, follow up for #2078.

---------

Signed-off-by: Florian Hotze <[email protected]>
  • Loading branch information
florian-h05 authored Dec 7, 2023
1 parent 5a9c0c9 commit ba7f126
Show file tree
Hide file tree
Showing 7 changed files with 222 additions and 315 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
</f7-list>
</f7-accordion-content>
</f7-list-item>
<f7-list-item accordion-item title="Tags" :disabled="page.uid === 'overview'">
<f7-list-item accordion-item title="Tags" :disabled="page.uid === 'overview'" :after="(page && page.tags && page.tags.length) ? page.tags.length : ''">
<f7-accordion-content>
<tag-input :item="page" />
</f7-accordion-content>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<template>
<div>
<f7-block v-if="ready" class="block-narrow">
<f7-col>
<f7-list inline-labels no-hairlines-md>
<f7-list-input label="Unique ID" type="text" placeholder="Required" :value="rule.uid" required validate
:disabled="!createMode" :info="(createMode) ? 'Note: cannot be changed after the creation' : ''"
pattern="[A-Za-z0-9_\-]+" error-message="Required. A-Z,a-z,0-9,_,- only"
@input="rule.uid = $event.target.value" :clear-button="createMode" />
<f7-list-input label="Name" type="text" placeholder="Required" :value="rule.name" required validate
:disabled="!isEditable" @input="rule.name = $event.target.value" :clear-button="isEditable" />
<f7-list-input label="Description" type="text" :value="rule.description"
:disabled="!isEditable" @input="rule.description = $event.target.value" :clear-button="isEditable" />
<f7-list-item v-if="(isEditable || rule.tags.length > 0) && (!createMode || !hasRuleTemplate)" accordion-item title="Tags" :after="numberOfTags" :disabled="!isEditable">
<f7-accordion-content>
<tag-input :item="rule" :showSemanticTags="true" :inScriptEditor="inScriptEditor" :inSceneEditor="inSceneEditor" />
</f7-accordion-content>
</f7-list-item>
</f7-list>
</f7-col>
</f7-block>

<!-- skeletons for not ready-->
<f7-block v-else class="block-narrow">
<f7-col class="skeleton-text skeleton-effect-blink">
<f7-list inline-labels no-hairlines-md>
<f7-list-input label="Unique ID" type="text" placeholder="Required" value="_______" required validate
:disabled="true" :info="(createMode) ? 'Note: cannot be changed after the creation' : ''"
@input="rule.uid = $event.target.value" :clear-button="createMode" />
<f7-list-input label="Name" type="text" placeholder="Required" required validate
:disabled="true" @input="rule.name = $event.target.value" :clear-button="isEditable" />
<f7-list-input label="Description" type="text" value="__ _____ ___ __ ___"
:disabled="true" @input="rule.description = $event.target.value" :clear-button="isEditable" />
<f7-list-item accordion-item title="Tags" :disabled="!isEditable">
<f7-accordion-content>
<tag-input :item="rule" :showSemanticTags="true" />
</f7-accordion-content>
</f7-list-item>
</f7-list>
</f7-col>
</f7-block>
</div>
</template>

<script>
import TagInput from '@/components/tags/tag-input.vue'
export default {
props: ['rule', 'ready', 'createMode', 'isEditable', 'hasRuleTemplate', 'inScriptEditor', 'inSceneEditor'],
components: {
TagInput
},
computed: {
numberOfTags () {
if (!this.rule.tags) return 0
return this.rule.tags.filter((t) => !this.isScriptTag(t) && !this.isSceneTag(t)).length
}
},
methods: {
isScriptTag (tag) {
if (this.inScriptEditor !== true) return false
if (tag === 'Script') return true
},
isSceneTag (tag) {
if (this.inSceneEditor !== true) return false
if (tag === 'Scene') return true
}
}
}
</script>
8 changes: 6 additions & 2 deletions bundles/org.openhab.ui/web/src/components/tags/tag-input.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<f7-list>
<f7-list-item>
<div slot="inner">
<f7-chip v-for="tag in item.tags.filter((t) => !isSemanticTag(t) && !isScriptTag(t))" :key="tag" :text="tag" :deleteable="!disabled" @delete="deleteTag" media-bg-color="blue">
<f7-chip v-for="tag in item.tags.filter((t) => (showSemanticTags ? true : !isSemanticTag(t)) && !isScriptTag(t) && !isSceneTag(t))" :key="tag" :text="tag" :deleteable="!disabled" @delete="deleteTag" media-bg-color="blue">
<f7-icon slot="media" ios="f7:tag_fill" md="material:label" aurora="f7:tag_fill" />
</f7-chip>
</div>
Expand All @@ -29,7 +29,7 @@ import TagMixin from '@/components/tags/tag-mixin'
export default {
mixins: [TagMixin],
props: ['item', 'disabled', 'inScriptEditor'],
props: ['item', 'disabled', 'inScriptEditor', 'inSceneEditor', 'showSemanticTags'],
data () {
return {
pendingTag: ''
Expand All @@ -40,6 +40,10 @@ export default {
if (this.inScriptEditor !== true) return false
if (tag === 'Script') return true
},
isSceneTag (tag) {
if (this.inSceneEditor !== true) return false
if (tag === 'Scene') return true
},
addTag () {
if (this.pendingTag && this.item.tags.indexOf(this.pendingTag) === -1) {
this.item.tags.push(this.pendingTag)
Expand Down
111 changes: 111 additions & 0 deletions bundles/org.openhab.ui/web/src/pages/settings/rules/rule-edit-mixin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import YAML from 'yaml'

export default {
data () {
return {
showModuleControls: false,
eventSource: null,
keyHandler: null
}
},
computed: {
isEditable () {
return this.rule && this.rule.editable !== false
},
yamlError () {
if (this.currentTab !== 'code') return null
try {
YAML.parse(this.ruleYaml, { prettyErrors: true })
return 'OK'
} catch (e) {
return e
}
}
},
methods: {
onPageAfterIn () {
if (window) {
window.addEventListener('keydown', this.keyDown)
}
this.load()
},
onPageAfterOut () {
this.stopEventSource()
if (window) {
window.removeEventListener('keydown', this.keyDown)
}
},
onEditorInput (value) {
this.ruleYaml = value
this.dirty = true
},
toggleDisabled () {
if (this.createMode) return
if (this.copyMode) return
const enable = (this.rule.status.statusDetail === 'DISABLED')
this.$oh.api.postPlain('/rest/rules/' + this.rule.uid + '/enable', enable.toString()).then((data) => {
this.$f7.toast.create({
text: (enable) ? 'Enabled' : 'Disabled',
destroyOnClose: true,
closeTimeout: 2000
}).open()
}).catch((err) => {
this.$f7.toast.create({
text: 'Error while disabling or enabling: ' + err,
destroyOnClose: true,
closeTimeout: 2000
}).open()
})
},
keyDown (ev) {
if ((ev.ctrlKey || ev.metaKey) && !(ev.altKey || ev.shiftKey)) {
if (this.currentModule) return
switch (ev.keyCode) {
case 68:
this.toggleDisabled()
ev.stopPropagation()
ev.preventDefault()
break
case 82:
this.runNow()
ev.stopPropagation()
ev.preventDefault()
break
case 83:
this.save()
ev.stopPropagation()
ev.preventDefault()
break
}
}
},
toggleModuleControls () {
this.showModuleControls = !this.showModuleControls
},
showSwipeout (ev) {
let swipeoutElement = ev.target
ev.cancelBubble = true
while (!swipeoutElement.classList.contains('swipeout')) {
swipeoutElement = swipeoutElement.parentElement
}

if (swipeoutElement) {
this.$f7.swipeout.open(swipeoutElement)
}
},
startEventSource () {
this.eventSource = this.$oh.sse.connect('/rest/events?topics=openhab/rules/' + this.ruleId + '/*', null, (event) => {
const topicParts = event.topic.split('/')
switch (topicParts[3]) {
case 'state':
this.$set(this.rule, 'status', JSON.parse(event.payload)) // e.g. {"status":"RUNNING","statusDetail":"NONE"}
break
}
})
},
stopEventSource () {
this.$oh.sse.close(this.eventSource)
this.eventSource = null
}
}
}
Loading

0 comments on commit ba7f126

Please sign in to comment.