Fix icons not displayed & not dynamic on all pages (#1849)

Fixes #1839.
Fixes #574.
Closes #1860.

* Fixes Item icon styling on the Item detail page.
* Add support for openHAB iconsets.
* Fix `oh:` icons not properly displayed in `default-list-item.vue`.
* Refactor `oh-icon` config & style binding.
* Enable real-time state on the semantic model page.
* Enable state for the channel link edit page.
* Enable dynamic icons in the settings where missing and possible
(semantic model page, channel link edit page, Item edit page when
setting category, Items list page).
* Enable dynamic icons in the default list widget for most Items except
some and update the docs accordingly.
* Add a refresh button to the Item list page.

--
Signed-off-by: Florian Hotze <florianh_dev@icloud.com>
pull/1850/head
Florian Hotze 2023-05-02 22:58:13 +02:00 committed by GitHub
parent 79fbc793e3
commit 095bb2f9cb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 88 additions and 58 deletions

View File

@ -56,7 +56,7 @@ Display a color picker in a list
</PropBlock> </PropBlock>
<PropBlock type="BOOLEAN" name="iconUseState" label="Icon depends on state"> <PropBlock type="BOOLEAN" name="iconUseState" label="Icon depends on state">
<PropDescription> <PropDescription>
Use the state of the item to get a dynamic icon (for openHAB icons only) Use the state of the Item to get a dynamic icon (enabled by default for all Item types except <code>Call</code>, <code>Image</code> & <code>Location</code>) (for openHAB icons only)
</PropDescription> </PropDescription>
</PropBlock> </PropBlock>
</PropGroup> </PropGroup>

View File

@ -56,7 +56,7 @@ Display an input field in a list
</PropBlock> </PropBlock>
<PropBlock type="BOOLEAN" name="iconUseState" label="Icon depends on state"> <PropBlock type="BOOLEAN" name="iconUseState" label="Icon depends on state">
<PropDescription> <PropDescription>
Use the state of the item to get a dynamic icon (for openHAB icons only) Use the state of the Item to get a dynamic icon (enabled by default for all Item types except <code>Call</code>, <code>Image</code> & <code>Location</code>) (for openHAB icons only)
</PropDescription> </PropDescription>
</PropBlock> </PropBlock>
</PropGroup> </PropGroup>

View File

@ -421,7 +421,7 @@ Display the state of an item in a card
</PropBlock> </PropBlock>
<PropBlock type="BOOLEAN" name="iconUseState" label="Icon depends on state"> <PropBlock type="BOOLEAN" name="iconUseState" label="Icon depends on state">
<PropDescription> <PropDescription>
Use the state of the item to get a dynamic icon (for openHAB icons only) Use the state of the Item to get a dynamic icon (enabled by default for all Item types except <code>Call</code>, <code>Image</code> & <code>Location</code>) (for openHAB icons only)
</PropDescription> </PropDescription>
</PropBlock> </PropBlock>
<PropBlock type="BOOLEAN" name="vertical" label="Vertical arrangement"> <PropBlock type="BOOLEAN" name="vertical" label="Vertical arrangement">

View File

@ -67,7 +67,7 @@ Display the state of an item in a list
</PropBlock> </PropBlock>
<PropBlock type="BOOLEAN" name="iconUseState" label="Icon depends on state"> <PropBlock type="BOOLEAN" name="iconUseState" label="Icon depends on state">
<PropDescription> <PropDescription>
Use the state of the item to get a dynamic icon (for openHAB icons only) Use the state of the Item to get a dynamic icon (enabled by default for all Item types except <code>Call</code>, <code>Image</code> & <code>Location</code>) (for openHAB icons only)
</PropDescription> </PropDescription>
</PropBlock> </PropBlock>
</PropGroup> </PropGroup>

View File

@ -56,7 +56,7 @@ A list item
</PropBlock> </PropBlock>
<PropBlock type="BOOLEAN" name="iconUseState" label="Icon depends on state"> <PropBlock type="BOOLEAN" name="iconUseState" label="Icon depends on state">
<PropDescription> <PropDescription>
Use the state of the item to get a dynamic icon (for openHAB icons only) Use the state of the Item to get a dynamic icon (enabled by default for all Item types except <code>Call</code>, <code>Image</code> & <code>Location</code>) (for openHAB icons only)
</PropDescription> </PropDescription>
</PropBlock> </PropBlock>
</PropGroup> </PropGroup>

View File

@ -62,7 +62,7 @@ A marker on a floor plan
</PropBlock> </PropBlock>
<PropBlock type="BOOLEAN" name="iconUseState" label="Icon depends on state"> <PropBlock type="BOOLEAN" name="iconUseState" label="Icon depends on state">
<PropDescription> <PropDescription>
Use the state of the item to get a dynamic icon (for openHAB icons only) Use the state of the Item to get a dynamic icon (enabled by default for all Item types except <code>Call</code>, <code>Image</code> & <code>Location</code>) (for openHAB icons only)
</PropDescription> </PropDescription>
</PropBlock> </PropBlock>
<PropBlock type="INTEGER" name="iconSize" label="Icon Size"> <PropBlock type="INTEGER" name="iconSize" label="Icon Size">

View File

@ -56,7 +56,7 @@ Display player controls in a list
</PropBlock> </PropBlock>
<PropBlock type="BOOLEAN" name="iconUseState" label="Icon depends on state"> <PropBlock type="BOOLEAN" name="iconUseState" label="Icon depends on state">
<PropDescription> <PropDescription>
Use the state of the item to get a dynamic icon (for openHAB icons only) Use the state of the Item to get a dynamic icon (enabled by default for all Item types except <code>Call</code>, <code>Image</code> & <code>Location</code>) (for openHAB icons only)
</PropDescription> </PropDescription>
</PropBlock> </PropBlock>
</PropGroup> </PropGroup>

View File

@ -56,7 +56,7 @@ Display rollershutter controls in a list
</PropBlock> </PropBlock>
<PropBlock type="BOOLEAN" name="iconUseState" label="Icon depends on state"> <PropBlock type="BOOLEAN" name="iconUseState" label="Icon depends on state">
<PropDescription> <PropDescription>
Use the state of the item to get a dynamic icon (for openHAB icons only) Use the state of the Item to get a dynamic icon (enabled by default for all Item types except <code>Call</code>, <code>Image</code> & <code>Location</code>) (for openHAB icons only)
</PropDescription> </PropDescription>
</PropBlock> </PropBlock>
</PropGroup> </PropGroup>

View File

@ -56,7 +56,7 @@ Display a slider control in a list
</PropBlock> </PropBlock>
<PropBlock type="BOOLEAN" name="iconUseState" label="Icon depends on state"> <PropBlock type="BOOLEAN" name="iconUseState" label="Icon depends on state">
<PropDescription> <PropDescription>
Use the state of the item to get a dynamic icon (for openHAB icons only) Use the state of the Item to get a dynamic icon (enabled by default for all Item types except <code>Call</code>, <code>Image</code> & <code>Location</code>) (for openHAB icons only)
</PropDescription> </PropDescription>
</PropBlock> </PropBlock>
</PropGroup> </PropGroup>

View File

@ -56,7 +56,7 @@ Display a stepper control in a list
</PropBlock> </PropBlock>
<PropBlock type="BOOLEAN" name="iconUseState" label="Icon depends on state"> <PropBlock type="BOOLEAN" name="iconUseState" label="Icon depends on state">
<PropDescription> <PropDescription>
Use the state of the item to get a dynamic icon (for openHAB icons only) Use the state of the Item to get a dynamic icon (enabled by default for all Item types except <code>Call</code>, <code>Image</code> & <code>Location</code>) (for openHAB icons only)
</PropDescription> </PropDescription>
</PropBlock> </PropBlock>
</PropGroup> </PropGroup>

View File

@ -56,7 +56,7 @@ Display a toggle switch in a list
</PropBlock> </PropBlock>
<PropBlock type="BOOLEAN" name="iconUseState" label="Icon depends on state"> <PropBlock type="BOOLEAN" name="iconUseState" label="Icon depends on state">
<PropDescription> <PropDescription>
Use the state of the item to get a dynamic icon (for openHAB icons only) Use the state of the Item to get a dynamic icon (enabled by default for all Item types except <code>Call</code>, <code>Image</code> & <code>Location</code>) (for openHAB icons only)
</PropDescription> </PropDescription>
</PropBlock> </PropBlock>
</PropGroup> </PropGroup>

View File

@ -34,7 +34,7 @@ export const OhPlanMarkerDefinition = () => new WidgetDefinition('oh-plan-marker
]) ])
.paramGroup(pg('icon', 'Icon', 'You can customize the styles further with CSS attributes in the <code>iconStyle</code> parameter (in YAML only)'), [ .paramGroup(pg('icon', 'Icon', 'You can customize the styles further with CSS attributes in the <code>iconStyle</code> parameter (in YAML only)'), [
pt('icon', 'Icon', 'Use <code>oh:iconName</code> (<a class="external text-color-blue" target="_blank" href="https://www.openhab.org/link/icons">openHAB icon</a>), <code>f7:iconName</code> (<a class="external text-color-blue" target="_blank" href="https://framework7.io/icons/">Framework7 icon</a>), <code>material:iconName</code> (<a class="external text-color-blue" target="_blank" href="https://jossef.github.io/material-design-icons-iconfont/">Material icon</a>) or <code>iconify:iconSet:iconName</code> (<a class="external text-color-blue" target="_blank" href="https://icon-sets.iconify.design">Iconify icon</a>, requires being online if not in cache)'), pt('icon', 'Icon', 'Use <code>oh:iconName</code> (<a class="external text-color-blue" target="_blank" href="https://www.openhab.org/link/icons">openHAB icon</a>), <code>f7:iconName</code> (<a class="external text-color-blue" target="_blank" href="https://framework7.io/icons/">Framework7 icon</a>), <code>material:iconName</code> (<a class="external text-color-blue" target="_blank" href="https://jossef.github.io/material-design-icons-iconfont/">Material icon</a>) or <code>iconify:iconSet:iconName</code> (<a class="external text-color-blue" target="_blank" href="https://icon-sets.iconify.design">Iconify icon</a>, requires being online if not in cache)'),
pb('iconUseState', 'Icon depends on state', 'Use the state of the item to get a dynamic icon (for openHAB icons only)'), pb('iconUseState', 'Icon depends on state', 'Use the state of the Item to get a dynamic icon (enabled by default for all Item types except <code>Call</code>, <code>Image</code> & <code>Location</code>) (for openHAB icons only)').a(),
pn('iconSize', 'Icon Size', 'Size of the icon in pixels (40 by default)'), pn('iconSize', 'Icon Size', 'Size of the icon in pixels (40 by default)'),
pn('iconWidth', 'Icon Width', 'Width of the icon in pixels (for openHAB icons only, 40 by default)').a(), pn('iconWidth', 'Icon Width', 'Width of the icon in pixels (for openHAB icons only, 40 by default)').a(),
pn('iconHeight', 'Icon Height', 'Height of the icon in pixels (for openHAB icons only, 40 by default)').a(), pn('iconHeight', 'Icon Height', 'Height of the icon in pixels (for openHAB icons only, 40 by default)').a(),

View File

@ -28,7 +28,7 @@ export const OhLabelCardDefinition = () => new WidgetDefinition('oh-label-card',
pt('icon', 'Icon', 'Use <code>oh:iconName</code> (<a class="external text-color-blue" target="_blank" href="https://www.openhab.org/link/icons">openHAB icon</a>), <code>f7:iconName</code> (<a class="external text-color-blue" target="_blank" href="https://framework7.io/icons/">Framework7 icon</a>), <code>material:iconName</code> (<a class="external text-color-blue" target="_blank" href="https://jossef.github.io/material-design-icons-iconfont/">Material icon</a>) or <code>iconify:iconSet:iconName</code> (<a class="external text-color-blue" target="_blank" href="https://icon-sets.iconify.design">Iconify icon</a>, requires being online if not in cache)'), pt('icon', 'Icon', 'Use <code>oh:iconName</code> (<a class="external text-color-blue" target="_blank" href="https://www.openhab.org/link/icons">openHAB icon</a>), <code>f7:iconName</code> (<a class="external text-color-blue" target="_blank" href="https://framework7.io/icons/">Framework7 icon</a>), <code>material:iconName</code> (<a class="external text-color-blue" target="_blank" href="https://jossef.github.io/material-design-icons-iconfont/">Material icon</a>) or <code>iconify:iconSet:iconName</code> (<a class="external text-color-blue" target="_blank" href="https://icon-sets.iconify.design">Iconify icon</a>, requires being online if not in cache)'),
pt('iconColor', 'Icon Color', 'Not applicable to openHAB icons').a(), pt('iconColor', 'Icon Color', 'Not applicable to openHAB icons').a(),
pn('iconSize', 'Icon Size', 'Size of the icon in px').a(), pn('iconSize', 'Icon Size', 'Size of the icon in px').a(),
pb('iconUseState', 'Icon depends on state', 'Use the state of the item to get a dynamic icon (for openHAB icons only)').a(), pb('iconUseState', 'Icon depends on state', 'Use the state of the Item to get a dynamic icon (enabled by default for all Item types except <code>Call</code>, <code>Image</code> & <code>Location</code>) (for openHAB icons only)').a(),
pb('vertical', 'Vertical arrangement', 'Display label below icon') pb('vertical', 'Vertical arrangement', 'Display label below icon')
]) ])
.paramGroup(pg('trend', 'Trend Line', 'Show a trend line in the background'), TrendParameters()) .paramGroup(pg('trend', 'Trend Line', 'Show a trend line in the background'), TrendParameters())

View File

@ -11,7 +11,7 @@ export const ListItemParameters = () => [
pt('after', 'After', 'Text to display on the opposite side of the item (set either this or a badge)').a(), pt('after', 'After', 'Text to display on the opposite side of the item (set either this or a badge)').a(),
pt('icon', 'Icon', 'Use <code>oh:iconName</code> (<a class="external text-color-blue" target="_blank" href="https://www.openhab.org/link/icons">openHAB icon</a>), <code>f7:iconName</code> (<a class="external text-color-blue" target="_blank" href="https://framework7.io/icons/">Framework7 icon</a>), <code>material:iconName</code> (<a class="external text-color-blue" target="_blank" href="https://jossef.github.io/material-design-icons-iconfont/">Material icon</a>) or <code>iconify:iconSet:iconName</code> (<a class="external text-color-blue" target="_blank" href="https://icon-sets.iconify.design">Iconify icon</a>, requires being online if not in cache)'), pt('icon', 'Icon', 'Use <code>oh:iconName</code> (<a class="external text-color-blue" target="_blank" href="https://www.openhab.org/link/icons">openHAB icon</a>), <code>f7:iconName</code> (<a class="external text-color-blue" target="_blank" href="https://framework7.io/icons/">Framework7 icon</a>), <code>material:iconName</code> (<a class="external text-color-blue" target="_blank" href="https://jossef.github.io/material-design-icons-iconfont/">Material icon</a>) or <code>iconify:iconSet:iconName</code> (<a class="external text-color-blue" target="_blank" href="https://icon-sets.iconify.design">Iconify icon</a>, requires being online if not in cache)'),
pt('iconColor', 'Icon Color', 'Not applicable to openHAB icons').a(), pt('iconColor', 'Icon Color', 'Not applicable to openHAB icons').a(),
pb('iconUseState', 'Icon depends on state', 'Use the state of the item to get a dynamic icon (for openHAB icons only)').a() pb('iconUseState', 'Icon depends on state', 'Use the state of the Item to get a dynamic icon (enabled by default for all Item types except <code>Call</code>, <code>Image</code> & <code>Location</code>) (for openHAB icons only)').a()
] ]
// OhListItem // OhListItem

