Label and templates support (#134)

* Label and templates support

* Fix blank card with fixed width

* Fix some layouts
This commit is contained in:
Jérôme W 2019-04-29 18:44:04 +02:00 committed by GitHub
parent 21c60bb033
commit 56a8fac0fa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 504 additions and 70 deletions

View File

@ -21,6 +21,7 @@ Lovelace Button card for your entities.
- custom color (optional), or based on light rgb value
- custom state definition with customizable color, icon and style (optional)
- [custom size of the icon, width and height](#Play-with-width-height-and-icon-size) (optional)
- Support for [templates](#templates) in some fields
- custom icon (optional)
- custom css style (optional)
- multiple [layout](#Layout) support
@ -52,10 +53,13 @@ Lovelace Button card for your entities.
| `tap_action` | object | optional | See [Action](#Action) | Define the type of action on click, if undefined, toggle will be used. |
| `hold_action` | object | optional | See [Action](#Action) | Define the type of action on hold, if undefined, nothing happens. |
| `name` | string | optional | `Air conditioner` | Define an optional text to show below the icon |
| `label` | string | optional | Any string that you want | Display a label below the card. See [Layouts](#layout) for more information. |
| `label_template` | string | optional | `states['light.mylight'].attributes.brightness` | See [templates](#templates). Any javascript code which returns a string. Overrides `label` |
| `show_name` | boolean | `true` | `true` \| `false` | Wether to show the name or not. Will pick entity_id's name by default, unless redefined in the `name` property or in any state `name` property |
| `show_state` | boolean | `false` | `true` \| `false` | Show the state on the card. defaults to false if not set |
| `show_icon` | boolean | `true` | `true` \| `false` | Wether to show the icon or not. Unless redefined in `icon`, uses the default entity icon from hass |
| `show_units` | boolean | `true` | `true` \| `false` | Display or hide the units of a sensor, if any. |
| `show_label` | boolean | `false` | `true` \| `false` | Display or hide the `label`/`label_template`
| `show_entity_picture` | boolean | `false` | `true` \| `false` | Replace the icon by the entity picture (if any) or the custom picture (if any). Falls back to using the icon if both are undefined |
| `entity_picture` | string | optional | Can be any of `/local/*` file or a URL | Will override the icon/the default entity_picture with your own image. Best is to use a square image. You can also define one per state |
| `units` | string | optional | `Kb/s`, `lux`, ... | Override or define the units to display after the state of the entity. If omitted, it's using the entity's units |
@ -89,9 +93,13 @@ Lovelace Button card for your entities.
| `spin` | boolean | `false` | `true` \| `false` | Should the icon spin for this state? |
| `entity_picture` | string | optional | Can be any of `/local/*` file or a URL | Will override the icon/the default entity_picture with your own image for this state. Best is to use a square image |
| `entity_picture_style` | object list | optional | `- border-radius: 50%`, `- filter: grayscale(100%)`, ... | Style applied to the entity picture for this state |
| `label` | string | optional | Any string that you want | Display a label below the card. See [Layouts](#layout) for more information. |
| `label_template` | string | optional | `states['light.mylight'].attributes.brightness` | See [templates](#templates). Any javascript code which returns a string. Overrides `label` |
### Available operators
The order of your elements in the `state` object matters. The first one which is `true` will match.
| Operator | `value` example | Description |
| :-------: | --------------- | -------------------------------------------------------------------------------------------------------- |
| `<` | `5` | Current state is inferior to `value` |
@ -101,6 +109,7 @@ Lovelace Button card for your entities.
| `>` | `12` | Current state is superior to `value` |
| `!=` | `'normal'` | Current state is not equal (`!=` javascript) to `value` |
| `regex` | `'^norm.*$'` | `value` regex applied to current state does match |
| `template` | `return states['input_select.light_mode'].state === 'night_mode'` | See [here](#state-templates) for examples. `value` needs to be a javascript expression which returns a boolean. If the boolean is true, it will match this state |
| `default` | N/A | If nothing matches, this is used |
### Layout
@ -111,15 +120,29 @@ It is fully compatible with every `show_*` option. Make sure you set `show_state
Multiple values are possible, see the image below for examples:
* `vertical` (default value if nothing is provided): Everything is centered vertically on top of each other
* `icon_name_state`: Everything is aligned horizontally, name and state are concatenated
* `name_state`: Icon sits on top of name and state concatenated on one line
* `icon_name`: Icon and name are horizontally aligned, state is centered below
* `icon_state`: Icon and state are horizontally aligned, name is centered below
* `icon_name_state2nd`: Icon, name and state are horizontally aligned, name is above state
* `icon_state_name2nd`: Icon, name and state are horizontally aligned, state is above name
* `icon_name_state`: Everything is aligned horizontally, name and state are concatenated, label is centered below
* `name_state`: Icon sits on top of name and state concatenated on one line, label below
* `icon_name`: Icon and name are horizontally aligned, state and label are centered below
* `icon_state`: Icon and state are horizontally aligned, name and label are centered below
* `icon_label`: Icon and label are horizontally aligned, name and state are centered below
* `icon_name_state2nd`: Icon, name and state are horizontally aligned, name is above state, label below name and state
* `icon_state_name2nd`: Icon, name and state are horizontally aligned, state is above name, label below name and state
![layout_image](examples/layout.png)
### Templates
`label_template` supports templating.
It will be interpreted as javascript code and the code should return a value.
Inside the javascript code, you'll have access to those variables:
* `entity`: The current entity object, if the entity is defined in the card
* `states`: An object with all the states of all the entities (equivalent to `hass.states`)
* `user`: The user object (equivalent to `hass.user`)
* `hass`: The complete `hass` object
See [here](#playing-with-label-templates) for some examples.
## Installation
### Manual Installation
@ -466,6 +489,68 @@ If you specify a width for the card, it has to be in `px`. All the cards without
style:
- height: 300px
```
### Templates Support
#### Playing with label templates
![label_template](examples/labels.png)
```yaml
- type: "custom:button-card"
color_type: icon
entity: light.test_light
label_template: >
var bri = states['light.test_light'].attributes.brightness;
return 'Brightness: ' + (bri ? bri : '0') + '%';
show_label: true
size: 15%
style:
- height: 100px
- type: "custom:button-card"
color_type: icon
entity: light.test_light
layout: icon_label
label_template: >
return 'Other State: ' + states['switch.skylight'].state;
show_label: true
show_name: false
style:
- height: 100px
```
#### State Templates
The javascript code inside `value` needs to return `true` of `false`.
Example with `template`:
```yaml
- type: "custom:button-card"
color_type: icon
entity: switch.skylight
show_state: true
show_label: true
state:
- operator: template
value: >
return states['light.test_light'].attributes
&& (states['light.test_light'].attributes.brightness <= 100)
icon: mdi:alert
- operator: default
icon: mdi:lightbulb
- type: "custom:button-card"
color_type: icon
entity: light.test_light
show_label: true
state:
- operator: template
value: >
return states['input_select.light_mode'].state === 'night_mode'
icon: mdi:weather-night
label: Night Mode
- operator: default
icon: mdi:white-balance-sunny
label: Day Mode
```
## Credits

222
dist/button-card.js vendored
View File

@ -3336,8 +3336,8 @@ function applyBrightnessToColor(color, brightness) {
return color;
}
// Check if config or Entity changed
function hasConfigOrEntityChanged(element, changedProps) {
if (changedProps.has('config')) {
function hasConfigOrEntityChanged(element, changedProps, forceUpdate) {
if (changedProps.has('config') || forceUpdate) {
return true;
}
if (element.config.entity) {
@ -3711,8 +3711,9 @@ const styles = css`
}
.img-cell {
grid-area: i;
min-height: 0;
min-width: 0;
height: 100%;
width: 100%;
max-width: 100%;
}
.icon {
@ -3736,64 +3737,164 @@ const styles = css`
/* margin: auto; */
}
.container.vertical {
grid-template-areas: "i" "n" "s";
grid-template-columns: 1fr;
grid-template-rows: 1fr min-content min-content;
.label {
grid-area: l;
max-width: 100%;
align-self: center;
justify-self: center;
}
.container.vertical.no-icon {
grid-template-areas: "n" "s";
.container.vertical {
grid-template-areas: "i" "n" "s" "l";
grid-template-columns: 1fr;
grid-template-rows: 1fr 1fr;
grid-template-rows: 1fr min-content min-content min-content;
}
/* Vertical No Icon */
.container.vertical.no-icon {
grid-template-areas: "n" "s" "l";
grid-template-columns: 1fr;
grid-template-rows: 1fr min-content 1fr;
}
.container.vertical.no-icon .state {
align-self: start;
align-self: center;
}
.container.vertical.no-icon .name {
align-self: end;
}
.container.vertical.no-icon .label {
align-self: start;
}
/* Vertical No Icon No Name */
.container.vertical.no-icon.no-name {
grid-template-areas: "s" "l";
grid-template-columns: 1fr;
grid-template-rows: 1fr 1fr;
}
.container.vertical.no-icon.no-name .state {
align-self: end;
}
.container.vertical.no-icon.no-name .label {
align-self: start;
}
/* Vertical No Icon No State */
.container.vertical.no-icon.no-state {
grid-template-areas: "n" "l";
grid-template-columns: 1fr;
grid-template-rows: 1fr 1fr;
}
.container.vertical.no-icon.no-state .name {
align-self: end;
}
.container.vertical.no-icon.no-state .label {
align-self: start;
}
/* Vertical No Icon No Label */
.container.vertical.no-icon.no-label {
grid-template-areas: "n" "s";
grid-template-columns: 1fr;
grid-template-rows: 1fr 1fr;
}
.container.vertical.no-icon.no-label .name {
align-self: end;
}
.container.vertical.no-icon.no-label .state {
align-self: start;
}
/* Vertical No Icon No Label No Name */
.container.vertical.no-icon.no-label.no-name {
grid-template-areas: "s";
grid-template-columns: 1fr;
grid-template-rows: 1fr;
}
.container.vertical.no-icon.no-name .state {
.container.vertical.no-icon.no-label.no-name .state {
align-self: center;
}
.container.vertical.no-icon.no-state {
/* Vertical No Icon No Label No State */
.container.vertical.no-icon.no-label.no-state {
grid-template-areas: "n";
grid-template-columns: 1fr;
grid-template-rows: 1fr;
}
.container.vertical.no-icon.no-state .name {
.container.vertical.no-icon.no-label.no-state .name {
align-self: center;
}
/* Vertical No Icon No Name No State */
.container.vertical.no-icon.no-name.no-state {
grid-template-areas: "l";
grid-template-columns: 1fr;
grid-template-rows: 1fr;
}
.container.vertical.no-icon.no-name.no-state .label {
align-self: center;
}
.container.icon_name_state {
grid-template-areas: "i n";
grid-template-areas: "i n" "l l";
grid-template-columns: 40% 1fr;
grid-template-rows: 1fr;
grid-template-rows: 1fr min-content;
}
.container.icon_name {
grid-template-areas: "i n" "s s";
grid-template-areas: "i n" "s s" "l l";
grid-template-columns: 40% 1fr;
grid-template-rows: 1fr min-content;
grid-template-rows: 1fr min-content min-content;
}
.container.icon_state {
grid-template-areas: "i s" "n n";
grid-template-areas: "i s" "n n" "l l";
grid-template-columns: 40% 1fr;
grid-template-rows: 1fr min-content;
grid-template-rows: 1fr min-content min-content;
}
.container.name_state {
grid-template-areas: "i" "n";
grid-template-areas: "i" "n" "l";
grid-template-columns: 1fr;
grid-template-rows: 1fr min-content;
grid-template-rows: 1fr min-content min-content;
}
.container.name_state.no-icon {
grid-template-areas: "n" "l";
grid-template-columns: 1fr;
grid-template-rows: 1fr 1fr;
}
.container.name_state.no-icon .name {
align-self: end
}
.container.name_state.no-icon .label {
align-self: start
}
.container.name_state.no-icon.no-label {
grid-template-areas: "n";
grid-template-columns: 1fr;
grid-template-rows: 1fr;
}
.container.name_state.no-icon.no-label .name {
align-self: center
}
/* icon_name_state2nd default */
.container.icon_name_state2nd {
grid-template-areas: "i n" "i s" "i l";
grid-template-columns: 40% 1fr;
grid-template-rows: 1fr min-content 1fr;
}
.container.icon_name_state2nd .name {
align-self: end;
}
.container.icon_name_state2nd .state {
align-self: center;
}
.container.icon_name_state2nd .label {
align-self: start;
}
/* icon_name_state2nd No Label */
.container.icon_name_state2nd.no-label {
grid-template-areas: "i n" "i s";
grid-template-columns: 40% 1fr;
grid-template-rows: 1fr 1fr;
@ -3805,7 +3906,24 @@ const styles = css`
align-self: start;
}
/* icon_state_name2nd Default */
.container.icon_state_name2nd {
grid-template-areas: "i s" "i n" "i l";
grid-template-columns: 40% 1fr;
grid-template-rows: 1fr min-content 1fr;
}
.container.icon_state_name2nd .state {
align-self: end;
}
.container.icon_state_name2nd .name {
align-self: center;
}
.container.icon_state_name2nd .state {
align-self: start;
}
/* icon_state_name2nd No Label */
.container.icon_state_name2nd.no-label {
grid-template-areas: "i s" "i n";
grid-template-columns: 40% 1fr;
grid-template-rows: 1fr 1fr;
@ -3816,6 +3934,12 @@ const styles = css`
.container.icon_state_name2nd .name {
align-self: start;
}
.container.icon_label {
grid-template-areas: "i l" "n n" "s s";
grid-template-columns: 40% 1fr;
grid-template-rows: 1fr min-content min-content;
}
`;
let ButtonCard = class ButtonCard extends LitElement {
@ -3829,7 +3953,12 @@ let ButtonCard = class ButtonCard extends LitElement {
return this._cardHtml();
}
shouldUpdate(changedProps) {
return hasConfigOrEntityChanged(this, changedProps);
const state = this.config.entity ? this.hass.states[this.config.entity] : undefined;
const configState = this._getMatchingConfigState(state);
const forceUpdate = this.config.show_label && (configState && configState.label_template || this.config.label_template) || this.config.state && this.config.state.find(elt => {
return elt.operator === 'template';
}) ? true : false;
return hasConfigOrEntityChanged(this, changedProps, forceUpdate);
}
_getMatchingConfigState(state) {
if (!state || !this.config.state) {
@ -3858,6 +3987,10 @@ let ButtonCard = class ButtonCard extends LitElement {
const matches = state.state.match(elt.value) ? true : false;
return matches;
}
case 'template':
{
return new Function('states', 'entity', 'user', 'hass', `'use strict'; ${elt.value}`).call(this, this.hass.states, state, this.hass.user, this.hass);
}
case 'default':
def = elt;
return false;
@ -4018,6 +4151,28 @@ let ButtonCard = class ButtonCard extends LitElement {
}
return units;
}
_buildLabel(state, configState) {
if (!this.config.show_label) {
return undefined;
}
let label;
let matchingLabelTemplate;
if (configState && configState.label_template) {
matchingLabelTemplate = configState.label_template;
} else {
matchingLabelTemplate = this.config.label_template;
}
if (!matchingLabelTemplate) {
if (configState && configState.label) {
label = configState.label;
} else {
label = this.config.label;
}
return label;
}
/* eslint no-new-func: 0 */
return new Function('states', 'entity', 'user', 'hass', `'use strict'; ${matchingLabelTemplate}`).call(this, this.hass.states, state, this.hass.user, this.hass);
}
_isClickable(state) {
let clickable = true;
if (this.config.tap_action.action === 'toggle' && this.config.hold_action.action === 'none' || this.config.hold_action.action === 'toggle' && this.config.tap_action.action === 'none') {
@ -4045,11 +4200,11 @@ let ButtonCard = class ButtonCard extends LitElement {
_rotate(configState) {
return configState && configState.spin ? true : false;
}
_blankCardColoredHtml(state) {
_blankCardColoredHtml(state, cardStyle) {
const color = this._buildCssColorAttribute(state, undefined);
const fontColor = getFontColorBasedOnBackgroundColor(color);
return html`
<ha-card class="disabled">
<ha-card class="disabled" style=${styleMap(cardStyle)}>
<div style="color: ${fontColor}; background-color: ${color};"></div>
</ha-card>
`;
@ -4061,9 +4216,13 @@ let ButtonCard = class ButtonCard extends LitElement {
let buttonColor = color;
let cardStyle = {};
const configCardStyle = this._buildStyle(state, configState);
if (configCardStyle.width) {
this.style.setProperty('flex', '0 0 auto');
this.style.setProperty('max-width', 'fit-content');
}
switch (this.config.color_type) {
case 'blank-card':
return this._blankCardColoredHtml(state);
return this._blankCardColoredHtml(state, configCardStyle);
case 'card':
case 'label-card':
{
@ -4078,10 +4237,6 @@ let ButtonCard = class ButtonCard extends LitElement {
cardStyle = configCardStyle;
break;
}
if (configCardStyle.width) {
this.style.setProperty('flex', '0 0 auto');
this.style.setProperty('max-width', 'fit-content');
}
return html`
<ha-card class="button-card-main ${this._isClickable(state) ? '' : 'disabled'}" style=${styleMap(cardStyle)} @ha-click="${this._handleTap}" @ha-hold="${this._handleHold}" .longpress="${longPress()}" .config="${this.config}">
${this._buttonContent(state, configState, buttonColor)}
@ -4104,14 +4259,17 @@ let ButtonCard = class ButtonCard extends LitElement {
_gridHtml(state, configState, containerClass, color, name, stateString) {
const iconTemplate = this._getIconHtml(state, configState, color);
const itemClass = ['container', containerClass];
const label = this._buildLabel(state, configState);
if (!iconTemplate) itemClass.push('no-icon');
if (!name) itemClass.push('no-name');
if (!stateString) itemClass.push('no-state');
if (!label) itemClass.push('no-label');
return html`
<div class=${itemClass.join(' ')}>
${iconTemplate ? iconTemplate : ''}
${name ? html`<div class="name">${name}</div>` : ''}
${stateString ? html`<div class="state">${stateString}</div>` : ''}
${label ? html`<div class="label">${label}</div>` : ''}
</div>
`;
}
@ -4142,7 +4300,7 @@ let ButtonCard = class ButtonCard extends LitElement {
if (!config) {
throw new Error('Invalid configuration');
}
this.config = Object.assign({ tap_action: { action: 'toggle' }, hold_action: { action: 'none' }, layout: 'vertical', size: '40%', color_type: 'icon', show_name: true, show_state: false, show_icon: true, show_units: true, show_entity_picture: false }, config);
this.config = Object.assign({ tap_action: { action: 'toggle' }, hold_action: { action: 'none' }, layout: 'vertical', size: '40%', color_type: 'icon', show_name: true, show_state: false, show_icon: true, show_units: true, show_label: false, show_entity_picture: false }, config);
this.config.default_color = 'var(--primary-text-color)';
if (this.config.color_type !== 'icon') {
this.config.color_off = 'var(--paper-card-background-color)';

BIN
examples/labels.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

View File

@ -48,7 +48,17 @@ class ButtonCard extends LitElement {
}
protected shouldUpdate(changedProps: PropertyValues): boolean {
return hasConfigOrEntityChanged(this, changedProps);
const state = this.config!.entity ? this.hass!.states[this.config!.entity] : undefined;
const configState = this._getMatchingConfigState(state);
const forceUpdate = (this.config!.show_label
&& (configState
&& configState.label_template
|| this.config!.label_template)
)
|| this.config!.state
&& this.config!.state.find((elt) => { return elt.operator === 'template'; })
? true : false;
return hasConfigOrEntityChanged(this, changedProps, forceUpdate);
}
private _getMatchingConfigState(state: HassEntity | undefined): StateConfig | undefined {
@ -77,6 +87,11 @@ class ButtonCard extends LitElement {
const matches = state.state.match(elt.value) ? true : false;
return matches;
}
case 'template': {
return new Function('states', 'entity', 'user', 'hass',
`'use strict'; ${elt.value}`)
.call(this, this.hass!.states, state, this.hass!.user, this.hass);
}
case 'default':
def = elt;
return false;
@ -266,6 +281,36 @@ class ButtonCard extends LitElement {
return units;
}
private _buildLabel(
state: HassEntity | undefined,
configState: StateConfig | undefined,
): string | undefined {
if (!this.config!.show_label) {
return undefined;
}
let label: string | undefined;
let matchingLabelTemplate: string | undefined;
if (configState && configState.label_template) {
matchingLabelTemplate = configState.label_template;
} else {
matchingLabelTemplate = this.config!.label_template;
}
if (!matchingLabelTemplate) {
if (configState && configState.label) {
label = configState.label;
} else {
label = this.config!.label;
}
return label;
}
/* eslint no-new-func: 0 */
return new Function('states', 'entity', 'user', 'hass',
`'use strict'; ${matchingLabelTemplate}`)
.call(this, this.hass!.states, state, this.hass!.user, this.hass);
}
private _isClickable(state: HassEntity | undefined): boolean {
let clickable = true;
if (this.config!.tap_action!.action === 'toggle' && this.config!.hold_action!.action === 'none'
@ -297,11 +342,14 @@ class ButtonCard extends LitElement {
return configState && configState.spin ? true : false;
}
private _blankCardColoredHtml(state: HassEntity | undefined): TemplateResult {
private _blankCardColoredHtml(
state: HassEntity | undefined,
cardStyle: StyleInfo,
): TemplateResult {
const color = this._buildCssColorAttribute(state, undefined);
const fontColor = getFontColorBasedOnBackgroundColor(color);
return html`
<ha-card class="disabled">
<ha-card class="disabled" style=${styleMap(cardStyle)}>
<div style="color: ${fontColor}; background-color: ${color};"></div>
</ha-card>
`;
@ -315,9 +363,13 @@ class ButtonCard extends LitElement {
let cardStyle: StyleInfo = {};
const configCardStyle = this._buildStyle(state, configState);
if (configCardStyle.width) {
this.style.setProperty('flex', '0 0 auto');
this.style.setProperty('max-width', 'fit-content');
}
switch (this.config!.color_type) {
case 'blank-card':
return this._blankCardColoredHtml(state);
return this._blankCardColoredHtml(state, configCardStyle);
case 'card':
case 'label-card': {
const fontColor = getFontColorBasedOnBackgroundColor(color);
@ -331,10 +383,6 @@ class ButtonCard extends LitElement {
cardStyle = configCardStyle;
break;
}
if (configCardStyle.width) {
this.style.setProperty('flex', '0 0 auto');
this.style.setProperty('max-width', 'fit-content');
}
return html`
<ha-card class="button-card-main ${this._isClickable(state) ? '' : 'disabled'}" style=${styleMap(cardStyle)} @ha-click="${this._handleTap}" @ha-hold="${this._handleHold}" .longpress="${longPress()}" .config="${this.config}">
@ -374,15 +422,18 @@ class ButtonCard extends LitElement {
): TemplateResult {
const iconTemplate = this._getIconHtml(state, configState, color);
const itemClass: string[] = ['container', containerClass];
const label = this._buildLabel(state, configState);
if (!iconTemplate) itemClass.push('no-icon');
if (!name) itemClass.push('no-name');
if (!stateString) itemClass.push('no-state');
if (!label) itemClass.push('no-label');
return html`
<div class=${itemClass.join(' ')}>
${iconTemplate ? iconTemplate : ''}
${name ? html`<div class="name">${name}</div>` : ''}
${stateString ? html`<div class="state">${stateString}</div>` : ''}
${label ? html`<div class="label">${label}</div>` : ''}
</div>
`;
}
@ -435,6 +486,7 @@ class ButtonCard extends LitElement {
show_state: false,
show_icon: true,
show_units: true,
show_label: false,
show_entity_picture: false,
...config,
};

View File

@ -61,8 +61,9 @@ export function applyBrightnessToColor(
export function hasConfigOrEntityChanged(
element: any,
changedProps: PropertyValues,
forceUpdate: Boolean,
): boolean {
if (changedProps.has('config')) {
if (changedProps.has('config') || forceUpdate) {
return true;
}

View File

@ -79,8 +79,9 @@ export const styles = css`
}
.img-cell {
grid-area: i;
min-height: 0;
min-width: 0;
height: 100%;
width: 100%;
max-width: 100%;
}
.icon {
@ -104,64 +105,164 @@ export const styles = css`
/* margin: auto; */
}
.container.vertical {
grid-template-areas: "i" "n" "s";
grid-template-columns: 1fr;
grid-template-rows: 1fr min-content min-content;
.label {
grid-area: l;
max-width: 100%;
align-self: center;
justify-self: center;
}
.container.vertical.no-icon {
grid-template-areas: "n" "s";
.container.vertical {
grid-template-areas: "i" "n" "s" "l";
grid-template-columns: 1fr;
grid-template-rows: 1fr 1fr;
grid-template-rows: 1fr min-content min-content min-content;
}
/* Vertical No Icon */
.container.vertical.no-icon {
grid-template-areas: "n" "s" "l";
grid-template-columns: 1fr;
grid-template-rows: 1fr min-content 1fr;
}
.container.vertical.no-icon .state {
align-self: start;
align-self: center;
}
.container.vertical.no-icon .name {
align-self: end;
}
.container.vertical.no-icon .label {
align-self: start;
}
/* Vertical No Icon No Name */
.container.vertical.no-icon.no-name {
grid-template-areas: "s" "l";
grid-template-columns: 1fr;
grid-template-rows: 1fr 1fr;
}
.container.vertical.no-icon.no-name .state {
align-self: end;
}
.container.vertical.no-icon.no-name .label {
align-self: start;
}
/* Vertical No Icon No State */
.container.vertical.no-icon.no-state {
grid-template-areas: "n" "l";
grid-template-columns: 1fr;
grid-template-rows: 1fr 1fr;
}
.container.vertical.no-icon.no-state .name {
align-self: end;
}
.container.vertical.no-icon.no-state .label {
align-self: start;
}
/* Vertical No Icon No Label */
.container.vertical.no-icon.no-label {
grid-template-areas: "n" "s";
grid-template-columns: 1fr;
grid-template-rows: 1fr 1fr;
}
.container.vertical.no-icon.no-label .name {
align-self: end;
}
.container.vertical.no-icon.no-label .state {
align-self: start;
}
/* Vertical No Icon No Label No Name */
.container.vertical.no-icon.no-label.no-name {
grid-template-areas: "s";
grid-template-columns: 1fr;
grid-template-rows: 1fr;
}
.container.vertical.no-icon.no-name .state {
.container.vertical.no-icon.no-label.no-name .state {
align-self: center;
}
.container.vertical.no-icon.no-state {
/* Vertical No Icon No Label No State */
.container.vertical.no-icon.no-label.no-state {
grid-template-areas: "n";
grid-template-columns: 1fr;
grid-template-rows: 1fr;
}
.container.vertical.no-icon.no-state .name {
.container.vertical.no-icon.no-label.no-state .name {
align-self: center;
}
/* Vertical No Icon No Name No State */
.container.vertical.no-icon.no-name.no-state {
grid-template-areas: "l";
grid-template-columns: 1fr;
grid-template-rows: 1fr;
}
.container.vertical.no-icon.no-name.no-state .label {
align-self: center;
}
.container.icon_name_state {
grid-template-areas: "i n";
grid-template-areas: "i n" "l l";
grid-template-columns: 40% 1fr;
grid-template-rows: 1fr;
grid-template-rows: 1fr min-content;
}
.container.icon_name {
grid-template-areas: "i n" "s s";
grid-template-areas: "i n" "s s" "l l";
grid-template-columns: 40% 1fr;
grid-template-rows: 1fr min-content;
grid-template-rows: 1fr min-content min-content;
}
.container.icon_state {
grid-template-areas: "i s" "n n";
grid-template-areas: "i s" "n n" "l l";
grid-template-columns: 40% 1fr;
grid-template-rows: 1fr min-content;
grid-template-rows: 1fr min-content min-content;
}
.container.name_state {
grid-template-areas: "i" "n";
grid-template-areas: "i" "n" "l";
grid-template-columns: 1fr;
grid-template-rows: 1fr min-content;
grid-template-rows: 1fr min-content min-content;
}
.container.name_state.no-icon {
grid-template-areas: "n" "l";
grid-template-columns: 1fr;
grid-template-rows: 1fr 1fr;
}
.container.name_state.no-icon .name {
align-self: end
}
.container.name_state.no-icon .label {
align-self: start
}
.container.name_state.no-icon.no-label {
grid-template-areas: "n";
grid-template-columns: 1fr;
grid-template-rows: 1fr;
}
.container.name_state.no-icon.no-label .name {
align-self: center
}
/* icon_name_state2nd default */
.container.icon_name_state2nd {
grid-template-areas: "i n" "i s" "i l";
grid-template-columns: 40% 1fr;
grid-template-rows: 1fr min-content 1fr;
}
.container.icon_name_state2nd .name {
align-self: end;
}
.container.icon_name_state2nd .state {
align-self: center;
}
.container.icon_name_state2nd .label {
align-self: start;
}
/* icon_name_state2nd No Label */
.container.icon_name_state2nd.no-label {
grid-template-areas: "i n" "i s";
grid-template-columns: 40% 1fr;
grid-template-rows: 1fr 1fr;
@ -173,7 +274,24 @@ export const styles = css`
align-self: start;
}
/* icon_state_name2nd Default */
.container.icon_state_name2nd {
grid-template-areas: "i s" "i n" "i l";
grid-template-columns: 40% 1fr;
grid-template-rows: 1fr min-content 1fr;
}
.container.icon_state_name2nd .state {
align-self: end;
}
.container.icon_state_name2nd .name {
align-self: center;
}
.container.icon_state_name2nd .state {
align-self: start;
}
/* icon_state_name2nd No Label */
.container.icon_state_name2nd.no-label {
grid-template-areas: "i s" "i n";
grid-template-columns: 40% 1fr;
grid-template-rows: 1fr 1fr;
@ -184,6 +302,12 @@ export const styles = css`
.container.icon_state_name2nd .name {
align-self: start;
}
.container.icon_label {
grid-template-areas: "i l" "n n" "s s";
grid-template-columns: 40% 1fr;
grid-template-rows: 1fr min-content min-content;
}
`;
export default styles;

View File

@ -23,12 +23,15 @@ export interface ButtonCardConfig {
show_icon?: boolean;
show_units?: boolean;
show_entity_picture?: boolean;
show_label?: boolean;
label?: string;
label_template?: string;
entity_picture?: string;
units?: string;
style?: CssStyleConfig[];
state?: StateConfig[];
confirmation?: string;
layout: 'vertical' | 'icon_name_state' | 'name_state' | 'icon_name' | 'icon_state' | 'icon_name_state2nd' | 'icon_state_name2nd';
layout: Layout;
entity_picture_style?: CssStyleConfig[];
default_color: string;
@ -36,8 +39,17 @@ export interface ButtonCardConfig {
color_off: string;
}
export type Layout = 'vertical'
| 'icon_name_state'
| 'name_state'
| 'icon_name'
| 'icon_state'
| 'icon_name_state2nd'
| 'icon_state_name2nd'
| 'icon_label';
export interface StateConfig {
operator?: '<' | '<=' | '==' | '>=' | '>' | '!=' | 'regex' | 'default';
operator?: '<' | '<=' | '==' | '>=' | '>' | '!=' | 'regex' | 'template' | 'default';
value?: any;
name?: string;
icon?: string;
@ -46,6 +58,8 @@ export interface StateConfig {
entity_picture_style?: CssStyleConfig[];
entity_picture?: string;
spin?: boolean;
label?: string;
label_template?: string;
}
export interface CssStyleConfig {