Add theme-choosing UI to config panel (#363)

* Add theme-choosing UI to config panel

* Add Backend-selected option

* Use Object.assign to change hass

* Fix #368 - support theme-color update.
This commit is contained in:
Andrey 2017-08-05 09:09:25 +03:00 committed by Paulus Schoutsen
parent 1ea7137c78
commit ebc0c776e2
5 changed files with 145 additions and 26 deletions

View File

@ -8,6 +8,7 @@
<!-- <link rel="import" href="./group/ha-config-section-group.html"> --> <!-- <link rel="import" href="./group/ha-config-section-group.html"> -->
<link rel="import" href="./hassbian/ha-config-section-hassbian.html"> <link rel="import" href="./hassbian/ha-config-section-hassbian.html">
<link rel="import" href="./z-wave/ha-config-section-zwave.html"> <link rel="import" href="./z-wave/ha-config-section-zwave.html">
<link rel="import" href="themes/ha-config-section-themes.html">
<dom-module id="ha-panel-config"> <dom-module id="ha-panel-config">
<template> <template>
@ -53,6 +54,14 @@
hass='[[hass]]' hass='[[hass]]'
></ha-config-section-core> ></ha-config-section-core>
<template is='dom-if' if='[[computeIsThemesLoaded(hass)]]'>
<div class='border'></div>
<ha-config-section-themes
is-wide='[[isWide]]'
hass='[[hass]]'
></ha-config-section-themes>
</template>
<template is='dom-if' if='[[computeIsHassbianLoaded(hass)]]'> <template is='dom-if' if='[[computeIsHassbianLoaded(hass)]]'>
<div class='border'></div> <div class='border'></div>
<ha-config-section-hassbian <ha-config-section-hassbian
@ -120,5 +129,10 @@ Polymer({
computeIsZwaveLoaded: function (hass) { computeIsZwaveLoaded: function (hass) {
return window.hassUtil.isComponentLoaded(hass, 'config.zwave'); return window.hassUtil.isComponentLoaded(hass, 'config.zwave');
}, },
computeIsThemesLoaded: function (hass) {
return hass.themes && hass.themes.themes &&
Object.keys(hass.themes.themes).length;
},
}); });
</script> </script>

View File

@ -0,0 +1,85 @@
<link rel="import" href="../../../bower_components/polymer/polymer.html">
<link rel="import" href="../../../bower_components/paper-card/paper-card.html">
<link rel="import" href="../../../bower_components/paper-dropdown-menu/paper-dropdown-menu.html">
<link rel='import' href='../../../bower_components/paper-listbox/paper-listbox.html'>
<link rel='import' href='../../../bower_components/paper-item/paper-item.html'>
<link rel="import" href="../ha-config-section.html">
<dom-module id="ha-config-section-themes">
<template>
<ha-config-section is-wide='[[isWide]]'>
<span slot='header'>Set a theme</span>
<span slot='introduction'>
Choose 'default' to use whatever theme the backend chooses or pick a theme for this device.
</span>
<paper-card>
<div class='card-content'>
<paper-dropdown-menu label='Theme' vertical-align='bottom'>
<paper-listbox
slot="dropdown-content"
selected='{{selectedTheme}}'
>
<template is='dom-repeat' items='[[themes]]' as='theme'>
<paper-item>[[theme]]</paper-item>
</template>
</paper-listbox>
</paper-dropdown-menu>
</div>
</paper-card>
</ha-config-section>
</template>
</dom-module>
<script>
Polymer({
is: 'ha-config-section-themes',
properties: {
hass: {
type: Object,
},
isWide: {
type: Boolean,
},
themes: {
type: Array,
computed: 'computeThemes(hass)',
},
selectedTheme: {
type: Number,
},
},
ready: function () {
if (this.hass.selectedTheme && this.themes.indexOf(this.hass.selectedTheme) > 0) {
this.selectedTheme = this.themes.indexOf(this.hass.selectedTheme);
} else if (!this.hass.selectedTheme) {
this.selectedTheme = 0;
}
},
observers: [
'selectionChanged(hass, selectedTheme)',
],
computeThemes: function (hass) {
if (!hass) return [];
return ['Backend-selected', 'default'].concat(Object.keys(hass.themes.themes).sort());
},
selectionChanged: function (hass, selection) {
if (selection > 0 && selection < this.themes.length) {
if (hass.selectedTheme !== this.themes[selection]) {
this.fire('settheme', this.themes[selection]);
}
} else if (selection === 0 && hass.selectedTheme !== '') {
this.fire('settheme', '');
}
}
});
</script>

View File

