Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sitemap editor: implement color temperature picker #2880

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions bundles/org.openhab.ui/web/src/assets/sitemap-lexer.nearley
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
widgetclickattr: 'click=',
widgetreleaseattr:'release=',
widgetperiodattr: 'period=',
nlwidget: ['Switch ', 'Selection ', 'Slider ', 'Setpoint ', 'Input ', 'Video ', 'Chart ', 'Webview ', 'Colorpicker ', 'Mapview ', 'Button ', 'Default '],
nlwidget: ['Switch ', 'Selection ', 'Slider ', 'Setpoint ', 'Input ', 'Video ', 'Chart ', 'Webview ', 'Colorpicker ', 'Colortemperaturepicker', 'Mapview ', 'Button ', 'Default '],
lwidget: ['Text ', 'Group ', 'Image ', 'Frame ', 'Buttongrid '],
lparen: '(',
rparen: ')',
Expand All @@ -49,7 +49,7 @@
number: /[+-]?[0-9]+(?:\.[0-9]*)?/,
string: { match: /"(?:\\["\\]|[^\n"\\])*"/, value: x => x.slice(1, -1) }
})
const requiresItem = ['Group', 'Chart', 'Switch', 'Mapview', 'Slider', 'Selection', 'Setpoint', 'Input ', 'Colorpicker', 'Button', 'Default']
const requiresItem = ['Group', 'Chart', 'Switch', 'Mapview', 'Slider', 'Selection', 'Setpoint', 'Input ', 'Colorpicker', 'Colortemperaturepicker', 'Button', 'Default']

