Format html (#2006)

* Upgrade prettier

* Format files with prettier
This commit is contained in:
Paulus Schoutsen 2018-11-07 09:56:43 +01:00 committed by GitHub
parent 7c2135f444
commit a58a324073
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
268 changed files with 10821 additions and 8543 deletions

View File

@ -38,9 +38,9 @@ class DemoCard extends PolymerElement {
} }
</style> </style>
<h2>[[config.heading]]</h2> <h2>[[config.heading]]</h2>
<div class='root'> <div class="root">
<div id="card"></div> <div id="card"></div>
<template is='dom-if' if='[[showConfig]]'> <template is="dom-if" if="[[showConfig]]">
<pre>[[_trim(config.config)]]</pre> <pre>[[_trim(config.config)]]</pre>
</template> </template>
</div> </div>

View File

@ -25,18 +25,18 @@ class DemoCards extends PolymerElement {
} }
</style> </style>
<app-toolbar> <app-toolbar>
<div class='filters'> <div class="filters">
<paper-toggle-button <paper-toggle-button checked="{{_showConfig}}"
checked='{{_showConfig}}' >Show config</paper-toggle-button
>Show config</paper-toggle-button> >
</div> </div>
</app-toolbar> </app-toolbar>
<div class='cards'> <div class="cards">
<template is='dom-repeat' items='[[configs]]'> <template is="dom-repeat" items="[[configs]]">
<demo-card <demo-card
config='[[item]]' config="[[item]]"
show-config='[[_showConfig]]' show-config="[[_showConfig]]"
hass='[[hass]]' hass="[[hass]]"
></demo-card> ></demo-card>
</template> </template>
</div> </div>

View File