@ -85,6 +85,10 @@ Polymer({
}, },
}, },
listeners: {
settheme: 'setTheme',
},
computeShowMain: function (hass, iconsLoaded) { computeShowMain: function (hass, iconsLoaded) {
return hass && hass.states && hass.config && iconsLoaded; return hass && hass.states && hass.config && iconsLoaded;
}, },
@ -158,13 +162,13 @@ Polymer({
}, this.$.storage.getStoredState()); }, this.$.storage.getStoredState());
var reconnected = () => { var reconnected = () => {
this.hass = Object.assign({}, this.hass, { connected: true }); this._updateHass({ connected: true });
}; };
conn.addEventListener('ready', reconnected); conn.addEventListener('ready', reconnected);
var disconnected = () => { var disconnected = () => {
this.hass = Object.assign({}, this.hass, { connected: false }); this._updateHass({ connected: false });
}; };
conn.addEventListener('disconnected', disconnected); conn.addEventListener('disconnected', disconnected);
@ -172,7 +176,7 @@ Polymer({
var unsubEntities; var unsubEntities;
window.HAWS.subscribeEntities(conn, (states) => { window.HAWS.subscribeEntities(conn, (states) => {
this.hass = Object.assign({}, this.hass, { states: states }); this._updateHass({ states: states });
}).then(function (unsub) { }).then(function (unsub) {
unsubEntities = unsub; unsubEntities = unsub;
}); });
@ -180,7 +184,7 @@ Polymer({
var unsubConfig; var unsubConfig;
window.HAWS.subscribeConfig(conn, (config) => { window.HAWS.subscribeConfig(conn, (config) => {
this.hass = Object.assign({}, this.hass, { config: config }); this._updateHass({ config: config });
}).then(function (unsub) { }).then(function (unsub) {
unsubConfig = unsub; unsubConfig = unsub;
}); });
@ -188,12 +192,12 @@ Polymer({
var unsubThemes; var unsubThemes;
this.hass.callApi('get', 'themes').then((themes) => { this.hass.callApi('get', 'themes').then((themes) => {
this.hass.themes = themes; this._updateHass({ themes: themes });
window.hassUtil.applyThemesOnElement(this, themes); window.hassUtil.applyThemesOnElement(this, themes, this.hass.selectedTheme, true);
}); });
conn.subscribeEvents((event) => { conn.subscribeEvents((event) => {
this.hass.themes = event.data; this._updateHass({ themes: event.data });
window.hassUtil.applyThemesOnElement(this, event.data); window.hassUtil.applyThemesOnElement(this, event.data, this.hass.selectedTheme, true);
}, 'themes_updated').then(function (unsub) { }, 'themes_updated').then(function (unsub) {
unsubThemes = unsub; unsubThemes = unsub;
}); });
@ -220,16 +224,12 @@ Polymer({
handleMoreInfo: function (ev) { handleMoreInfo: function (ev) {
ev.stopPropagation(); ev.stopPropagation();
this.hass = Object.assign( this._updateHass({ moreInfoEntityId: ev.detail.entityId });
{}, this.hass,
{ moreInfoEntityId: ev.detail.entityId });
}, },
handleDockSidebar: function (ev) { handleDockSidebar: function (ev) {
ev.stopPropagation(); ev.stopPropagation();
this.hass = Object.assign( this._updateHass({ dockedSidebar: ev.detail.dock });
{}, this.hass,
{ dockedSidebar: ev.detail.dock });
this.$.storage.storeState(); this.$.storage.storeState();
}, },
@ -253,5 +253,15 @@ Polymer({
ready: function () { ready: function () {
this.loadIcons(); this.loadIcons();
}, },
setTheme: function (event) {
this._updateHass({ selectedTheme: event.detail });
window.hassUtil.applyThemesOnElement(this, this.hass.themes, this.hass.selectedTheme, true);
this.$.storage.storeState();
},
_updateHass: function (obj) {
this.hass = Object.assign({}, this.hass, obj);
},
}); });
</script> </script>

View File

@ -2,6 +2,7 @@
(function () { (function () {
var STORED_STATE = [ var STORED_STATE = [
'dockedSidebar', 'dockedSidebar',
'selectedTheme',
]; ];
Polymer({ Polymer({

View File

@ -492,25 +492,34 @@ window.hassUtil.computeLocationName = function (hass) {
return hass && hass.config.core.location_name; return hass && hass.config.core.location_name;
}; };
window.hassUtil.applyThemesOnElement = function (element, themes, localTheme) { window.hassUtil.applyThemesOnElement = function (element, themes, localTheme, updateMeta) {
if (!element._themes) { if (!element._themes) {
element._themes = {}; element._themes = {};
} }
var themeName = themes.default_theme; let themeName = themes.default_theme;
if (localTheme === 'default' || (localTheme && themes.themes[localTheme])) { if (localTheme === 'default' || (localTheme && themes.themes[localTheme])) {
themeName = localTheme; themeName = localTheme;
} }
if (themeName === 'default') { const styles = Object.assign({}, element._themes);
element.updateStyles(element._themes); if (themeName !== 'default') {
return; var theme = themes.themes[themeName];
Object.keys(theme).forEach((key) => {
var prefixedKey = '--' + key;
element._themes[prefixedKey] = '';
styles[prefixedKey] = theme[key];
});
} }
var theme = themes.themes[themeName];
var styles = Object.assign({}, element._themes);
Object.keys(theme).forEach(function (key) {
var prefixedKey = '--' + key;
element._themes[prefixedKey] = '';
styles[prefixedKey] = theme[key];
});
element.updateStyles(styles); element.updateStyles(styles);
if (!updateMeta) return;
const meta = document.querySelector('meta[name=theme-color]');
if (meta) {
if (!meta.hasAttribute('default-content')) {
meta.setAttribute('default-content', meta.getAttribute('content'));
}
const themeColor = styles['--primary-color'] || meta.getAttribute('default-content');
meta.setAttribute('content', themeColor);
}
}; };
</script> </script>