Skip to content

Commit 19178a1

Browse files
authored
Standard list item widgets; default widget logic for list display (openhab#270)
Add list items widgets: - oh-label-item - oh-toggle-item - oh-slider-item - oh-rollershutter-item - oh-player-item - oh-stepper-item - oh-colorpicker-item Most config options are forwarded to the system widget displayed on the "after" slot, which may also be defined in the widgets' definitions. Move default standalone widget logic to dedicated JS file. Default list widget logic. Change the label of the standard library's card widgets. Add standard list widgets to listWidget metadata editor picker; add preview sheet. Misc fixes in global app styles, analyzer. Let the user choose the list item widget to add in an oh-list. "Add from Model..." option in new widget action sheets. Add a model treeview popup to add default widgets (standalone or list) easily to a layout (block column or masonry) or a list, simply by browsing the semantic model and picking items. Other renames and file structure reorganizations. Add a new action to show group members in a popup, using their default list representation. Make it the default action for Group items, for both standalone and list widgets. Make semantic cards on the home page functional: Use the default list widgets to render items in the card. (order not considered for now, but visible and visibleTo configured in the listWidget metadata might work to filter out items based on an expression/the current user's roles?) Remove warning messages. Make cards (and headers) a little bit thinner. Signed-off-by: Yannick Schaus <[email protected]>
1 parent b0bdefe commit 19178a1

39 files changed

+1322
-252
lines changed

bundles/org.openhab.ui/web/src/components/cards/equipments-card.vue

+38-32
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
<template>
2-
<f7-card expandable :animate="$f7.data.themeOptions.expandableCardAnimation !== 'disabled'" card-tablet-fullscreen v-on:card:opened="cardOpening" v-on:card:closed="cardClosed">
2+
<f7-card expandable class="equipments-card" :animate="$f7.data.themeOptions.expandableCardAnimation !== 'disabled'" card-tablet-fullscreen v-on:card:opened="cardOpening" v-on:card:closed="cardClosed">
33
<f7-card-content :padding="false">
4-
<div :class="`bg-color-${color}`" :style="{height: '300px'}">
4+
<div :class="`bg-color-${color}`" :style="{height: '150px'}">
55
<f7-card-header text-color="white" class="display-block">
66
{{title || 'Something'}}
77
<div><small v-if="subtitle">{{subtitle}}</small></div>
88
<br>
9-
<h1>State</h1>
9+
<!-- <h1>State</h1> -->
1010
</f7-card-header>
1111
<f7-link
1212
card-close
@@ -16,28 +16,23 @@
1616
icon-f7="multiply_circle_fill"
1717
></f7-link>
1818
</div>
19-
<div class="card-content-padding" v-if="opened">
20-
<f7-list>
21-
<ul>
22-
<sitemap-widget-generic v-for="model in standaloneEquipments" :key="model.item"
23-
:model="model" />
24-
</ul>
25-
<ul v-for="equipment in equipmentsWithPoints" :key="equipment.item">
26-
<f7-list-item divider :title="equipment.title"></f7-list-item>
27-
<sitemap-widget-generic v-for="model in equipment.points" :key="model.item"
28-
:model="model" />
29-
</ul>
30-
</f7-list>
19+
<div v-if="opened">
20+
<generic-widget-component :context="listContext" />
3121
<p>
32-
<f7-button fill round large card-close :color="color">Close</f7-button>
22+
<f7-button fill round large card-close :color="color" class="margin-horizontal">Close</f7-button>
3323
</p>
3424
</div>
3525
</f7-card-content>
3626
</f7-card>
3727
</template>
3828

29+
<style lang="stylus">
30+
.equipments-card
31+
height 150px
32+
</style>
33+
3934
<script>
40-
import item2SitemapModel from './item2SitemapModel.js'
35+
import itemDefaultListComponent from '@/components/widgets/standard/list/default-list-item'
4136
4237
export default {
4338
props: ['color', 'type', 'header', 'title', 'subtitle', 'items'],
@@ -50,29 +45,40 @@ export default {
5045
cardOpening () {
5146
console.log('card opened')
5247
setTimeout(() => { this.opened = true })
53-
this.$f7.toast.create({
54-
text: 'The semantic cards rendering is currently for demonstration purposes only. It is not functional nor updates in real time. Please use another app like Basic UI or HABPanel to interact with your items.',
55-
closeButton: true,
56-
destroyOnClose: true
57-
}).open()
5848
},
5949
cardClosed () {
6050
console.log('card closed')
6151
this.opened = false
6252
}
6353
},
6454
computed: {
65-
standaloneEquipments () {
66-
return this.items.filter((i) => i.points.length === 0).map((i) => item2SitemapModel(i.item))
67-
},
68-
equipmentsWithPoints () {
69-
return this.items.filter((i) => i.points.length !== 0).map((i) => {
70-
return {
71-
item: i.item.name,
72-
title: (i.item.label || i.item.name),
73-
points: i.points.map(item2SitemapModel)
74-
}
55+
listContext () {
56+
const standaloneEquipments = this.items.filter((i) => i.points.length === 0).map((i) => itemDefaultListComponent(i.item, true))
57+
const equipmentsWithPoints = this.items.filter((i) => i.points.length !== 0).map((i) => {
58+
return [
59+
{
60+
component: 'oh-list-item',
61+
config: {
62+
title: i.item.label || i.item.name,
63+
divider: true
64+
}
65+
},
66+
...i.points.map((p) => itemDefaultListComponent(p))
67+
]
7568
})
69+
70+
return {
71+
store: this.$store.getters.trackedItems,
72+
component: {
73+
component: 'oh-list',
74+
config: {
75+
mediaList: true
76+
},
77+
slots: {
78+
default: [...standaloneEquipments, ...equipmentsWithPoints].flat()
79+
}
80+
}
81+
}
7682
}
7783
}
7884
}

bundles/org.openhab.ui/web/src/components/cards/location-card.vue

+48-42
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
<template>
2-
<f7-card expandable :animate="$f7.data.themeOptions.expandableCardAnimation !== 'disabled'" card-tablet-fullscreen v-on:card:opened="cardOpening" v-on:card:closed="cardClosed">
2+
<f7-card expandable class="location-card" :animate="$f7.data.themeOptions.expandableCardAnimation !== 'disabled'" card-tablet-fullscreen v-on:card:opened="cardOpening" v-on:card:closed="cardClosed">
33
<f7-card-content :padding="false">
4-
<div :class="`bg-color-${color}`" :style="{height: '300px'}">
4+
<div :class="`bg-color-${color}`" :style="{height: '200px'}">
55
<f7-card-header text-color="white" class="display-block">
66
{{title || 'Something'}}
77
<div><small>{{subtitle || '&nbsp;'}}</small></div>
8-
<br>
98
<div class="location-stats" v-if="items.equipments.length > 0"><small>{{items.equipments.length}} equipment{{items.equipments.length === 1 ? '' : 's'}}</small></div>
109
<div class="location-stats" v-if="items.properties.length > 0"><small>{{items.properties.length}} propert{{items.properties.length === 1 ? 'y' : 'ies'}}</small></div>
1110
</f7-card-header>
@@ -23,41 +22,26 @@
2322
<f7-button round outline :active="activeTab === 'properties'" :color="color" @click="activeTab = 'properties'">Properties</f7-button>
2423
</f7-segmented>
2524
</div>
26-
<div class="card-content-padding" v-if="opened">
27-
<f7-list v-if="activeTab === 'equipments'">
28-
<ul>
29-
<sitemap-widget-generic v-for="model in standaloneEquipments" :key="model.item"
30-
:model="model" />
31-
</ul>
32-
<ul v-for="equipment in equipmentsWithPoints" :key="equipment.item">
33-
<f7-list-item divider :title="equipment.title"></f7-list-item>
34-
<sitemap-widget-generic v-for="model in equipment.points" :key="model.item"
35-
:model="model" />
36-
</ul>
37-
</f7-list>
38-
<f7-list v-if="activeTab === 'properties' || items.equipments.length === 0">
39-
<ul>
40-
<sitemap-widget-generic v-for="model in properties" :key="model.item"
41-
:model="model" />
42-
</ul>
43-
</f7-list>
44-
</div>
45-
<div class="card-content-padding" v-if="opened">
25+
<div v-if="opened">
26+
<generic-widget-component v-if="activeTab === 'equipments'" :context="equipmentsListContext" />
27+
<generic-widget-component v-if="activeTab === 'properties'" :context="propertiesListContext" />
4628
<p>
47-
<f7-button fill round large card-close :color="color">Close</f7-button>
29+
<f7-button fill round large card-close :color="color" class="margin-horizontal">Close</f7-button>
4830
</p>
4931
</div>
5032
</f7-card-content>
5133
</f7-card>
5234
</template>
5335

5436
<style lang="stylus">
37+
.location-card
38+
height 200px
5539
.location-stats
5640
font-weight normal
5741
</style>
5842

5943
<script>
60-
import item2SitemapModel from './item2SitemapModel.js'
44+
import itemDefaultListComponent from '@/components/widgets/standard/list/default-list-item'
6145
6246
export default {
6347
props: ['color', 'type', 'header', 'title', 'subtitle', 'items'],
@@ -71,32 +55,54 @@ export default {
7155
cardOpening () {
7256
console.log('card opened')
7357
setTimeout(() => { this.opened = true })
74-
this.$f7.toast.create({
75-
text: 'The semantic cards rendering is currently for demonstration purposes only. It is not functional nor updates in real time. Please use another app like Basic UI or HABPanel to interact with your items.',
76-
closeButton: true,
77-
destroyOnClose: true
78-
}).open()
7958
},
8059
cardClosed () {
8160
console.log('card closed')
8261
this.opened = false
8362
}
8463
},
8564
computed: {
86-
properties () {
87-
return this.items.properties.map(item2SitemapModel)
88-
},
89-
standaloneEquipments () {
90-
return this.items.equipments.filter((i) => i.points.length === 0).map((i) => item2SitemapModel(i.item))
91-
},
92-
equipmentsWithPoints () {
93-
return this.items.equipments.filter((i) => i.points.length !== 0).map((i) => {
94-
return {
95-
item: i.item.name,
96-
title: (i.item.label || i.item.name),
97-
points: i.points.map(item2SitemapModel)
65+
propertiesListContext () {
66+
return {
67+
store: this.$store.getters.trackedItems,
68+
component: {
69+
component: 'oh-list',
70+
config: {
71+
mediaList: true
72+
},
73+
slots: {
74+
default: this.items.properties.map(itemDefaultListComponent)
75+
}
9876
}
77+
}
78+
},
79+
equipmentsListContext () {
80+
const standaloneEquipments = this.items.equipments.filter((i) => i.points.length === 0).map((i) => itemDefaultListComponent(i.item))
81+
const equipmentsWithPoints = this.items.equipments.filter((i) => i.points.length !== 0).map((i) => {
82+
return [
83+
{
84+
component: 'oh-list-item',
85+
config: {
86+
title: i.item.label || i.item.name,
87+
divider: true
88+
}
89+
},
90+
...i.points.map((p) => itemDefaultListComponent(p))
91+
]
9992
})
93+
94+
return {
95+
store: this.$store.getters.trackedItems,
96+
component: {
97+
component: 'oh-list',
98+
config: {
99+
mediaList: true
100+
},
101+
slots: {
102+
default: [...standaloneEquipments, ...equipmentsWithPoints].flat()
103+
}
104+
}
105+
}
100106
}
101107
}
102108
}

bundles/org.openhab.ui/web/src/components/cards/property-card.vue

+45-40
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
<template>
2-
<f7-card expandable :animate="$f7.data.themeOptions.expandableCardAnimation !== 'disabled'" card-tablet-fullscreen v-on:card:opened="cardOpening" v-on:card:closed="cardClosed">
2+
<f7-card expandable class="property-card" :animate="$f7.data.themeOptions.expandableCardAnimation !== 'disabled'" card-tablet-fullscreen v-on:card:opened="cardOpening" v-on:card:closed="cardClosed">
33
<f7-card-content :padding="false">
4-
<div :class="`bg-color-${color}`" :style="{height: '300px'}">
4+
<div :class="`bg-color-${color}`" :style="{height: '150px'}">
55
<f7-card-header text-color="white" class="display-block">
66
{{title || 'Something'}}
77
<div><small v-if="subtitle">{{subtitle}}</small></div>
88
<br>
9-
<h1>State</h1>
9+
<!-- <h1>State</h1> -->
1010
</f7-card-header>
1111
<f7-link
1212
card-close
@@ -16,41 +16,26 @@
1616
icon-f7="multiply_circle_fill"
1717
></f7-link>
1818
</div>
19-
<!-- <div class="card-content-padding" v-if="opened">
20-
<f7-block>
21-
<div class="row padding-bottom">
22-
<div class="col-50 tablet-75"></div>
23-
<div class="col-50 tablet-25">
24-
<f7-button outline round :color="color" :href="`/analyzer/?items=${sitemapModels.map((m) => m.item).join(',')}`">Analyze{{sitemapModels.length > 1 ? ' all' : ''}}</f7-button>
25-
</div>
26-
</div>
27-
</f7-block>
28-
</div> -->
2919
<div class="card-content-padding" v-if="opened">
30-
<f7-list>
31-
<!-- <ul>
32-
<sitemap-widget-generic v-for="model in sitemapModels" :key="model.item"
33-
:model="model" />
34-
</ul> -->
35-
<ul v-for="(models, pointType) in sitemapModelsByPointType" :key="pointType">
36-
<f7-list-item divider :title="pointType"></f7-list-item>
37-
<sitemap-widget-generic v-for="model in models" :key="model.item"
38-
:model="model" />
39-
</ul>
40-
</f7-list>
41-
<p class="padding-top">
42-
<f7-button outline round :color="color" :href="`/analyzer/?items=${sitemapModels.map((m) => m.item).join(',')}`">Analyze{{sitemapModels.length > 1 ? ' all' : ''}}</f7-button>
20+
<generic-widget-component :context="listContext" />
21+
<p class="padding-top margin-horizontal">
22+
<f7-button outline round :color="color" :href="`/analyzer/?items=${items.map((m) => m.name).join(',')}`">Analyze{{items.length > 1 ? ' all' : ''}}</f7-button>
4323
</p>
44-
<p>
24+
<p class="margin-horizontal">
4525
<f7-button fill round large card-close :color="color">Close</f7-button>
4626
</p>
4727
</div>
4828
</f7-card-content>
4929
</f7-card>
5030
</template>
5131

32+
<style lang="stylus">
33+
.property-card
34+
height 150px
35+
</style>
36+
5237
<script>
53-
import item2SitemapModel from './item2SitemapModel.js'
38+
import itemDefaultListComponent from '@/components/widgets/standard/list/default-list-item'
5439
5540
export default {
5641
props: ['color', 'type', 'header', 'title', 'subtitle', 'items'],
@@ -63,29 +48,49 @@ export default {
6348
cardOpening () {
6449
console.log('card opened')
6550
setTimeout(() => { this.opened = true })
66-
this.$f7.toast.create({
67-
text: 'The semantic cards rendering is currently for demonstration purposes only. It is not functional nor updates in real time. Please use another app like Basic UI or HABPanel to interact with your items.',
68-
closeButton: true,
69-
destroyOnClose: true
70-
}).open()
7151
},
7252
cardClosed () {
7353
console.log('card closed')
7454
this.opened = false
7555
}
7656
},
7757
computed: {
78-
sitemapModels () {
79-
return this.items.map(item2SitemapModel)
58+
listContext () {
59+
let pointsByType = []
60+
for (let pointType in this.itemsByPointType) {
61+
pointsByType.push([
62+
{
63+
component: 'oh-list-item',
64+
config: {
65+
title: pointType,
66+
divider: true
67+
}
68+
},
69+
...this.itemsByPointType[pointType].map((p) => itemDefaultListComponent(p, true))
70+
])
71+
}
72+
73+
return {
74+
store: this.$store.getters.trackedItems,
75+
component: {
76+
component: 'oh-list',
77+
config: {
78+
mediaList: true
79+
},
80+
slots: {
81+
default: pointsByType.flat()
82+
}
83+
}
84+
}
8085
},
81-
sitemapModelsByPointType () {
82-
const models = {}
86+
itemsByPointType () {
87+
const points = {}
8388
this.items.forEach((item) => {
8489
const pointType = item.metadata.semantics.value.replace('Point_', '')
85-
if (!models[pointType]) models[pointType] = []
86-
models[pointType].push(item2SitemapModel(item))
90+
if (!points[pointType]) points[pointType] = []
91+
points[pointType].push(item)
8792
})
88-
return models
93+
return points
8994
}
9095
}
9196
}

0 commit comments

Comments
 (0)