View File

@ -2,9 +2,9 @@
<div v-if="item" class="quick-link-form no-padding"> <div v-if="item" class="quick-link-form no-padding">
<f7-list inline-labels no-hairlines-md> <f7-list inline-labels no-hairlines-md>
<f7-list-input label="Name" type="text" placeholder="Required" :value="item.name" <f7-list-input label="Name" type="text" placeholder="Required" :value="item.name"
:disabled="!enableName" :info="(enableName) ? 'Note: cannot be changed after the creation' : ''" :disabled="!createMode" :info="(createMode) ? 'Note: cannot be changed after the creation' : ''"
required :error-message="nameErrorMessage" :error-message-force="!!nameErrorMessage" required :error-message="nameErrorMessage" :error-message-force="!!nameErrorMessage"
@input="onNameInput" :clear-button="enableName" /> @input="onNameInput" :clear-button="createMode" />
<f7-list-input label="Label" type="text" placeholder="Label" :value="item.label" <f7-list-input label="Label" type="text" placeholder="Label" :value="item.label"
@input="item.label = $event.target.value" clear-button /> @input="item.label = $event.target.value" clear-button />
<f7-list-item v-if="item.type && !hideType" title="Type" type="text" smart-select :smart-select-params="{searchbar: true, openIn: 'popup', closeOnSelect: true}"> <f7-list-item v-if="item.type && !hideType" title="Type" type="text" smart-select :smart-select-params="{searchbar: true, openIn: 'popup', closeOnSelect: true}">
@ -24,7 +24,7 @@
<f7-list-input v-if="!hideCategory" ref="category" label="Category" autocomplete="off" type="text" placeholder="temperature, firstfloor..." :value="item.category" <f7-list-input v-if="!hideCategory" ref="category" label="Category" autocomplete="off" type="text" placeholder="temperature, firstfloor..." :value="item.category"
@input="item.category = $event.target.value" clear-button> @input="item.category = $event.target.value" clear-button>
<div slot="root-end" style="margin-left: calc(35% + 8px)"> <div slot="root-end" style="margin-left: calc(35% + 8px)">
<oh-icon :icon="item.category" height="32" width="32" /> <oh-icon :icon="item.category" :state="(createMode) ? null : item.state" height="32" width="32" />
</div> </div>
</f7-list-input> </f7-list-input>
</f7-list> </f7-list>
@ -46,7 +46,7 @@ import * as Types from '@/assets/item-types.js'
import { Categories } from '@/assets/categories.js' import { Categories } from '@/assets/categories.js'
export default { export default {
props: ['item', 'items', 'enableName', 'hideCategory', 'hideType', 'hideSemantics', 'forceSemantics'], props: ['item', 'items', 'createMode', 'hideCategory', 'hideType', 'hideSemantics', 'forceSemantics'],
components: { components: {
SemanticsPicker SemanticsPicker
}, },
@ -91,7 +91,7 @@ export default {
mounted () { mounted () {
if (!this.item) return if (!this.item) return
if (!this.item.category) this.$set(this.item, 'category', '') if (!this.item.category) this.$set(this.item, 'category', '')
if (this.enableName) { if (this.createMode) {
if (!this.items) this.items = [] if (!this.items) this.items = []
this.validateName(this.item.name) this.validateName(this.item.name)
} }

View File

@ -8,7 +8,7 @@
:subtitle="getItemTypeAndMetaLabel(item)" :subtitle="getItemTypeAndMetaLabel(item)"
:after="state" :after="state"
v-on="$listeners"> v-on="$listeners">
<oh-icon v-if="!noIcon && item.category" slot="media" :icon="item.category" height="32" width="32" /> <oh-icon v-if="!noIcon && item.category" slot="media" :icon="item.category" :state="(noState) ? null : (context && context.store) ? context.store[item.name].state : item.state" height="32" width="32" />
<span v-else-if="!noIcon" slot="media" class="item-initial">{{ item.name[0] }}</span> <span v-else-if="!noIcon" slot="media" class="item-initial">{{ item.name[0] }}</span>
<f7-icon v-if="!item.editable && !ignoreEditable" slot="after-title" f7="lock_fill" size="1rem" color="gray" /> <f7-icon v-if="!item.editable && !ignoreEditable" slot="after-title" f7="lock_fill" size="1rem" color="gray" />
<slot name="footer" #footer /> <slot name="footer" #footer />

View File

@ -3,7 +3,7 @@
<item-state-preview v-if="model.item.created !== false" :item="model.item" :context="context" :key="$utils.id()" /> <item-state-preview v-if="model.item.created !== false" :item="model.item" :context="context" :key="$utils.id()" />
<f7-block-title>Item</f7-block-title> <f7-block-title>Item</f7-block-title>
<item-details :model="model" :links="links" :items="items" @item-updated="$emit('item-updated')" @item-created="$emit('item-created')" @item-removed="$emit('item-removed')" @cancel-create="$emit('cancel-create')" /> <item-details :model="model" :links="links" :items="items" :context="context" @item-updated="$emit('item-updated')" @item-created="$emit('item-created')" @item-removed="$emit('item-removed')" @cancel-create="$emit('cancel-create')" />
<f7-block-title v-if="model.item.created !== false"> <f7-block-title v-if="model.item.created !== false">
Metadata Metadata
</f7-block-title> </f7-block-title>

View File

@ -3,7 +3,7 @@
<f7-card-content> <f7-card-content>
<f7-list media-list accordion-list> <f7-list media-list accordion-list>
<ul> <ul>
<item v-if="!createMode" :item="model.item" :link="'/settings/items/' + model.item.name" :no-state="true" /> <item v-if="!createMode" :item="model.item" :link="'/settings/items/' + model.item.name" :context="context" />
<!-- <f7-list-button v-if="!editMode && !createMode" color="blue" title="Edit Item" @click="editMode = true">Edit Item</f7-list-button> --> <!-- <f7-list-button v-if="!editMode && !createMode" color="blue" title="Edit Item" @click="editMode = true">Edit Item</f7-list-button> -->
</ul> </ul>
</f7-list> </f7-list>
@ -12,7 +12,7 @@
<item-form :item="editedItem" :hide-type="true" :force-semantics="forceSemantics" /> <item-form :item="editedItem" :hide-type="true" :force-semantics="forceSemantics" />
</div> </div>
<div class="padding-top" v-else-if="createMode"> <div class="padding-top" v-else-if="createMode">
<item-form :item="editedItem" :items="items" :enable-name="true" :force-semantics="forceSemantics" /> <item-form :item="editedItem" :items="items" :createMode="true" :force-semantics="forceSemantics" />
</div> </div>
</f7-card-content> </f7-card-content>
<f7-card-footer v-if="createMode || editMode" key="item-card-buttons"> <f7-card-footer v-if="createMode || editMode" key="item-card-buttons">
@ -42,7 +42,7 @@ import Item from '@/components/item/item.vue'
import ItemForm from '@/components/item/item-form.vue' import ItemForm from '@/components/item/item-form.vue'
export default { export default {
props: ['model', 'links', 'items'], props: ['model', 'links', 'items', 'context'],
components: { components: {
Item, Item,
ItemForm ItemForm

View File

@ -34,7 +34,7 @@
:footer="(link.item.label) ? link.item.name : '\xa0'" :footer="(link.item.label) ? link.item.name : '\xa0'"
:subtitle="getItemTypeAndMetaLabel(link.item)" :subtitle="getItemTypeAndMetaLabel(link.item)"
:after="context.store[link.item.name] ? context.store[link.item.name].displayState || context.store[link.item.name].state : link.item.state"> :after="context.store[link.item.name] ? context.store[link.item.name].displayState || context.store[link.item.name].state : link.item.state">
<oh-icon v-if="link.item.category" slot="media" :icon="link.item.category" height="32" width="32" /> <oh-icon v-if="link.item.category" slot="media" :icon="link.item.category" :state="context.store[link.item.name] ? context.store[link.item.name].state : link.item.state" height="32" width="32" />
<span v-else slot="media" class="item-initial">{{ link.item.name[0] }}</span> <span v-else slot="media" class="item-initial">{{ link.item.name[0] }}</span>
<f7-icon v-if="!link.item.editable" slot="after-title" f7="lock_fill" size="1rem" color="gray" /> <f7-icon v-if="!link.item.editable" slot="after-title" f7="lock_fill" size="1rem" color="gray" />
<!-- <f7-button slot="after-start" color="blue" icon-f7="compose" icon-size="24px" :link="`${item.name}/edit`"></f7-button> --> <!-- <f7-button slot="after-start" color="blue" icon-f7="compose" icon-size="24px" :link="`${item.name}/edit`"></f7-button> -->

View File

@ -48,7 +48,7 @@
@channel-updated="(e) => $emit('channels-updated', e)" /> @channel-updated="(e) => $emit('channels-updated', e)" />
</template> </template>
<template #default="{ channel }" v-else-if="multipleLinksMode"> <template #default="{ channel }" v-else-if="multipleLinksMode">
<item-form v-if="isChecked(channel)" :item="newItem(channel)" :items="items" :enable-name="true" :channel="channel" :checked="isChecked(channel)" /> <item-form v-if="isChecked(channel)" :item="newItem(channel)" :items="items" :createMode="true" :channel="channel" :checked="isChecked(channel)" />
</template> </template>
<!-- <channel-link #default="{ channelId }" /> --> <!-- <channel-link #default="{ channelId }" /> -->
</channel-group> </channel-group>

View File

@ -165,8 +165,8 @@ export default function itemDefaultListComponent (item, footer) {
} }
if (!component.config.item) component.config.item = item.name if (!component.config.item) component.config.item = item.name
if (!component.config.title) component.config.title = item.label || item.name if (!component.config.title) component.config.title = item.label || item.name
if (item.category && !component.config.icon) component.config.icon = 'oh:' + item.category if (item.category && !component.config.icon) component.config.icon = item.category
if (item.category && ['Switch', 'Rollershutter', 'Contact', 'Dimmer', 'Group'].indexOf(item.type) >= 0) component.config.iconUseState = true if (item.category && component.config.iconUseState === undefined && !['Call', 'Image', 'Location'].includes(item.type)) component.config.iconUseState = true
if (item.label && footer && footer.contextLabelSource) { if (item.label && footer && footer.contextLabelSource) {
let text = itemContextLabel(item, footer) let text = itemContextLabel(item, footer)
if (text) component.config.footer = text if (text) component.config.footer = text

View File

@ -2,21 +2,16 @@
<img v-if="iconType === 'oh'" <img v-if="iconType === 'oh'"
:src="iconUrl" v-bind="config" @click="performAction()" :src="iconUrl" v-bind="config" @click="performAction()"
:style="{ :style="{
width: (context && config && config.width) ? config.width + 'px' : (width) ? width + 'px' : 'auto', width: (resolvedConfig.width !== null) ? resolvedConfig.width + 'px' : 'auto',
height: (context && config && config.height) ? config.height + 'px' : (height) ? height + 'px' : 'auto', height: (resolvedConfig.height !== null) ? resolvedConfig.height + 'px' : 'auto',
...(config) ? config.style : {} }" ...resolvedStyle }"
onload="this.classList.remove('no-icon')" onerror="this.classList.add('no-icon')"> onload="this.classList.remove('no-icon')" onerror="this.classList.add('no-icon')">
<f7-icon v-else-if="iconType === 'f7'" <f7-icon v-else-if="iconType === 'f7'" v-bind="resolvedConfig"
:ios="icon || ((config) ? config.icon : null)" :md="icon || ((config) ? config.icon : null)" :aurora="icon || ((config) ? config.icon : null)" :size="resolvedConfig.width || resolvedConfig.height || null"
:color="color || ((config) ? config.color : null)" :size="width || height || ((config) ? (config.width || config.height) : null)" :style="resolvedStyle" />
:style="(config) ? config.style : null" /> <iconify-icon v-else-if="iconType === 'iconify'" v-bind="resolvedConfig"
<iconify-icon v-else-if="iconType === 'iconify'"
:icon="iconName" :icon="iconName"
:width="width || ((config) ? config.width : null)" :height="height || ((config) ? config.height : null)" :style="resolvedStyle" />
:color="color || ((config) ? config.color : null)" :rotate="rotate || ((config) ? config.rotate : null)"
:horizontal-flip="horizontalFlip || ((config) ? config.horizontalFlip : null)"
:vertical-flip="verticalFlip || ((config) ? config.verticalFlip : null)"
:style="(config) ? config.style : null" />
</template> </template>
<style lang="stylus"> <style lang="stylus">
@ -35,7 +30,7 @@ export default {
components: { components: {
'iconify-icon': Icon 'iconify-icon': Icon
}, },
props: ['icon', 'width', 'height', 'color', 'flip', 'state', 'rotate', 'horizontalFlip', 'verticalFlip'], props: ['icon', 'width', 'height', 'color', 'state', 'rotate', 'horizontalFlip', 'verticalFlip'],
widget: OhIconDefinition, widget: OhIconDefinition,
data () { data () {
return { return {
@ -45,6 +40,24 @@ export default {
} }
}, },
computed: { computed: {
resolvedStyle () {
return {
...(this.config && this.config.style) ? this.config.style : {}
}
},
resolvedConfig () {
return {
width: (this.width) ? this.width : (this.config && this.config.width) ? this.config.width : null,
height: (this.height) ? this.height : (this.config && this.config.height) ? this.config.height : null,
color: (this.color) ? this.color : (this.config && this.config.color) ? this.config.color : null,
rotate: (this.rotate) ? this.rotate : (this.config && this.config.rotate) ? this.config.rotate : null,
horizontalFlip: (this.horizontalFlip) ? this.horizontalFlip : (this.config && this.config.horizontalFlip) ? this.config.horizontalFlip : null,
verticalFlip: (this.verticalFlip) ? this.verticalFlip : (this.config && this.config.verticalFlip) ? this.config.verticalFlip : null,
ios: (this.icon) ? this.icon : (this.config && this.config.icon) ? this.config.icon : null,
md: (this.icon) ? this.icon : (this.config && this.config.icon) ? this.config.icon : null,
aurora: (this.icon) ? this.icon : (this.config && this.config.icon) ? this.config.icon : null
}
},
iconType () { iconType () {
const icon = (this.context) ? this.config.icon : this.icon const icon = (this.context) ? this.config.icon : this.icon
if (!icon) return 'oh' if (!icon) return 'oh'
@ -53,9 +66,20 @@ export default {
if (icon.indexOf('if') === 0 || icon.indexOf('iconify') === 0) return 'iconify' if (icon.indexOf('if') === 0 || icon.indexOf('iconify') === 0) return 'iconify'
return 'oh' return 'oh'
}, },
/**
* Icon set, for openHAB icons only.
* Defaults to 'classic'.
* @returns {*|string}
*/
iconSet () {
const icon = (this.context) ? this.config.icon : this.icon
if (icon.indexOf('oh:') === 0 && icon.split(':').length === 3) return icon.split(':')[1]
return 'classic'
},
iconName () { iconName () {
const icon = (this.context) ? this.config.icon : this.icon const icon = (this.context) ? this.config.icon : this.icon
if (!(typeof icon === 'string' || icon instanceof String)) return '' if (!(typeof icon === 'string' || icon instanceof String)) return ''
if (icon.indexOf('oh:') === 0 && icon.split(':').length === 3) return icon.split(':')[2]
if (icon.indexOf(':') >= 0) return icon.substring(icon.indexOf(':') + 1) if (icon.indexOf(':') >= 0) return icon.substring(icon.indexOf(':') + 1)
return icon return icon
}, },
@ -90,7 +114,7 @@ export default {
methods: { methods: {
updateIcon () { updateIcon () {
if (!this.currentIcon) return if (!this.currentIcon) return
this.$oh.media.getIcon(this.currentIcon, this.iconFormat, this.currentState).then((url) => { this.$oh.media.getIcon(this.currentIcon, this.iconFormat, this.currentState, this.iconSet).then((url) => {
if (url !== this.iconUrl) { if (url !== this.iconUrl) {
this.iconUrl = url this.iconUrl = url
} }

View File

@ -2,10 +2,11 @@ import { getBasicCredentials } from '@/js/openhab/auth'
import Framework7 from 'framework7/framework7-lite.esm.bundle.js' import Framework7 from 'framework7/framework7-lite.esm.bundle.js'
export default { export default {
getIcon: (icon, format, state) => { getIcon: (icon, format, state, iconSet) => {
if (!format) format = 'svg' if (!format) format = 'svg'
let url = `/icon/${icon}?format=${format}&anyFormat=true` let url = `/icon/${icon}?format=${format}&anyFormat=true`
if (state) url += `&state=${encodeURIComponent(state)}` if (state) url += `&state=${encodeURIComponent(state)}`
if (iconSet) url += `&iconset=${iconSet}`
if (getBasicCredentials()) { if (getBasicCredentials()) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {

View File

@ -8,7 +8,7 @@
</f7-nav-right> </f7-nav-right>
<f7-subnavbar sliding class="item-header"> <f7-subnavbar sliding class="item-header">
<div class="item-icon" v-if="item.name"> <div class="item-icon" v-if="item.name">
<oh-icon v-if="item.category" :icon="item.category" height="60" width="60" /> <oh-icon v-if="item.category" :icon="item.category" :state="context.store[item.name] ? context.store[item.name].state : item.state" height="60" width="60" />
<span v-else> <span v-else>
{{ item.label ? item.label[0] : item.name[0] }} {{ item.label ? item.label[0] : item.name[0] }}
</span> </span>
@ -107,7 +107,7 @@
width 60px width 60px
padding 10px padding 10px
border-radius 40px border-radius 40px
background white border 1px solid white
img img
height 60px height 60px
width 60px width 60px

View File

@ -25,7 +25,7 @@
</div> </div>
</f7-col> </f7-col>
<f7-col> <f7-col>
<item-form :item="item" :items="items" :enable-name="createMode" /> <item-form :item="item" :items="items" :createMode="createMode" />
</f7-col> </f7-col>
<f7-col> <f7-col>
<f7-block-title>Group Membership</f7-block-title> <f7-block-title>Group Membership</f7-block-title>

View File

@ -1,5 +1,5 @@
<template> <template>
<f7-page @page:beforein="load" @page:afterout="stopEventSource"> <f7-page @page:afterin="onPageAfterIn" @page:beforeout="onPageBeforeOut">
<f7-navbar title="Items" back-link="Settings" back-link-url="/settings/" back-link-force> <f7-navbar title="Items" back-link="Settings" back-link-url="/settings/" back-link-force>
<f7-nav-right> <f7-nav-right>
<f7-link icon-md="material:done_all" @click="toggleCheck()" <f7-link icon-md="material:done_all" @click="toggleCheck()"
@ -52,7 +52,7 @@
</f7-col> </f7-col>
<f7-col v-show="ready"> <f7-col v-show="ready">
<f7-block-title class="searchbar-hide-on-search"> <f7-block-title class="searchbar-hide-on-search">
{{ items.length }} items {{ items.length }} Items
</f7-block-title> </f7-block-title>
<f7-list <f7-list
v-show="items.length > 0" v-show="items.length > 0"
@ -78,7 +78,8 @@
:subtitle="getItemTypeAndMetaLabel(item)" :subtitle="getItemTypeAndMetaLabel(item)"
:style="`top: ${vlData.topPosition}px`" :style="`top: ${vlData.topPosition}px`"
:after="(item.state) ? item.state : '\xa0'"> :after="(item.state) ? item.state : '\xa0'">
<oh-icon v-if="item.category" slot="media" :icon="item.category" height="32" width="32" /> <!-- Note: Using dynamic states is not possible since state tracking has a heavy performance impact -->
<oh-icon v-if="item.category" slot="media" :icon="item.category" :state="(item.state) ? item.state : null" height="32" width="32" />
<span v-else slot="media" class="item-initial">{{ item.name[0] }}</span> <span v-else slot="media" class="item-initial">{{ item.name[0] }}</span>
<f7-icon v-if="!item.editable" slot="after-title" f7="lock_fill" size="1rem" color="gray" /> <f7-icon v-if="!item.editable" slot="after-title" f7="lock_fill" size="1rem" color="gray" />
<!-- <f7-button slot="after-start" color="blue" icon-f7="compose" icon-size="24px" :link="`${item.name}/edit`"></f7-button> --> <!-- <f7-button slot="after-start" color="blue" icon-f7="compose" icon-size="24px" :link="`${item.name}/edit`"></f7-button> -->
@ -90,6 +91,9 @@
<f7-block v-if="ready && !items.length" class="service-config block-narrow"> <f7-block v-if="ready && !items.length" class="service-config block-narrow">
<empty-state-placeholder icon="square_on_circle" title="items.title" text="items.text" /> <empty-state-placeholder icon="square_on_circle" title="items.title" text="items.text" />
</f7-block> </f7-block>
<f7-fab v-show="!showCheckboxes" position="center-bottom" text="Refresh" slot="fixed" color="blue" @click="load()">
<f7-icon ios="f7:arrow_clockwise" md="material:refresh" aurora="f7:arrow_clockwise" />
</f7-fab>
<f7-fab v-show="!showCheckboxes" position="right-bottom" slot="fixed" color="blue"> <f7-fab v-show="!showCheckboxes" position="right-bottom" slot="fixed" color="blue">
<f7-icon ios="f7:plus" md="material:add" aurora="f7:plus" /> <f7-icon ios="f7:plus" md="material:add" aurora="f7:plus" />
<f7-icon ios="f7:multiply" md="material:close" aurora="f7:multiply" /> <f7-icon ios="f7:multiply" md="material:close" aurora="f7:multiply" />
@ -139,6 +143,12 @@ export default {
} }
}, },
methods: { methods: {
onPageAfterIn (event) {
this.load()
},
onPageBeforeOut (event) {
this.stopEventSource()
},
load () { load () {
this.ready = false this.ready = false
this.$oh.api.get('/rest/items?metadata=semantics').then(data => { this.$oh.api.get('/rest/items?metadata=semantics').then(data => {
@ -270,11 +280,6 @@ export default {
}) })
} }
}, },
asyncComputed: {
iconUrl () {
return icon => this.$oh.media.getIcon(icon)
}
},
computed: { computed: {
searchPlaceholder () { searchPlaceholder () {
return window.innerWidth >= 1280 ? 'Search (for advanced search, use the developer sidebar (Shift+Alt+D))' : 'Search' return window.innerWidth >= 1280 ? 'Search (for advanced search, use the developer sidebar (Shift+Alt+D))' : 'Search'

View File

@ -60,7 +60,7 @@
<div>Loading...</div> <div>Loading...</div>
</f7-block> </f7-block>
<div v-else-if="selectedThing.UID && selectedThingType.UID"> <div v-else-if="selectedThing.UID && selectedThingType.UID">
<item-form v-if="createEquipment" :item="newEquipmentItem" :items="items" :enable-name="true" :hide-type="true" :force-semantics="true" /> <item-form v-if="createEquipment" :item="newEquipmentItem" :items="items" :createMode="true" :hide-type="true" :force-semantics="true" />
<f7-block-title>Channels</f7-block-title> <f7-block-title>Channels</f7-block-title>
<f7-block-footer class="padding-left padding-right"> <f7-block-footer class="padding-left padding-right">
Check the channels you wish to create as new Point items. Check the channels you wish to create as new Point items.

View File

@ -43,7 +43,7 @@
<!-- Create new item --> <!-- Create new item -->
<f7-col v-else> <f7-col v-else>
<item-form :item="newItem" :items="items" :enable-name="true" @valid="itemValid = $event" /> <item-form :item="newItem" :items="items" :createMode="true" @valid="itemValid = $event" />
<f7-list> <f7-list>
<item-picker key="newItem-groups" title="Parent Group(s)" name="parent-groups" :value="newItem.groupNames" :items="items" @input="(value) => newItem.groupNames = value" :multiple="true" filterType="Group" /> <item-picker key="newItem-groups" title="Parent Group(s)" name="parent-groups" :value="newItem.groupNames" :items="items" @input="(value) => newItem.groupNames = value" :multiple="true" filterType="Group" />
</f7-list> </f7-list>

View File

@ -30,7 +30,7 @@
<span slot="media" class="item-initial">{{ (channel.label) ? channel.label[0] : (channelType.label) ? channelType.label[0] : '?' }}</span> <span slot="media" class="item-initial">{{ (channel.label) ? channel.label[0] : (channelType.label) ? channelType.label[0] : '?' }}</span>
</f7-list-item> </f7-list-item>
<f7-list-item divider title="Item" /> <f7-list-item divider title="Item" />
<item :item="item" :context="context" :no-state="true" :link="'/settings/items/' + item.name" /> <item :item="item" :context="context" :link="'/settings/items/' + item.name" />
</ul> </ul>
</f7-list> </f7-list>
</f7-card-content> </f7-card-content>