@ -8,56 +8,56 @@ import "../../../src/components/ha-card";
class DemoMoreInfo extends PolymerElement { class DemoMoreInfo extends PolymerElement {
static get template() { static get template() {
return html` return html`
<style> <style>
:host {
display: flex;
align-items: start;
}
ha-card {
width: 333px;
}
state-card-content {
display: block;
padding: 16px;
}
more-info-content {
padding: 0 16px;
}
pre {
width: 400px;
margin: 16px;
overflow: auto;
}
@media only screen and (max-width: 800px) {
:host { :host {
display: flex; flex-direction: column;
align-items: start;
} }
ha-card {
width: 333px;
}
state-card-content {
display: block;
padding: 16px;
}
more-info-content {
padding: 0 16px;
}
pre { pre {
width: 400px; margin-left: 0;
margin: 16px;
overflow: auto;
} }
}
</style>
<ha-card>
<state-card-content
state-obj="[[_stateObj]]"
hass="[[hass]]"
in-dialog
></state-card-content>
@media only screen and (max-width: 800px) { <more-info-content
:host { hass="[[hass]]"
flex-direction: column; state-obj="[[_stateObj]]"
} ></more-info-content>
pre { </ha-card>
margin-left: 0; <template is="dom-if" if="[[showConfig]]">
} <pre>[[_jsonEntity(_stateObj)]]</pre>
} </template>
</style> `;
<ha-card>
<state-card-content
state-obj="[[_stateObj]]"
hass="[[hass]]"
in-dialog
></state-card-content>
<more-info-content
hass='[[hass]]'
state-obj='[[_stateObj]]'
></more-info-content>
</ha-card>
<template is='dom-if' if='[[showConfig]]'>
<pre>[[_jsonEntity(_stateObj)]]</pre>
</template>
`;
} }
static get properties() { static get properties() {

View File

@ -25,18 +25,18 @@ class DemoMoreInfos extends PolymerElement {
} }
</style> </style>
<app-toolbar> <app-toolbar>
<div class='filters'> <div class="filters">
<paper-toggle-button <paper-toggle-button checked="{{_showConfig}}"
checked='{{_showConfig}}' >Show entity</paper-toggle-button
>Show entity</paper-toggle-button> >
</div> </div>
</app-toolbar> </app-toolbar>
<div class='cards'> <div class="cards">
<template is='dom-repeat' items='[[entities]]'> <template is="dom-repeat" items="[[entities]]">
<demo-more-info <demo-more-info
entity-id='[[item]]' entity-id="[[item]]"
show-config='[[_showConfig]]' show-config="[[_showConfig]]"
hass='[[hass]]' hass="[[hass]]"
></demo-more-info> ></demo-more-info>
</template> </template>
</div> </div>

View File

@ -51,7 +51,11 @@ const CONFIGS = [
class DemoAlarmPanelEntity extends PolymerElement { class DemoAlarmPanelEntity extends PolymerElement {
static get template() { static get template() {
return html` return html`
<demo-cards id='demos' hass='[[hass]]' configs="[[_configs]]"></demo-cards> <demo-cards
id="demos"
hass="[[hass]]"
configs="[[_configs]]"
></demo-cards>
`; `;
} }

View File

@ -57,8 +57,8 @@ class DemoConditional extends PolymerElement {
static get template() { static get template() {
return html` return html`
<demo-cards <demo-cards
id='demos' id="demos"
hass='[[hass]]' hass="[[hass]]"
configs="[[_configs]]" configs="[[_configs]]"
></demo-cards> ></demo-cards>
`; `;

View File

@ -175,10 +175,7 @@ const CONFIGS = [
class DemoEntities extends PolymerElement { class DemoEntities extends PolymerElement {
static get template() { static get template() {
return html` return html`
<demo-cards <demo-cards id="demos" configs="[[_configs]]"></demo-cards>
id='demos'
configs="[[_configs]]"
></demo-cards>
`; `;
} }

View File

@ -71,7 +71,11 @@ const CONFIGS = [
class DemoEntityButtonEntity extends PolymerElement { class DemoEntityButtonEntity extends PolymerElement {
static get template() { static get template() {
return html` return html`
<demo-cards id='demos' hass='[[hass]]' configs="[[_configs]]"></demo-cards> <demo-cards
id="demos"
hass="[[hass]]"
configs="[[_configs]]"
></demo-cards>
`; `;
} }

View File

@ -92,10 +92,7 @@ const CONFIGS = [
class DemoFilter extends PolymerElement { class DemoFilter extends PolymerElement {
static get template() { static get template() {
return html` return html`
<demo-cards <demo-cards id="demos" configs="[[_configs]]"></demo-cards>
id='demos'
configs="[[_configs]]"
></demo-cards>
`; `;
} }

View File

@ -217,10 +217,7 @@ const CONFIGS = [
class DemoPicEntity extends PolymerElement { class DemoPicEntity extends PolymerElement {
static get template() { static get template() {
return html` return html`
<demo-cards <demo-cards id="demos" configs="[[_configs]]"></demo-cards>
id='demos'
configs="[[_configs]]"
></demo-cards>
`; `;
} }

View File

@ -25,7 +25,7 @@ const CONFIGS = [
class DemoLightEntity extends PolymerElement { class DemoLightEntity extends PolymerElement {
static get template() { static get template() {
return html` return html`
<demo-cards id='demos' configs="[[_configs]]"></demo-cards> <demo-cards id="demos" configs="[[_configs]]"></demo-cards>
`; `;
} }

View File

@ -78,8 +78,8 @@ class DemoHuiMediaPlayerRows extends PolymerElement {
static get template() { static get template() {
return html` return html`
<demo-cards <demo-cards
id='demos' id="demos"
hass='[[hass]]' hass="[[hass]]"
configs="[[_configs]]" configs="[[_configs]]"
></demo-cards> ></demo-cards>
`; `;

View File

@ -80,10 +80,7 @@ const CONFIGS = [
class DemoPicElements extends PolymerElement { class DemoPicElements extends PolymerElement {
static get template() { static get template() {
return html` return html`
<demo-cards <demo-cards id="demos" configs="[[_configs]]"></demo-cards>
id='demos'
configs="[[_configs]]"
></demo-cards>
`; `;
} }

View File

@ -23,10 +23,7 @@ const CONFIGS = [
class DemoShoppingListEntity extends PolymerElement { class DemoShoppingListEntity extends PolymerElement {
static get template() { static get template() {
return html` return html`
<demo-cards <demo-cards id="demos" configs="[[_configs]]"></demo-cards>
id='demos'
configs="[[_configs]]"
></demo-cards>
`; `;
} }

View File

@ -91,10 +91,7 @@ const CONFIGS = [
class DemoStack extends PolymerElement { class DemoStack extends PolymerElement {
static get template() { static get template() {
return html` return html`
<demo-cards <demo-cards id="demos" configs="[[_configs]]"></demo-cards>
id='demos'
configs="[[_configs]]"
></demo-cards>
`; `;
} }

View File

@ -62,10 +62,7 @@ const CONFIGS = [
class DemoThermostatEntity extends PolymerElement { class DemoThermostatEntity extends PolymerElement {
static get template() { static get template() {
return html` return html`
<demo-cards <demo-cards id="demos" configs="[[_configs]]"></demo-cards>
id='demos'
configs="[[_configs]]"
></demo-cards>
`; `;
} }

View File

@ -34,8 +34,8 @@ class DemoMoreInfoLight extends PolymerElement {
static get template() { static get template() {
return html` return html`
<demo-more-infos <demo-more-infos
hass='[[hass]]' hass="[[hass]]"
entities='[[_entities]]' entities="[[_entities]]"
></demo-more-infos> ></demo-more-infos>
`; `;
} }

View File

@ -9,34 +9,48 @@ import NavigateMixin from "../../../src/mixins/navigate-mixin";
class HassioAddonRepository extends NavigateMixin(PolymerElement) { class HassioAddonRepository extends NavigateMixin(PolymerElement) {
static get template() { static get template() {
return html` return html`
<style include="iron-flex ha-style hassio-style"> <style include="iron-flex ha-style hassio-style">
paper-card { paper-card {
cursor: pointer; cursor: pointer;
} }
a.repo { a.repo {
display: block; display: block;
color: var(--primary-text-color); color: var(--primary-text-color);
} }
</style> </style>
<template is="dom-if" if="[[addons.length]]"> <template is="dom-if" if="[[addons.length]]">
<div class="card-group"> <div class="card-group">
<div class="title"> <div class="title">
[[repo.name]] [[repo.name]]
<div class="description"> <div class="description">
Maintained by [[repo.maintainer]] Maintained by [[repo.maintainer]]
<a class="repo" href="[[repo.url]]" target="_blank">[[repo.url]]</a> <a class="repo" href="[[repo.url]]" target="_blank"
</div> >[[repo.url]]</a
</div> >
<template is="dom-repeat" items="[[addons]]" as="addon" sort="sortAddons">
<paper-card on-click="addonTapped">
<div class="card-content">
<hassio-card-content hass="[[hass]]" title="[[addon.name]]" description="[[addon.description]]" icon="[[computeIcon(addon)]]" icon-title="[[computeIconTitle(addon)]]" icon-class="[[computeIconClass(addon)]]"></hassio-card-content>
</div> </div>
</paper-card> </div>
</template> <template
</div> is="dom-repeat"
</template> items="[[addons]]"
`; as="addon"
sort="sortAddons"
>
<paper-card on-click="addonTapped">
<div class="card-content">
<hassio-card-content
hass="[[hass]]"
title="[[addon.name]]"
description="[[addon.description]]"
icon="[[computeIcon(addon)]]"
icon-title="[[computeIconTitle(addon)]]"
icon-class="[[computeIconClass(addon)]]"
></hassio-card-content>
</div>
</paper-card>
</template>
</div>
</template>
`;
} }
static get properties() { static get properties() {

View File

@ -7,17 +7,24 @@ import "./hassio-repositories-editor";
class HassioAddonStore extends PolymerElement { class HassioAddonStore extends PolymerElement {
static get template() { static get template() {
return html` return html`
<style include="iron-flex ha-style"> <style include="iron-flex ha-style">
hassio-addon-repository { hassio-addon-repository {
margin-top: 24px; margin-top: 24px;
} }
</style> </style>
<hassio-repositories-editor hass="[[hass]]" repos="[[repos]]"></hassio-repositories-editor> <hassio-repositories-editor
hass="[[hass]]"
repos="[[repos]]"
></hassio-repositories-editor>
<template is="dom-repeat" items="[[repos]]" as="repo" sort="sortRepos"> <template is="dom-repeat" items="[[repos]]" as="repo" sort="sortRepos">
<hassio-addon-repository hass="[[hass]]" repo="[[repo]]" addons="[[computeAddons(repo.slug)]]"></hassio-addon-repository> <hassio-addon-repository
</template> hass="[[hass]]"
`; repo="[[repo]]"
addons="[[computeAddons(repo.slug)]]"
></hassio-addon-repository>
</template>
`;
} }
static get properties() { static get properties() {

View File

@ -11,48 +11,73 @@ import "../resources/hassio-style";
class HassioRepositoriesEditor extends PolymerElement { class HassioRepositoriesEditor extends PolymerElement {
static get template() { static get template() {
return html` return html`
<style include="ha-style hassio-style"> <style include="ha-style hassio-style">
.add { .add {
padding: 12px 16px; padding: 12px 16px;
} }
iron-icon { iron-icon {
color: var(--secondary-text-color); color: var(--secondary-text-color);
margin-right: 16px; margin-right: 16px;
display: inline-block; display: inline-block;
} }
paper-input { paper-input {
width: calc(100% - 49px); width: calc(100% - 49px);
display: inline-block; display: inline-block;
} }
</style> </style>
<div class="card-group"> <div class="card-group">
<div class="title"> <div class="title">
Repositories Repositories
<div class="description"> <div class="description">
Configure which add-on repositories to fetch data from: Configure which add-on repositories to fetch data from:
</div>
</div> </div>
</div> <template
<template id="list" is="dom-repeat" items="[[repoList]]" as="repo" sort="sortRepos"> id="list"
is="dom-repeat"
items="[[repoList]]"
as="repo"
sort="sortRepos"
>
<paper-card>
<div class="card-content">
<hassio-card-content
hass="[[hass]]"
title="[[repo.name]]"
description="[[repo.url]]"
icon="hassio:github-circle"
></hassio-card-content>
</div>
<div class="card-actions">
<ha-call-api-button
hass="[[hass]]"
path="hassio/supervisor/options"
data="[[computeRemoveRepoData(repoList, repo.url)]]"
class="warning"
>Remove</ha-call-api-button
>
</div>
</paper-card>
</template>
<paper-card> <paper-card>
<div class="card-content"> <div class="card-content add">
<hassio-card-content hass="[[hass]]" title="[[repo.name]]" description="[[repo.url]]" icon="hassio:github-circle"></hassio-card-content> <iron-icon icon="hassio:github-circle"></iron-icon>
<paper-input
label="Add new repository by URL"
value="{{repoUrl}}"
></paper-input>
</div> </div>
<div class="card-actions"> <div class="card-actions">
<ha-call-api-button hass="[[hass]]" path="hassio/supervisor/options" data="[[computeRemoveRepoData(repoList, repo.url)]]" class="warning">Remove</ha-call-api-button> <ha-call-api-button
hass="[[hass]]"
path="hassio/supervisor/options"
data="[[computeAddRepoData(repoList, repoUrl)]]"
>Add</ha-call-api-button
>
</div> </div>
</paper-card> </paper-card>
</template> </div>
<paper-card> `;
<div class="card-content add">
<iron-icon icon="hassio:github-circle"></iron-icon>
<paper-input label="Add new repository by URL" value="{{repoUrl}}"></paper-input>
</div>
<div class="card-actions">
<ha-call-api-button hass="[[hass]]" path="hassio/supervisor/options" data="[[computeAddRepoData(repoList, repoUrl)]]">Add</ha-call-api-button>
</div>
</paper-card>
</div>
`;
} }
static get properties() { static get properties() {

View File

@ -14,49 +14,61 @@ import EventsMixin from "../../../src/mixins/events-mixin";
class HassioAddonAudio extends EventsMixin(PolymerElement) { class HassioAddonAudio extends EventsMixin(PolymerElement) {
static get template() { static get template() {
return html` return html`
<style include="ha-style"> <style include="ha-style">
:host, :host,
paper-card, paper-card,
paper-dropdown-menu { paper-dropdown-menu {
display: block; display: block;
} }
.errors { .errors {
color: var(--google-red-500); color: var(--google-red-500);
margin-bottom: 16px; margin-bottom: 16px;
} }
paper-item { paper-item {
width: 450px; width: 450px;
} }
.card-actions { .card-actions {
text-align: right; text-align: right;
} }
</style> </style>
<paper-card heading="Audio"> <paper-card heading="Audio">
<div class="card-content"> <div class="card-content">
<template is="dom-if" if="[[error]]"> <template is="dom-if" if="[[error]]">
<div class="errors">[[error]]</div> <div class="errors">[[error]]</div>
</template> </template>
<paper-dropdown-menu label="Input"> <paper-dropdown-menu label="Input">
<paper-listbox slot="dropdown-content" attr-for-selected="device" selected="{{selectedInput}}"> <paper-listbox
<template is="dom-repeat" items="[[inputDevices]]"> slot="dropdown-content"
<paper-item device\$="[[item.device]]">[[item.name]]</paper-item> attr-for-selected="device"
</template> selected="{{selectedInput}}"
</paper-listbox> >
</paper-dropdown-menu> <template is="dom-repeat" items="[[inputDevices]]">
<paper-dropdown-menu label="Output"> <paper-item device\$="[[item.device]]"
<paper-listbox slot="dropdown-content" attr-for-selected="device" selected="{{selectedOutput}}"> >[[item.name]]</paper-item
<template is="dom-repeat" items="[[outputDevices]]"> >
<paper-item device\$="[[item.device]]">[[item.name]]</paper-item> </template>
</template> </paper-listbox>
</paper-listbox> </paper-dropdown-menu>
</paper-dropdown-menu> <paper-dropdown-menu label="Output">
</div> <paper-listbox
<div class="card-actions"> slot="dropdown-content"
<paper-button on-click="_saveSettings">Save</paper-button> attr-for-selected="device"
</div> selected="{{selectedOutput}}"
</paper-card> >
`; <template is="dom-repeat" items="[[outputDevices]]">
<paper-item device\$="[[item.device]]"
>[[item.name]]</paper-item
>
</template>
</paper-listbox>
</paper-dropdown-menu>
</div>
<div class="card-actions">
<paper-button on-click="_saveSettings">Save</paper-button>
</div>
</paper-card>
`;
} }
static get properties() { static get properties() {

View File

@ -9,42 +9,53 @@ import "../../../src/components/buttons/ha-call-api-button";
class HassioAddonConfig extends PolymerElement { class HassioAddonConfig extends PolymerElement {
static get template() { static get template() {
return html` return html`
<style include="ha-style"> <style include="ha-style">
:host { :host {
display: block; display: block;
} }
paper-card { paper-card {
display: block; display: block;
} }
.card-actions { .card-actions {
@apply --layout; @apply --layout;
@apply --layout-justified; @apply --layout-justified;
} }
.errors { .errors {
color: var(--google-red-500); color: var(--google-red-500);
margin-bottom: 16px; margin-bottom: 16px;
} }
iron-autogrow-textarea { iron-autogrow-textarea {
width: 100%; width: 100%;
font-family: monospace; font-family: monospace;
} }
.syntaxerror { .syntaxerror {
color: var(--google-red-500); color: var(--google-red-500);
} }
</style> </style>
<paper-card heading="Config"> <paper-card heading="Config">
<div class="card-content"> <div class="card-content">
<template is="dom-if" if="[[error]]"> <template is="dom-if" if="[[error]]">
<div class="errors">[[error]]</div> <div class="errors">[[error]]</div>
</template> </template>
<iron-autogrow-textarea id="config" value="{{config}}"></iron-autogrow-textarea> <iron-autogrow-textarea
</div> id="config"
<div class="card-actions"> value="{{config}}"
<ha-call-api-button class="warning" hass="[[hass]]" path="hassio/addons/[[addonSlug]]/options" data="[[resetData]]">Reset to defaults</ha-call-api-button> ></iron-autogrow-textarea>
<paper-button on-click="saveTapped" disabled="[[!configParsed]]">Save</paper-button> </div>
</div> <div class="card-actions">
</paper-card> <ha-call-api-button
`; class="warning"
hass="[[hass]]"
path="hassio/addons/[[addonSlug]]/options"
data="[[resetData]]"
>Reset to defaults</ha-call-api-button
>
<paper-button on-click="saveTapped" disabled="[[!configParsed]]"
>Save</paper-button
>
</div>
</paper-card>
`;
} }
static get properties() { static get properties() {

View File

@ -15,139 +15,200 @@ import "../components/hassio-card-content";
class HassioAddonInfo extends EventsMixin(PolymerElement) { class HassioAddonInfo extends EventsMixin(PolymerElement) {
static get template() { static get template() {
return html` return html`
<style include="ha-style"> <style include="ha-style">
:host { :host {
display: block; display: block;
} }
paper-card { paper-card {
display: block; display: block;
margin-bottom: 16px; margin-bottom: 16px;
} }
.addon-header { .addon-header {
@apply --paper-font-headline; @apply --paper-font-headline;
} }
.light-color { .light-color {
color: var(--secondary-text-color); color: var(--secondary-text-color);
} }
.addon-version { .addon-version {
float: right; float: right;
font-size: 15px; font-size: 15px;
vertical-align: middle; vertical-align: middle;
} }
.description { .description {
margin-bottom: 16px; margin-bottom: 16px;
} }
.logo img { .logo img {
max-height: 60px; max-height: 60px;
margin: 16px 0; margin: 16px 0;
display: block; display: block;
} }
.state div{ .state div {
width: 150px; width: 150px;
display: inline-block; display: inline-block;
} }
paper-toggle-button { paper-toggle-button {
display: inline; display: inline;
} }
iron-icon.running { iron-icon.running {
color: var(--paper-green-400); color: var(--paper-green-400);
} }
iron-icon.stopped { iron-icon.stopped {
color: var(--google-red-300); color: var(--google-red-300);
} }
ha-call-api-button { ha-call-api-button {
font-weight: 500; font-weight: 500;
color: var(--primary-color); color: var(--primary-color);
} }
.right { .right {
float: right; float: right;
} }
ha-markdown img { ha-markdown img {
max-width: 100%; max-width: 100%;
} }
</style> </style>
<template is="dom-if" if="[[computeUpdateAvailable(addon)]]"> <template is="dom-if" if="[[computeUpdateAvailable(addon)]]">
<paper-card heading="Update available! 🎉"> <paper-card heading="Update available! 🎉">
<div class="card-content"> <div class="card-content">
<hassio-card-content hass="[[hass]]" title="[[addon.name]] [[addon.last_version]] is available" description="You are currently running version [[addon.version]]" icon="hassio:arrow-up-bold-circle" icon-class="update"></hassio-card-content> <hassio-card-content
</div> hass="[[hass]]"
<div class="card-actions"> title="[[addon.name]] [[addon.last_version]] is available"
<ha-call-api-button hass="[[hass]]" path="hassio/addons/[[addonSlug]]/update">Update</ha-call-api-button> description="You are currently running version [[addon.version]]"
<template is="dom-if" if="[[addon.changelog]]"> icon="hassio:arrow-up-bold-circle"
<paper-button on-click="openChangelog">Changelog</paper-button> icon-class="update"
</template> ></hassio-card-content>
</div> </div>
</paper-card> <div class="card-actions">
</template> <ha-call-api-button
hass="[[hass]]"
path="hassio/addons/[[addonSlug]]/update"
>Update</ha-call-api-button
>
<template is="dom-if" if="[[addon.changelog]]">
<paper-button on-click="openChangelog">Changelog</paper-button>
</template>
</div>
</paper-card>
</template>
<paper-card>
<div class="card-content">
<div class="addon-header">[[addon.name]]
<div class="addon-version light-color">
<template is="dom-if" if="[[addon.version]]">
[[addon.version]]
<template is="dom-if" if="[[isRunning]]">
<iron-icon title="Add-on is running" class="running" icon="hassio:circle"></iron-icon>
</template>
<template is="dom-if" if="[[!isRunning]]">
<iron-icon title="Add-on is stopped" class="stopped" icon="hassio:circle"></iron-icon>
</template>
</template>
<template is="dom-if" if="[[!addon.version]]">
[[addon.last_version]]
</template>
</div>
</div>
<div class="description light-color">
[[addon.description]].<br>
Visit <a href="[[addon.url]]" target="_blank">[[addon.name]] page</a> for details.
</div>
<template is="dom-if" if="[[addon.logo]]">
<a href="[[addon.url]]" target="_blank" class="logo">
<img src="/api/hassio/addons/[[addonSlug]]/logo">
</a>
</template>
<template is="dom-if" if="[[addon.version]]">
<div class="state">
<div>Start on boot</div>
<paper-toggle-button on-change="startOnBootToggled" checked="[[computeStartOnBoot(addon.boot)]]"></paper-toggle-button>
</div>
<div class="state">
<div>Auto update</div>
<paper-toggle-button on-change="autoUpdateToggled" checked="[[addon.auto_update]]"></paper-toggle-button>
</div>
</template>
</div>
<div class="card-actions">
<template is="dom-if" if="[[addon.version]]">
<paper-button class="warning" on-click="_unistallClicked">Uninstall</paper-button>
<template is="dom-if" if="[[addon.build]]">
<ha-call-api-button class="warning" hass="[[hass]]" path="hassio/addons/[[addonSlug]]/rebuild">Rebuild</ha-call-api-button>
</template>
<template is="dom-if" if="[[isRunning]]">
<ha-call-api-button class="warning" hass="[[hass]]" path="hassio/addons/[[addonSlug]]/restart">Restart</ha-call-api-button>
<ha-call-api-button class="warning" hass="[[hass]]" path="hassio/addons/[[addonSlug]]/stop">Stop</ha-call-api-button>
</template>
<template is="dom-if" if="[[!isRunning]]">
<ha-call-api-button hass="[[hass]]" path="hassio/addons/[[addonSlug]]/start">Start</ha-call-api-button>
</template>
<template is="dom-if" if="[[computeShowWebUI(addon.webui, isRunning)]]">
<a href="[[pathWebui(addon.webui)]]" tabindex="-1" target="_blank" class="right"><paper-button>Open web UI</paper-button></a>
</template>
</template>
<template is="dom-if" if="[[!addon.version]]">
<ha-call-api-button hass="[[hass]]" path="hassio/addons/[[addonSlug]]/install">Install</ha-call-api-button>
</template>
</div>
</paper-card>
<template is="dom-if" if="[[addon.long_description]]">
<paper-card> <paper-card>
<div class="card-content"> <div class="card-content">
<ha-markdown content="[[addon.long_description]]"></ha-markdown> <div class="addon-header">
[[addon.name]]
<div class="addon-version light-color">
<template is="dom-if" if="[[addon.version]]">
[[addon.version]]
<template is="dom-if" if="[[isRunning]]">
<iron-icon
title="Add-on is running"
class="running"
icon="hassio:circle"
></iron-icon>
</template>
<template is="dom-if" if="[[!isRunning]]">
<iron-icon
title="Add-on is stopped"
class="stopped"
icon="hassio:circle"
></iron-icon>
</template>
</template>
<template is="dom-if" if="[[!addon.version]]">
[[addon.last_version]]
</template>
</div>
</div>
<div class="description light-color">
[[addon.description]].<br />
Visit
<a href="[[addon.url]]" target="_blank">[[addon.name]] page</a> for
details.
</div>
<template is="dom-if" if="[[addon.logo]]">
<a href="[[addon.url]]" target="_blank" class="logo">
<img src="/api/hassio/addons/[[addonSlug]]/logo" />
</a>
</template>
<template is="dom-if" if="[[addon.version]]">
<div class="state">
<div>Start on boot</div>
<paper-toggle-button
on-change="startOnBootToggled"
checked="[[computeStartOnBoot(addon.boot)]]"
></paper-toggle-button>
</div>
<div class="state">
<div>Auto update</div>
<paper-toggle-button
on-change="autoUpdateToggled"
checked="[[addon.auto_update]]"
></paper-toggle-button>
</div>
</template>
</div>
<div class="card-actions">
<template is="dom-if" if="[[addon.version]]">
<paper-button class="warning" on-click="_unistallClicked"
>Uninstall</paper-button
>
<template is="dom-if" if="[[addon.build]]">
<ha-call-api-button
class="warning"
hass="[[hass]]"
path="hassio/addons/[[addonSlug]]/rebuild"
>Rebuild</ha-call-api-button
>
</template>
<template is="dom-if" if="[[isRunning]]">
<ha-call-api-button
class="warning"
hass="[[hass]]"
path="hassio/addons/[[addonSlug]]/restart"
>Restart</ha-call-api-button
>
<ha-call-api-button
class="warning"
hass="[[hass]]"
path="hassio/addons/[[addonSlug]]/stop"
>Stop</ha-call-api-button
>
</template>
<template is="dom-if" if="[[!isRunning]]">
<ha-call-api-button
hass="[[hass]]"
path="hassio/addons/[[addonSlug]]/start"
>Start</ha-call-api-button
>
</template>
<template
is="dom-if"
if="[[computeShowWebUI(addon.webui, isRunning)]]"
>
<a
href="[[pathWebui(addon.webui)]]"
tabindex="-1"
target="_blank"
class="right"
><paper-button>Open web UI</paper-button></a
>
</template>
</template>
<template is="dom-if" if="[[!addon.version]]">
<ha-call-api-button
hass="[[hass]]"
path="hassio/addons/[[addonSlug]]/install"
>Install</ha-call-api-button
>
</template>
</div> </div>
</paper-card> </paper-card>
</template> <template is="dom-if" if="[[addon.long_description]]">
`; <paper-card>
<div class="card-content">
<ha-markdown content="[[addon.long_description]]"></ha-markdown>
</div>
</paper-card>
</template>
`;
} }
static get properties() { static get properties() {

View File

@ -8,24 +8,22 @@ import "../../../src/resources/ha-style";
class HassioAddonLogs extends PolymerElement { class HassioAddonLogs extends PolymerElement {
static get template() { static get template() {
return html` return html`
<style include="ha-style"> <style include="ha-style">
:host, :host,
paper-card { paper-card {
display: block; display: block;
} }
pre { pre {
overflow-x: auto; overflow-x: auto;
} }
</style> </style>
<paper-card heading="Log"> <paper-card heading="Log">
<div class="card-content"> <div class="card-content"><pre>[[log]]</pre></div>
<pre>[[log]]</pre> <div class="card-actions">
</div> <paper-button on-click="refresh">Refresh</paper-button>
<div class="card-actions"> </div>
<paper-button on-click="refresh">Refresh</paper-button> </paper-card>
</div> `;
</paper-card>
`;
} }
static get properties() { static get properties() {

View File

@ -10,51 +10,60 @@ import EventsMixin from "../../../src/mixins/events-mixin";
class HassioAddonNetwork extends EventsMixin(PolymerElement) { class HassioAddonNetwork extends EventsMixin(PolymerElement) {
static get template() { static get template() {
return html` return html`
<style include="ha-style"> <style include="ha-style">
:host { :host {
display: block; display: block;
} }
paper-card { paper-card {
display: block; display: block;
} }
.errors { .errors {
color: var(--google-red-500); color: var(--google-red-500);
margin-bottom: 16px; margin-bottom: 16px;
} }
.card-actions { .card-actions {
@apply --layout; @apply --layout;
@apply --layout-justified; @apply --layout-justified;
} }
</style> </style>
<paper-card heading="Network"> <paper-card heading="Network">
<div class="card-content"> <div class="card-content">
<template is="dom-if" if="[[error]]"> <template is="dom-if" if="[[error]]">
<div class="errors">[[error]]</div> <div class="errors">[[error]]</div>
</template>
<table>
<tbody><tr>
<th>Container</th>
<th>Host</th>
</tr>
<template is="dom-repeat" items="[[config]]">
<tr>
<td>
[[item.container]]
</td>
<td>
<paper-input value="{{item.host}}" no-label-float=""></paper-input>
</td>
</tr>
</template> </template>
</tbody></table>
</div> <table>
<div class="card-actions"> <tbody>
<ha-call-api-button class="warning" hass="[[hass]]" path="hassio/addons/[[addonSlug]]/options" data="[[resetData]]">Reset to defaults</ha-call-api-button> <tr>
<paper-button on-click="saveTapped">Save</paper-button> <th>Container</th>
</div> <th>Host</th>
</paper-card> </tr>
`; <template is="dom-repeat" items="[[config]]">
<tr>
<td>[[item.container]]</td>
<td>
<paper-input
value="{{item.host}}"
no-label-float=""
></paper-input>
</td>
</tr>
</template>
</tbody>
</table>
</div>
<div class="card-actions">
<ha-call-api-button
class="warning"
hass="[[hass]]"
path="hassio/addons/[[addonSlug]]/options"
data="[[resetData]]"
>Reset to defaults</ha-call-api-button
>
<paper-button on-click="saveTapped">Save</paper-button>
</div>
</paper-card>
`;
} }
static get properties() { static get properties() {

View File

@ -18,69 +18,102 @@ import "./hassio-addon-network";
class HassioAddonView extends PolymerElement { class HassioAddonView extends PolymerElement {
static get template() { static get template() {
return html` return html`
<style include="iron-flex ha-style"> <style include="iron-flex ha-style">
:host { :host {
color: var(--primary-text-color); color: var(--primary-text-color);
--paper-card-header-color: var(--primary-text-color); --paper-card-header-color: var(--primary-text-color);
} }
.content { .content {
padding: 24px 0 32px; padding: 24px 0 32px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
} }
hassio-addon-info,
hassio-addon-network,
hassio-addon-audio,
hassio-addon-config {
margin-bottom: 24px;
width: 600px;
}
hassio-addon-logs {
max-width: calc(100% - 8px);
min-width: 600px;
}
@media only screen and (max-width: 600px) {
hassio-addon-info, hassio-addon-info,
hassio-addon-network, hassio-addon-network,
hassio-addon-audio, hassio-addon-audio,
hassio-addon-config, hassio-addon-config {
hassio-addon-logs { margin-bottom: 24px;
max-width: 100%; width: 600px;
min-width: 100%;
} }
} hassio-addon-logs {
</style> max-width: calc(100% - 8px);
<app-route route="[[route]]" pattern="/addon/:slug" data="{{routeData}}" active="{{routeMatches}}"></app-route> min-width: 600px;
<app-header-layout has-scrolling-region=""> }
<app-header fixed="" slot="header"> @media only screen and (max-width: 600px) {
<app-toolbar> hassio-addon-info,
<ha-menu-button hassio narrow="[[narrow]]" show-menu="[[showMenu]]"></ha-menu-button> hassio-addon-network,
<paper-icon-button icon="hassio:arrow-left" on-click="backTapped"></paper-icon-button> hassio-addon-audio,
<div main-title="">Hass.io: add-on details</div> hassio-addon-config,
</app-toolbar> hassio-addon-logs {
</app-header> max-width: 100%;
<div class="content"> min-width: 100%;
<hassio-addon-info hass="[[hass]]" addon="[[addon]]" addon-slug="[[routeData.slug]]"></hassio-addon-info> }
}
</style>
<app-route
route="[[route]]"
pattern="/addon/:slug"
data="{{routeData}}"
active="{{routeMatches}}"
></app-route>
<app-header-layout has-scrolling-region="">
<app-header fixed="" slot="header">
<app-toolbar>
<ha-menu-button
hassio
narrow="[[narrow]]"
show-menu="[[showMenu]]"
></ha-menu-button>
<paper-icon-button
icon="hassio:arrow-left"
on-click="backTapped"
></paper-icon-button>
<div main-title="">Hass.io: add-on details</div>
</app-toolbar>
</app-header>
<div class="content">
<hassio-addon-info
hass="[[hass]]"
addon="[[addon]]"
addon-slug="[[routeData.slug]]"
></hassio-addon-info>
<template is="dom-if" if="[[addon.version]]"> <template is="dom-if" if="[[addon.version]]">
<hassio-addon-config hass="[[hass]]" addon="[[addon]]" addon-slug="[[routeData.slug]]"></hassio-addon-config> <hassio-addon-config
hass="[[hass]]"
addon="[[addon]]"
addon-slug="[[routeData.slug]]"
></hassio-addon-config>
<template is="dom-if" if="[[addon.audio]]"> <template is="dom-if" if="[[addon.audio]]">
<hassio-addon-audio hass="[[hass]]" addon="[[addon]]"></hassio-addon-audio> <hassio-addon-audio
hass="[[hass]]"
addon="[[addon]]"
></hassio-addon-audio>
</template>
<template is="dom-if" if="[[addon.network]]">
<hassio-addon-network
hass="[[hass]]"
addon="[[addon]]"
addon-slug="[[routeData.slug]]"
></hassio-addon-network>
</template>
<hassio-addon-logs
hass="[[hass]]"
addon-slug="[[routeData.slug]]"
></hassio-addon-logs>
</template> </template>
</div>
</app-header-layout>
<template is="dom-if" if="[[addon.network]]"> <hassio-markdown-dialog
<hassio-addon-network hass="[[hass]]" addon="[[addon]]" addon-slug="[[routeData.slug]]"></hassio-addon-network> title="[[markdownTitle]]"
</template> content="[[markdownContent]]"
></hassio-markdown-dialog>
<hassio-addon-logs hass="[[hass]]" addon-slug="[[routeData.slug]]"></hassio-addon-logs> `;
</template>
</div>
</app-header-layout>
<hassio-markdown-dialog title="[[markdownTitle]]" content="[[markdownContent]]"></hassio-markdown-dialog>
`;
} }
static get properties() { static get properties() {

View File

@ -7,54 +7,62 @@ import "../../../src/components/ha-relative-time";
class HassioCardContent extends PolymerElement { class HassioCardContent extends PolymerElement {
static get template() { static get template() {
return html` return html`
<style> <style>
iron-icon { iron-icon {
margin-right: 16px; margin-right: 16px;
margin-top: 16px; margin-top: 16px;
float: left; float: left;
color: var(--secondary-text-color); color: var(--secondary-text-color);
} }
iron-icon.update { iron-icon.update {
color: var(--paper-orange-400); color: var(--paper-orange-400);
} }
iron-icon.running, iron-icon.running,
iron-icon.installed { iron-icon.installed {
color: var(--paper-green-400); color: var(--paper-green-400);
} }
iron-icon.hassupdate, iron-icon.hassupdate,
iron-icon.snapshot { iron-icon.snapshot {
color: var(--paper-item-icon-color); color: var(--paper-item-icon-color);
} }
.title { .title {
color: var(--primary-text-color); color: var(--primary-text-color);
white-space: nowrap; white-space: nowrap;
text-overflow: ellipsis; text-overflow: ellipsis;
overflow: hidden; overflow: hidden;
} }
.addition { .addition {
color: var(--secondary-text-color); color: var(--secondary-text-color);
overflow: hidden; overflow: hidden;
position: relative; position: relative;
height: 2.4em; height: 2.4em;
line-height: 1.2em; line-height: 1.2em;
} }
ha-relative-time { ha-relative-time {
display: block; display: block;
} }
</style> </style>
<iron-icon icon="[[icon]]" class\$="[[iconClass]]" title="[[iconTitle]]"></iron-icon> <iron-icon
<div> icon="[[icon]]"
<div class="title">[[title]]</div> class\$="[[iconClass]]"
<div class="addition"> title="[[iconTitle]]"
<template is="dom-if" if="[[description]]"> ></iron-icon>
[[description]] <div>
</template> <div class="title">[[title]]</div>
<template is="dom-if" if="[[datetime]]"> <div class="addition">
<ha-relative-time hass="[[hass]]" class="addition" datetime="[[datetime]]"></ha-relative-time> <template is="dom-if" if="[[description]]">
</template> [[description]]
</template>
<template is="dom-if" if="[[datetime]]">
<ha-relative-time
hass="[[hass]]"
class="addition"
datetime="[[datetime]]"
></ha-relative-time>
</template>
</div>
</div> </div>
</div> `;
`;
} }
static get properties() { static get properties() {

View File

@ -9,29 +9,43 @@ import NavigateMixin from "../../../src/mixins/navigate-mixin";
class HassioAddons extends NavigateMixin(PolymerElement) { class HassioAddons extends NavigateMixin(PolymerElement) {
static get template() { static get template() {
return html` return html`
<style include="ha-style hassio-style"> <style include="ha-style hassio-style">
paper-card { paper-card {
cursor: pointer; cursor: pointer;
} }
</style> </style>
<div class="content card-group"> <div class="content card-group">
<div class="title">Add-ons</div> <div class="title">Add-ons</div>
<template is="dom-if" if="[[!addons.length]]"> <template is="dom-if" if="[[!addons.length]]">
<paper-card> <paper-card>
<div class="card-content"> <div class="card-content">
You don't have any add-ons installed yet. Head over to <a href="#" on-click="openStore">the add-on store</a> to get started! You don't have any add-ons installed yet. Head over to
</div> <a href="#" on-click="openStore">the add-on store</a> to get
</paper-card> started!
</template> </div>
<template is="dom-repeat" items="[[addons]]" as="addon" sort="sortAddons"> </paper-card>
<paper-card on-click="addonTapped"> </template>
<div class="card-content"> <template
<hassio-card-content hass="[[hass]]" title="[[addon.name]]" description="[[addon.description]]" icon="[[computeIcon(addon)]]" icon-title="[[computeIconTitle(addon)]]" icon-class="[[computeIconClass(addon)]]"></hassio-card-content> is="dom-repeat"
</div> items="[[addons]]"
</paper-card> as="addon"
</template> sort="sortAddons"
</div> >
`; <paper-card on-click="addonTapped">
<div class="card-content">
<hassio-card-content
hass="[[hass]]"
title="[[addon.name]]"
description="[[addon.description]]"
icon="[[computeIcon(addon)]]"
icon-title="[[computeIconTitle(addon)]]"
icon-class="[[computeIconClass(addon)]]"
></hassio-card-content>
</div>
</paper-card>
</template>
</div>
`;
} }
static get properties() { static get properties() {

View File

@ -8,16 +8,22 @@ import EventsMixin from "../../../src/mixins/events-mixin";
class HassioDashboard extends EventsMixin(PolymerElement) { class HassioDashboard extends EventsMixin(PolymerElement) {
static get template() { static get template() {
return html` return html`
<style include="iron-flex ha-style"> <style include="iron-flex ha-style">
.content { .content {
margin: 0 auto; margin: 0 auto;
} }
</style> </style>
<div class="content"> <div class="content">
<hassio-hass-update hass="[[hass]]" hass-info="[[hassInfo]]"></hassio-hass-update> <hassio-hass-update
<hassio-addons hass="[[hass]]" addons="[[supervisorInfo.addons]]"></hassio-addons> hass="[[hass]]"
</div> hass-info="[[hassInfo]]"
`; ></hassio-hass-update>
<hassio-addons
hass="[[hass]]"
addons="[[supervisorInfo.addons]]"
></hassio-addons>
</div>
`;
} }
static get properties() { static get properties() {

View File

@ -10,40 +10,60 @@ import "../resources/hassio-style";
class HassioHassUpdate extends PolymerElement { class HassioHassUpdate extends PolymerElement {
static get template() { static get template() {
return html` return html`
<style include="ha-style hassio-style"> <style include="ha-style hassio-style">
paper-card { paper-card {
display: block; display: block;
margin-bottom: 32px; margin-bottom: 32px;
} }
.errors { .errors {
color: var(--google-red-500); color: var(--google-red-500);
margin-top: 16px; margin-top: 16px;
} }
a { a {
color: var(--primary-color); color: var(--primary-color);
} }
</style> </style>
<template is="dom-if" if="[[computeUpdateAvailable(hassInfo)]]"> <template is="dom-if" if="[[computeUpdateAvailable(hassInfo)]]">
<div class="content"> <div class="content">
<div class="card-group"> <div class="card-group">
<div class="title">Update available! 🎉</div> <div class="title">Update available! 🎉</div>
<paper-card> <paper-card>
<div class="card-content"> <div class="card-content">
<hassio-card-content hass="[[hass]]" title="Home Assistant [[hassInfo.last_version]] is available" description="You are currently running version [[hassInfo.version]]" icon="hassio:home-assistant" icon-class="hassupdate"></hassio-card-content> <hassio-card-content
<template is="dom-if" if="[[error]]"> hass="[[hass]]"
<div class="error">Error: [[error]]</div> title="Home Assistant [[hassInfo.last_version]] is available"
</template> description="You are currently running version [[hassInfo.version]]"
<p><a href='https://www.home-assistant.io/latest-release-notes/' target='_blank'>Read the release notes</a></p> icon="hassio:home-assistant"
</div> icon-class="hassupdate"
<div class="card-actions"> ></hassio-card-content>
<ha-call-api-button hass="[[hass]]" path="hassio/homeassistant/update">Update</ha-call-api-button> <template is="dom-if" if="[[error]]">
<a href="https://github.com/home-assistant/home-assistant/releases" target="_blank"><paper-button>Release notes</paper-button></a> <div class="error">Error: [[error]]</div>
</div> </template>
</paper-card> <p>
<a
href="https://www.home-assistant.io/latest-release-notes/"
target="_blank"
>Read the release notes</a
>
</p>
</div>
<div class="card-actions">
<ha-call-api-button
hass="[[hass]]"
path="hassio/homeassistant/update"
>Update</ha-call-api-button
>
<a
href="https://github.com/home-assistant/home-assistant/releases"
target="_blank"
><paper-button>Release notes</paper-button></a
>
</div>
</paper-card>
</div>
</div> </div>
</div> </template>
</template> `;
`;
} }
static get properties() { static get properties() {

View File

@ -7,10 +7,15 @@ import "./resources/hassio-icons";
class HassioApp extends PolymerElement { class HassioApp extends PolymerElement {
static get template() { static get template() {
return html` return html`
<template is="dom-if" if="[[hass]]"> <template is="dom-if" if="[[hass]]">
<hassio-main hass="[[hass]]" narrow="[[narrow]]" show-menu="[[showMenu]]" route="[[route]]"></hassio-main> <hassio-main
</template> hass="[[hass]]"
`; narrow="[[narrow]]"
show-menu="[[showMenu]]"
route="[[route]]"
></hassio-main>
</template>
`;
} }
static get properties() { static get properties() {

View File

@ -14,22 +14,48 @@ import NavigateMixin from "../../src/mixins/navigate-mixin";
class HassioMain extends EventsMixin(NavigateMixin(PolymerElement)) { class HassioMain extends EventsMixin(NavigateMixin(PolymerElement)) {
static get template() { static get template() {
return html` return html`
<app-route route="[[route]]" pattern="/:page" data="{{routeData}}"></app-route> <app-route
<hassio-data id="data" hass="[[hass]]" supervisor="{{supervisorInfo}}" homeassistant="{{hassInfo}}" host="{{hostInfo}}"></hassio-data> route="[[route]]"
pattern="/:page"
data="{{routeData}}"
></app-route>
<hassio-data
id="data"
hass="[[hass]]"
supervisor="{{supervisorInfo}}"
homeassistant="{{hassInfo}}"
host="{{hostInfo}}"
></hassio-data>
<template is="dom-if" if="[[!loaded]]"> <template is="dom-if" if="[[!loaded]]">
<hass-loading-screen narrow="[[narrow]]" show-menu="[[showMenu]]"></hass-loading-screen> <hass-loading-screen
</template> narrow="[[narrow]]"
show-menu="[[showMenu]]"
></hass-loading-screen>
</template>
<template is="dom-if" if="[[loaded]]"> <template is="dom-if" if="[[loaded]]">
<template is="dom-if" if="[[!equalsAddon(routeData.page)]]"> <template is="dom-if" if="[[!equalsAddon(routeData.page)]]">
<hassio-pages-with-tabs hass="[[hass]]" narrow="[[narrow]]" show-menu="[[showMenu]]" page="[[routeData.page]]" supervisor-info="[[supervisorInfo]]" hass-info="[[hassInfo]]" host-info="[[hostInfo]]"></hassio-pages-with-tabs> <hassio-pages-with-tabs
hass="[[hass]]"
narrow="[[narrow]]"
show-menu="[[showMenu]]"
page="[[routeData.page]]"
supervisor-info="[[supervisorInfo]]"
hass-info="[[hassInfo]]"
host-info="[[hostInfo]]"
></hassio-pages-with-tabs>
</template>
<template is="dom-if" if="[[equalsAddon(routeData.page)]]">
<hassio-addon-view
hass="[[hass]]"
narrow="[[narrow]]"
show-menu="[[showMenu]]"
route="[[route]]"
></hassio-addon-view>
</template>
</template> </template>
<template is="dom-if" if="[[equalsAddon(routeData.page)]]"> `;
<hassio-addon-view hass="[[hass]]" narrow="[[narrow]]" show-menu="[[showMenu]]" route="[[route]]"></hassio-addon-view>
</template>
</template>
`;
} }
static get properties() { static get properties() {

View File

@ -11,55 +11,58 @@ import "../../src/resources/ha-style";
class HassioMarkdownDialog extends PolymerElement { class HassioMarkdownDialog extends PolymerElement {
static get template() { static get template() {
return html` return html`
<style include="ha-style-dialog"> <style include="ha-style-dialog">
paper-dialog {
min-width: 350px;
font-size: 14px;
border-radius: 2px;
}
app-toolbar {
margin: 0;
padding: 0 16px;
color: var(--primary-text-color);
background-color: var(--secondary-background-color);
}
app-toolbar [main-title] {
margin-left: 16px;
}
paper-checkbox {
display: block;
margin: 4px;
}
@media all and (max-width: 450px), all and (max-height: 500px) {
paper-dialog { paper-dialog {
max-height: 100%; min-width: 350px;
} font-size: 14px;
paper-dialog::before { border-radius: 2px;
content: "";
position: fixed;
z-index: -1;
top: 0px;
left: 0px;
right: 0px;
bottom: 0px;
background-color: inherit;
} }
app-toolbar { app-toolbar {
color: var(--text-primary-color); margin: 0;
background-color: var(--primary-color); padding: 0 16px;
color: var(--primary-text-color);
background-color: var(--secondary-background-color);
} }
} app-toolbar [main-title] {
</style> margin-left: 16px;
<paper-dialog id="dialog" with-backdrop=""> }
<app-toolbar> paper-checkbox {
<paper-icon-button icon="hassio:close" dialog-dismiss=""></paper-icon-button> display: block;
<div main-title="">[[title]]</div> margin: 4px;
</app-toolbar> }
<paper-dialog-scrollable> @media all and (max-width: 450px), all and (max-height: 500px) {
<ha-markdown content="[[content]]"></ha-markdown> paper-dialog {
</paper-dialog-scrollable> max-height: 100%;
</paper-dialog> }
`; paper-dialog::before {
content: "";
position: fixed;
z-index: -1;
top: 0px;
left: 0px;
right: 0px;
bottom: 0px;
background-color: inherit;
}
app-toolbar {
color: var(--text-primary-color);
background-color: var(--primary-color);
}
}
</style>
<paper-dialog id="dialog" with-backdrop="">
<app-toolbar>
<paper-icon-button
icon="hassio:close"
dialog-dismiss=""
></paper-icon-button>
<div main-title="">[[title]]</div>
</app-toolbar>
<paper-dialog-scrollable>
<ha-markdown content="[[content]]"></ha-markdown>
</paper-dialog-scrollable>
</paper-dialog>
`;
} }
static get properties() { static get properties() {

View File

@ -23,53 +23,85 @@ import NavigateMixin from "../../src/mixins/navigate-mixin";
class HassioPagesWithTabs extends NavigateMixin(PolymerElement) { class HassioPagesWithTabs extends NavigateMixin(PolymerElement) {
static get template() { static get template() {
return html` return html`
<style include="iron-flex iron-positioning ha-style"> <style include="iron-flex iron-positioning ha-style">
:host { :host {
color: var(--primary-text-color); color: var(--primary-text-color);
--paper-card-header-color: var(--primary-text-color); --paper-card-header-color: var(--primary-text-color);
} }
paper-tabs { paper-tabs {
margin-left: 12px; margin-left: 12px;
--paper-tabs-selection-bar-color: #FFF; --paper-tabs-selection-bar-color: #fff;
text-transform: uppercase; text-transform: uppercase;
} }
</style> </style>
<app-header-layout id="layout" has-scrolling-region> <app-header-layout id="layout" has-scrolling-region>
<app-header fixed slot="header"> <app-header fixed slot="header">
<app-toolbar> <app-toolbar>
<ha-menu-button hassio narrow="[[narrow]]" show-menu="[[showMenu]]"></ha-menu-button> <ha-menu-button
<div main-title>Hass.io</div> hassio
<template is="dom-if" if="[[showRefreshButton(page)]]"> narrow="[[narrow]]"
<paper-icon-button icon="hassio:refresh" on-click="refreshClicked"></paper-icon-button> show-menu="[[showMenu]]"
</template> ></ha-menu-button>
</app-toolbar> <div main-title>Hass.io</div>
<paper-tabs scrollable="" selected="[[page]]" attr-for-selected="page-name" on-iron-activate="handlePageSelected"> <template is="dom-if" if="[[showRefreshButton(page)]]">
<paper-tab page-name="dashboard">Dashboard</paper-tab> <paper-icon-button
<paper-tab page-name="snapshots">Snapshots</paper-tab> icon="hassio:refresh"
<paper-tab page-name="store">Add-on store</paper-tab> on-click="refreshClicked"
<paper-tab page-name="system">System</paper-tab> ></paper-icon-button>
</paper-tabs> </template>
</app-header> </app-toolbar>
<template is="dom-if" if="[[equals(page, &quot;dashboard&quot;)]]"> <paper-tabs
<hassio-dashboard hass="[[hass]]" supervisor-info="[[supervisorInfo]]" hass-info="[[hassInfo]]"></hassio-dashboard> scrollable=""
</template> selected="[[page]]"
attr-for-selected="page-name"
on-iron-activate="handlePageSelected"
>
<paper-tab page-name="dashboard">Dashboard</paper-tab>
<paper-tab page-name="snapshots">Snapshots</paper-tab>
<paper-tab page-name="store">Add-on store</paper-tab>
<paper-tab page-name="system">System</paper-tab>
</paper-tabs>
</app-header>
<template is="dom-if" if="[[equals(page, &quot;dashboard&quot;)]]">
<hassio-dashboard
hass="[[hass]]"
supervisor-info="[[supervisorInfo]]"
hass-info="[[hassInfo]]"
></hassio-dashboard>
</template>
<template is="dom-if" if="[[equals(page, &quot;snapshots&quot;)]]">
<hassio-snapshots
hass="[[hass]]"
installed-addons="[[supervisorInfo.addons]]"
snapshot-slug="{{snapshotSlug}}"
snapshot-deleted="{{snapshotDeleted}}"
></hassio-snapshots>
</template>
<template is="dom-if" if="[[equals(page, &quot;store&quot;)]]">
<hassio-addon-store hass="[[hass]]"></hassio-addon-store>
</template>
<template is="dom-if" if="[[equals(page, &quot;system&quot;)]]">
<hassio-system
hass="[[hass]]"
supervisor-info="[[supervisorInfo]]"
host-info="[[hostInfo]]"
></hassio-system>
</template>
</app-header-layout>
<hassio-markdown-dialog
title="[[markdownTitle]]"
content="[[markdownContent]]"
></hassio-markdown-dialog>
<template is="dom-if" if="[[equals(page, &quot;snapshots&quot;)]]"> <template is="dom-if" if="[[equals(page, &quot;snapshots&quot;)]]">
<hassio-snapshots hass="[[hass]]" installed-addons="[[supervisorInfo.addons]]" snapshot-slug="{{snapshotSlug}}" snapshot-deleted="{{snapshotDeleted}}"></hassio-snapshots> <hassio-snapshot
hass="[[hass]]"
snapshot-slug="{{snapshotSlug}}"
snapshot-deleted="{{snapshotDeleted}}"
></hassio-snapshot>
</template> </template>
<template is="dom-if" if="[[equals(page, &quot;store&quot;)]]"> `;
<hassio-addon-store hass="[[hass]]"></hassio-addon-store>
</template>
<template is="dom-if" if="[[equals(page, &quot;system&quot;)]]">
<hassio-system hass="[[hass]]" supervisor-info="[[supervisorInfo]]" host-info="[[hostInfo]]"></hassio-system>
</template>
</app-header-layout>
<hassio-markdown-dialog title="[[markdownTitle]]" content="[[markdownContent]]"></hassio-markdown-dialog>
<template is="dom-if" if="[[equals(page, &quot;snapshots&quot;)]]">
<hassio-snapshot hass="[[hass]]" snapshot-slug="{{snapshotSlug}}" snapshot-deleted="{{snapshotDeleted}}"></hassio-snapshot>
</template>
`;
} }
static get properties() { static get properties() {

View File

@ -13,99 +13,130 @@ import "../../../src/resources/ha-style";
class HassioSnapshot extends PolymerElement { class HassioSnapshot extends PolymerElement {
static get template() { static get template() {
return html` return html`
<style include="ha-style-dialog"> <style include="ha-style-dialog">
paper-dialog {
min-width: 350px;
font-size: 14px;
border-radius: 2px;
}
app-toolbar {
margin: 0;
padding: 0 16px;
color: var(--primary-text-color);
background-color: var(--secondary-background-color);
}
app-toolbar [main-title] {
margin-left: 16px;
}
paper-dialog-scrollable {
margin: 0;
}
paper-checkbox {
display: block;
margin: 4px;
}
@media all and (max-width: 450px), all and (max-height: 500px) {
paper-dialog { paper-dialog {
max-height: 100%; min-width: 350px;
height: 100%; font-size: 14px;
border-radius: 2px;
} }
app-toolbar { app-toolbar {
color: var(--text-primary-color); margin: 0;
background-color: var(--primary-color); padding: 0 16px;
color: var(--primary-text-color);
background-color: var(--secondary-background-color);
} }
} app-toolbar [main-title] {
.details { margin-left: 16px;
color: var(--secondary-text-color); }
} paper-dialog-scrollable {
.download { margin: 0;
color: var(--primary-color); }
} paper-checkbox {
.warning, display: block;
.error { margin: 4px;
color: var(--google-red-500); }
} @media all and (max-width: 450px), all and (max-height: 500px) {
</style> paper-dialog {
<paper-dialog id="dialog" with-backdrop="" on-iron-overlay-closed="_dialogClosed"> max-height: 100%;
<app-toolbar> height: 100%;
<paper-icon-button icon="hassio:close" dialog-dismiss=""></paper-icon-button> }
<div main-title="">[[_computeName(snapshot)]]</div> app-toolbar {
</app-toolbar> color: var(--text-primary-color);
<div class="details"> background-color: var(--primary-color);
[[_computeType(snapshot.type)]] ([[_computeSize(snapshot.size)]])<br> }
[[_formatDatetime(snapshot.date)]] }
</div> .details {
<div>Home Assistant:</div> color: var(--secondary-text-color);
<paper-checkbox checked="{{restoreHass}}"> }
Home Assistant [[snapshot.homeassistant]] .download {
</paper-checkbox> color: var(--primary-color);
<template is="dom-if" if="[[snapshot.addons.length]]"> }
<div>Folders:</div> .warning,
<template is="dom-repeat" items="[[snapshot.folders]]"> .error {
<paper-checkbox checked="{{item.checked}}"> color: var(--google-red-500);
[[item.name]] }
</paper-checkbox> </style>
</template> <paper-dialog
</template> id="dialog"
<template is="dom-if" if="[[snapshot.addons.length]]"> with-backdrop=""
<div>Add-ons:</div> on-iron-overlay-closed="_dialogClosed"
<paper-dialog-scrollable> >
<template is="dom-repeat" items="[[snapshot.addons]]" sort="_sortAddons"> <app-toolbar>
<paper-icon-button
icon="hassio:close"
dialog-dismiss=""
></paper-icon-button>
<div main-title="">[[_computeName(snapshot)]]</div>
</app-toolbar>
<div class="details">
[[_computeType(snapshot.type)]] ([[_computeSize(snapshot.size)]])<br />
[[_formatDatetime(snapshot.date)]]
</div>
<div>Home Assistant:</div>
<paper-checkbox checked="{{restoreHass}}">
Home Assistant [[snapshot.homeassistant]]
</paper-checkbox>
<template is="dom-if" if="[[snapshot.addons.length]]">
<div>Folders:</div>
<template is="dom-repeat" items="[[snapshot.folders]]">
<paper-checkbox checked="{{item.checked}}"> <paper-checkbox checked="{{item.checked}}">
[[item.name]] [[item.name]]
<span class="details">([[item.version]])</span>
</paper-checkbox> </paper-checkbox>
</template> </template>
</paper-dialog-scrollable>
</template>
<template is="dom-if" if="[[snapshot.protected]]">
<paper-input autofocus="" label="Password" type="password" value="{{snapshotPassword}}"></paper-input>
</template>
<template is="dom-if" if="[[error]]">
<p class="error">Error: [[error]]</p>
</template>
<div class="buttons">
<paper-icon-button icon="hassio:delete" on-click="_deleteClicked" class="warning" title="Delete snapshot"></paper-icon-button>
<a href="[[_computeDownloadUrl(snapshotSlug)]]" download="[[_computeDownloadName(snapshot)]]">
<paper-icon-button icon="hassio:download" class="download" title="Download snapshot"></paper-icon-button>
</a>
<paper-button on-click="_partialRestoreClicked">Restore selected</paper-button>
<template is="dom-if" if="[[_isFullSnapshot(snapshot.type)]]">
<paper-button on-click="_fullRestoreClicked">Wipe &amp; restore</paper-button>
</template> </template>
</div> <template is="dom-if" if="[[snapshot.addons.length]]">
</paper-dialog> <div>Add-ons:</div>
`; <paper-dialog-scrollable>
<template
is="dom-repeat"
items="[[snapshot.addons]]"
sort="_sortAddons"
>
<paper-checkbox checked="{{item.checked}}">
[[item.name]] <span class="details">([[item.version]])</span>
</paper-checkbox>
</template>
</paper-dialog-scrollable>
</template>
<template is="dom-if" if="[[snapshot.protected]]">
<paper-input
autofocus=""
label="Password"
type="password"
value="{{snapshotPassword}}"
></paper-input>
</template>
<template is="dom-if" if="[[error]]">
<p class="error">Error: [[error]]</p>
</template>
<div class="buttons">
<paper-icon-button
icon="hassio:delete"
on-click="_deleteClicked"
class="warning"
title="Delete snapshot"
></paper-icon-button>
<a
href="[[_computeDownloadUrl(snapshotSlug)]]"
download="[[_computeDownloadName(snapshot)]]"
>
<paper-icon-button
icon="hassio:download"
class="download"
title="Download snapshot"
></paper-icon-button>
</a>
<paper-button on-click="_partialRestoreClicked"
>Restore selected</paper-button
>
<template is="dom-if" if="[[_isFullSnapshot(snapshot.type)]]">
<paper-button on-click="_fullRestoreClicked"
>Wipe &amp; restore</paper-button
>
</template>
</div>
</paper-dialog>
`;
} }
static get properties() { static get properties() {

View File

@ -14,90 +14,120 @@ import EventsMixin from "../../../src/mixins/events-mixin";
class HassioSnapshots extends EventsMixin(PolymerElement) { class HassioSnapshots extends EventsMixin(PolymerElement) {
static get template() { static get template() {
return html` return html`
<style include="ha-style hassio-style"> <style include="ha-style hassio-style">
paper-radio-group { paper-radio-group {
display: block; display: block;
} }
paper-radio-button { paper-radio-button {
padding: 0 0 2px 2px; padding: 0 0 2px 2px;
} }
paper-radio-button, paper-radio-button,
paper-checkbox, paper-checkbox,
paper-input[type="password"] { paper-input[type="password"] {
display: block; display: block;
margin: 4px 0 4px 48px; margin: 4px 0 4px 48px;
} }
.pointer { .pointer {
cursor: pointer; cursor: pointer;
} }
</style> </style>
<div class="content"> <div class="content">
<div class="card-group"> <div class="card-group">
<div class="title"> <div class="title">
Create snapshot Create snapshot
<div class="description"> <div class="description">
Snapshots allow you to easily backup and Snapshots allow you to easily backup and restore all data of your
restore all data of your Hass.io instance. Hass.io instance.
</div>
</div> </div>
</div>
<paper-card>
<div class="card-content">
<paper-input autofocus="" label="Name" value="{{snapshotName}}"></paper-input>
Type:
<paper-radio-group selected="{{snapshotType}}">
<paper-radio-button name="full">
Full snapshot
</paper-radio-button>
<paper-radio-button name="partial">
Partial snapshot
</paper-radio-button>
</paper-radio-group>
<template is="dom-if" if="[[!_fullSelected(snapshotType)]]">
Folders:
<template is="dom-repeat" items="[[folderList]]">
<paper-checkbox checked="{{item.checked}}">
[[item.name]]
</paper-checkbox>
</template>
Add-ons:
<template is="dom-repeat" items="[[addonList]]" sort="_sortAddons">
<paper-checkbox checked="{{item.checked}}">
[[item.name]]
</paper-checkbox>
</template>
</template>
Security:
<paper-checkbox checked="{{snapshotHasPassword}}">Password protection</paper-checkbox>
<template is="dom-if" if="[[snapshotHasPassword]]">
<paper-input label="Password" type="password" value="{{snapshotPassword}}"></paper-input>
</template>
<template is="dom-if" if="[[error]]">
<p class="error">[[error]]</p>
</template>
</div>
<div class="card-actions">
<paper-button disabled="[[creatingSnapshot]]" on-click="_createSnapshot">Create</paper-button>
</div>
</paper-card>
</div>
<div class="card-group">
<div class="title">Available snapshots</div>
<template is="dom-if" if="[[!snapshots.length]]">
<paper-card> <paper-card>
<div class="card-content">You don't have any snapshots yet.</div>
</paper-card>
</template>
<template is="dom-repeat" items="[[snapshots]]" as="snapshot" sort="_sortSnapshots">
<paper-card class="pointer" on-click="_snapshotClicked">
<div class="card-content"> <div class="card-content">
<hassio-card-content hass="[[hass]]" title="[[_computeName(snapshot)]]" description="[[_computeDetails(snapshot)]]" datetime="[[snapshot.date]]" icon="[[_computeIcon(snapshot.type)]]" icon-class="snapshot"></hassio-card-content> <paper-input
autofocus=""
label="Name"
value="{{snapshotName}}"
></paper-input>
Type:
<paper-radio-group selected="{{snapshotType}}">
<paper-radio-button name="full">
Full snapshot
</paper-radio-button>
<paper-radio-button name="partial">
Partial snapshot
</paper-radio-button>
</paper-radio-group>
<template is="dom-if" if="[[!_fullSelected(snapshotType)]]">
Folders:
<template is="dom-repeat" items="[[folderList]]">
<paper-checkbox checked="{{item.checked}}">
[[item.name]]
</paper-checkbox>
</template>
Add-ons:
<template
is="dom-repeat"
items="[[addonList]]"
sort="_sortAddons"
>
<paper-checkbox checked="{{item.checked}}">
[[item.name]]
</paper-checkbox>
</template>
</template>
Security:
<paper-checkbox checked="{{snapshotHasPassword}}"
>Password protection</paper-checkbox
>
<template is="dom-if" if="[[snapshotHasPassword]]">
<paper-input
label="Password"
type="password"
value="{{snapshotPassword}}"
></paper-input>
</template>
<template is="dom-if" if="[[error]]">
<p class="error">[[error]]</p>
</template>
</div>
<div class="card-actions">
<paper-button
disabled="[[creatingSnapshot]]"
on-click="_createSnapshot"
>Create</paper-button
>
</div> </div>
</paper-card> </paper-card>
</template> </div>
<div class="card-group">
<div class="title">Available snapshots</div>
<template is="dom-if" if="[[!snapshots.length]]">
<paper-card>
<div class="card-content">You don't have any snapshots yet.</div>
</paper-card>
</template>
<template
is="dom-repeat"
items="[[snapshots]]"
as="snapshot"
sort="_sortSnapshots"
>
<paper-card class="pointer" on-click="_snapshotClicked">
<div class="card-content">
<hassio-card-content
hass="[[hass]]"
title="[[_computeName(snapshot)]]"
description="[[_computeDetails(snapshot)]]"
datetime="[[snapshot.date]]"
icon="[[_computeIcon(snapshot.type)]]"
icon-class="snapshot"
></hassio-card-content>
</div>
</paper-card>
</template>
</div>
</div> </div>
</div> `;
`;
} }
static get properties() { static get properties() {

View File

@ -9,90 +9,110 @@ import EventsMixin from "../../../src/mixins/events-mixin";
class HassioHostInfo extends EventsMixin(PolymerElement) { class HassioHostInfo extends EventsMixin(PolymerElement) {
static get template() { static get template() {
return html` return html`
<style include="iron-flex ha-style"> <style include="iron-flex ha-style">
paper-card {
display: inline-block;
width: 400px;
margin-left: 8px;
}
.card-content {
height: 200px;
color: var(--primary-text-color);
}
@media screen and (max-width: 830px) {
paper-card { paper-card {
margin-top: 8px; display: inline-block;
margin-left: 0; width: 400px;
width: 100%; margin-left: 8px;
} }
.card-content { .card-content {
height: auto; height: 200px;
color: var(--primary-text-color);
} }
} @media screen and (max-width: 830px) {
.info { paper-card {
width: 100%; margin-top: 8px;
} margin-left: 0;
.info td:nth-child(2) { width: 100%;
text-align: right; }
} .card-content {
.errors { height: auto;
color: var(--google-red-500); }
margin-top: 16px; }
} .info {
paper-button.info { width: 100%;
max-width: calc(50% - 12px); }
} .info td:nth-child(2) {
table.info { text-align: right;
margin-bottom: 10px; }
} .errors {
</style> color: var(--google-red-500);
<paper-card> margin-top: 16px;
<div class="card-content"> }
<h2>Host system</h2> paper-button.info {
<table class="info"> max-width: calc(50% - 12px);
<tbody><tr> }
<td>Hostname</td> table.info {
<td>[[data.hostname]]</td> margin-bottom: 10px;
</tr> }
<tr> </style>
<td>System</td> <paper-card>
<td>[[data.operating_system]]</td> <div class="card-content">
</tr> <h2>Host system</h2>
<template is="dom-if" if="[[data.deployment]]"> <table class="info">
<tr> <tbody>
<td>Deployment</td> <tr>
<td>[[data.deployment]]</td> <td>Hostname</td>
</tr> <td>[[data.hostname]]</td>
</template> </tr>
</tbody></table> <tr>
<paper-button raised on-click="_showHardware" class="info"> <td>System</td>
Hardware <td>[[data.operating_system]]</td>
</paper-button> </tr>
<template is="dom-if" if="[[_featureAvailable(data, 'hostname')]]"> <template is="dom-if" if="[[data.deployment]]">
<paper-button raised on-click="_changeHostnameClicked" class="info"> <tr>
Change hostname <td>Deployment</td>
<td>[[data.deployment]]</td>
</tr>
</template>
</tbody>
</table>
<paper-button raised on-click="_showHardware" class="info">
Hardware
</paper-button> </paper-button>
</template> <template is="dom-if" if="[[_featureAvailable(data, 'hostname')]]">
<template is="dom-if" if="[[errors]]"> <paper-button raised on-click="_changeHostnameClicked" class="info">
<div class="errors">Error: [[errors]]</div> Change hostname
</template> </paper-button>
</div> </template>
<div class="card-actions"> <template is="dom-if" if="[[errors]]">
<template is="dom-if" if="[[_featureAvailable(data, 'reboot')]]"> <div class="errors">Error: [[errors]]</div>
<ha-call-api-button class="warning" hass="[[hass]]" path="hassio/host/reboot">Reboot</ha-call-api-button> </template>
</template> </div>
<template is="dom-if" if="[[_featureAvailable(data, 'shutdown')]]"> <div class="card-actions">
<ha-call-api-button class="warning" hass="[[hass]]" path="hassio/host/shutdown">Shutdown</ha-call-api-button> <template is="dom-if" if="[[_featureAvailable(data, 'reboot')]]">
</template> <ha-call-api-button
<template is="dom-if" if="[[_featureAvailable(data, 'hassos')]]"> class="warning"
<ha-call-api-button class="warning" hass="[[hass]]" path="hassio/hassos/config/sync" title="Load HassOS configs or updates from USB">Import from USB</ha-call-api-button> hass="[[hass]]"
</template> path="hassio/host/reboot"
<template is="dom-if" if="[[_computeUpdateAvailable(_hassOs)]]"> >Reboot</ha-call-api-button
<ha-call-api-button hass="[[hass]]" path="hassio/hassos/update">Update</ha-call-api-button> >
</template> </template>
</div> <template is="dom-if" if="[[_featureAvailable(data, 'shutdown')]]">
</paper-card> <ha-call-api-button
`; class="warning"
hass="[[hass]]"
path="hassio/host/shutdown"
>Shutdown</ha-call-api-button
>
</template>
<template is="dom-if" if="[[_featureAvailable(data, 'hassos')]]">
<ha-call-api-button
class="warning"
hass="[[hass]]"
path="hassio/hassos/config/sync"
title="Load HassOS configs or updates from USB"
>Import from USB</ha-call-api-button
>
</template>
<template is="dom-if" if="[[_computeUpdateAvailable(_hassOs)]]">
<ha-call-api-button hass="[[hass]]" path="hassio/hassos/update"
>Update</ha-call-api-button
>
</template>
</div>
</paper-card>
`;
} }
static get properties() { static get properties() {

View File

@ -9,73 +9,96 @@ import EventsMixin from "../../../src/mixins/events-mixin";
class HassioSupervisorInfo extends EventsMixin(PolymerElement) { class HassioSupervisorInfo extends EventsMixin(PolymerElement) {
static get template() { static get template() {
return html` return html`
<style include="iron-flex ha-style"> <style include="iron-flex ha-style">
paper-card {
display: inline-block;
width: 400px;
}
.card-content {
height: 200px;
color: var(--primary-text-color);
}
@media screen and (max-width: 830px) {
paper-card { paper-card {
width: 100%; display: inline-block;
width: 400px;
} }
.card-content { .card-content {
height: auto; height: 200px;
color: var(--primary-text-color);
} }
} @media screen and (max-width: 830px) {
.info { paper-card {
width: 100%; width: 100%;
} }
.info td:nth-child(2) { .card-content {
text-align: right; height: auto;
} }
.errors { }
color: var(--google-red-500); .info {
margin-top: 16px; width: 100%;
} }
</style> .info td:nth-child(2) {
<paper-card> text-align: right;
<div class="card-content"> }
<h2>Hass.io supervisor</h2> .errors {
<table class="info"> color: var(--google-red-500);
<tbody><tr> margin-top: 16px;
<td>Version</td> }
<td> </style>
[[data.version]] <paper-card>
</td> <div class="card-content">
</tr> <h2>Hass.io supervisor</h2>
<tr> <table class="info">
<td>Latest version</td> <tbody>
<td>[[data.last_version]]</td> <tr>
</tr> <td>Version</td>
<template is="dom-if" if="[[!_equals(data.channel, &quot;stable&quot;)]]"> <td>[[data.version]]</td>
<tr> </tr>
<td>Channel</td> <tr>
<td>[[data.channel]]</td> <td>Latest version</td>
</tr> <td>[[data.last_version]]</td>
</tr>
<template
is="dom-if"
if="[[!_equals(data.channel, &quot;stable&quot;)]]"
>
<tr>
<td>Channel</td>
<td>[[data.channel]]</td>
</tr>
</template>
</tbody>
</table>
<template is="dom-if" if="[[errors]]">
<div class="errors">Error: [[errors]]</div>
</template> </template>
</tbody></table> </div>
<template is="dom-if" if="[[errors]]"> <div class="card-actions">
<div class="errors">Error: [[errors]]</div> <ha-call-api-button hass="[[hass]]" path="hassio/supervisor/reload"
</template> >Reload</ha-call-api-button
</div> >
<div class="card-actions"> <template is="dom-if" if="[[computeUpdateAvailable(data)]]">
<ha-call-api-button hass="[[hass]]" path="hassio/supervisor/reload">Reload</ha-call-api-button> <ha-call-api-button hass="[[hass]]" path="hassio/supervisor/update"
<template is="dom-if" if="[[computeUpdateAvailable(data)]]"> >Update</ha-call-api-button
<ha-call-api-button hass="[[hass]]" path="hassio/supervisor/update">Update</ha-call-api-button> >
</template> </template>
<template is="dom-if" if="[[_equals(data.channel, &quot;beta&quot;)]]"> <template
<ha-call-api-button hass="[[hass]]" path="hassio/supervisor/options" data="[[leaveBeta]]">Leave beta channel</ha-call-api-button> is="dom-if"
</template> if="[[_equals(data.channel, &quot;beta&quot;)]]"
<template is="dom-if" if="[[_equals(data.channel, &quot;stable&quot;)]]"> >
<paper-button on-click="_joinBeta" class="warning" title="Get beta updates for Home Assistant (RCs), supervisor and host">Join beta channel</paper-button> <ha-call-api-button
</template> hass="[[hass]]"
</div> path="hassio/supervisor/options"
</paper-card> data="[[leaveBeta]]"
`; >Leave beta channel</ha-call-api-button
>
</template>
<template
is="dom-if"
if="[[_equals(data.channel, &quot;stable&quot;)]]"
>
<paper-button
on-click="_joinBeta"
class="warning"
title="Get beta updates for Home Assistant (RCs), supervisor and host"
>Join beta channel</paper-button
>
</template>
</div>
</paper-card>
`;
} }
static get properties() { static get properties() {

View File

@ -6,23 +6,21 @@ import { PolymerElement } from "@polymer/polymer/polymer-element";
class HassioSupervisorLog extends PolymerElement { class HassioSupervisorLog extends PolymerElement {
static get template() { static get template() {
return html` return html`
<style include="ha-style"> <style include="ha-style">
paper-card { paper-card {
display: block; display: block;
} }
pre { pre {
overflow-x: auto; overflow-x: auto;
} }
</style> </style>
<paper-card> <paper-card>
<div class="card-content"> <div class="card-content"><pre>[[log]]</pre></div>
<pre>[[log]]</pre> <div class="card-actions">
</div> <paper-button on-click="refreshTapped">Refresh</paper-button>
<div class="card-actions"> </div>
<paper-button on-click="refreshTapped">Refresh</paper-button> </paper-card>
</div> `;
</paper-card>
`;
} }
static get properties() { static get properties() {

View File

@ -9,26 +9,32 @@ import "./hassio-supervisor-log";
class HassioSystem extends PolymerElement { class HassioSystem extends PolymerElement {
static get template() { static get template() {
return html` return html`
<style include="iron-flex ha-style"> <style include="iron-flex ha-style">
.content { .content {
margin: 4px; margin: 4px;
} }
.title { .title {
margin-top: 24px; margin-top: 24px;
color: var(--primary-text-color); color: var(--primary-text-color);
font-size: 2em; font-size: 2em;
padding-left: 8px; padding-left: 8px;
margin-bottom: 8px; margin-bottom: 8px;
} }
</style> </style>
<div class="content"> <div class="content">
<div class="title">Information</div> <div class="title">Information</div>
<hassio-supervisor-info hass="[[hass]]" data="[[supervisorInfo]]"></hassio-supervisor-info> <hassio-supervisor-info
<hassio-host-info hass="[[hass]]" data="[[hostInfo]]"></hassio-host-info> hass="[[hass]]"
<div class="title">System log</div> data="[[supervisorInfo]]"
<hassio-supervisor-log hass="[[hass]]"></hassio-supervisor-log> ></hassio-supervisor-info>
</div> <hassio-host-info
`; hass="[[hass]]"
data="[[hostInfo]]"
></hassio-host-info>
<div class="title">System log</div>
<hassio-supervisor-log hass="[[hass]]"></hassio-supervisor-log>
</div>
`;
} }
static get properties() { static get properties() {

View File

@ -7,54 +7,61 @@ import LocalizeLiteMixin from "../mixins/localize-lite-mixin";
class HaAuthFlow extends LocalizeLiteMixin(PolymerElement) { class HaAuthFlow extends LocalizeLiteMixin(PolymerElement) {
static get template() { static get template() {
return html` return html`
<style> <style>
:host { :host {
/* So we can set min-height to avoid jumping during loading */ /* So we can set min-height to avoid jumping during loading */
display: block; display: block;
} }
.action { .action {
margin: 24px 0 8px; margin: 24px 0 8px;
text-align: center; text-align: center;
} }
.error { .error {
color: red; color: red;
} }
</style> </style>
<form> <form>
<template is="dom-if" if="[[_equals(_state, &quot;loading&quot;)]]"> <template is="dom-if" if="[[_equals(_state, &quot;loading&quot;)]]">
[[localize('ui.panel.page-authorize.form.working')]]: [[localize('ui.panel.page-authorize.form.working')]]:
</template>
<template is="dom-if" if="[[_equals(_state, &quot;error&quot;)]]">
<div class='error'>Error: [[_errorMsg]]</div>
</template>
<template is="dom-if" if="[[_equals(_state, &quot;step&quot;)]]">
<template is="dom-if" if="[[_equals(_step.type, &quot;abort&quot;)]]">
[[localize('ui.panel.page-authorize.abort_intro')]]:
<ha-markdown content="[[_computeStepAbortedReason(localize, _step)]]"></ha-markdown>
</template> </template>
<template is="dom-if" if="[[_equals(_state, &quot;error&quot;)]]">
<template is="dom-if" if="[[_equals(_step.type, &quot;form&quot;)]]"> <div class="error">Error: [[_errorMsg]]</div>
<template is="dom-if" if="[[_computeStepDescription(localize, _step)]]"> </template>
<ha-markdown content="[[_computeStepDescription(localize, _step)]]" allow-svg></ha-markdown> <template is="dom-if" if="[[_equals(_state, &quot;step&quot;)]]">
<template is="dom-if" if="[[_equals(_step.type, &quot;abort&quot;)]]">
[[localize('ui.panel.page-authorize.abort_intro')]]:
<ha-markdown
content="[[_computeStepAbortedReason(localize, _step)]]"
></ha-markdown>
</template> </template>
<ha-form <template is="dom-if" if="[[_equals(_step.type, &quot;form&quot;)]]">
data="{{_stepData}}" <template
schema="[[_step.data_schema]]" is="dom-if"
error="[[_step.errors]]" if="[[_computeStepDescription(localize, _step)]]"
compute-label="[[_computeLabelCallback(localize, _step)]]" >
compute-error="[[_computeErrorCallback(localize, _step)]]" <ha-markdown
></ha-form> content="[[_computeStepDescription(localize, _step)]]"
allow-svg
></ha-markdown>
</template>
<ha-form
data="{{_stepData}}"
schema="[[_step.data_schema]]"
error="[[_step.errors]]"
compute-label="[[_computeLabelCallback(localize, _step)]]"
compute-error="[[_computeErrorCallback(localize, _step)]]"
></ha-form>
</template>
<div class="action">
<paper-button raised on-click="_handleSubmit"
>[[_computeSubmitCaption(_step.type)]]</paper-button
>
</div>
</template> </template>
<div class='action'> </form>
<paper-button `;
raised
on-click='_handleSubmit'
>[[_computeSubmitCaption(_step.type)]]</paper-button>
</div>
</template>
</form>
`;
} }
static get properties() { static get properties() {

View File

@ -12,49 +12,51 @@ import "./ha-auth-flow";
class HaAuthorize extends LocalizeLiteMixin(PolymerElement) { class HaAuthorize extends LocalizeLiteMixin(PolymerElement) {
static get template() { static get template() {
return html` return html`
<style> <style>
ha-markdown { ha-markdown {
display: block; display: block;
margin-bottom: 16px; margin-bottom: 16px;
} }
ha-markdown a { ha-markdown a {
color: var(--primary-color); color: var(--primary-color);
} }
ha-markdown p:last-child{ ha-markdown p:last-child {
margin-bottom: 0; margin-bottom: 0;
} }
ha-pick-auth-provider { ha-pick-auth-provider {
display: block; display: block;
margin-top: 48px; margin-top: 48px;
} }
</style> </style>
<template is="dom-if" if="[[!_authProviders]]"> <template is="dom-if" if="[[!_authProviders]]">
<p>[[localize('ui.panel.page-authorize.initializing')]]</p> <p>[[localize('ui.panel.page-authorize.initializing')]]</p>
</template> </template>
<template is="dom-if" if="[[_authProviders]]"> <template is="dom-if" if="[[_authProviders]]">
<ha-markdown content='[[_computeIntro(localize, clientId, _authProvider)]]'></ha-markdown> <ha-markdown
content="[[_computeIntro(localize, clientId, _authProvider)]]"
></ha-markdown>
<ha-auth-flow <ha-auth-flow
resources="[[resources]]"
client-id="[[clientId]]"
redirect-uri="[[redirectUri]]"
oauth2-state="[[oauth2State]]"
auth-provider="[[_authProvider]]"
step="{{step}}"
></ha-auth-flow>
<template is="dom-if" if="[[_computeMultiple(_authProviders)]]">
<ha-pick-auth-provider
resources="[[resources]]" resources="[[resources]]"
client-id="[[clientId]]" client-id="[[clientId]]"
auth-providers="[[_computeInactiveProvders(_authProvider, _authProviders)]]" redirect-uri="[[redirectUri]]"
on-pick="_handleAuthProviderPick" oauth2-state="[[oauth2State]]"
></ha-pick-auth-provider> auth-provider="[[_authProvider]]"
step="{{step}}"
></ha-auth-flow>
<template is="dom-if" if="[[_computeMultiple(_authProviders)]]">
<ha-pick-auth-provider
resources="[[resources]]"
client-id="[[clientId]]"
auth-providers="[[_computeInactiveProvders(_authProvider, _authProviders)]]"
on-pick="_handleAuthProviderPick"
></ha-pick-auth-provider>
</template>
</template> </template>
</template> `;
`;
} }
static get properties() { static get properties() {

View File

@ -14,22 +14,22 @@ class HaPickAuthProvider extends EventsMixin(
) { ) {
static get template() { static get template() {
return html` return html`
<style> <style>
paper-item { paper-item {
cursor: pointer; cursor: pointer;
} }
p { p {
margin-top: 0; margin-top: 0;
} }
</style> </style>
<p>[[localize('ui.panel.page-authorize.pick_auth_provider')]]:</p> <p>[[localize('ui.panel.page-authorize.pick_auth_provider')]]:</p>
<template is="dom-repeat" items="[[authProviders]]"> <template is="dom-repeat" items="[[authProviders]]">
<paper-item on-click="_handlePick"> <paper-item on-click="_handlePick">
<paper-item-body>[[item.name]]</paper-item-body> <paper-item-body>[[item.name]]</paper-item-body>
<iron-icon icon="hass:chevron-right"></iron-icon> <iron-icon icon="hass:chevron-right"></iron-icon>
</paper-item> </paper-item>
</template> </template>
`; `;
} }
static get properties() { static get properties() {

View File

@ -6,16 +6,19 @@ import "../components/entity/ha-state-label-badge";
class HaBadgesCard extends PolymerElement { class HaBadgesCard extends PolymerElement {
static get template() { static get template() {
return html` return html`
<style> <style>
ha-state-label-badge { ha-state-label-badge {
display: inline-block; display: inline-block;
margin-bottom: var(--ha-state-label-badge-margin-bottom, 16px); margin-bottom: var(--ha-state-label-badge-margin-bottom, 16px);
} }
</style> </style>
<template is="dom-repeat" items="[[states]]"> <template is="dom-repeat" items="[[states]]">
<ha-state-label-badge hass="[[hass]]" state="[[item]]"></ha-state-label-badge> <ha-state-label-badge
</template> hass="[[hass]]"
`; state="[[item]]"
></ha-state-label-badge>
</template>
`;
} }
static get properties() { static get properties() {

View File

@ -14,51 +14,55 @@ const UPDATE_INTERVAL = 10000; // ms
class HaCameraCard extends LocalizeMixin(EventsMixin(PolymerElement)) { class HaCameraCard extends LocalizeMixin(EventsMixin(PolymerElement)) {
static get template() { static get template() {
return html` return html`
<style include="paper-material-styles"> <style include="paper-material-styles">
:host { :host {
@apply --paper-material-elevation-1; @apply --paper-material-elevation-1;
display: block; display: block;
position: relative; position: relative;
font-size: 0px; font-size: 0px;
border-radius: 2px; border-radius: 2px;
cursor: pointer; cursor: pointer;
min-height: 48px; min-height: 48px;
line-height: 0; line-height: 0;
} }
.camera-feed { .camera-feed {
width: 100%; width: 100%;
height: auto; height: auto;
border-radius: 2px; border-radius: 2px;
} }
.caption { .caption {
@apply --paper-font-common-nowrap; @apply --paper-font-common-nowrap;
position: absolute; position: absolute;
left: 0px; left: 0px;
right: 0px; right: 0px;
bottom: 0px; bottom: 0px;
border-bottom-left-radius: 2px; border-bottom-left-radius: 2px;
border-bottom-right-radius: 2px; border-bottom-right-radius: 2px;
background-color: rgba(0, 0, 0, 0.3); background-color: rgba(0, 0, 0, 0.3);
padding: 16px; padding: 16px;
font-size: 16px; font-size: 16px;
font-weight: 500; font-weight: 500;
line-height: 16px; line-height: 16px;
color: white; color: white;
} }
</style> </style>
<template is="dom-if" if="[[cameraFeedSrc]]"> <template is="dom-if" if="[[cameraFeedSrc]]">
<img src="[[cameraFeedSrc]]" class="camera-feed" alt="[[_computeStateName(stateObj)]]"> <img
</template> src="[[cameraFeedSrc]]"
<div class="caption"> class="camera-feed"
[[_computeStateName(stateObj)]] alt="[[_computeStateName(stateObj)]]"
<template is="dom-if" if="[[!imageLoaded]]"> />
([[localize('ui.card.camera.not_available')]]) </template>
</template> <div class="caption">
</div> [[_computeStateName(stateObj)]]
`; <template is="dom-if" if="[[!imageLoaded]]">
([[localize('ui.card.camera.not_available')]])
</template>
</div>
`;
} }
static get properties() { static get properties() {

View File

@ -16,54 +16,68 @@ import LocalizeMixin from "../mixins/localize-mixin";
class HaEntitiesCard extends LocalizeMixin(EventsMixin(PolymerElement)) { class HaEntitiesCard extends LocalizeMixin(EventsMixin(PolymerElement)) {
static get template() { static get template() {
return html` return html`
<style include="iron-flex"></style> <style include="iron-flex"></style>
<style> <style>
ha-card { ha-card {
padding: 16px; padding: 16px;
} }
.states { .states {
margin: -4px 0; margin: -4px 0;
} }
.state { .state {
padding: 4px 0; padding: 4px 0;
} }
.header { .header {
@apply --paper-font-headline; @apply --paper-font-headline;
/* overwriting line-height +8 because entity-toggle can be 40px height, /* overwriting line-height +8 because entity-toggle can be 40px height,
compensating this with reduced padding */ compensating this with reduced padding */
line-height: 40px; line-height: 40px;
color: var(--primary-text-color); color: var(--primary-text-color);
padding: 4px 0 12px; padding: 4px 0 12px;
} }
.header .name { .header .name {
@apply --paper-font-common-nowrap; @apply --paper-font-common-nowrap;
} }
ha-entity-toggle { ha-entity-toggle {
margin-left: 16px; margin-left: 16px;
} }
.more-info { .more-info {
cursor: pointer; cursor: pointer;
} }
</style> </style>
<ha-card> <ha-card>
<template is="dom-if" if="[[title]]"> <template is="dom-if" if="[[title]]">
<div class$="[[computeTitleClass(groupEntity)]]" on-click="entityTapped"> <div
<div class="flex name">[[title]]</div> class$="[[computeTitleClass(groupEntity)]]"
<template is="dom-if" if="[[showGroupToggle(groupEntity, states)]]"> on-click="entityTapped"
<ha-entity-toggle hass="[[hass]]" state-obj="[[groupEntity]]"></ha-entity-toggle> >
</template> <div class="flex name">[[title]]</div>
</div> <template is="dom-if" if="[[showGroupToggle(groupEntity, states)]]">
</template> <ha-entity-toggle
<div class="states"> hass="[[hass]]"
<template is="dom-repeat" items="[[states]]" on-dom-change="addTapEvents"> state-obj="[[groupEntity]]"
<div class$="[[computeStateClass(item)]]"> ></ha-entity-toggle>
<state-card-content hass="[[hass]]" class="state-card" state-obj="[[item]]"></state-card-content> </template>
</div> </div>
</template> </template>
</div> <div class="states">
</ha-card> <template
`; is="dom-repeat"
items="[[states]]"
on-dom-change="addTapEvents"
>
<div class$="[[computeStateClass(item)]]">
<state-card-content
hass="[[hass]]"
class="state-card"
state-obj="[[item]]"
></state-card-content>
</div>
</template>
</div>
</ha-card>
`;
} }
static get properties() { static get properties() {

View File

@ -14,39 +14,56 @@ import EventsMixin from "../mixins/events-mixin";
class HaHistoryGraphCard extends EventsMixin(PolymerElement) { class HaHistoryGraphCard extends EventsMixin(PolymerElement) {
static get template() { static get template() {
return html` return html`
<style> <style>
paper-card:not([dialog]) .content { paper-card:not([dialog]) .content {
padding: 0 16px 16px; padding: 0 16px 16px;
} }
paper-card[dialog] { paper-card[dialog] {
padding-top: 16px; padding-top: 16px;
background-color: transparent; background-color: transparent;
} }
paper-card { paper-card {
width: 100%; width: 100%;
/* prevent new stacking context, chart tooltip needs to overflow */ /* prevent new stacking context, chart tooltip needs to overflow */
position: static; position: static;
} }
.header { .header {
@apply --paper-font-headline; @apply --paper-font-headline;
line-height: 40px; line-height: 40px;
color: var(--primary-text-color); color: var(--primary-text-color);
padding: 20px 16px 12px; padding: 20px 16px 12px;
@apply --paper-font-common-nowrap; @apply --paper-font-common-nowrap;
} }
paper-card[dialog] .header { paper-card[dialog] .header {
display: none; display: none;
} }
</style> </style>
<ha-state-history-data hass="[[hass]]" filter-type="recent-entity" entity-id="[[computeHistoryEntities(stateObj)]]" data="{{stateHistory}}" is-loading="{{stateHistoryLoading}}" cache-config="[[cacheConfig]]"></ha-state-history-data> <ha-state-history-data
<paper-card dialog$="[[inDialog]]" on-click="cardTapped" elevation="[[computeElevation(inDialog)]]"> hass="[[hass]]"
<div class="header">[[computeTitle(stateObj)]]</div> filter-type="recent-entity"
<div class="content"> entity-id="[[computeHistoryEntities(stateObj)]]"
<state-history-charts hass="[[hass]]" history-data="[[stateHistory]]" is-loading-data="[[stateHistoryLoading]]" up-to-now no-single> data="{{stateHistory}}"
</state-history-charts> is-loading="{{stateHistoryLoading}}"
</div> cache-config="[[cacheConfig]]"
</paper-card> ></ha-state-history-data>
`; <paper-card
dialog$="[[inDialog]]"
on-click="cardTapped"
elevation="[[computeElevation(inDialog)]]"
>
<div class="header">[[computeTitle(stateObj)]]</div>
<div class="content">
<state-history-charts
hass="[[hass]]"
history-data="[[stateHistory]]"
is-loading-data="[[stateHistoryLoading]]"
up-to-now
no-single
>
</state-history-charts>
</div>
</paper-card>
`;
} }
static get properties() { static get properties() {

View File

@ -18,169 +18,200 @@ import LocalizeMixin from "../mixins/localize-mixin";
class HaMediaPlayerCard extends LocalizeMixin(EventsMixin(PolymerElement)) { class HaMediaPlayerCard extends LocalizeMixin(EventsMixin(PolymerElement)) {
static get template() { static get template() {
return html` return html`
<style include="paper-material-styles iron-flex iron-flex-alignment iron-positioning"> <style
:host { include="paper-material-styles iron-flex iron-flex-alignment iron-positioning"
@apply --paper-material-elevation-1; >
display: block; :host {
position: relative; @apply --paper-material-elevation-1;
font-size: 0px; display: block;
border-radius: 2px; position: relative;
} font-size: 0px;
border-radius: 2px;
}
.banner { .banner {
position: relative; position: relative;
background-color: white; background-color: white;
border-top-left-radius: 2px; border-top-left-radius: 2px;
border-top-right-radius: 2px; border-top-right-radius: 2px;
} }
.banner:before { .banner:before {
display: block; display: block;
content: ""; content: "";
width: 100%; width: 100%;
/* removed .25% from 16:9 ratio to fix YT black bars */ /* removed .25% from 16:9 ratio to fix YT black bars */
padding-top: 56%; padding-top: 56%;
transition: padding-top .8s; transition: padding-top 0.8s;
} }
.banner.no-cover { .banner.no-cover {
background-position: center center; background-position: center center;
background-image: url(/static/images/card_media_player_bg.png); background-image: url(/static/images/card_media_player_bg.png);
background-repeat: no-repeat; background-repeat: no-repeat;
background-color: var(--primary-color); background-color: var(--primary-color);
} }
.banner.content-type-music:before { .banner.content-type-music:before {
padding-top: 100%; padding-top: 100%;
} }
.banner.no-cover:before { .banner.no-cover:before {
padding-top: 88px; padding-top: 88px;
} }
.banner > .cover { .banner > .cover {
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
right: 0; right: 0;
bottom: 0; bottom: 0;
border-top-left-radius: 2px; border-top-left-radius: 2px;
border-top-right-radius: 2px; border-top-right-radius: 2px;
background-position: center center; background-position: center center;
background-size: cover; background-size: cover;
transition: opacity .8s; transition: opacity 0.8s;
opacity: 1; opacity: 1;
} }
.banner.is-off > .cover { .banner.is-off > .cover {
opacity: 0; opacity: 0;
} }
.banner > .caption { .banner > .caption {
@apply --paper-font-caption; @apply --paper-font-caption;
position: absolute; position: absolute;
left: 0; left: 0;
right: 0; right: 0;
bottom: 0; bottom: 0;
background-color: rgba(0, 0, 0, var(--dark-secondary-opacity)); background-color: rgba(0, 0, 0, var(--dark-secondary-opacity));
padding: 8px 16px; padding: 8px 16px;
font-size: 14px; font-size: 14px;
font-weight: 500; font-weight: 500;
color: white; color: white;
transition: background-color .5s; transition: background-color 0.5s;
} }
.banner.is-off > .caption { .banner.is-off > .caption {
background-color: initial; background-color: initial;
} }
.banner > .caption .title { .banner > .caption .title {
@apply --paper-font-common-nowrap; @apply --paper-font-common-nowrap;
font-size: 1.2em; font-size: 1.2em;
margin: 8px 0 4px; margin: 8px 0 4px;
} }
.progress { .progress {
width: 100%; width: 100%;
height: var(--paper-progress-height, 4px); height: var(--paper-progress-height, 4px);
margin-top: calc(-1*var(--paper-progress-height, 4px)); margin-top: calc(-1 * var(--paper-progress-height, 4px));
--paper-progress-active-color: var(--accent-color); --paper-progress-active-color: var(--accent-color);
--paper-progress-container-color: rgba(200,200,200,0.5); --paper-progress-container-color: rgba(200, 200, 200, 0.5);
} }
.controls { .controls {
position: relative; position: relative;
@apply --paper-font-body1; @apply --paper-font-body1;
padding: 8px; padding: 8px;
border-bottom-left-radius: 2px; border-bottom-left-radius: 2px;
border-bottom-right-radius: 2px; border-bottom-right-radius: 2px;
background-color: var(--paper-card-background-color, white); background-color: var(--paper-card-background-color, white);
} }
.controls paper-icon-button { .controls paper-icon-button {
width: 44px; width: 44px;
height: 44px; height: 44px;
} }
paper-icon-button { paper-icon-button {
opacity: var(--dark-primary-opacity); opacity: var(--dark-primary-opacity);
} }
paper-icon-button[disabled] { paper-icon-button[disabled] {
opacity: var(--dark-disabled-opacity); opacity: var(--dark-disabled-opacity);
} }
paper-icon-button.primary { paper-icon-button.primary {
width: 56px !important; width: 56px !important;
height: 56px !important; height: 56px !important;
background-color: var(--primary-color); background-color: var(--primary-color);
color: white; color: white;
border-radius: 50%; border-radius: 50%;
padding: 8px; padding: 8px;
transition: background-color .5s; transition: background-color 0.5s;
} }
paper-icon-button.primary[disabled] { paper-icon-button.primary[disabled] {
background-color: rgba(0, 0, 0, var(--dark-disabled-opacity)); background-color: rgba(0, 0, 0, var(--dark-disabled-opacity));
} }
[invisible] { [invisible] {
visibility: hidden !important; visibility: hidden !important;
} }
</style> </style>
<div class$="[[computeBannerClasses(playerObj)]]"> <div class$="[[computeBannerClasses(playerObj)]]">
<div class="cover" id="cover"></div> <div class="cover" id="cover"></div>
<div class="caption"> <div class="caption">
[[_computeStateName(stateObj)]] [[_computeStateName(stateObj)]]
<div class="title">[[computePrimaryText(localize, playerObj)]]</div> <div class="title">[[computePrimaryText(localize, playerObj)]]</div>
[[playerObj.secondaryTitle]]<br> [[playerObj.secondaryTitle]]<br />
</div> </div>
</div>
<paper-progress max="[[stateObj.attributes.media_duration]]" value="[[playbackPosition]]" hidden$="[[computeHideProgress(playerObj)]]" class="progress"></paper-progress>
<div class="controls layout horizontal justified">
<paper-icon-button icon="hass:power" on-click="handleTogglePower" invisible$="[[computeHidePowerButton(playerObj)]]" class="self-center secondary"></paper-icon-button>
<div>
<paper-icon-button icon="hass:skip-previous" invisible$="[[!playerObj.supportsPreviousTrack]]" disabled="[[playerObj.isOff]]" on-click="handlePrevious"></paper-icon-button>
<paper-icon-button class="primary" icon="[[computePlaybackControlIcon(playerObj)]]" invisible$="[[!computePlaybackControlIcon(playerObj)]]" disabled="[[playerObj.isOff]]" on-click="handlePlaybackControl"></paper-icon-button>
<paper-icon-button icon="hass:skip-next" invisible$="[[!playerObj.supportsNextTrack]]" disabled="[[playerObj.isOff]]" on-click="handleNext"></paper-icon-button>
</div> </div>
<paper-icon-button icon="hass:dots-vertical" on-click="handleOpenMoreInfo" class="self-center secondary"></paper-icon-button> <paper-progress
max="[[stateObj.attributes.media_duration]]"
value="[[playbackPosition]]"
hidden$="[[computeHideProgress(playerObj)]]"
class="progress"
></paper-progress>
</div> <div class="controls layout horizontal justified">
`; <paper-icon-button
icon="hass:power"
on-click="handleTogglePower"
invisible$="[[computeHidePowerButton(playerObj)]]"
class="self-center secondary"
></paper-icon-button>
<div>
<paper-icon-button
icon="hass:skip-previous"
invisible$="[[!playerObj.supportsPreviousTrack]]"
disabled="[[playerObj.isOff]]"
on-click="handlePrevious"
></paper-icon-button>
<paper-icon-button
class="primary"
icon="[[computePlaybackControlIcon(playerObj)]]"
invisible$="[[!computePlaybackControlIcon(playerObj)]]"
disabled="[[playerObj.isOff]]"
on-click="handlePlaybackControl"
></paper-icon-button>
<paper-icon-button
icon="hass:skip-next"
invisible$="[[!playerObj.supportsNextTrack]]"
disabled="[[playerObj.isOff]]"
on-click="handleNext"
></paper-icon-button>
</div>
<paper-icon-button
icon="hass:dots-vertical"
on-click="handleOpenMoreInfo"
class="self-center secondary"
></paper-icon-button>
</div>
`;
} }
static get properties() { static get properties() {

View File

@ -15,40 +15,42 @@ import computeObjectId from "../common/entity/compute_object_id";
class HaPersistentNotificationCard extends LocalizeMixin(PolymerElement) { class HaPersistentNotificationCard extends LocalizeMixin(PolymerElement) {
static get template() { static get template() {
return html` return html`
<style> <style>
:host { :host {
@apply --paper-font-body1; @apply --paper-font-body1;
} }
ha-markdown { ha-markdown {
display: block; display: block;
padding: 0 16px; padding: 0 16px;
-ms-user-select: initial; -ms-user-select: initial;
-webkit-user-select: initial; -webkit-user-select: initial;
-moz-user-select: initial; -moz-user-select: initial;
} }
ha-markdown p:first-child { ha-markdown p:first-child {
margin-top: 0; margin-top: 0;
} }
ha-markdown p:last-child { ha-markdown p:last-child {
margin-bottom: 0; margin-bottom: 0;
} }
ha-markdown a { ha-markdown a {
color: var(--primary-color); color: var(--primary-color);
} }
ha-markdown img { ha-markdown img {
max-width: 100%; max-width: 100%;
} }
paper-button { paper-button {
margin: 8px; margin: 8px;
font-weight: 500; font-weight: 500;
} }
</style> </style>
<ha-card header="[[computeTitle(stateObj)]]"> <ha-card header="[[computeTitle(stateObj)]]">
<ha-markdown content="[[stateObj.attributes.message]]"></ha-markdown> <ha-markdown content="[[stateObj.attributes.message]]"></ha-markdown>
<paper-button on-click="dismissTap">[[localize('ui.card.persistent_notification.dismiss')]]</paper-button> <paper-button on-click="dismissTap"
</ha-card> >[[localize('ui.card.persistent_notification.dismiss')]]</paper-button
`; >
</ha-card>
`;
} }
static get properties() { static get properties() {

View File

@ -10,76 +10,93 @@ import EventsMixin from "../mixins/events-mixin";
class HaPlantCard extends EventsMixin(PolymerElement) { class HaPlantCard extends EventsMixin(PolymerElement) {
static get template() { static get template() {
return html` return html`
<style> <style>
.banner { .banner {
display: flex; display: flex;
align-items: flex-end; align-items: flex-end;
background-repeat: no-repeat; background-repeat: no-repeat;
background-size: cover; background-size: cover;
background-position: center; background-position: center;
padding-top: 12px; padding-top: 12px;
} }
.has-plant-image .banner { .has-plant-image .banner {
padding-top: 30%; padding-top: 30%;
} }
.header { .header {
@apply --paper-font-headline; @apply --paper-font-headline;
line-height: 40px; line-height: 40px;
padding: 8px 16px; padding: 8px 16px;
} }
.has-plant-image .header { .has-plant-image .header {
font-size: 16px; font-size: 16px;
font-weight: 500; font-weight: 500;
line-height: 16px; line-height: 16px;
padding: 16px; padding: 16px;
color: white; color: white;
width: 100%; width: 100%;
background:rgba(0, 0, 0, var(--dark-secondary-opacity)); background: rgba(0, 0, 0, var(--dark-secondary-opacity));
} }
.content { .content {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
padding: 16px 32px 24px 32px; padding: 16px 32px 24px 32px;
} }
.has-plant-image .content { .has-plant-image .content {
padding-bottom: 16px; padding-bottom: 16px;
} }
ha-icon { ha-icon {
color: var(--paper-item-icon-color); color: var(--paper-item-icon-color);
margin-bottom: 8px; margin-bottom: 8px;
} }
.attributes { .attributes {
cursor: pointer; cursor: pointer;
} }
.attributes div { .attributes div {
text-align: center; text-align: center;
} }
.problem { .problem {
color: var(--google-red-500); color: var(--google-red-500);
font-weight: bold; font-weight: bold;
} }
.uom { .uom {
color: var(--secondary-text-color); color: var(--secondary-text-color);
} }
</style> </style>
<ha-card class$="[[computeImageClass(stateObj.attributes.entity_picture)]]"> <ha-card
<div class="banner" style="background-image:url([[stateObj.attributes.entity_picture]])"> class$="[[computeImageClass(stateObj.attributes.entity_picture)]]"
<div class="header">[[computeTitle(stateObj)]]</div> >
</div> <div
<div class="content"> class="banner"
<template is="dom-repeat" items="[[computeAttributes(stateObj.attributes)]]"> style="background-image:url([[stateObj.attributes.entity_picture]])"
<div class="attributes" on-click="attributeClicked"> >
<div><ha-icon icon="[[computeIcon(item, stateObj.attributes.battery)]]"></ha-icon></div> <div class="header">[[computeTitle(stateObj)]]</div>
<div class$="[[computeAttributeClass(stateObj.attributes.problem, item)]]"> </div>
[[computeValue(stateObj.attributes, item)]] <div class="content">
<template
is="dom-repeat"
items="[[computeAttributes(stateObj.attributes)]]"
>
<div class="attributes" on-click="attributeClicked">
<div>
<ha-icon
icon="[[computeIcon(item, stateObj.attributes.battery)]]"
></ha-icon>
</div>
<div
class$="[[computeAttributeClass(stateObj.attributes.problem, item)]]"
>
[[computeValue(stateObj.attributes, item)]]
</div>
<div class="uom">
[[computeUom(stateObj.attributes.unit_of_measurement_dict,
item)]]
</div>
</div> </div>
<div class="uom">[[computeUom(stateObj.attributes.unit_of_measurement_dict, item)]]</div> </template>
</div> </div>
</template> </ha-card>
</div> `;
</ha-card>
`;
} }
static get properties() { static get properties() {

View File

@ -106,9 +106,7 @@ class HaWeatherCard extends LocalizeMixin(EventsMixin(PolymerElement)) {
<ha-card> <ha-card>
<div class="header"> <div class="header">
[[computeState(stateObj.state, localize)]] [[computeState(stateObj.state, localize)]]
<div class="name"> <div class="name">[[stateObj.attributes.friendly_name]]</div>
[[stateObj.attributes.friendly_name]]
</div>
</div> </div>
<div class="content"> <div class="content">
<div class="now"> <div class="now">
@ -117,26 +115,38 @@ class HaWeatherCard extends LocalizeMixin(EventsMixin(PolymerElement)) {
<ha-icon icon="[[getWeatherIcon(stateObj.state)]]"></ha-icon> <ha-icon icon="[[getWeatherIcon(stateObj.state)]]"></ha-icon>
</template> </template>
<div class="temp"> <div class="temp">
[[stateObj.attributes.temperature]]<span>[[getUnit('temperature')]]</span> [[stateObj.attributes.temperature]]<span
>[[getUnit('temperature')]]</span
>
</div> </div>
</div> </div>
<div class="attributes"> <div class="attributes">
<template is="dom-if" if="[[_showValue(stateObj.attributes.pressure)]]"> <template
is="dom-if"
if="[[_showValue(stateObj.attributes.pressure)]]"
>
<div> <div>
[[localize('ui.card.weather.attributes.air_pressure')]]: [[localize('ui.card.weather.attributes.air_pressure')]]:
[[stateObj.attributes.pressure]] [[getUnit('air_pressure')]] [[stateObj.attributes.pressure]] [[getUnit('air_pressure')]]
</div> </div>
</template> </template>
<template is="dom-if" if="[[_showValue(stateObj.attributes.humidity)]]"> <template
is="dom-if"
if="[[_showValue(stateObj.attributes.humidity)]]"
>
<div> <div>
[[localize('ui.card.weather.attributes.humidity')]]: [[localize('ui.card.weather.attributes.humidity')]]:
[[stateObj.attributes.humidity]] % [[stateObj.attributes.humidity]] %
</div> </div>
</template> </template>
<template is="dom-if" if="[[_showValue(stateObj.attributes.wind_speed)]]"> <template
is="dom-if"
if="[[_showValue(stateObj.attributes.wind_speed)]]"
>
<div> <div>
[[localize('ui.card.weather.attributes.wind_speed')]]: [[localize('ui.card.weather.attributes.wind_speed')]]:
[[getWind(stateObj.attributes.wind_speed, stateObj.attributes.wind_bearing, localize)]] [[getWind(stateObj.attributes.wind_speed,
stateObj.attributes.wind_bearing, localize)]]
</div> </div>
</template> </template>
</div> </div>
@ -145,22 +155,31 @@ class HaWeatherCard extends LocalizeMixin(EventsMixin(PolymerElement)) {
<div class="forecast"> <div class="forecast">
<template is="dom-repeat" items="[[forecast]]"> <template is="dom-repeat" items="[[forecast]]">
<div> <div>
<div class="weekday">[[computeDate(item.datetime)]]<br> <div class="weekday">
[[computeDate(item.datetime)]]<br />
<template is="dom-if" if="[[!_showValue(item.templow)]]"> <template is="dom-if" if="[[!_showValue(item.templow)]]">
[[computeTime(item.datetime)]] [[computeTime(item.datetime)]]
</template> </template>
</div> </div>
<template is="dom-if" if="[[_showValue(item.condition)]]"> <template is="dom-if" if="[[_showValue(item.condition)]]">
<div class="icon"> <div class="icon">
<ha-icon icon="[[getWeatherIcon(item.condition)]]"></ha-icon> <ha-icon
icon="[[getWeatherIcon(item.condition)]]"
></ha-icon>
</div> </div>
</template> </template>
<div class="temp">[[item.temperature]] [[getUnit('temperature')]]</div> <div class="temp">
[[item.temperature]] [[getUnit('temperature')]]
</div>
<template is="dom-if" if="[[_showValue(item.templow)]]"> <template is="dom-if" if="[[_showValue(item.templow)]]">
<div class="templow">[[item.templow]] [[getUnit('temperature')]]</div> <div class="templow">
[[item.templow]] [[getUnit('temperature')]]
</div>
</template> </template>
<template is="dom-if" if="[[_showValue(item.precipitation)]]"> <template is="dom-if" if="[[_showValue(item.precipitation)]]">
<div class="precipitation">[[item.precipitation]] [[getUnit('precipitation')]]</div> <div class="precipitation">
[[item.precipitation]] [[getUnit('precipitation')]]
</div>
</template> </template>
</div> </div>
</template> </template>

View File

@ -8,9 +8,8 @@ export default function attributeClassNames(
return ""; return "";
} }
return attributes return attributes
.map( .map((attribute) =>
(attribute) => attribute in stateObj.attributes ? "has-" + attribute : ""
attribute in stateObj.attributes ? "has-" + attribute : ""
) )
.filter((attr) => attr !== "") .filter((attr) => attr !== "")
.join(" "); .join(" ");

View File

@ -12,10 +12,9 @@ export default function featureClassNames(
const features = stateObj.attributes.supported_features; const features = stateObj.attributes.supported_features;
return Object.keys(classNames) return Object.keys(classNames)
.map( .map((feature) =>
(feature) => // tslint:disable-next-line
// tslint:disable-next-line (features & Number(feature)) !== 0 ? classNames[feature] : ""
(features & Number(feature)) !== 0 ? classNames[feature] : ""
) )
.filter((attr) => attr !== "") .filter((attr) => attr !== "")
.join(" "); .join(" ");

View File

@ -10,7 +10,8 @@ class HaCallApiButton extends LitElement {
.progress="${this.progress}" .progress="${this.progress}"
@click="${this._buttonTapped}" @click="${this._buttonTapped}"
?disabled="${this.disabled}" ?disabled="${this.disabled}"
><slot></slot></ha-progress-button> ><slot></slot
></ha-progress-button>
`; `;
} }

View File

@ -10,8 +10,13 @@ import EventsMixin from "../../mixins/events-mixin";
class HaCallServiceButton extends EventsMixin(PolymerElement) { class HaCallServiceButton extends EventsMixin(PolymerElement) {
static get template() { static get template() {
return html` return html`
<ha-progress-button id="progress" progress="[[progress]]" on-click="buttonTapped"><slot></slot></ha-progress-button> <ha-progress-button
`; id="progress"
progress="[[progress]]"
on-click="buttonTapped"
><slot></slot
></ha-progress-button>
`;
} }
static get properties() { static get properties() {

View File

@ -6,53 +6,55 @@ import { PolymerElement } from "@polymer/polymer/polymer-element";
class HaProgressButton extends PolymerElement { class HaProgressButton extends PolymerElement {
static get template() { static get template() {
return html` return html`
<style> <style>
.container { .container {
position: relative; position: relative;
display: inline-block; display: inline-block;
} }
paper-button { paper-button {
transition: all 1s; transition: all 1s;
} }
.success paper-button { .success paper-button {
color: white; color: white;
background-color: var(--google-green-500); background-color: var(--google-green-500);
transition: none; transition: none;
} }
.error paper-button { .error paper-button {
color: white; color: white;
background-color: var(--google-red-500); background-color: var(--google-red-500);
transition: none; transition: none;
} }
paper-button[disabled] { paper-button[disabled] {
color: #c8c8c8; color: #c8c8c8;
} }
.progress { .progress {
@apply --layout; @apply --layout;
@apply --layout-center-center; @apply --layout-center-center;
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
right: 0; right: 0;
bottom: 0; bottom: 0;
} }
</style> </style>
<div class="container" id="container"> <div class="container" id="container">
<paper-button id="button" disabled="[[computeDisabled(disabled, progress)]]" on-click="buttonTapped"> <paper-button
<slot></slot> id="button"
</paper-button> disabled="[[computeDisabled(disabled, progress)]]"
<template is="dom-if" if="[[progress]]"> on-click="buttonTapped"
<div class="progress"> >
<paper-spinner active=""></paper-spinner> <slot></slot>
</div> </paper-button>
</template> <template is="dom-if" if="[[progress]]">
</div> <div class="progress"><paper-spinner active=""></paper-spinner></div>
`; </template>
</div>
`;
} }
static get properties() { static get properties() {

View File

@ -18,120 +18,126 @@ class HaChartBase extends mixinBehaviors(
) { ) {
static get template() { static get template() {
return html` return html`
<style> <style>
:host { :host {
display: block; display: block;
} }
.chartHeader { .chartHeader {
padding: 6px 0 0 0; padding: 6px 0 0 0;
width: 100%; width: 100%;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
} }
.chartHeader > div { .chartHeader > div {
vertical-align: top; vertical-align: top;
padding: 0 8px; padding: 0 8px;
} }
.chartHeader > div.chartTitle { .chartHeader > div.chartTitle {
padding-top: 8px; padding-top: 8px;
flex: 0 0 0; flex: 0 0 0;
max-width: 30%; max-width: 30%;
} }
.chartHeader > div.chartLegend { .chartHeader > div.chartLegend {
flex: 1 1; flex: 1 1;
min-width: 70%; min-width: 70%;
} }
:root{ :root {
user-select: none; user-select: none;
-moz-user-select: none; -moz-user-select: none;
-webkit-user-select: none; -webkit-user-select: none;
-ms-user-select: none; -ms-user-select: none;
} }
.chartTooltip { .chartTooltip {
font-size: 90%; font-size: 90%;
opacity: 1; opacity: 1;
position: absolute; position: absolute;
background: rgba(80, 80, 80, .9); background: rgba(80, 80, 80, 0.9);
color: white; color: white;
border-radius: 3px; border-radius: 3px;
pointer-events: none; pointer-events: none;
transform: translate(-50%, 12px); transform: translate(-50%, 12px);
z-index: 1000; z-index: 1000;
width: 200px; width: 200px;
transition: opacity 0.15s ease-in-out; transition: opacity 0.15s ease-in-out;
} }
.chartLegend ul, .chartLegend ul,
.chartTooltip ul { .chartTooltip ul {
display: inline-block; display: inline-block;
padding: 0 0px; padding: 0 0px;
margin: 5px 0 0 0; margin: 5px 0 0 0;
width: 100% width: 100%;
} }
.chartTooltip li { .chartTooltip li {
display: block; display: block;
white-space: pre-line; white-space: pre-line;
} }
.chartTooltip .title { .chartTooltip .title {
text-align: center; text-align: center;
font-weight: 500; font-weight: 500;
} }
.chartLegend li { .chartLegend li {
display: inline-block; display: inline-block;
padding: 0 6px; padding: 0 6px;
max-width: 49%; max-width: 49%;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
box-sizing: border-box; box-sizing: border-box;
} }
.chartLegend li:nth-child(odd):last-of-type { .chartLegend li:nth-child(odd):last-of-type {
/* Make last item take full width if it is odd-numbered. */ /* Make last item take full width if it is odd-numbered. */
max-width: 100%; max-width: 100%;
} }
.chartLegend li[data-hidden] { .chartLegend li[data-hidden] {
text-decoration: line-through; text-decoration: line-through;
} }
.chartLegend em, .chartLegend em,
.chartTooltip em { .chartTooltip em {
border-radius: 5px; border-radius: 5px;
display: inline-block; display: inline-block;
height: 10px; height: 10px;
margin-right: 4px; margin-right: 4px;
width: 10px; width: 10px;
} }
paper-icon-button { paper-icon-button {
color: var(--secondary-text-color); color: var(--secondary-text-color);
} }
</style> </style>
<template is="dom-if" if="[[unit]]"> <template is="dom-if" if="[[unit]]">
<div class="chartHeader"> <div class="chartHeader">
<div class="chartTitle">[[unit]]</div> <div class="chartTitle">[[unit]]</div>
<div class="chartLegend"> <div class="chartLegend">
<ul> <ul>
<template is="dom-repeat" items="[[metas]]"> <template is="dom-repeat" items="[[metas]]">
<li on-click="_legendClick" data-hidden$="[[item.hidden]]"> <li on-click="_legendClick" data-hidden$="[[item.hidden]]">
<em style$="background-color:[[item.bgColor]]"></em> <em style$="background-color:[[item.bgColor]]"></em>
[[item.label]] [[item.label]]
</li> </li>
</template> </template>
</ul> </ul>
</div>
</div>
</template>
<div id="chartTarget" style="height:40px; width:100%">
<canvas id="chartCanvas"></canvas>
<div
class$="chartTooltip [[tooltip.yAlign]]"
style$="opacity:[[tooltip.opacity]]; top:[[tooltip.top]]; left:[[tooltip.left]]; padding:[[tooltip.yPadding]]px [[tooltip.xPadding]]px"
>
<div class="title">[[tooltip.title]]</div>
<div>
<ul>
<template is="dom-repeat" items="[[tooltip.lines]]">
<li>
<em style$="background-color:[[item.bgColor]]"></em
>[[item.text]]
</li>
</template>
</ul>
</div>
</div>
</div> </div>
</div> `;
</template>
<div id="chartTarget" style="height:40px; width:100%">
<canvas id="chartCanvas"></canvas>
<div class$="chartTooltip [[tooltip.yAlign]]" style$="opacity:[[tooltip.opacity]]; top:[[tooltip.top]]; left:[[tooltip.left]]; padding:[[tooltip.yPadding]]px [[tooltip.xPadding]]px">
<div class="title">[[tooltip.title]]</div>
<div>
<ul>
<template is="dom-repeat" items="[[tooltip.lines]]">
<li><em style$="background-color:[[item.bgColor]]"></em>[[item.text]]</li>
</template>
</ul>
</div>
</div>
</div>
`;
} }
get chart() { get chart() {

View File

@ -18,56 +18,70 @@ import EventsMixin from "../../mixins/events-mixin";
class HaEntityPicker extends EventsMixin(LocalizeMixin(PolymerElement)) { class HaEntityPicker extends EventsMixin(LocalizeMixin(PolymerElement)) {
static get template() { static get template() {
return html` return html`
<style> <style>
paper-input > paper-icon-button { paper-input > paper-icon-button {
width: 24px; width: 24px;
height: 24px; height: 24px;
padding: 2px; padding: 2px;
color: var(--secondary-text-color); color: var(--secondary-text-color);
} }
[hidden] { [hidden] {
display: none; display: none;
} }
</style> </style>
<vaadin-combo-box-light <vaadin-combo-box-light
items="[[_states]]" items="[[_states]]"
item-value-path="entity_id" item-value-path="entity_id"
item-label-path="entity_id" item-label-path="entity_id"
value="{{value}}" value="{{value}}"
opened="{{opened}}" opened="{{opened}}"
allow-custom-value="[[allowCustomEntity]]" allow-custom-value="[[allowCustomEntity]]"
on-change='_fireChanged' on-change="_fireChanged"
> >
<paper-input <paper-input
autofocus="[[autofocus]]" autofocus="[[autofocus]]"
label="[[_computeLabel(label, localize)]]" label="[[_computeLabel(label, localize)]]"
class="input" class="input"
autocapitalize='none' autocapitalize="none"
autocomplete='off' autocomplete="off"
autocorrect='off' autocorrect="off"
spellcheck='false' spellcheck="false"
value="[[value]]" value="[[value]]"
disabled="[[disabled]]"> disabled="[[disabled]]"
<paper-icon-button slot="suffix" class="clear-button" icon="hass:close" no-ripple="" hidden$="[[!value]]">Clear</paper-icon-button> >
<paper-icon-button slot="suffix" class="toggle-button" icon="[[_computeToggleIcon(opened)]]" hidden="[[!_states.length]]">Toggle</paper-icon-button> <paper-icon-button
</paper-input> slot="suffix"
<template> class="clear-button"
<style> icon="hass:close"
paper-icon-item { no-ripple=""
margin: -10px; hidden$="[[!value]]"
padding: 0; >Clear</paper-icon-button
} >
</style> <paper-icon-button
<paper-icon-item> slot="suffix"
<state-badge state-obj="[[item]]" slot="item-icon"></state-badge> class="toggle-button"
<paper-item-body two-line=""> icon="[[_computeToggleIcon(opened)]]"
<div>[[_computeStateName(item)]]</div> hidden="[[!_states.length]]"
<div secondary="">[[item.entity_id]]</div> >Toggle</paper-icon-button
</paper-item-body> >
</paper-icon-item> </paper-input>
</template> <template>
</vaadin-combo-box-light> <style>
`; paper-icon-item {
margin: -10px;
padding: 0;
}
</style>
<paper-icon-item>
<state-badge state-obj="[[item]]" slot="item-icon"></state-badge>
<paper-item-body two-line="">
<div>[[_computeStateName(item)]]</div>
<div secondary="">[[item.entity_id]]</div>
</paper-item-body>
</paper-icon-item>
</template>
</vaadin-combo-box-light>
`;
} }
static get properties() { static get properties() {

View File

@ -9,34 +9,48 @@ import computeStateDomain from "../../common/entity/compute_state_domain";
class HaEntityToggle extends PolymerElement { class HaEntityToggle extends PolymerElement {
static get template() { static get template() {
return html` return html`
<style> <style>
:host { :host {
white-space: nowrap; white-space: nowrap;
min-width: 38px; min-width: 38px;
} }
paper-icon-button { paper-icon-button {
color: var(--paper-icon-button-inactive-color, var(--primary-text-color)); color: var(
transition: color .5s; --paper-icon-button-inactive-color,
} var(--primary-text-color)
paper-icon-button[state-active] { );
color: var(--paper-icon-button-active-color, var(--primary-color)); transition: color 0.5s;
} }
paper-toggle-button { paper-icon-button[state-active] {
cursor: pointer; color: var(--paper-icon-button-active-color, var(--primary-color));
--paper-toggle-button-label-spacing: 0; }
padding: 13px 5px; paper-toggle-button {
margin: -4px -5px; cursor: pointer;
} --paper-toggle-button-label-spacing: 0;
</style> padding: 13px 5px;
margin: -4px -5px;
}
</style>
<template is="dom-if" if="[[stateObj.attributes.assumed_state]]"> <template is="dom-if" if="[[stateObj.attributes.assumed_state]]">
<paper-icon-button icon="hass:flash-off" on-click="turnOff" state-active$="[[!isOn]]"></paper-icon-button> <paper-icon-button
<paper-icon-button icon="hass:flash" on-click="turnOn" state-active$="[[isOn]]"></paper-icon-button> icon="hass:flash-off"
</template> on-click="turnOff"
<template is="dom-if" if="[[!stateObj.attributes.assumed_state]]"> state-active$="[[!isOn]]"
<paper-toggle-button checked="[[toggleChecked]]" on-change="toggleChanged"></paper-toggle-button> ></paper-icon-button>
</template> <paper-icon-button
`; icon="hass:flash"
on-click="turnOn"
state-active$="[[isOn]]"
></paper-icon-button>
</template>
<template is="dom-if" if="[[!stateObj.attributes.assumed_state]]">
<paper-toggle-button
checked="[[toggleChecked]]"
on-change="toggleChanged"
></paper-toggle-button>
</template>
`;
} }
static get properties() { static get properties() {

View File

@ -6,7 +6,9 @@ import stateIcon from "../../common/entity/state_icon";
class HaStateIcon extends PolymerElement { class HaStateIcon extends PolymerElement {
static get template() { static get template() {
return html`<ha-icon icon="[[computeIcon(stateObj)]]"></ha-icon>`; return html`
<ha-icon icon="[[computeIcon(stateObj)]]"></ha-icon>
`;
} }
static get properties() { static get properties() {

View File

@ -21,46 +21,56 @@ import LocalizeMixin from "../../mixins/localize-mixin";
class HaStateLabelBadge extends LocalizeMixin(EventsMixin(PolymerElement)) { class HaStateLabelBadge extends LocalizeMixin(EventsMixin(PolymerElement)) {
static get template() { static get template() {
return html` return html`
<style> <style>
:host { :host {
cursor: pointer; cursor: pointer;
} }
ha-label-badge { ha-label-badge {
--ha-label-badge-color: var(--label-badge-red, #DF4C1E); --ha-label-badge-color: var(--label-badge-red, #df4c1e);
} }
ha-label-badge.has-unit_of_measurement { ha-label-badge.has-unit_of_measurement {
--ha-label-badge-label-text-transform: none; --ha-label-badge-label-text-transform: none;
} }
ha-label-badge.binary_sensor, ha-label-badge.binary_sensor,
ha-label-badge.updater { ha-label-badge.updater {
--ha-label-badge-color: var(--label-badge-blue, #039be5); --ha-label-badge-color: var(--label-badge-blue, #039be5);
} }
.red { .red {
--ha-label-badge-color: var(--label-badge-red, #DF4C1E); --ha-label-badge-color: var(--label-badge-red, #df4c1e);
} }
.blue { .blue {
--ha-label-badge-color: var(--label-badge-blue, #039be5); --ha-label-badge-color: var(--label-badge-blue, #039be5);
} }
.green { .green {
--ha-label-badge-color: var(--label-badge-green, #0DA035); --ha-label-badge-color: var(--label-badge-green, #0da035);
} }
.yellow { .yellow {
--ha-label-badge-color: var(--label-badge-yellow, #f4b400); --ha-label-badge-color: var(--label-badge-yellow, #f4b400);
} }
.grey { .grey {
--ha-label-badge-color: var(--label-badge-grey, var(--paper-grey-500)); --ha-label-badge-color: var(
} --label-badge-grey,
</style> var(--paper-grey-500)
);
}
</style>
<ha-label-badge class$="[[computeClassNames(state)]]" value="[[computeValue(localize, state)]]" icon="[[computeIcon(state)]]" image="[[computeImage(state)]]" label="[[computeLabel(localize, state, _timerTimeRemaining)]]" description="[[computeDescription(state)]]"></ha-label-badge> <ha-label-badge
`; class$="[[computeClassNames(state)]]"
value="[[computeValue(localize, state)]]"
icon="[[computeIcon(state)]]"
image="[[computeImage(state)]]"
label="[[computeLabel(localize, state, _timerTimeRemaining)]]"
description="[[computeDescription(state)]]"
></ha-label-badge>
`;
} }
static get properties() { static get properties() {

View File

@ -8,45 +8,45 @@ import stateIcon from "../../common/entity/state_icon";
class StateBadge extends PolymerElement { class StateBadge extends PolymerElement {
static get template() { static get template() {
return html` return html`
<style> <style>
:host { :host {
position: relative; position: relative;
display: inline-block; display: inline-block;
width: 40px; width: 40px;
color: var(--paper-item-icon-color, #44739e); color: var(--paper-item-icon-color, #44739e);
border-radius: 50%; border-radius: 50%;
height: 40px; height: 40px;
text-align: center; text-align: center;
background-size: cover; background-size: cover;
line-height: 40px; line-height: 40px;
} }
ha-icon { ha-icon {
transition: color .3s ease-in-out, filter .3s ease-in-out; transition: color 0.3s ease-in-out, filter 0.3s ease-in-out;
} }
/* Color the icon if light or sun is on */ /* Color the icon if light or sun is on */
ha-icon[data-domain=light][data-state=on], ha-icon[data-domain="light"][data-state="on"],
ha-icon[data-domain=switch][data-state=on], ha-icon[data-domain="switch"][data-state="on"],
ha-icon[data-domain=binary_sensor][data-state=on], ha-icon[data-domain="binary_sensor"][data-state="on"],
ha-icon[data-domain=fan][data-state=on], ha-icon[data-domain="fan"][data-state="on"],
ha-icon[data-domain=sun][data-state=above_horizon] { ha-icon[data-domain="sun"][data-state="above_horizon"] {
color: var(--paper-item-icon-active-color, #FDD835); color: var(--paper-item-icon-active-color, #fdd835);
} }
/* Color the icon if unavailable */ /* Color the icon if unavailable */
ha-icon[data-state=unavailable] { ha-icon[data-state="unavailable"] {
color: var(--state-icon-unavailable-color); color: var(--state-icon-unavailable-color);
} }
</style> </style>
<ha-icon <ha-icon
id="icon" id="icon"
data-domain$="[[_computeDomain(stateObj)]]" data-domain$="[[_computeDomain(stateObj)]]"
data-state$="[[stateObj.state]]" data-state$="[[stateObj.state]]"
icon="[[_computeIcon(stateObj)]]" icon="[[_computeIcon(stateObj)]]"
></ha-icon> ></ha-icon>
`; `;
} }
static get properties() { static get properties() {

View File

@ -8,70 +8,74 @@ import computeStateName from "../../common/entity/compute_state_name";
class StateInfo extends PolymerElement { class StateInfo extends PolymerElement {
static get template() { static get template() {
return html` return html`
${this.styleTemplate} ${this.styleTemplate} ${this.stateBadgeTemplate} ${this.infoTemplate}
${this.stateBadgeTemplate} `;
${this.infoTemplate}
`;
} }
static get styleTemplate() { static get styleTemplate() {
return html` return html`
<style> <style>
:host { :host {
@apply --paper-font-body1; @apply --paper-font-body1;
min-width: 120px; min-width: 120px;
white-space: nowrap; white-space: nowrap;
} }
state-badge { state-badge {
float: left; float: left;
} }
.info { .info {
margin-left: 56px; margin-left: 56px;
} }
.name { .name {
@apply --paper-font-common-nowrap; @apply --paper-font-common-nowrap;
color: var(--primary-text-color); color: var(--primary-text-color);
line-height: 40px; line-height: 40px;
} }
.name[in-dialog], :host([secondary-line]) .name { .name[in-dialog],
line-height: 20px; :host([secondary-line]) .name {
} line-height: 20px;
}
.time-ago, .extra-info, .extra-info > * { .time-ago,
@apply --paper-font-common-nowrap; .extra-info,
color: var(--secondary-text-color); .extra-info > * {
} @apply --paper-font-common-nowrap;
</style> color: var(--secondary-text-color);
`; }
</style>
`;
} }
static get stateBadgeTemplate() { static get stateBadgeTemplate() {
return html` return html`
<state-badge state-obj="[[stateObj]]"></state-badge> <state-badge state-obj="[[stateObj]]"></state-badge>
`; `;
} }
static get infoTemplate() { static get infoTemplate() {
return html` return html`
<div class="info"> <div class="info">
<div class="name" in-dialog$="[[inDialog]]">[[computeStateName(stateObj)]]</div> <div class="name" in-dialog$="[[inDialog]]">
[[computeStateName(stateObj)]]
<template is="dom-if" if="[[inDialog]]">
<div class="time-ago">
<ha-relative-time hass="[[hass]]" datetime="[[stateObj.last_changed]]"></ha-relative-time>
</div> </div>
</template>
<template is="dom-if" if="[[!inDialog]]"> <template is="dom-if" if="[[inDialog]]">
<div class="extra-info"> <div class="time-ago">
<slot> <ha-relative-time
</slot></div> hass="[[hass]]"
</template> datetime="[[stateObj.last_changed]]"
</div> ></ha-relative-time>
`; </div>
</template>
<template is="dom-if" if="[[!inDialog]]">
<div class="extra-info"><slot> </slot></div>
</template>
</div>
`;
} }
static get properties() { static get properties() {

View File

@ -7,27 +7,35 @@ import hassAttributeUtil from "../util/hass-attributes-util";
class HaAttributes extends PolymerElement { class HaAttributes extends PolymerElement {
static get template() { static get template() {
return html` return html`
<style include="iron-flex iron-flex-alignment"></style> <style include="iron-flex iron-flex-alignment"></style>
<style> <style>
.data-entry .value { .data-entry .value {
max-width: 200px; max-width: 200px;
} }
.attribution { .attribution {
color: var(--secondary-text-color); color: var(--secondary-text-color);
text-align: right; text-align: right;
} }
</style> </style>
<div class="layout vertical"> <div class="layout vertical">
<template is="dom-repeat" items="[[computeDisplayAttributes(stateObj, filtersArray)]]" as="attribute"> <template
<div class="data-entry layout justified horizontal"> is="dom-repeat"
<div class="key">[[formatAttribute(attribute)]]</div> items="[[computeDisplayAttributes(stateObj, filtersArray)]]"
<div class="value">[[formatAttributeValue(stateObj, attribute)]]</div> as="attribute"
>
<div class="data-entry layout justified horizontal">
<div class="key">[[formatAttribute(attribute)]]</div>
<div class="value">
[[formatAttributeValue(stateObj, attribute)]]
</div>
</div>
</template>
<div class="attribution" hidden$="[[!computeAttribution(stateObj)]]">
[[computeAttribution(stateObj)]]
</div> </div>
</template> </div>
<div class="attribution" hidden$="[[!computeAttribution(stateObj)]]">[[computeAttribution(stateObj)]]</div> `;
</div>
`;
} }
static get properties() { static get properties() {

View File

@ -5,27 +5,27 @@ import { PolymerElement } from "@polymer/polymer/polymer-element";
class HaCard extends PolymerElement { class HaCard extends PolymerElement {
static get template() { static get template() {
return html` return html`
<style include="paper-material-styles"> <style include="paper-material-styles">
:host { :host {
@apply --paper-material-elevation-1; @apply --paper-material-elevation-1;
display: block; display: block;
border-radius: 2px; border-radius: 2px;
transition: all 0.30s ease-out; transition: all 0.3s ease-out;
background-color: var(--paper-card-background-color, white); background-color: var(--paper-card-background-color, white);
} }
.header { .header {
@apply --paper-font-headline; @apply --paper-font-headline;
@apply --paper-font-common-expensive-kerning; @apply --paper-font-common-expensive-kerning;
opacity: var(--dark-primary-opacity); opacity: var(--dark-primary-opacity);
padding: 24px 16px 16px; padding: 24px 16px 16px;
} }
</style> </style>
<template is="dom-if" if="[[header]]"> <template is="dom-if" if="[[header]]">
<div class="header">[[header]]</div> <div class="header">[[header]]</div>
</template> </template>
<slot></slot> <slot></slot>
`; `;
} }
static get properties() { static get properties() {

View File

@ -78,72 +78,75 @@ const iterateDomainSorted = (collection, func) => {
class HaCards extends PolymerElement { class HaCards extends PolymerElement {
static get template() { static get template() {
return html` return html`
<style include="iron-flex iron-flex-factors"></style> <style include="iron-flex iron-flex-factors"></style>
<style> <style>
:host { :host {
display: block; display: block;
padding: 4px 4px 0; padding: 4px 4px 0;
transform: translateZ(0); transform: translateZ(0);
position: relative; position: relative;
} }
.badges { .badges {
font-size: 85%; font-size: 85%;
text-align: center; text-align: center;
padding-top: 16px; padding-top: 16px;
} }
.column { .column {
max-width: 500px; max-width: 500px;
overflow-x: hidden; overflow-x: hidden;
} }
ha-card-chooser { ha-card-chooser {
display: block; display: block;
margin: 4px 4px 8px; margin: 4px 4px 8px;
} }
@media (max-width: 500px) { @media (max-width: 500px) {
:host { :host {
padding-left: 0; padding-left: 0;
padding-right: 0; padding-right: 0;
} }
ha-card-chooser { ha-card-chooser {
margin-left: 0; margin-left: 0;
margin-right: 0; margin-right: 0;
} }
} }
@media (max-width: 599px) { @media (max-width: 599px) {
.column { .column {
max-width: 600px; max-width: 600px;
} }
} }
</style> </style>
<div id="main"> <div id="main">
<template is="dom-if" if="[[cards.badges]]"> <template is="dom-if" if="[[cards.badges]]">
<div class="badges"> <div class="badges">
<template is="dom-if" if="[[cards.demo]]"> <template is="dom-if" if="[[cards.demo]]">
<ha-demo-badge></ha-demo-badge> <ha-demo-badge></ha-demo-badge>
</template>
<ha-badges-card
states="[[cards.badges]]"
hass="[[hass]]"
></ha-badges-card>
</div>
</template> </template>
<ha-badges-card states="[[cards.badges]]" hass="[[hass]]"></ha-badges-card> <div class="horizontal layout center-justified">
</div> <template is="dom-repeat" items="[[cards.columns]]" as="column">
</template> <div class="column flex-1">
<template is="dom-repeat" items="[[column]]" as="card">
<div class="horizontal layout center-justified"> <ha-card-chooser card-data="[[card]]"></ha-card-chooser>
<template is="dom-repeat" items="[[cards.columns]]" as="column"> </template>
<div class="column flex-1"> </div>
<template is="dom-repeat" items="[[column]]" as="card">
<ha-card-chooser card-data="[[card]]"></ha-card-chooser>
</template> </template>
</div> </div>
</template> </div>
</div> `;
</div>
`;
} }
static get properties() { static get properties() {

View File

@ -11,44 +11,48 @@ import EventsMixin from "../mixins/events-mixin";
class HaClimateControl extends EventsMixin(PolymerElement) { class HaClimateControl extends EventsMixin(PolymerElement) {
static get template() { static get template() {
return html` return html`
<style include="iron-flex iron-flex-alignment"></style> <style include="iron-flex iron-flex-alignment"></style>
<style> <style>
/* local DOM styles go here */ /* local DOM styles go here */
:host { :host {
@apply --layout-flex; @apply --layout-flex;
@apply --layout-horizontal; @apply --layout-horizontal;
@apply --layout-justified; @apply --layout-justified;
} }
.in-flux#target_temperature { .in-flux#target_temperature {
color: var(--google-red-500); color: var(--google-red-500);
} }
#target_temperature { #target_temperature {
@apply --layout-self-center; @apply --layout-self-center;
font-size: 200%; font-size: 200%;
} }
.control-buttons { .control-buttons {
font-size: 200%; font-size: 200%;
text-align: right; text-align: right;
} }
paper-icon-button { paper-icon-button {
height: 48px; height: 48px;
width: 48px; width: 48px;
} }
</style> </style>
<!-- local DOM goes here --> <!-- local DOM goes here -->
<div id="target_temperature"> <div id="target_temperature">[[value]] [[units]]</div>
[[value]] [[units]] <div class="control-buttons">
</div> <div>
<div class="control-buttons"> <paper-icon-button
<div> icon="hass:chevron-up"
<paper-icon-button icon="hass:chevron-up" on-click="incrementValue"></paper-icon-button> on-click="incrementValue"
></paper-icon-button>
</div>
<div>
<paper-icon-button
icon="hass:chevron-down"
on-click="decrementValue"
></paper-icon-button>
</div>
</div> </div>
<div> `;
<paper-icon-button icon="hass:chevron-down" on-click="decrementValue"></paper-icon-button>
</div>
</div>
`;
} }
static get properties() { static get properties() {

View File

@ -9,43 +9,41 @@ import LocalizeMixin from "../mixins/localize-mixin";
class HaClimateState extends LocalizeMixin(PolymerElement) { class HaClimateState extends LocalizeMixin(PolymerElement) {
static get template() { static get template() {
return html` return html`
<style> <style>
:host { :host {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: center;
white-space: nowrap; white-space: nowrap;
} }
.target { .target {
color: var(--primary-text-color); color: var(--primary-text-color);
} }
.current { .current {
color: var(--secondary-text-color); color: var(--secondary-text-color);
} }
.state-label { .state-label {
font-weight: bold; font-weight: bold;
text-transform: capitalize; text-transform: capitalize;
} }
</style> </style>
<div class="target"> <div class="target">
<template is="dom-if" if="[[_hasKnownState(stateObj.state)]]"> <template is="dom-if" if="[[_hasKnownState(stateObj.state)]]">
<span class="state-label"> <span class="state-label"> [[_localizeState(stateObj.state)]] </span>
[[_localizeState(stateObj.state)]] </template>
</span> [[computeTarget(hass, stateObj)]]
</template>
[[computeTarget(hass, stateObj)]]
</div>
<template is="dom-if" if="[[currentStatus]]">
<div class="current">
[[localize('ui.card.climate.currently')]]: [[currentStatus]]
</div> </div>
</template>
`; <template is="dom-if" if="[[currentStatus]]">
<div class="current">
[[localize('ui.card.climate.currently')]]: [[currentStatus]]
</div>
</template>
`;
} }
static get properties() { static get properties() {

View File

@ -11,73 +11,95 @@ import EventsMixin from "../mixins/events-mixin";
class HaColorPicker extends EventsMixin(PolymerElement) { class HaColorPicker extends EventsMixin(PolymerElement) {
static get template() { static get template() {
return html` return html`
<style> <style>
:host { :host {
user-select: none; user-select: none;
-webkit-user-select: none; -webkit-user-select: none;
} }
#canvas { #canvas {
position: relative; position: relative;
width: 100%; width: 100%;
max-width: 330px; max-width: 330px;
} }
#canvas > * { #canvas > * {
display: block; display: block;
} }
#interactionLayer { #interactionLayer {
color: white; color: white;
position: absolute; position: absolute;
cursor: crosshair; cursor: crosshair;
width: 100%; width: 100%;
height: 100%; height: 100%;
overflow: visible; overflow: visible;
} }
#backgroundLayer { #backgroundLayer {
width: 100%; width: 100%;
overflow: visible; overflow: visible;
--wheel-bordercolor: var(--ha-color-picker-wheel-bordercolor, white); --wheel-bordercolor: var(--ha-color-picker-wheel-bordercolor, white);
--wheel-borderwidth: var(--ha-color-picker-wheel-borderwidth, 3); --wheel-borderwidth: var(--ha-color-picker-wheel-borderwidth, 3);
--wheel-shadow: var(--ha-color-picker-wheel-shadow, rgb(15, 15, 15) 10px 5px 5px 0px); --wheel-shadow: var(
} --ha-color-picker-wheel-shadow,
rgb(15, 15, 15) 10px 5px 5px 0px
);
}
#marker { #marker {
fill: currentColor; fill: currentColor;
stroke: var(--ha-color-picker-marker-bordercolor, white); stroke: var(--ha-color-picker-marker-bordercolor, white);
stroke-width: var(--ha-color-picker-marker-borderwidth, 3); stroke-width: var(--ha-color-picker-marker-borderwidth, 3);
filter: url(#marker-shadow) filter: url(#marker-shadow);
} }
.dragging #marker { .dragging #marker {
} }
#colorTooltip { #colorTooltip {
display: none; display: none;
fill: currentColor; fill: currentColor;
stroke: var(--ha-color-picker-tooltip-bordercolor, white); stroke: var(--ha-color-picker-tooltip-bordercolor, white);
stroke-width: var(--ha-color-picker-tooltip-borderwidth, 3); stroke-width: var(--ha-color-picker-tooltip-borderwidth, 3);
} }
.touch.dragging #colorTooltip { .touch.dragging #colorTooltip {
display: inherit; display: inherit;
} }
</style>
</style> <div id="canvas">
<div id="canvas"> <svg id="interactionLayer">
<svg id="interactionLayer"> <defs>
<defs> <filter
<filter id="marker-shadow" x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox"> id="marker-shadow"
<feOffset result="offOut" in="SourceAlpha" dx="2" dy="2"></feOffset> x="-50%"
<feGaussianBlur result="blurOut" in="offOut" stdDeviation="2"></feGaussianBlur> y="-50%"
<feComponentTransfer in="blurOut" result="alphaOut"> width="200%"
<feFuncA type="linear" slope="0.3"></feFuncA> height="200%"
</feComponentTransfer> filterUnits="objectBoundingBox"
<feBlend in="SourceGraphic" in2="alphaOut" mode="normal"></feBlend> >
</filter> <feOffset
</defs> result="offOut"
</svg> in="SourceAlpha"
<canvas id="backgroundLayer"></canvas> dx="2"
</div> dy="2"
`; ></feOffset>
<feGaussianBlur
result="blurOut"
in="offOut"
stdDeviation="2"
></feGaussianBlur>
<feComponentTransfer in="blurOut" result="alphaOut">
<feFuncA type="linear" slope="0.3"></feFuncA>
</feComponentTransfer>
<feBlend
in="SourceGraphic"
in2="alphaOut"
mode="normal"
></feBlend>
</filter>
</defs>
</svg>
<canvas id="backgroundLayer"></canvas>
</div>
`;
} }
static get properties() { static get properties() {
@ -246,8 +268,8 @@ class HaColorPicker extends EventsMixin(PolymerElement) {
} }
/* /*
* General event/selection handling * General event/selection handling
*/ */
// Process user input to color // Process user input to color
processUserSelect(ev) { processUserSelect(ev) {
@ -290,8 +312,8 @@ class HaColorPicker extends EventsMixin(PolymerElement) {
} }
/* /*
* Interface updating * Interface updating
*/ */
// set marker position to the given color // set marker position to the given color
setMarkerOnColor(hs) { setMarkerOnColor(hs) {
@ -328,8 +350,8 @@ class HaColorPicker extends EventsMixin(PolymerElement) {
} }
/* /*
* input processing helpers * input processing helpers
*/ */
// get angle (degrees) // get angle (degrees)
getAngle(dX, dY) { getAngle(dX, dY) {
@ -349,8 +371,8 @@ class HaColorPicker extends EventsMixin(PolymerElement) {
} }
/* /*
* Getting colors * Getting colors
*/ */
getColor(x, y) { getColor(x, y) {
var hue = this.getAngle(x, y); // degrees, clockwise from right var hue = this.getAngle(x, y); // degrees, clockwise from right
@ -387,8 +409,8 @@ class HaColorPicker extends EventsMixin(PolymerElement) {
} }
/* /*
* Drawing related stuff * Drawing related stuff
*/ */
setupLayers() { setupLayers() {
this.canvas = this.$.canvas; this.canvas = this.$.canvas;
@ -410,8 +432,8 @@ class HaColorPicker extends EventsMixin(PolymerElement) {
drawColorWheel() { drawColorWheel() {
/* /*
* Setting up all paremeters * Setting up all paremeters
*/ */
let shadowColor; let shadowColor;
let shadowOffsetX; let shadowOffsetX;
let shadowOffsetY; let shadowOffsetY;
@ -446,8 +468,8 @@ class HaColorPicker extends EventsMixin(PolymerElement) {
const shadowRadius = radius + borderWidth; const shadowRadius = radius + borderWidth;
/* /*
* Drawing functions * Drawing functions
*/ */
function drawCircle(hueSegments, saturationSegments) { function drawCircle(hueSegments, saturationSegments) {
hueSegments = hueSegments || 360; // reset 0 segments to 360 hueSegments = hueSegments || 360; // reset 0 segments to 360
const angleStep = 360 / hueSegments; const angleStep = 360 / hueSegments;
@ -524,9 +546,9 @@ class HaColorPicker extends EventsMixin(PolymerElement) {
} }
/* /*
* Call the drawing functions * Call the drawing functions
* draws the shadow, wheel and border * draws the shadow, wheel and border
*/ */
if (wheelStyle.shadow !== "none") { if (wheelStyle.shadow !== "none") {
drawShadow(); drawShadow();
} }
@ -537,9 +559,9 @@ class HaColorPicker extends EventsMixin(PolymerElement) {
} }
/* /*
* Draw the (draggable) marker and tooltip * Draw the (draggable) marker and tooltip
* on the interactionLayer) * on the interactionLayer)
*/ */
drawMarker() { drawMarker() {
const svgElement = this.interactionLayer; const svgElement = this.interactionLayer;

View File

@ -10,41 +10,58 @@ import EventsMixin from "../mixins/events-mixin";
class HaComboBox extends EventsMixin(PolymerElement) { class HaComboBox extends EventsMixin(PolymerElement) {
static get template() { static get template() {
return html` return html`
<style> <style>
paper-input > paper-icon-button { paper-input > paper-icon-button {
width: 24px; width: 24px;
height: 24px; height: 24px;
padding: 2px; padding: 2px;
color: var(--secondary-text-color); color: var(--secondary-text-color);
} }
[hidden] { [hidden] {
display: none; display: none;
} }
</style> </style>
<vaadin-combo-box-light <vaadin-combo-box-light
items="[[_items]]" items="[[_items]]"
item-value-path="[[itemValuePath]]" item-value-path="[[itemValuePath]]"
item-label-path="[[itemLabelPath]]" item-label-path="[[itemLabelPath]]"
value="{{value}}" value="{{value}}"
opened="{{opened}}" opened="{{opened}}"
allow-custom-value="[[allowCustomValue]]" allow-custom-value="[[allowCustomValue]]"
on-change='_fireChanged' on-change="_fireChanged"
> >
<paper-input autofocus="[[autofocus]]" label="[[label]]" class="input" value="[[value]]"> <paper-input
<paper-icon-button slot="suffix" class="clear-button" icon="hass:close" hidden$="[[!value]]">Clear</paper-icon-button> autofocus="[[autofocus]]"
<paper-icon-button slot="suffix" class="toggle-button" icon="[[_computeToggleIcon(opened)]]" hidden$="[[!items.length]]">Toggle</paper-icon-button> label="[[label]]"
</paper-input> class="input"
<template> value="[[value]]"
<style> >
<paper-icon-button
slot="suffix"
class="clear-button"
icon="hass:close"
hidden$="[[!value]]"
>Clear</paper-icon-button
>
<paper-icon-button
slot="suffix"
class="toggle-button"
icon="[[_computeToggleIcon(opened)]]"
hidden$="[[!items.length]]"
>Toggle</paper-icon-button
>
</paper-input>
<template>
<style>
paper-item { paper-item {
margin: -5px -10px; margin: -5px -10px;
padding: 0; padding: 0;
} }
</style> </style>
<paper-item>[[_computeItemLabel(item, itemLabelPath)]]</paper-item> <paper-item>[[_computeItemLabel(item, itemLabelPath)]]</paper-item>
</template> </template>
</vaadin-combo-box-light> </vaadin-combo-box-light>
`; `;
} }
static get properties() { static get properties() {

View File

@ -7,21 +7,35 @@ import CoverEntity from "../util/cover-model";
class HaCoverControls extends PolymerElement { class HaCoverControls extends PolymerElement {
static get template() { static get template() {
return html` return html`
<style> <style>
.state { .state {
white-space: nowrap; white-space: nowrap;
} }
[invisible] { [invisible] {
visibility: hidden !important; visibility: hidden !important;
} }
</style> </style>
<div class="state"> <div class="state">
<paper-icon-button icon="hass:arrow-up" on-click="onOpenTap" invisible$="[[!entityObj.supportsOpen]]" disabled="[[computeOpenDisabled(stateObj, entityObj)]]"></paper-icon-button> <paper-icon-button
<paper-icon-button icon="hass:stop" on-click="onStopTap" invisible$="[[!entityObj.supportsStop]]"></paper-icon-button> icon="hass:arrow-up"
<paper-icon-button icon="hass:arrow-down" on-click="onCloseTap" invisible$="[[!entityObj.supportsClose]]" disabled="[[computeClosedDisabled(stateObj, entityObj)]]"></paper-icon-button> on-click="onOpenTap"
</div> invisible$="[[!entityObj.supportsOpen]]"
`; disabled="[[computeOpenDisabled(stateObj, entityObj)]]"
></paper-icon-button>
<paper-icon-button
icon="hass:stop"
on-click="onStopTap"
invisible$="[[!entityObj.supportsStop]]"
></paper-icon-button>
<paper-icon-button
icon="hass:arrow-down"
on-click="onCloseTap"
invisible$="[[!entityObj.supportsClose]]"
disabled="[[computeClosedDisabled(stateObj, entityObj)]]"
></paper-icon-button>
</div>
`;
} }
static get properties() { static get properties() {

View File

@ -8,19 +8,36 @@ import CoverEntity from "../util/cover-model";
class HaCoverTiltControls extends PolymerElement { class HaCoverTiltControls extends PolymerElement {
static get template() { static get template() {
return html` return html`
<style include="iron-flex"></style> <style include="iron-flex"></style>
<style> <style>
:host { :host {
white-space: nowrap; white-space: nowrap;
} }
[invisible] { [invisible] {
visibility: hidden !important; visibility: hidden !important;
} }
</style> </style>
<paper-icon-button icon="hass:arrow-top-right" on-click="onOpenTiltTap" title="Open tilt" invisible$="[[!entityObj.supportsOpenTilt]]" disabled="[[computeOpenDisabled(stateObj, entityObj)]]"></paper-icon-button> <paper-icon-button
<paper-icon-button icon="hass:stop" on-click="onStopTiltTap" invisible$="[[!entityObj.supportsStopTilt]]" title="Stop tilt"></paper-icon-button> icon="hass:arrow-top-right"
<paper-icon-button icon="hass:arrow-bottom-left" on-click="onCloseTiltTap" title="Close tilt" invisible$="[[!entityObj.supportsCloseTilt]]" disabled="[[computeClosedDisabled(stateObj, entityObj)]]"></paper-icon-button> on-click="onOpenTiltTap"
`; title="Open tilt"
invisible$="[[!entityObj.supportsOpenTilt]]"
disabled="[[computeOpenDisabled(stateObj, entityObj)]]"
></paper-icon-button>
<paper-icon-button
icon="hass:stop"
on-click="onStopTiltTap"
invisible$="[[!entityObj.supportsStopTilt]]"
title="Stop tilt"
></paper-icon-button>
<paper-icon-button
icon="hass:arrow-bottom-left"
on-click="onCloseTiltTap"
title="Close tilt"
invisible$="[[!entityObj.supportsCloseTilt]]"
disabled="[[computeClosedDisabled(stateObj, entityObj)]]"
></paper-icon-button>
`;
} }
static get properties() { static get properties() {

View File

@ -6,14 +6,18 @@ import "./ha-label-badge";
class HaDemoBadge extends PolymerElement { class HaDemoBadge extends PolymerElement {
static get template() { static get template() {
return html` return html`
<style> <style>
:host { :host {
--ha-label-badge-color: #dac90d; --ha-label-badge-color: #dac90d;
} }
</style> </style>
<ha-label-badge icon="hass:emoticon" label="Demo" description=""></ha-label-badge> <ha-label-badge
`; icon="hass:emoticon"
label="Demo"
description=""
></ha-label-badge>
`;
} }
} }

View File

@ -16,107 +16,156 @@ import EventsMixin from "../mixins/events-mixin";
class HaForm extends EventsMixin(PolymerElement) { class HaForm extends EventsMixin(PolymerElement) {
static get template() { static get template() {
return html` return html`
<style> <style>
.error { .error {
color: red; color: red;
} }
paper-checkbox { paper-checkbox {
display: inline-block; display: inline-block;
padding: 22px 0; padding: 22px 0;
} }
</style> </style>
<template is="dom-if" if="[[_isArray(schema)]]" restamp=""> <template is="dom-if" if="[[_isArray(schema)]]" restamp="">
<template is="dom-if" if="[[error.base]]"> <template is="dom-if" if="[[error.base]]">
<div class='error'>[[computeError(error.base, schema)]]</div> <div class="error">[[computeError(error.base, schema)]]</div>
</template>
<template is="dom-repeat" items="[[schema]]">
<ha-form data="[[_getValue(data, item)]]" schema="[[item]]" error="[[_getValue(error, item)]]" on-data-changed="_valueChanged" compute-label="[[computeLabel]]" compute-error="[[computeError]]"></ha-form>
</template>
</template>
<template is="dom-if" if="[[!_isArray(schema)]]" restamp="">
<template is="dom-if" if="[[error]]">
<div class="error">[[computeError(error, schema)]]</div>
</template>
<template is="dom-if" if="[[_equals(schema.type, &quot;string&quot;)]]" restamp="">
<template is="dom-if" if="[[_includes(schema.name, &quot;password&quot;)]]" restamp="">
<paper-input
type="[[_passwordFieldType(unmaskedPassword)]]"
label="[[computeLabel(schema)]]"
value="{{data}}"
required="[[schema.required]]"
auto-validate="[[schema.required]]"
error-message='Required'
>
<paper-icon-button toggles
active="{{unmaskedPassword}}"
slot="suffix"
icon="[[_passwordFieldIcon(unmaskedPassword)]]"
id="iconButton"
title="Click to toggle between masked and clear password">
</paper-icon-button>
</paper-input>
</template> </template>
<template is="dom-if" if="[[!_includes(schema.name, &quot;password&quot;)]]" restamp="">
<template is="dom-repeat" items="[[schema]]">
<ha-form
data="[[_getValue(data, item)]]"
schema="[[item]]"
error="[[_getValue(error, item)]]"
on-data-changed="_valueChanged"
compute-label="[[computeLabel]]"
compute-error="[[computeError]]"
></ha-form>
</template>
</template>
<template is="dom-if" if="[[!_isArray(schema)]]" restamp="">
<template is="dom-if" if="[[error]]">
<div class="error">[[computeError(error, schema)]]</div>
</template>
<template
is="dom-if"
if="[[_equals(schema.type, &quot;string&quot;)]]"
restamp=""
>
<template
is="dom-if"
if="[[_includes(schema.name, &quot;password&quot;)]]"
restamp=""
>
<paper-input
type="[[_passwordFieldType(unmaskedPassword)]]"
label="[[computeLabel(schema)]]"
value="{{data}}"
required="[[schema.required]]"
auto-validate="[[schema.required]]"
error-message="Required"
>
<paper-icon-button
toggles
active="{{unmaskedPassword}}"
slot="suffix"
icon="[[_passwordFieldIcon(unmaskedPassword)]]"
id="iconButton"
title="Click to toggle between masked and clear password"
>
</paper-icon-button>
</paper-input>
</template>
<template
is="dom-if"
if="[[!_includes(schema.name, &quot;password&quot;)]]"
restamp=""
>
<paper-input
label="[[computeLabel(schema)]]"
value="{{data}}"
required="[[schema.required]]"
auto-validate="[[schema.required]]"
error-message="Required"
></paper-input>
</template>
</template>
<template
is="dom-if"
if="[[_equals(schema.type, &quot;integer&quot;)]]"
restamp=""
>
<template is="dom-if" if="[[_isRange(schema)]]" restamp="">
<div>
[[computeLabel(schema)]]
<ha-paper-slider
pin=""
value="{{data}}"
min="[[schema.valueMin]]"
max="[[schema.valueMax]]"
></ha-paper-slider>
</div>
</template>
<template is="dom-if" if="[[!_isRange(schema)]]" restamp="">
<paper-input
label="[[computeLabel(schema)]]"
value="{{data}}"
type="number"
required="[[schema.required]]"
auto-validate="[[schema.required]]"
error-message="Required"
></paper-input>
</template>
</template>
<template
is="dom-if"
if="[[_equals(schema.type, &quot;float&quot;)]]"
restamp=""
>
<!-- TODO -->
<paper-input <paper-input
label="[[computeLabel(schema)]]" label="[[computeLabel(schema)]]"
value="{{data}}" value="{{data}}"
required="[[schema.required]]" required="[[schema.required]]"
auto-validate="[[schema.required]]" auto-validate="[[schema.required]]"
error-message='Required' error-message="Required"
></paper-input> ></paper-input>
</template> </template>
</template>
<template is="dom-if" if="[[_equals(schema.type, &quot;integer&quot;)]]" restamp=""> <template
<template is="dom-if" if="[[_isRange(schema)]]" restamp=""> is="dom-if"
if="[[_equals(schema.type, &quot;boolean&quot;)]]"
restamp=""
>
<div> <div>
[[computeLabel(schema)]] <paper-checkbox checked="{{data}}"
<ha-paper-slider pin="" value="{{data}}" min="[[schema.valueMin]]" max="[[schema.valueMax]]"></ha-paper-slider> >[[computeLabel(schema)]]</paper-checkbox
>
</div> </div>
</template> </template>
<template is="dom-if" if="[[!_isRange(schema)]]" restamp="">
<paper-input <template
label="[[computeLabel(schema)]]" is="dom-if"
value="{{data}}" if="[[_equals(schema.type, &quot;select&quot;)]]"
type="number" restamp=""
required="[[schema.required]]" >
auto-validate="[[schema.required]]" <paper-dropdown-menu label="[[computeLabel(schema)]]">
error-message='Required' <paper-listbox
></paper-input> slot="dropdown-content"
attr-for-selected="item-name"
selected="{{data}}"
>
<template is="dom-repeat" items="[[schema.options]]">
<paper-item item-name$="[[_optionValue(item)]]"
>[[_optionLabel(item)]]</paper-item
>
</template>
</paper-listbox>
</paper-dropdown-menu>
</template> </template>
</template> </template>
`;
<template is="dom-if" if="[[_equals(schema.type, &quot;float&quot;)]]" restamp="">
<!--TODO-->
<paper-input
label="[[computeLabel(schema)]]"
value="{{data}}"
required="[[schema.required]]"
auto-validate="[[schema.required]]"
error-message='Required'
></paper-input>
</template>
<template is="dom-if" if="[[_equals(schema.type, &quot;boolean&quot;)]]" restamp="">
<div>
<paper-checkbox checked="{{data}}">[[computeLabel(schema)]]</paper-checkbox>
</div>
</template>
<template is="dom-if" if="[[_equals(schema.type, &quot;select&quot;)]]" restamp="">
<paper-dropdown-menu label="[[computeLabel(schema)]]">
<paper-listbox slot="dropdown-content" attr-for-selected="item-name" selected="{{data}}">
<template is="dom-repeat" items="[[schema.options]]">
<paper-item item-name$="[[_optionValue(item)]]">[[_optionLabel(item)]]</paper-item>
</template>
</paper-listbox>
</paper-dropdown-menu>
</template>
</template>
`;
} }
static get properties() { static get properties() {

View File

@ -5,94 +5,100 @@ import "./ha-icon";
class HaLabelBadge extends PolymerElement { class HaLabelBadge extends PolymerElement {
static get template() { static get template() {
return html` return html`
<style> <style>
.badge-container { .badge-container {
display: inline-block; display: inline-block;
text-align: center; text-align: center;
vertical-align: top; vertical-align: top;
} }
.label-badge { .label-badge {
position: relative; position: relative;
display: block; display: block;
margin: 0 auto; margin: 0 auto;
width: var(--ha-label-badge-size, 2.5em); width: var(--ha-label-badge-size, 2.5em);
text-align: center; text-align: center;
height: var(--ha-label-badge-size, 2.5em); height: var(--ha-label-badge-size, 2.5em);
line-height: var(--ha-label-badge-size, 2.5em); line-height: var(--ha-label-badge-size, 2.5em);
font-size: var(--ha-label-badge-font-size, 1.5em); font-size: var(--ha-label-badge-font-size, 1.5em);
border-radius: 50%; border-radius: 50%;
border: 0.1em solid var(--ha-label-badge-color, var(--primary-color)); border: 0.1em solid var(--ha-label-badge-color, var(--primary-color));
color: var(--label-badge-text-color, rgb(76, 76, 76)); color: var(--label-badge-text-color, rgb(76, 76, 76));
white-space: nowrap; white-space: nowrap;
background-color: var(--label-badge-background-color, white); background-color: var(--label-badge-background-color, white);
background-size: cover; background-size: cover;
transition: border .3s ease-in-out; transition: border 0.3s ease-in-out;
} }
.label-badge .value { .label-badge .value {
font-size: 90%; font-size: 90%;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
} }
.label-badge .value.big { .label-badge .value.big {
font-size: 70%; font-size: 70%;
} }
.label-badge .label { .label-badge .label {
position: absolute; position: absolute;
bottom: -1em; bottom: -1em;
/* Make the label as wide as container+border. (parent_borderwidth / font-size) */ /* Make the label as wide as container+border. (parent_borderwidth / font-size) */
left: -0.2em; left: -0.2em;
right: -0.2em; right: -0.2em;
line-height: 1em; line-height: 1em;
font-size: 0.5em; font-size: 0.5em;
} }
.label-badge .label span { .label-badge .label span {
box-sizing: border-box; box-sizing: border-box;
max-width: 100%; max-width: 100%;
display: inline-block; display: inline-block;
background-color: var(--ha-label-badge-color, var(--primary-color)); background-color: var(--ha-label-badge-color, var(--primary-color));
color: var(--ha-label-badge-label-color, white); color: var(--ha-label-badge-label-color, white);
border-radius: 1em; border-radius: 1em;
padding: 9% 16% 8% 16%; /* mostly apitalized text, not much descenders => bit more top margin */ padding: 9% 16% 8% 16%; /* mostly apitalized text, not much descenders => bit more top margin */
font-weight: 500; font-weight: 500;
overflow: hidden; overflow: hidden;
text-transform: uppercase; text-transform: uppercase;
text-overflow: ellipsis; text-overflow: ellipsis;
transition: background-color .3s ease-in-out; transition: background-color 0.3s ease-in-out;
text-transform: var(--ha-label-badge-label-text-transform, uppercase); text-transform: var(--ha-label-badge-label-text-transform, uppercase);
} }
.label-badge .label.big span { .label-badge .label.big span {
font-size: 90%; font-size: 90%;
padding: 10% 12% 7% 12%; /* push smaller text a bit down to center vertically */ padding: 10% 12% 7% 12%; /* push smaller text a bit down to center vertically */
} }
.badge-container .title { .badge-container .title {
margin-top: 1em; margin-top: 1em;
font-size: var(--ha-label-badge-title-font-size, .9em); font-size: var(--ha-label-badge-title-font-size, 0.9em);
width: var(--ha-label-badge-title-width, 5em); width: var(--ha-label-badge-title-width, 5em);
font-weight: var(--ha-label-badge-title-font-weight, 400); font-weight: var(--ha-label-badge-title-font-weight, 400);
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
line-height: normal; line-height: normal;
} }
[hidden] { [hidden] {
display: none !important; display: none !important;
} }
</style> </style>
<div class="badge-container"> <div class="badge-container">
<div class="label-badge" id="badge"> <div class="label-badge" id="badge">
<div class$="[[computeValueClasses(value)]]"> <div class$="[[computeValueClasses(value)]]">
<ha-icon icon="[[icon]]" hidden$="[[computeHideIcon(icon, value, image)]]"></ha-icon> <ha-icon
<span hidden$="[[computeHideValue(value, image)]]">[[value]]</span> icon="[[icon]]"
</div> hidden$="[[computeHideIcon(icon, value, image)]]"
<div hidden$="[[computeHideLabel(label)]]" class$="[[computeLabelClasses(label)]]"> ></ha-icon>
<span>[[label]]</span> <span hidden$="[[computeHideValue(value, image)]]">[[value]]</span>
</div>
<div
hidden$="[[computeHideLabel(label)]]"
class$="[[computeLabelClasses(label)]]"
>
<span>[[label]]</span>
</div>
</div> </div>
<div class="title" hidden$="[[!description]]">[[description]]</div>
</div> </div>
<div class="title" hidden$="[[!description]]">[[description]]</div> `;
</div>
`;
} }
static get properties() { static get properties() {

View File

@ -7,40 +7,42 @@ import "./ha-icon";
class HaLabeledSlider extends PolymerElement { class HaLabeledSlider extends PolymerElement {
static get template() { static get template() {
return html` return html`
<style> <style>
:host { :host {
display: block; display: block;
} }
.title { .title {
margin-bottom: 16px; margin-bottom: 16px;
opacity: var(--dark-primary-opacity); opacity: var(--dark-primary-opacity);
} }
ha-icon { ha-icon {
float: left; float: left;
margin-top: 4px; margin-top: 4px;
opacity: var(--dark-secondary-opacity); opacity: var(--dark-secondary-opacity);
} }
ha-paper-slider { ha-paper-slider {
background-image: var(--ha-slider-background); background-image: var(--ha-slider-background);
} }
</style> </style>
<div class="title">[[caption]]</div> <div class="title">[[caption]]</div>
<div class="extra-container"> <div class="extra-container"><slot name="extra"></slot></div>
<slot name="extra"></slot> <div class="slider-container">
</div> <ha-icon icon="[[icon]]" hidden$="[[!icon]]"></ha-icon>
<div class="slider-container"> <ha-paper-slider
<ha-icon icon="[[icon]]" hidden$="[[!icon]]"></ha-icon> min="[[min]]"
<ha-paper-slider max="[[max]]"
min="[[min]]" max="[[max]]" step="[[step]]" step="[[step]]"
pin="[[pin]]" disabled="[[disabled]]" disabled="[[disabled]]" pin="[[pin]]"
value="{{value}}" disabled="[[disabled]]"
></ha-paper-slider> disabled="[[disabled]]"
</div> value="{{value}}"
`; ></ha-paper-slider>
</div>
`;
} }
static get properties() { static get properties() {

View File

@ -10,11 +10,11 @@ import EventsMixin from "../mixins/events-mixin";
class HaMenuButton extends EventsMixin(PolymerElement) { class HaMenuButton extends EventsMixin(PolymerElement) {
static get template() { static get template() {
return html` return html`
<paper-icon-button <paper-icon-button
icon="[[_getIcon(hassio)]]" icon="[[_getIcon(hassio)]]"
on-click="toggleMenu" on-click="toggleMenu"
></paper-icon-button> ></paper-icon-button>
`; `;
} }
static get properties() { static get properties() {

View File

@ -17,11 +17,11 @@ export const pushSupported =
class HaPushNotificationsToggle extends EventsMixin(PolymerElement) { class HaPushNotificationsToggle extends EventsMixin(PolymerElement) {
static get template() { static get template() {
return html` return html`
<paper-toggle-button <paper-toggle-button
disabled="[[_compDisabled(disabled, loading)]]" disabled="[[_compDisabled(disabled, loading)]]"
checked="{{pushChecked}}" checked="{{pushChecked}}"
></paper-toggle-button> ></paper-toggle-button>
`; `;
} }
static get properties() { static get properties() {

View File

@ -4,8 +4,8 @@ import { PolymerElement } from "@polymer/polymer/polymer-element";
class HaServiceDescription extends PolymerElement { class HaServiceDescription extends PolymerElement {
static get template() { static get template() {
return html` return html`
[[_getDescription(hass, domain, service)]] [[_getDescription(hass, domain, service)]]
`; `;
} }
static get properties() { static get properties() {

View File

@ -11,8 +11,13 @@ import LocalizeMixin from "../mixins/localize-mixin";
class HaServicePicker extends LocalizeMixin(PolymerElement) { class HaServicePicker extends LocalizeMixin(PolymerElement) {
static get template() { static get template() {
return html` return html`
<ha-combo-box label="[[localize('ui.components.service-picker.service')]]" items="[[_services]]" value="{{value}}" allow-custom-value=""></ha-combo-box> <ha-combo-box
`; label="[[localize('ui.components.service-picker.service')]]"
items="[[_services]]"
value="{{value}}"
allow-custom-value=""
></ha-combo-box>
`;
} }
static get properties() { static get properties() {

View File

@ -12,8 +12,12 @@ import isComponentLoaded from "../common/config/is_component_loaded";
class HaStartVoiceButton extends EventsMixin(PolymerElement) { class HaStartVoiceButton extends EventsMixin(PolymerElement) {
static get template() { static get template() {
return html` return html`
<paper-icon-button icon="hass:microphone" hidden$="[[!canListen]]" on-click="handleListenClick"></paper-icon-button> <paper-icon-button
`; icon="hass:microphone"
hidden$="[[!canListen]]"
on-click="handleListenClick"
></paper-icon-button>
`;
} }
static get properties() { static get properties() {

View File

@ -21,10 +21,7 @@ class HaTextarea extends PolymerElement {
display: block; display: block;
} }
</style> </style>
<paper-textarea <paper-textarea label="[[label]]" value="{{value}}"></paper-textarea>
label='[[label]]'
value='{{value}}'
></paper-textarea>
`; `;
} }

View File

@ -43,7 +43,7 @@ class HaVacuumState extends LocalizeMixin(PolymerElement) {
font-weight: 500; font-weight: 500;
top: 3px; top: 3px;
height: 37px; height: 37px;
margin-right: -.57em; margin-right: -0.57em;
} }
paper-button[disabled] { paper-button[disabled] {
background-color: transparent; background-color: transparent;
@ -51,10 +51,9 @@ class HaVacuumState extends LocalizeMixin(PolymerElement) {
} }
</style> </style>
<paper-button <paper-button on-click="_callService" disabled="[[!_interceptable]]"
on-click="_callService" >[[_computeLabel(stateObj.state, _interceptable)]]</paper-button
disabled="[[!_interceptable]]" >
>[[_computeLabel(stateObj.state, _interceptable)]]</paper-button>
`; `;
} }

View File

@ -11,44 +11,48 @@ import EventsMixin from "../mixins/events-mixin";
class HaWaterHeaterControl extends EventsMixin(PolymerElement) { class HaWaterHeaterControl extends EventsMixin(PolymerElement) {
static get template() { static get template() {
return html` return html`
<style include="iron-flex iron-flex-alignment"></style> <style include="iron-flex iron-flex-alignment"></style>
<style> <style>
/* local DOM styles go here */ /* local DOM styles go here */
:host { :host {
@apply --layout-flex; @apply --layout-flex;
@apply --layout-horizontal; @apply --layout-horizontal;
@apply --layout-justified; @apply --layout-justified;
} }
.in-flux#target_temperature { .in-flux#target_temperature {
color: var(--google-red-500); color: var(--google-red-500);
} }
#target_temperature { #target_temperature {
@apply --layout-self-center; @apply --layout-self-center;
font-size: 200%; font-size: 200%;
} }
.control-buttons { .control-buttons {
font-size: 200%; font-size: 200%;
text-align: right; text-align: right;
} }
paper-icon-button { paper-icon-button {
height: 48px; height: 48px;
width: 48px; width: 48px;
} }
</style> </style>
<!-- local DOM goes here --> <!-- local DOM goes here -->
<div id="target_temperature"> <div id="target_temperature">[[value]] [[units]]</div>
[[value]] [[units]] <div class="control-buttons">
</div> <div>
<div class="control-buttons"> <paper-icon-button
<div> icon="hass:chevron-up"
<paper-icon-button icon="hass:chevron-up" on-click="incrementValue"></paper-icon-button> on-click="incrementValue"
></paper-icon-button>
</div>
<div>
<paper-icon-button
icon="hass:chevron-down"
on-click="decrementValue"
></paper-icon-button>
</div>
</div> </div>
<div> `;
<paper-icon-button icon="hass:chevron-down" on-click="decrementValue"></paper-icon-button>
</div>
</div>
`;
} }
static get properties() { static get properties() {

View File

@ -9,41 +9,39 @@ import LocalizeMixin from "../mixins/localize-mixin";
class HaWaterHeaterState extends LocalizeMixin(PolymerElement) { class HaWaterHeaterState extends LocalizeMixin(PolymerElement) {
static get template() { static get template() {
return html` return html`
<style> <style>
:host { :host {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: center;
white-space: nowrap; white-space: nowrap;
} }
.target { .target {
color: var(--primary-text-color); color: var(--primary-text-color);
} }
.current { .current {
color: var(--secondary-text-color); color: var(--secondary-text-color);
} }
.state-label { .state-label {
font-weight: bold; font-weight: bold;
text-transform: capitalize; text-transform: capitalize;
} }
</style> </style>
<div class="target"> <div class="target">
<span class="state-label"> <span class="state-label"> [[_localizeState(stateObj.state)]] </span>
[[_localizeState(stateObj.state)]] [[computeTarget(hass, stateObj)]]
</span>
[[computeTarget(hass, stateObj)]]
</div>
<template is="dom-if" if="[[currentStatus]]">
<div class="current">
[[localize('ui.card.water_heater.currently')]]: [[currentStatus]]
</div> </div>
</template>
`; <template is="dom-if" if="[[currentStatus]]">
<div class="current">
[[localize('ui.card.water_heater.currently')]]: [[currentStatus]]
</div>
</template>
`;
} }
static get properties() { static get properties() {

View File

@ -43,13 +43,11 @@ class PaperTimeInput extends PolymerElement {
-moz-appearance: textfield; -moz-appearance: textfield;
@apply --paper-time-input-cotnainer; @apply --paper-time-input-cotnainer;
} }
;
--paper-input-container-input-webkit-spinner: { --paper-input-container-input-webkit-spinner: {
-webkit-appearance: none; -webkit-appearance: none;
margin: 0; margin: 0;
display: none; display: none;
} }
;
} }
paper-dropdown-menu { paper-dropdown-menu {
@ -57,20 +55,23 @@ class PaperTimeInput extends PolymerElement {
padding: 0; padding: 0;
/* Force ripple to use the whole container */ /* Force ripple to use the whole container */
--paper-dropdown-menu-ripple: { --paper-dropdown-menu-ripple: {
color: var(--paper-time-input-dropdown-ripple-color, var(--primary-color)); color: var(
}; --paper-time-input-dropdown-ripple-color,
var(--primary-color)
);
}
--paper-input-container-input: { --paper-input-container-input: {
@apply --paper-font-button; @apply --paper-font-button;
text-align: center; text-align: center;
padding-left: 5px; padding-left: 5px;
@apply --paper-time-dropdown-input-cotnainer; @apply --paper-time-dropdown-input-cotnainer;
}; }
--paper-input-container-underline: { --paper-input-container-underline: {
border-color: transparent; border-color: transparent;
} }
--paper-input-container-underline-focus: { --paper-input-container-underline-focus: {
border-color: transparent; border-color: transparent;
}; }
} }
paper-item { paper-item {
@ -99,27 +100,58 @@ class PaperTimeInput extends PolymerElement {
<label hidden$="[[hideLabel]]">[[label]]</label> <label hidden$="[[hideLabel]]">[[label]]</label>
<div class="time-input-wrap"> <div class="time-input-wrap">
<!-- Hour Input --> <!-- Hour Input -->
<paper-input id="hour" type="number" value="{{hour}}" on-change="_shouldFormatHour" required="" auto-validate="[[autoValidate]]" <paper-input
prevent-invalid-input="" maxlength="2" max="[[_computeHourMax(format)]]" min="0" no-label-float="" disabled="[[disabled]]"> id="hour"
type="number"
value="{{hour}}"
on-change="_shouldFormatHour"
required=""
auto-validate="[[autoValidate]]"
prevent-invalid-input=""
maxlength="2"
max="[[_computeHourMax(format)]]"
min="0"
no-label-float=""
disabled="[[disabled]]"
>
<span suffix="" slot="suffix">:</span> <span suffix="" slot="suffix">:</span>
</paper-input> </paper-input>
<!-- Min Input --> <!-- Min Input -->
<paper-input id="min" type="number" value="{{min}}" on-change="_formatMin" required="" auto-validate="[[autoValidate]]" prevent-invalid-input="" <paper-input
maxlength="2" max="59" min="0" no-label-float="" disabled="[[disabled]]"> id="min"
type="number"
value="{{min}}"
on-change="_formatMin"
required=""
auto-validate="[[autoValidate]]"
prevent-invalid-input=""
maxlength="2"
max="59"
min="0"
no-label-float=""
disabled="[[disabled]]"
>
</paper-input> </paper-input>
<!-- Dropdown Menu --> <!-- Dropdown Menu -->
<paper-dropdown-menu id="dropdown" required="" hidden$="[[_equal(format, 24)]]" no-label-float="" disabled="[[disabled]]"> <paper-dropdown-menu
id="dropdown"
<paper-listbox attr-for-selected="name" selected="{{amPm}}" slot="dropdown-content"> required=""
hidden$="[[_equal(format, 24)]]"
no-label-float=""
disabled="[[disabled]]"
>
<paper-listbox
attr-for-selected="name"
selected="{{amPm}}"
slot="dropdown-content"
>
<paper-item name="AM">AM</paper-item> <paper-item name="AM">AM</paper-item>
<paper-item name="PM">PM</paper-item> <paper-item name="PM">PM</paper-item>
</paper-listbox> </paper-listbox>
</paper-dropdown-menu> </paper-dropdown-menu>
</div> </div>
`; `;
} }

View File

@ -10,16 +10,21 @@ import formatDateTime from "../common/datetime/format_date_time";
class StateHistoryChartLine extends LocalizeMixin(PolymerElement) { class StateHistoryChartLine extends LocalizeMixin(PolymerElement) {
static get template() { static get template() {
return html` return html`
<style> <style>
:host { :host {
display: block; display: block;
overflow: hidden; overflow: hidden;
height: 0; height: 0;
transition: height 0.3s ease-in-out; transition: height 0.3s ease-in-out;
} }
</style> </style>
<ha-chart-base id="chart" data="[[chartData]]" identifier="[[identifier]]" rendered="{{rendered}}"></ha-chart-base> <ha-chart-base
`; id="chart"
data="[[chartData]]"
identifier="[[identifier]]"
rendered="{{rendered}}"
></ha-chart-base>
`;
} }
static get properties() { static get properties() {

View File

@ -11,19 +11,21 @@ import formatDateTime from "../common/datetime/format_date_time";
class StateHistoryChartTimeline extends LocalizeMixin(PolymerElement) { class StateHistoryChartTimeline extends LocalizeMixin(PolymerElement) {
static get template() { static get template() {
return html` return html`
<style> <style>
:host { :host {
display: block; display: block;
opacity: 0; opacity: 0;
transition: opacity 0.3s ease-in-out; transition: opacity 0.3s ease-in-out;
} }
:host([rendered]) { :host([rendered]) {
opacity: 1; opacity: 1;
} }
</style>
</style> <ha-chart-base
<ha-chart-base data="[[chartData]]" rendered="{{rendered}}"></ha-chart-base> data="[[chartData]]"
`; rendered="{{rendered}}"
></ha-chart-base>
`;
} }
static get properties() { static get properties() {

View File

@ -10,48 +10,60 @@ import LocalizeMixin from "../mixins/localize-mixin";
class StateHistoryCharts extends LocalizeMixin(PolymerElement) { class StateHistoryCharts extends LocalizeMixin(PolymerElement) {
static get template() { static get template() {
return html` return html`
<style> <style>
:host { :host {
display: block; display: block;
/* height of single timeline chart = 58px */ /* height of single timeline chart = 58px */
min-height: 58px; min-height: 58px;
} }
.info { .info {
text-align: center; text-align: center;
line-height: 58px; line-height: 58px;
color: var(--secondary-text-color); color: var(--secondary-text-color);
} }
</style> </style>
<template is="dom-if" class="info" if="[[_computeIsLoading(isLoadingData)]]"> <template
<div class="info">[[localize('ui.components.history_charts.loading_history')]]</div> is="dom-if"
</template> class="info"
if="[[_computeIsLoading(isLoadingData)]]"
>
<div class="info">
[[localize('ui.components.history_charts.loading_history')]]
</div>
</template>
<template is="dom-if" class="info" if="[[_computeIsEmpty(isLoadingData, historyData)]]"> <template
<div class="info">[[localize('ui.components.history_charts.no_history_found')]]</div> is="dom-if"
</template> class="info"
if="[[_computeIsEmpty(isLoadingData, historyData)]]"
>
<div class="info">
[[localize('ui.components.history_charts.no_history_found')]]
</div>
</template>
<template is="dom-if" if="[[historyData.timeline.length]]"> <template is="dom-if" if="[[historyData.timeline.length]]">
<state-history-chart-timeline <state-history-chart-timeline
hass='[[hass]]' hass="[[hass]]"
data="[[historyData.timeline]]" data="[[historyData.timeline]]"
end-time="[[_computeEndTime(endTime, upToNow, historyData)]]" end-time="[[_computeEndTime(endTime, upToNow, historyData)]]"
no-single="[[noSingle]]" no-single="[[noSingle]]"
names="[[names]]" names="[[names]]"
></state-history-chart-timeline> ></state-history-chart-timeline>
</template> </template>
<template is="dom-repeat" items="[[historyData.line]]"> <template is="dom-repeat" items="[[historyData.line]]">
<state-history-chart-line <state-history-chart-line
hass='[[hass]]' hass="[[hass]]"
unit="[[item.unit]]" unit="[[item.unit]]"
data="[[item.data]]" data="[[item.data]]"
identifier="[[item.identifier]]" identifier="[[item.identifier]]"
is-single-device="[[_computeIsSingleLineChart(item.data, noSingle)]]" is-single-device="[[_computeIsSingleLineChart(item.data, noSingle)]]"
end-time="[[_computeEndTime(endTime, upToNow, historyData)]]" end-time="[[_computeEndTime(endTime, upToNow, historyData)]]"
names="[[names]]" names="[[names]]"
></state-history-chart-line> ></state-history-chart-line>
</template> </template>
`; `;
} }
static get properties() { static get properties() {

View File

@ -19,67 +19,82 @@ import DialogMixin from "../mixins/dialog-mixin";
class HaMoreInfoDialog extends DialogMixin(PolymerElement) { class HaMoreInfoDialog extends DialogMixin(PolymerElement) {
static get template() { static get template() {
return html` return html`
<style include="ha-style-dialog paper-dialog-shared-styles"> <style include="ha-style-dialog paper-dialog-shared-styles">
:host {
font-size: 14px;
width: 365px;
border-radius: 2px;
}
more-info-controls, more-info-settings {
--more-info-header-background: var(--secondary-background-color);
--more-info-header-color: var(--primary-text-color);
--ha-more-info-app-toolbar-title: {
/* Design guideline states 24px, changed to 16 to align with state info */
margin-left: 16px;
line-height: 1.3em;
max-height: 2.6em;
overflow: hidden;
/* webkit and blink still support simple multiline text-overflow */
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
text-overflow: ellipsis;
}
}
/* overrule the ha-style-dialog max-height on small screens */
@media all and (max-width: 450px), all and (max-height: 500px) {
more-info-controls, more-info-settings {
--more-info-header-background: var(--primary-color);
--more-info-header-color: var(--text-primary-color);
}
:host { :host {
@apply --ha-dialog-fullscreen; font-size: 14px;
width: 365px;
border-radius: 2px;
} }
:host::before {
content: ""; more-info-controls,
position: fixed; more-info-settings {
z-index: -1; --more-info-header-background: var(--secondary-background-color);
top: 0px; --more-info-header-color: var(--primary-text-color);
left: 0px; --ha-more-info-app-toolbar-title: {
right: 0px; /* Design guideline states 24px, changed to 16 to align with state info */
bottom: 0px; margin-left: 16px;
background-color: inherit; line-height: 1.3em;
max-height: 2.6em;
overflow: hidden;
/* webkit and blink still support simple multiline text-overflow */
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
text-overflow: ellipsis;
}
} }
}
:host([data-domain=camera]) { /* overrule the ha-style-dialog max-height on small screens */
width: auto; @media all and (max-width: 450px), all and (max-height: 500px) {
} more-info-controls,
more-info-settings {
--more-info-header-background: var(--primary-color);
--more-info-header-color: var(--text-primary-color);
}
:host {
@apply --ha-dialog-fullscreen;
}
:host::before {
content: "";
position: fixed;
z-index: -1;
top: 0px;
left: 0px;
right: 0px;
bottom: 0px;
background-color: inherit;
}
}
:host([data-domain=history_graph]), :host([large]) { :host([data-domain="camera"]) {
width: 90%; width: auto;
} }
</style>
<template is="dom-if" if="[[!_page]]"> :host([data-domain="history_graph"]),
<more-info-controls class="no-padding" hass="[[hass]]" state-obj="[[stateObj]]" dialog-element="[[_dialogElement]]" can-configure="[[_registryInfo]]" large="{{large}}"></more-info-controls> :host([large]) {
</template> width: 90%;
<template is="dom-if" if="[[_equals(_page, &quot;settings&quot;)]]"> }
<more-info-settings class="no-padding" hass="[[hass]]" state-obj="[[stateObj]]" registry-info="{{_registryInfo}}"></more-info-settings> </style>
</template>
`; <template is="dom-if" if="[[!_page]]">
<more-info-controls
class="no-padding"
hass="[[hass]]"
state-obj="[[stateObj]]"
dialog-element="[[_dialogElement]]"
can-configure="[[_registryInfo]]"
large="{{large}}"
></more-info-controls>
</template>
<template is="dom-if" if="[[_equals(_page, &quot;settings&quot;)]]">
<more-info-settings
class="no-padding"
hass="[[hass]]"
state-obj="[[stateObj]]"
registry-info="{{_registryInfo}}"
></more-info-settings>
</template>
`;
} }
static get properties() { static get properties() {

View File

@ -10,7 +10,7 @@ import "../resources/ha-style";
class HaStoreAuth extends LocalizeMixin(PolymerElement) { class HaStoreAuth extends LocalizeMixin(PolymerElement) {
static get template() { static get template() {
return html` return html`
<style include='ha-style'> <style include="ha-style">
paper-card { paper-card {
position: fixed; position: fixed;
padding: 8px 0; padding: 8px 0;
@ -31,12 +31,14 @@ class HaStoreAuth extends LocalizeMixin(PolymerElement) {
} }
</style> </style>
<paper-card elevation="4"> <paper-card elevation="4">
<div class='card-content'> <div class="card-content">[[localize('ui.auth_store.ask')]]</div>
[[localize('ui.auth_store.ask')]] <div class="card-actions">
</div> <paper-button on-click="_done"
<div class='card-actions'> >[[localize('ui.auth_store.decline')]]</paper-button
<paper-button on-click='_done'>[[localize('ui.auth_store.decline')]]</paper-button> >
<paper-button primary on-click='_save'>[[localize('ui.auth_store.confirm')]]</paper-button> <paper-button primary on-click="_save"
>[[localize('ui.auth_store.confirm')]]</paper-button
>
</div> </div>
</paper-card> </paper-card>
`; `;

View File

@ -12,124 +12,128 @@ import DialogMixin from "../mixins/dialog-mixin";
class HaVoiceCommandDialog extends DialogMixin(PolymerElement) { class HaVoiceCommandDialog extends DialogMixin(PolymerElement) {
static get template() { static get template() {
return html` return html`
<style include="paper-dialog-shared-styles"> <style include="paper-dialog-shared-styles">
iron-icon { iron-icon {
margin-right: 8px; margin-right: 8px;
}
.content {
width: 450px;
min-height: 80px;
font-size: 18px;
padding: 16px;
}
.messages {
max-height: 50vh;
overflow: auto;
}
.messages::after {
content: "";
clear: both;
display: block;
}
.message {
clear: both;
margin: 8px 0;
padding: 8px;
border-radius: 15px;
}
.message.user {
margin-left: 24px;
float: right;
text-align: right;
border-bottom-right-radius: 0px;
background-color: var(--light-primary-color);
color: var(--primary-text-color);
}
.message.hass {
margin-right: 24px;
float: left;
border-bottom-left-radius: 0px;
background-color: var(--primary-color);
color: var(--text-primary-color);
}
.message.error {
background-color: var(--google-red-500);
color: var(--text-primary-color);
}
.icon {
text-align: center;
}
.icon paper-icon-button {
height: 52px;
width: 52px;
}
.interimTranscript {
color: darkgrey;
}
[hidden] {
display: none;
}
:host {
border-radius: 2px;
}
@media all and (max-width: 450px) {
:host {
margin: 0;
width: 100%;
max-height: calc(100% - 64px);
position: fixed !important;
bottom: 0px;
left: 0px;
right: 0px;
overflow: scroll;
border-bottom-left-radius: 0px;
border-bottom-right-radius: 0px;
} }
.content { .content {
width: auto; width: 450px;
min-height: 80px;
font-size: 18px;
padding: 16px;
} }
.messages { .messages {
max-height: 68vh; max-height: 50vh;
overflow: auto;
} }
}
</style>
<div class="content"> .messages::after {
<div class="messages" id="messages"> content: "";
<template is="dom-repeat" items="[[_conversation]]" as="message"> clear: both;
<div class$="[[_computeMessageClasses(message)]]">[[message.text]]</div> display: block;
</template> }
</div>
<template is="dom-if" if="[[results]]"> .message {
<div class="messages"> clear: both;
<div class="message user"> margin: 8px 0;
<span>{{results.final}}</span> padding: 8px;
<span class="interimTranscript">[[results.interim]]</span> border-radius: 15px;
}
</div>
.message.user {
margin-left: 24px;
float: right;
text-align: right;
border-bottom-right-radius: 0px;
background-color: var(--light-primary-color);
color: var(--primary-text-color);
}
.message.hass {
margin-right: 24px;
float: left;
border-bottom-left-radius: 0px;
background-color: var(--primary-color);
color: var(--text-primary-color);
}
.message.error {
background-color: var(--google-red-500);
color: var(--text-primary-color);
}
.icon {
text-align: center;
}
.icon paper-icon-button {
height: 52px;
width: 52px;
}
.interimTranscript {
color: darkgrey;
}
[hidden] {
display: none;
}
:host {
border-radius: 2px;
}
@media all and (max-width: 450px) {
:host {
margin: 0;
width: 100%;
max-height: calc(100% - 64px);
position: fixed !important;
bottom: 0px;
left: 0px;
right: 0px;
overflow: scroll;
border-bottom-left-radius: 0px;
border-bottom-right-radius: 0px;
}
.content {
width: auto;
}
.messages {
max-height: 68vh;
}
}
</style>
<div class="content">
<div class="messages" id="messages">
<template is="dom-repeat" items="[[_conversation]]" as="message">
<div class$="[[_computeMessageClasses(message)]]">
[[message.text]]
</div>
</template>
</div>
<template is="dom-if" if="[[results]]">
<div class="messages">
<div class="message user">
<span>{{results.final}}</span>
<span class="interimTranscript">[[results.interim]]</span>
</div>
</div>
</template>
<div class="icon" hidden$="[[results]]">
<paper-icon-button
icon="hass:text-to-speech"
on-click="startListening"
></paper-icon-button>
</div> </div>
</template>
<div class="icon" hidden$="[[results]]">
<paper-icon-button icon="hass:text-to-speech" on-click="startListening"></paper-icon-button>
</div> </div>
</div> `;
`;
} }
static get properties() { static get properties() {

View File

@ -51,21 +51,85 @@ class MoreInfoAlarmControlPanel extends LocalizeMixin(
<template is="dom-if" if="[[_isNumber(_codeFormat)]]"> <template is="dom-if" if="[[_isNumber(_codeFormat)]]">
<div class="pad"> <div class="pad">
<div> <div>
<paper-button on-click='_digitClicked' disabled='[[!_inputEnabled]]' data-digit="1" raised>1</paper-button> <paper-button
<paper-button on-click='_digitClicked' disabled='[[!_inputEnabled]]' data-digit="4" raised>4</paper-button> on-click="_digitClicked"
<paper-button on-click='_digitClicked' disabled='[[!_inputEnabled]]' data-digit="7" raised>7</paper-button> disabled="[[!_inputEnabled]]"
data-digit="1"
raised
>1</paper-button
>
<paper-button
on-click="_digitClicked"
disabled="[[!_inputEnabled]]"
data-digit="4"
raised
>4</paper-button
>
<paper-button
on-click="_digitClicked"
disabled="[[!_inputEnabled]]"
data-digit="7"
raised
>7</paper-button
>
</div> </div>
<div> <div>
<paper-button on-click='_digitClicked' disabled='[[!_inputEnabled]]' data-digit="2" raised>2</paper-button> <paper-button
<paper-button on-click='_digitClicked' disabled='[[!_inputEnabled]]' data-digit="5" raised>5</paper-button> on-click="_digitClicked"
<paper-button on-click='_digitClicked' disabled='[[!_inputEnabled]]' data-digit="8" raised>8</paper-button> disabled="[[!_inputEnabled]]"
<paper-button on-click='_digitClicked' disabled='[[!_inputEnabled]]' data-digit="0" raised>0</paper-button> data-digit="2"
raised
>2</paper-button
>
<paper-button
on-click="_digitClicked"
disabled="[[!_inputEnabled]]"
data-digit="5"
raised
>5</paper-button
>
<paper-button
on-click="_digitClicked"
disabled="[[!_inputEnabled]]"
data-digit="8"
raised
>8</paper-button
>
<paper-button
on-click="_digitClicked"
disabled="[[!_inputEnabled]]"
data-digit="0"
raised
>0</paper-button
>
</div> </div>
<div> <div>
<paper-button on-click='_digitClicked' disabled='[[!_inputEnabled]]' data-digit="3" raised>3</paper-button> <paper-button
<paper-button on-click='_digitClicked' disabled='[[!_inputEnabled]]' data-digit="6" raised>6</paper-button> on-click="_digitClicked"
<paper-button on-click='_digitClicked' disabled='[[!_inputEnabled]]' data-digit="9" raised>9</paper-button> disabled="[[!_inputEnabled]]"
<paper-button on-click='_clearEnteredCode' disabled='[[!_inputEnabled]]' raised> data-digit="3"
raised
>3</paper-button
>
<paper-button
on-click="_digitClicked"
disabled="[[!_inputEnabled]]"
data-digit="6"
raised
>6</paper-button
>
<paper-button
on-click="_digitClicked"
disabled="[[!_inputEnabled]]"
data-digit="9"
raised
>9</paper-button
>
<paper-button
on-click="_clearEnteredCode"
disabled="[[!_inputEnabled]]"
raised
>
[[localize('ui.card.alarm_control_panel.clear_code')]] [[localize('ui.card.alarm_control_panel.clear_code')]]
</paper-button> </paper-button>
</div> </div>
@ -75,15 +139,31 @@ class MoreInfoAlarmControlPanel extends LocalizeMixin(
<div class="layout horizontal center-justified actions"> <div class="layout horizontal center-justified actions">
<template is="dom-if" if="[[_disarmVisible]]"> <template is="dom-if" if="[[_disarmVisible]]">
<paper-button raised class="disarm" on-click="_callService" data-service="alarm_disarm" disabled="[[!_codeValid]]"> <paper-button
raised
class="disarm"
on-click="_callService"
data-service="alarm_disarm"
disabled="[[!_codeValid]]"
>
[[localize('ui.card.alarm_control_panel.disarm')]] [[localize('ui.card.alarm_control_panel.disarm')]]
</paper-button> </paper-button>
</template> </template>
<template is="dom-if" if="[[_armVisible]]"> <template is="dom-if" if="[[_armVisible]]">
<paper-button raised on-click="_callService" data-service="alarm_arm_home" disabled="[[!_codeValid]]"> <paper-button
raised
on-click="_callService"
data-service="alarm_arm_home"
disabled="[[!_codeValid]]"
>
[[localize('ui.card.alarm_control_panel.arm_home')]] [[localize('ui.card.alarm_control_panel.arm_home')]]
</paper-button> </paper-button>
<paper-button raised on-click="_callService" data-service="alarm_arm_away" disabled="[[!_codeValid]]"> <paper-button
raised
on-click="_callService"
data-service="alarm_arm_away"
disabled="[[!_codeValid]]"
>
[[localize('ui.card.alarm_control_panel.arm_away')]] [[localize('ui.card.alarm_control_panel.arm_away')]]
</paper-button> </paper-button>
</template> </template>

View File

@ -25,10 +25,11 @@ class MoreInfoAutomation extends LocalizeMixin(PolymerElement) {
</style> </style>
<div class="flex"> <div class="flex">
<div> <div>[[localize('ui.card.automation.last_triggered')]]:</div>
[[localize('ui.card.automation.last_triggered')]]: <ha-relative-time
</div> hass="[[hass]]"
<ha-relative-time hass="[[hass]]" datetime="[[stateObj.attributes.last_triggered]]"></ha-relative-time> datetime="[[stateObj.attributes.last_triggered]]"
></ha-relative-time>
</div> </div>
<div class="actions"> <div class="actions">

View File

@ -11,18 +11,23 @@ import EventsMixin from "../../../mixins/events-mixin";
class MoreInfoCamera extends EventsMixin(PolymerElement) { class MoreInfoCamera extends EventsMixin(PolymerElement) {
static get template() { static get template() {
return html` return html`
<style> <style>
:host { :host {
max-width:640px; max-width: 640px;
} }
.camera-image { .camera-image {
width: 100%; width: 100%;
} }
</style> </style>
<img class="camera-image" src="[[computeCameraImageUrl(hass, stateObj, isVisible)]]" on-load="imageLoaded" alt="[[_computeStateName(stateObj)]]"> <img
`; class="camera-image"
src="[[computeCameraImageUrl(hass, stateObj, isVisible)]]"
on-load="imageLoaded"
alt="[[_computeStateName(stateObj)]]"
/>
`;
} }
static get properties() { static get properties() {

View File

@ -24,188 +24,262 @@ import LocalizeMixin from "../../../mixins/localize-mixin";
class MoreInfoClimate extends LocalizeMixin(EventsMixin(PolymerElement)) { class MoreInfoClimate extends LocalizeMixin(EventsMixin(PolymerElement)) {
static get template() { static get template() {
return html` return html`
<style include="iron-flex"></style> <style include="iron-flex"></style>
<style> <style>
:host { :host {
color: var(--primary-text-color); color: var(--primary-text-color);
} }
.container-on, .container-on,
.container-away_mode, .container-away_mode,
.container-aux_heat, .container-aux_heat,
.container-temperature, .container-temperature,
.container-humidity, .container-humidity,
.container-operation_list, .container-operation_list,
.container-fan_list, .container-fan_list,
.container-swing_list { .container-swing_list {
display: none; display: none;
} }
.has-on .container-on, .has-on .container-on,
.has-away_mode .container-away_mode, .has-away_mode .container-away_mode,
.has-aux_heat .container-aux_heat, .has-aux_heat .container-aux_heat,
.has-target_temperature .container-temperature, .has-target_temperature .container-temperature,
.has-target_temperature_low .container-temperature, .has-target_temperature_low .container-temperature,
.has-target_temperature_high .container-temperature, .has-target_temperature_high .container-temperature,
.has-target_humidity .container-humidity, .has-target_humidity .container-humidity,
.has-operation_mode .container-operation_list, .has-operation_mode .container-operation_list,
.has-fan_mode .container-fan_list, .has-fan_mode .container-fan_list,
.has-swing_list .container-swing_list, .has-swing_list .container-swing_list,
.has-swing_mode .container-swing_list { .has-swing_mode .container-swing_list {
display: block; display: block;
margin-bottom: 5px; margin-bottom: 5px;
} }
.container-operation_list iron-icon, .container-operation_list iron-icon,
.container-fan_list iron-icon, .container-fan_list iron-icon,
.container-swing_list iron-icon { .container-swing_list iron-icon {
margin: 22px 16px 0 0; margin: 22px 16px 0 0;
} }
paper-dropdown-menu { paper-dropdown-menu {
width: 100%; width: 100%;
} }
paper-item { paper-item {
cursor: pointer; cursor: pointer;
} }
ha-paper-slider { ha-paper-slider {
width: 100%; width: 100%;
} }
.container-humidity .single-row { .container-humidity .single-row {
display: flex; display: flex;
height: 50px; height: 50px;
} }
.target-humidity { .target-humidity {
width: 90px; width: 90px;
font-size: 200%; font-size: 200%;
margin: auto; margin: auto;
} }
ha-climate-control.range-control-left, ha-climate-control.range-control-left,
ha-climate-control.range-control-right { ha-climate-control.range-control-right {
float: left; float: left;
width: 46%; width: 46%;
} }
ha-climate-control.range-control-left { ha-climate-control.range-control-left {
margin-right: 4%; margin-right: 4%;
} }
ha-climate-control.range-control-right { ha-climate-control.range-control-right {
margin-left: 4%; margin-left: 4%;
} }
.humidity { .humidity {
--paper-slider-active-color: var(--paper-blue-400); --paper-slider-active-color: var(--paper-blue-400);
--paper-slider-secondary-color: var(--paper-blue-400); --paper-slider-secondary-color: var(--paper-blue-400);
} }
.single-row { .single-row {
padding: 8px 0; padding: 8px 0;
} }
} }
</style> </style>
<div class$="[[computeClassNames(stateObj)]]"> <div class$="[[computeClassNames(stateObj)]]">
<template is="dom-if" if="[[supportsOn(stateObj)]]">
<div class="container-on">
<div class="center horizontal layout single-row">
<div class="flex">[[localize('ui.card.climate.on_off')]]</div>
<paper-toggle-button
checked="[[onToggleChecked]]"
on-change="onToggleChanged"
>
</paper-toggle-button>
</div>
</div>
</template>
<template is="dom-if" if="[[supportsOn(stateObj)]]"> <div class="container-temperature">
<div class="container-on"> <div class$="[[stateObj.attributes.operation_mode]]">
<div class="center horizontal layout single-row"> <div hidden$="[[!supportsTemperatureControls(stateObj)]]">
<div class="flex">[[localize('ui.card.climate.on_off')]]</div> [[localize('ui.card.climate.target_temperature')]]
<paper-toggle-button checked="[[onToggleChecked]]" on-change="onToggleChanged"> </div>
</paper-toggle-button> <template is="dom-if" if="[[supportsTemperature(stateObj)]]">
<ha-climate-control
value="[[stateObj.attributes.temperature]]"
units="[[hass.config.unit_system.temperature]]"
step="[[computeTemperatureStepSize(hass, stateObj)]]"
min="[[stateObj.attributes.min_temp]]"
max="[[stateObj.attributes.max_temp]]"
on-change="targetTemperatureChanged"
>
</ha-climate-control>
</template>
<template is="dom-if" if="[[supportsTemperatureRange(stateObj)]]">
<ha-climate-control
value="[[stateObj.attributes.target_temp_low]]"
units="[[hass.config.unit_system.temperature]]"
step="[[computeTemperatureStepSize(hass, stateObj)]]"
min="[[stateObj.attributes.min_temp]]"
max="[[stateObj.attributes.target_temp_high]]"
class="range-control-left"
on-change="targetTemperatureLowChanged"
>
</ha-climate-control>
<ha-climate-control
value="[[stateObj.attributes.target_temp_high]]"
units="[[hass.config.unit_system.temperature]]"
step="[[computeTemperatureStepSize(hass, stateObj)]]"
min="[[stateObj.attributes.target_temp_low]]"
max="[[stateObj.attributes.max_temp]]"
class="range-control-right"
on-change="targetTemperatureHighChanged"
>
</ha-climate-control>
</template>
</div> </div>
</div> </div>
</template>
<div class="container-temperature"> <template is="dom-if" if="[[supportsHumidity(stateObj)]]">
<div class$="[[stateObj.attributes.operation_mode]]"> <div class="container-humidity">
<div hidden$="[[!supportsTemperatureControls(stateObj)]]">[[localize('ui.card.climate.target_temperature')]]</div> <div>[[localize('ui.card.climate.target_humidity')]]</div>
<template is="dom-if" if="[[supportsTemperature(stateObj)]]">
<ha-climate-control value="[[stateObj.attributes.temperature]]" units="[[hass.config.unit_system.temperature]]" step="[[computeTemperatureStepSize(hass, stateObj)]]" min="[[stateObj.attributes.min_temp]]" max="[[stateObj.attributes.max_temp]]" on-change="targetTemperatureChanged">
</ha-climate-control>
</template>
<template is="dom-if" if="[[supportsTemperatureRange(stateObj)]]">
<ha-climate-control value="[[stateObj.attributes.target_temp_low]]" units="[[hass.config.unit_system.temperature]]" step="[[computeTemperatureStepSize(hass, stateObj)]]" min="[[stateObj.attributes.min_temp]]" max="[[stateObj.attributes.target_temp_high]]" class="range-control-left" on-change="targetTemperatureLowChanged">
</ha-climate-control>
<ha-climate-control value="[[stateObj.attributes.target_temp_high]]" units="[[hass.config.unit_system.temperature]]" step="[[computeTemperatureStepSize(hass, stateObj)]]" min="[[stateObj.attributes.target_temp_low]]" max="[[stateObj.attributes.max_temp]]" class="range-control-right" on-change="targetTemperatureHighChanged">
</ha-climate-control>
</template>
</div>
</div>
<template is="dom-if" if="[[supportsHumidity(stateObj)]]">
<div class="container-humidity">
<div>[[localize('ui.card.climate.target_humidity')]]</div>
<div class="single-row"> <div class="single-row">
<div class="target-humidity">[[stateObj.attributes.humidity]] %</div> <div class="target-humidity">
<ha-paper-slider class="humidity" min="[[stateObj.attributes.min_humidity]]" max="[[stateObj.attributes.max_humidity]]" secondary-progress="[[stateObj.attributes.max_humidity]]" step="1" pin="" value="[[stateObj.attributes.humidity]]" on-change="targetHumiditySliderChanged" ignore-bar-touch=""> [[stateObj.attributes.humidity]] %
</div>
<ha-paper-slider
class="humidity"
min="[[stateObj.attributes.min_humidity]]"
max="[[stateObj.attributes.max_humidity]]"
secondary-progress="[[stateObj.attributes.max_humidity]]"
step="1"
pin=""
value="[[stateObj.attributes.humidity]]"
on-change="targetHumiditySliderChanged"
ignore-bar-touch=""
>
</ha-paper-slider> </ha-paper-slider>
</div>
</div> </div>
</div> </template>
</template>
<template is="dom-if" if="[[supportsOperationMode(stateObj)]]"> <template is="dom-if" if="[[supportsOperationMode(stateObj)]]">
<div class="container-operation_list"> <div class="container-operation_list">
<div class="controls"> <div class="controls">
<paper-dropdown-menu label-float="" dynamic-align="" label="[[localize('ui.card.climate.operation')]]"> <paper-dropdown-menu
<paper-listbox slot="dropdown-content" selected="{{operationIndex}}"> label-float=""
<template is="dom-repeat" items="[[stateObj.attributes.operation_list]]" on-dom-change="handleOperationListUpdate"> dynamic-align=""
<paper-item>[[_localizeOperationMode(localize, item)]]</paper-item> label="[[localize('ui.card.climate.operation')]]"
>
<paper-listbox
slot="dropdown-content"
selected="{{operationIndex}}"
>
<template
is="dom-repeat"
items="[[stateObj.attributes.operation_list]]"
on-dom-change="handleOperationListUpdate"
>
<paper-item
>[[_localizeOperationMode(localize, item)]]</paper-item
>
</template>
</paper-listbox>
</paper-dropdown-menu>
</div>
</div>
</template>
<template is="dom-if" if="[[supportsFanMode(stateObj)]]">
<div class="container-fan_list">
<paper-dropdown-menu
label-float=""
dynamic-align=""
label="[[localize('ui.card.climate.fan_mode')]]"
>
<paper-listbox slot="dropdown-content" selected="{{fanIndex}}">
<template
is="dom-repeat"
items="[[stateObj.attributes.fan_list]]"
on-dom-change="handleFanListUpdate"
>
<paper-item>[[item]]</paper-item>
</template> </template>
</paper-listbox> </paper-listbox>
</paper-dropdown-menu> </paper-dropdown-menu>
</div> </div>
</div> </template>
</template>
<template is="dom-if" if="[[supportsFanMode(stateObj)]]"> <template is="dom-if" if="[[supportsSwingMode(stateObj)]]">
<div class="container-fan_list"> <div class="container-swing_list">
<paper-dropdown-menu label-float="" dynamic-align="" label="[[localize('ui.card.climate.fan_mode')]]"> <paper-dropdown-menu
<paper-listbox slot="dropdown-content" selected="{{fanIndex}}"> label-float=""
<template is="dom-repeat" items="[[stateObj.attributes.fan_list]]" on-dom-change="handleFanListUpdate"> dynamic-align=""
<paper-item>[[item]]</paper-item> label="[[localize('ui.card.climate.swing_mode')]]"
</template> >
</paper-listbox> <paper-listbox slot="dropdown-content" selected="{{swingIndex}}">
</paper-dropdown-menu> <template
</div> is="dom-repeat"
</template> items="[[stateObj.attributes.swing_list]]"
on-dom-change="handleSwingListUpdate"
<template is="dom-if" if="[[supportsSwingMode(stateObj)]]"> >
<div class="container-swing_list"> <paper-item>[[item]]</paper-item>
<paper-dropdown-menu label-float="" dynamic-align="" label="[[localize('ui.card.climate.swing_mode')]]"> </template>
<paper-listbox slot="dropdown-content" selected="{{swingIndex}}"> </paper-listbox>
<template is="dom-repeat" items="[[stateObj.attributes.swing_list]]" on-dom-change="handleSwingListUpdate"> </paper-dropdown-menu>
<paper-item>[[item]]</paper-item>
</template>
</paper-listbox>
</paper-dropdown-menu>
</div>
</template>
<template is="dom-if" if="[[supportsAwayMode(stateObj)]]">
<div class="container-away_mode">
<div class="center horizontal layout single-row">
<div class="flex">[[localize('ui.card.climate.away_mode')]]</div>
<paper-toggle-button checked="[[awayToggleChecked]]" on-change="awayToggleChanged">
</paper-toggle-button>
</div> </div>
</div> </template>
</template>
<template is="dom-if" if="[[supportsAuxHeat(stateObj)]]"> <template is="dom-if" if="[[supportsAwayMode(stateObj)]]">
<div class="container-aux_heat"> <div class="container-away_mode">
<div class="center horizontal layout single-row"> <div class="center horizontal layout single-row">
<div class="flex">[[localize('ui.card.climate.aux_heat')]]</div> <div class="flex">[[localize('ui.card.climate.away_mode')]]</div>
<paper-toggle-button checked="[[auxToggleChecked]]" on-change="auxToggleChanged"> <paper-toggle-button
</paper-toggle-button> checked="[[awayToggleChecked]]"
on-change="awayToggleChanged"
>
</paper-toggle-button>
</div>
</div> </div>
</div> </template>
</template>
</div> <template is="dom-if" if="[[supportsAuxHeat(stateObj)]]">
`; <div class="container-aux_heat">
<div class="center horizontal layout single-row">
<div class="flex">[[localize('ui.card.climate.aux_heat')]]</div>
<paper-toggle-button
checked="[[auxToggleChecked]]"
on-change="auxToggleChanged"
>
</paper-toggle-button>
</div>
</div>
</template>
</div>
`;
} }
static get properties() { static get properties() {

View File

@ -11,67 +11,80 @@ import "../../../components/ha-markdown";
class MoreInfoConfigurator extends PolymerElement { class MoreInfoConfigurator extends PolymerElement {
static get template() { static get template() {
return html` return html`
<style include="iron-flex"></style> <style include="iron-flex"></style>
<style> <style>
p { p {
margin: 8px 0; margin: 8px 0;
} }
a { a {
color: var(--primary-color); color: var(--primary-color);
} }
p > img { p > img {
max-width: 100%; max-width: 100%;
} }
p.center { p.center {
text-align: center; text-align: center;
} }
p.error { p.error {
color: #C62828; color: #c62828;
} }
p.submit { p.submit {
text-align: center; text-align: center;
height: 41px; height: 41px;
} }
paper-spinner { paper-spinner {
width: 14px; width: 14px;
height: 14px; height: 14px;
margin-right: 20px; margin-right: 20px;
} }
[hidden] { [hidden] {
display: none; display: none;
} }
</style> </style>
<div class="layout vertical"> <div class="layout vertical">
<template is="dom-if" if="[[isConfigurable]]"> <template is="dom-if" if="[[isConfigurable]]">
<ha-markdown content="[[stateObj.attributes.description]]"></ha-markdown> <ha-markdown
content="[[stateObj.attributes.description]]"
></ha-markdown>
<p class="error" hidden$="[[!stateObj.attributes.errors]]"> <p class="error" hidden$="[[!stateObj.attributes.errors]]">
[[stateObj.attributes.errors]] [[stateObj.attributes.errors]]
</p> </p>
<template is="dom-repeat" items="[[stateObj.attributes.fields]]"> <template is="dom-repeat" items="[[stateObj.attributes.fields]]">
<paper-input label="[[item.name]]" name="[[item.id]]" type="[[item.type]]" on-change="fieldChanged"></paper-input> <paper-input
label="[[item.name]]"
name="[[item.id]]"
type="[[item.type]]"
on-change="fieldChanged"
></paper-input>
</template>
<p class="submit" hidden$="[[!stateObj.attributes.submit_caption]]">
<paper-button
raised=""
disabled="[[isConfiguring]]"
on-click="submitClicked"
>
<paper-spinner
active="[[isConfiguring]]"
hidden="[[!isConfiguring]]"
alt="Configuring"
></paper-spinner>
[[stateObj.attributes.submit_caption]]
</paper-button>
</p>
</template> </template>
</div>
<p class="submit" hidden$="[[!stateObj.attributes.submit_caption]]"> `;
<paper-button raised="" disabled="[[isConfiguring]]" on-click="submitClicked">
<paper-spinner active="[[isConfiguring]]" hidden="[[!isConfiguring]]" alt="Configuring"></paper-spinner>
[[stateObj.attributes.submit_caption]]
</paper-button>
</p>
</template>
</div>
`;
} }
static get properties() { static get properties() {

View File

@ -18,52 +18,54 @@ const FEATURE_CLASS_NAMES = {
class MoreInfoCover extends LocalizeMixin(PolymerElement) { class MoreInfoCover extends LocalizeMixin(PolymerElement) {
static get template() { static get template() {
return html` return html`
<style include="iron-flex"></style> <style include="iron-flex"></style>
<style> <style>
.current_position, .tilt { .current_position,
max-height: 0px; .tilt {
overflow: hidden; max-height: 0px;
} overflow: hidden;
}
.has-current_position .current_position, .has-current_position .current_position,
.has-set_tilt_position .tilt, .has-set_tilt_position .tilt,
.has-current_tilt_position .tilt .has-current_tilt_position .tilt {
{ max-height: 208px;
max-height: 208px; }
}
[invisible] { [invisible] {
visibility: hidden !important; visibility: hidden !important;
} }
</style> </style>
<div class$="[[computeClassNames(stateObj)]]"> <div class$="[[computeClassNames(stateObj)]]">
<div class="current_position">
<ha-labeled-slider
caption="[[localize('ui.card.cover.position')]]"
pin=""
value="{{coverPositionSliderValue}}"
disabled="[[!entityObj.supportsSetPosition]]"
on-change="coverPositionSliderChanged"
></ha-labeled-slider>
</div>
<div class="current_position"> <div class="tilt">
<ha-labeled-slider <ha-labeled-slider
caption="[[localize('ui.card.cover.position')]]" pin="" caption="[[localize('ui.card.cover.tilt_position')]]"
value="{{coverPositionSliderValue}}" pin=""
disabled="[[!entityObj.supportsSetPosition]]" extra=""
on-change="coverPositionSliderChanged" value="{{coverTiltPositionSliderValue}}"
></ha-labeled-slider> disabled="[[!entityObj.supportsSetTiltPosition]]"
</div> on-change="coverTiltPositionSliderChanged"
>
<div class="tilt"> <ha-cover-tilt-controls
<ha-labeled-slider slot="extra"
caption="[[localize('ui.card.cover.tilt_position')]]" pin="" extra="" hidden$="[[entityObj.isTiltOnly]]"
value="{{coverTiltPositionSliderValue}}" hass="[[hass]]"
disabled="[[!entityObj.supportsSetTiltPosition]]" state-obj="[[stateObj]]"
on-change="coverTiltPositionSliderChanged"> ></ha-cover-tilt-controls>
</ha-labeled-slider>
<ha-cover-tilt-controls </div>
slot="extra" hidden$="[[entityObj.isTiltOnly]]" </div>
hass="[[hass]]" state-obj="[[stateObj]]" `;
></ha-cover-tilt-controls>
</ha-labeled-slider>
</div>
</div>
`;
} }
static get properties() { static get properties() {

View File

@ -6,8 +6,8 @@ import "../../../components/ha-attributes";
class MoreInfoDefault extends PolymerElement { class MoreInfoDefault extends PolymerElement {
static get template() { static get template() {
return html` return html`
<ha-attributes state-obj="[[stateObj]]"></ha-attributes> <ha-attributes state-obj="[[stateObj]]"></ha-attributes>
`; `;
} }
static get properties() { static get properties() {

View File

@ -19,60 +19,82 @@ import LocalizeMixin from "../../../mixins/localize-mixin";
class MoreInfoFan extends LocalizeMixin(EventsMixin(PolymerElement)) { class MoreInfoFan extends LocalizeMixin(EventsMixin(PolymerElement)) {
static get template() { static get template() {
return html` return html`
<style include="iron-flex"></style> <style include="iron-flex"></style>
<style> <style>
.container-speed_list, .container-speed_list,
.container-direction, .container-direction,
.container-oscillating { .container-oscillating {
display: none; display: none;
} }
.has-speed_list .container-speed_list, .has-speed_list .container-speed_list,
.has-direction .container-direction, .has-direction .container-direction,
.has-oscillating .container-oscillating { .has-oscillating .container-oscillating {
display: block; display: block;
} }
paper-dropdown-menu { paper-dropdown-menu {
width: 100%; width: 100%;
} }
paper-item { paper-item {
cursor: pointer; cursor: pointer;
} }
</style> </style>
<div class$="[[computeClassNames(stateObj)]]"> <div class$="[[computeClassNames(stateObj)]]">
<div class="container-speed_list">
<paper-dropdown-menu
label-float=""
dynamic-align=""
label="[[localize('ui.card.fan.speed')]]"
>
<paper-listbox slot="dropdown-content" selected="{{speedIndex}}">
<template
is="dom-repeat"
items="[[stateObj.attributes.speed_list]]"
>
<paper-item>[[item]]</paper-item>
</template>
</paper-listbox>
</paper-dropdown-menu>
</div>
<div class="container-speed_list"> <div class="container-oscillating">
<paper-dropdown-menu label-float="" dynamic-align="" label="[[localize('ui.card.fan.speed')]]"> <div class="center horizontal layout single-row">
<paper-listbox slot="dropdown-content" selected="{{speedIndex}}"> <div class="flex">[[localize('ui.card.fan.oscillate')]]</div>
<template is="dom-repeat" items="[[stateObj.attributes.speed_list]]"> <paper-toggle-button
<paper-item>[[item]]</paper-item> checked="[[oscillationToggleChecked]]"
</template> on-change="oscillationToggleChanged"
</paper-listbox> >
</paper-dropdown-menu> </paper-toggle-button>
</div> </div>
</div>
<div class="container-oscillating"> <div class="container-direction">
<div class="center horizontal layout single-row"> <div class="direction">
<div class="flex">[[localize('ui.card.fan.oscillate')]]</div> <div>[[localize('ui.card.fan.direction')]]</div>
<paper-toggle-button checked="[[oscillationToggleChecked]]" on-change="oscillationToggleChanged"> <paper-icon-button
</paper-toggle-button> icon="hass:rotate-left"
on-click="onDirectionLeft"
title="Left"
disabled="[[computeIsRotatingLeft(stateObj)]]"
></paper-icon-button>
<paper-icon-button
icon="hass:rotate-right"
on-click="onDirectionRight"
title="Right"
disabled="[[computeIsRotatingRight(stateObj)]]"
></paper-icon-button>
</div>
</div> </div>
</div> </div>
<div class="container-direction"> <ha-attributes
<div class="direction"> state-obj="[[stateObj]]"
<div>[[localize('ui.card.fan.direction')]]</div> extra-filters="speed,speed_list,oscillating,direction"
<paper-icon-button icon="hass:rotate-left" on-click="onDirectionLeft" title="Left" disabled="[[computeIsRotatingLeft(stateObj)]]"></paper-icon-button> ></ha-attributes>
<paper-icon-button icon="hass:rotate-right" on-click="onDirectionRight" title="Right" disabled="[[computeIsRotatingRight(stateObj)]]"></paper-icon-button> `;
</div>
</div>
</div>
<ha-attributes state-obj="[[stateObj]]" extra-filters="speed,speed_list,oscillating,direction"></ha-attributes>
`;
} }
static get properties() { static get properties() {

Some files were not shown because too many files have changed in this diff Show More