function getSitemap(d) {
return {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
export default {
data () {
return {
items: [],
itemsReady: false
}
},
created () {
this.WIDGET_TYPES = [
{ type: 'Sitemap', icon: 'slider_horizontal_below_rectangle' },
{ type: 'Text', icon: 'textformat' },
{ type: 'Switch', icon: 'power' },
{ type: 'Selection', icon: 'text_justify' },
{ type: 'Slider', icon: 'slider_horizontal_3' },
{ type: 'Frame', icon: 'macwindow' },
{ type: 'Setpoint', icon: 'plus_slash_minus' },
{ type: 'Input', icon: 'text_cursor' },
{ type: 'Buttongrid', label: 'Button Grid', icon: 'square_grid_3x2' },
{ type: 'Button', icon: 'square_fill_line_vertical_square' },
{ type: 'Default', icon: 'rectangle' },
{ type: 'Group', icon: 'square_stack_3d_down_right' },
{ type: 'Chart', icon: 'chart_bar_square' },
{ type: 'Webview', label: 'Web View', icon: 'globe' },
{ type: 'Colorpicker', label: 'Color Picker', icon: 'drop' },
{ type: 'Colortemperaturepicker', label: 'Color Temperature Picker', icon: 'thermometer' },
{ type: 'Mapview', label: 'Map View', icon: 'map' },
{ type: 'Image', icon: 'photo' },
{ type: 'Video', icon: 'videocam' }
]
this.LINKABLE_WIDGET_TYPES = ['Sitemap', 'Text', 'Frame', 'Group', 'Image', 'Buttongrid']
this.WIDGET_TYPES_REQUIRING_ITEM = ['Group', 'Chart', 'Switch', 'Mapview', 'Slider', 'Selection', 'Setpoint', 'Input', 'Colorpicker', 'Colortemperaturepicker', 'Default']
this.WIDGET_TYPES_SHOWING_VALUE = ['Text', 'Switch', 'Selection', 'Slider', 'Setpoint', 'Input', 'Default', 'Group']

this.REGEX_PERIOD = /^((P(\d+Y)?(\d+M)?(\d+W)?(\d+D)?(T(\d+H)?(\d+M)?(\d+S)?)?|\d*[YMWDh])-)?-?(P(\d+Y)?(\d+M)?(\d+W)?(\d+D)?(T(\d+H)?(\d+M)?(\d+S)?)?|\d*[YMWDh])$/
this.REGEX_DECIMAL_PATTERN = /^(?:'[0#.,;E]?'|[^0#.,;E'])*((#[,#]*|0)[,0]*)(\.(0+#*|#+))?(?:E0+)?(?:';'|[^;])*(?:;(?:'[0#.,;E]?'|[^0#.,;E'])*((#[,#]*|0)[,0]*)(\.(0+#*|#+))?(?:E0+)?.*)?$/
this.REGEX_MAPPING = /^\s*("[^\n"]*"|\w+)\s*=\s*("[^\n"]*"|\w+)\s*(=\s*("[^\n"]*"|\w+))?$/u
this.REGEX_MAPPING_SWITCH = /^\s*("[^\n"]*"|\w+)\s*(:\s*("[^\n"]*"|\w+)\s*)?=\s*("[^\n"]*"|\w+)\s*(=\s*("[^\n"]*"|\w+))?$/u
this.REGEX_RULE_VISIBILITY = /^(\s*((\w+\s*)?(==|>=|<=|!=|>|<)\s*)?("[^\n"]*"|\w+)\s*AND)*\s*((\w+\s*)?(==|>=|<=|!=|>|<)\s*)?("[^\n"]*"|\w+)\s*$/u
this.REGEX_RULE = /^(((\s*((\w+\s*)?(==|>=|<=|!=|>|<)\s*)?("[^\n"]*"|\w+)\s*AND)*\s*((\w+\s*)?(==|>=|<=|!=|>|<)\s*)?("[^\n"]*"|\w+)\s*)?\s*=)?\s*("#?(\w|:|-)+"|#?(\w|:|-)+)$/u

this.ADDITIONAL_CONTROLS = {
Image: ['url', 'refresh'],
Video: ['url', 'encoding'],
Chart: ['service', 'period', 'refresh', 'legend', 'forceAsItem', 'yAxisDecimalPattern'],
Webview: ['url', 'height'],
Mapview: ['height'],
Slider: ['switchEnabled', 'releaseOnly', 'minValue', 'maxValue', 'step'],
Setpoint: ['minValue', 'maxValue', 'step'],
Colortemperaturepicker: ['minValue', 'maxValue'],
Input: ['inputHint'],
Button: ['row', 'column', 'stateless', 'cmd', 'releaseCmd'],
Default: ['height']
}
this.ENCODING_DEFS = [
{ key: 'mjpeg', value: 'MJPEG Video' },
{ key: 'HLS', value: 'HTTP Live Streaming' }
]
this.INPUT_HINT_DEFS = [
{ key: 'text', value: 'Text' },
{ key: 'number', value: 'Number' },
{ key: 'date', value: 'Date' },
{ key: 'time', value: 'Time' },
{ key: 'datetime', value: 'Date and Time' }
]

if (!this.itemsList) {
this.$oh.api.get('/rest/items?staticDataOnly=true').then((items) => {
this.items = items
this.itemsReady = true
})
} else {
this.items = this.itemsList ?? []
this.itemsReady = true
}
},
methods: {
widgetTypeDef (component) {
const componentType = component ?? this.widget.component
return this.WIDGET_TYPES.find(w => w.type === componentType)
},
widgetTypeIcon (component) {
return this.widgetTypeDef(component).icon
},
widgetTypeLabel (component) {
return this.widgetTypeDef(component).label ?? this.widgetTypeDef(component).type
},
widgetConfigLabel () {
return this.widget.config.label ?? ((this.widget.component === 'Button') ? this.widget.config.cmd : '')
},
widgetItemLabel (includeItemName) {
const item = this.items.find(i => i.name === this.widget.config.item)
return (item?.label ?? this.widget.config.item) + (includeItemName ? ' (' + item?.name ?? '' + ')' : '')
},
widgetConfigDescription (includeItemName) {
const buttonPosition = this.widget.component === 'Button' ? ' (' + (this.widget.config?.row ?? '-') + ',' + (this.widget.config?.column ?? '-') + ')' : ''
return (this.widget.config?.item ? ': ' + this.widgetItemLabel(includeItemName) : '') + buttonPosition
}
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
<template>
<f7-treeview-item selectable :label="widget.config.label ? widget.config.label : ((widget.component === 'Button') ? widget.config.cmd : '')"
:icon-ios="icon('ios')" :icon-aurora="icon('aurora')" :icon-md="icon('md')"
<f7-treeview-item v-if="itemsReady" selectable :label="widgetConfigLabel()"
:icon-f7="widgetTypeIcon()"
:textColor="iconColor" :color="'blue'"
:selected="selected && selected === widget"
:opened="!widget.closed"
@click="select">
<sitemap-treeview-item v-for="(childwidget, idx) in children"
<sitemap-treeview-item class="sitemap-treeview-item" v-for="(childwidget, idx) in children"
:key="idx"
:includeItemName="includeItemName"
:widget="childwidget" :parent-widget="widget"
:itemsList="items"
@selected="(event) => $emit('selected', event)"
:selected="selected" />
<div slot="label" class="subtitle">
Expand All @@ -16,53 +18,25 @@
</f7-treeview-item>
</template>

<style lang="stylus">
.sitemap-tree
.treeview
.treeview-item-content
width calc(100% - (var(--f7-treeview-toggle-size) + 5px))
.subtitle
overflow hidden
text-overflow ellipsis
</style>

<script>
import SitemapMixin from '@/components/pagedesigner/sitemap/sitemap-mixin'

export default {
props: ['widget', 'parentWidget', 'selected'],
mixins: [SitemapMixin],
props: ['includeItemName', 'widget', 'parentWidget', 'itemsList', 'selected'],
methods: {
icon (theme) {
switch (this.widget.component) {
case 'Switch':
return 'f7:power'
case 'Selection':
return 'f7:text_justify'
case 'Slider':
return 'f7:slider_horizontal_3'
case 'Setpoint':
return 'f7:plus_slash_minus'
case 'Input':
return 'f7:text_cursor'
case 'Video':
return 'f7:videocam'
case 'Chart':
return 'f7:chart_bar_square'
case 'Webview':
return 'f7:globe'
case 'Colorpicker':
return 'f7:drop'
case 'Mapview':
return 'f7:map'
case 'Buttongrid':
return 'f7:square_grid_3x2'
case 'Button':
return 'f7:square_fill_line_vertical_square'
case 'Default':
return 'f7:rectangle'
case 'Text':
return 'f7:textformat'
case 'Group':
return 'f7:square_stack_3d_down_right'
case 'Image':
return 'f7:photo'
case 'Frame':
return 'f7:macwindow'
default:
return 'f7:slider_horizontal_below_rectangle'
}
},
subtitle () {
const buttonPosition = this.widget.component === 'Button' ? ' (' + (this.widget.config?.row ?? '-') + ',' + (this.widget.config?.column ?? '-') + ')' : ''
return this.widget.component + ((this.widget.config && this.widget.config.item) ? ': ' + this.widget.config.item : '') + buttonPosition
return this.widgetTypeLabel() + this.widgetConfigDescription(this.includeItemName)
},
select (event) {
let self = this
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,10 @@
import { Categories } from '@/assets/categories.js'
import ItemPicker from '@/components/config/controls/item-picker.vue'
import PersistencePicker from '@/components/config/controls/persistence-picker.vue'
import SitemapMixin from '@/components/pagedesigner/sitemap/sitemap-mixin'

export default {
mixins: [SitemapMixin],
components: {
ItemPicker,
PersistencePicker
Expand All @@ -110,31 +112,6 @@ export default {
iconAutocomplete: null
}
},
created () {
this.ADDITIONAL_CONTROLS = {
Image: ['url', 'refresh'],
Video: ['url', 'encoding'],
Chart: ['service', 'period', 'refresh', 'legend', 'forceAsItem', 'yAxisDecimalPattern'],
Webview: ['url', 'height'],
Mapview: ['height'],
Slider: ['switchEnabled', 'releaseOnly', 'minValue', 'maxValue', 'step'],
Setpoint: ['minValue', 'maxValue', 'step'],
Input: ['inputHint'],
Button: ['row', 'column', 'stateless', 'cmd', 'releaseCmd'],
Default: ['height']
}
this.ENCODING_DEFS = [
{ key: 'mjpeg', value: 'MJPEG Video' },
{ key: 'HLS', value: 'HTTP Live Streaming' }
]
this.INPUT_HINT_DEFS = [
{ key: 'text', value: 'Text' },
{ key: 'number', value: 'Number' },
{ key: 'date', value: 'Date' },
{ key: 'time', value: 'Time' },
{ key: 'datetime', value: 'Date and Time' }
]
},
methods: {
initializeAutocomplete (inputElement) {
this.iconAutocomplete = this.$f7.autocomplete.create({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import PagesList from '../../pages-list.vue'
import SitemapEdit from '../sitemap-edit.vue'
import { shallowMount, createLocalVue } from '@vue/test-utils'
import Framework7 from 'framework7'
Expand All @@ -24,7 +23,8 @@ describe('SitemapEdit', () => {
open: () => { }
}
}
}
},
data: { sitemap: {} }
}
}
})
Expand All @@ -34,7 +34,8 @@ describe('SitemapEdit', () => {
localVue,
propsData: {
createMode: true,
uid: 'test'
uid: 'test',
itemsList: []
}
})
})
Expand Down
Loading