Convert tile button into more generic button (#15485)
* Add button group component * Add focus style * Use new component in tile card
This commit is contained in:
parent
d5fb924cb4
commit
cabbbcf9f3
|
@ -0,0 +1,3 @@
|
|||
---
|
||||
title: Bar Button
|
||||
---
|
|
@ -0,0 +1,189 @@
|
|||
import {
|
||||
mdiFanSpeed1,
|
||||
mdiFanSpeed2,
|
||||
mdiFanSpeed3,
|
||||
mdiLightbulb,
|
||||
} from "@mdi/js";
|
||||
import { css, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement } from "lit/decorators";
|
||||
import { ifDefined } from "lit/directives/if-defined";
|
||||
import { repeat } from "lit/directives/repeat";
|
||||
import "../../../../src/components/ha-bar-button";
|
||||
import "../../../../src/components/ha-card";
|
||||
import "../../../../src/components/ha-svg-icon";
|
||||
import "../../../../src/components/ha-bar-button-group";
|
||||
|
||||
type Button = {
|
||||
label: string;
|
||||
icon?: string;
|
||||
class?: string;
|
||||
disabled?: boolean;
|
||||
};
|
||||
|
||||
const buttons: Button[] = [
|
||||
{
|
||||
label: "Button",
|
||||
},
|
||||
{
|
||||
label: "Button and custom style",
|
||||
class: "custom",
|
||||
},
|
||||
{
|
||||
label: "Disabled Button",
|
||||
disabled: true,
|
||||
},
|
||||
];
|
||||
|
||||
type ButtonGroup = {
|
||||
vertical?: boolean;
|
||||
class?: string;
|
||||
};
|
||||
|
||||
const buttonGroups: ButtonGroup[] = [
|
||||
{},
|
||||
{
|
||||
class: "custom-group",
|
||||
},
|
||||
];
|
||||
|
||||
@customElement("demo-components-ha-bar-button")
|
||||
export class DemoHaBarButton extends LitElement {
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<ha-card>
|
||||
${repeat(
|
||||
buttons,
|
||||
(btn) => html`
|
||||
<div class="card-content">
|
||||
<pre>Config: ${JSON.stringify(btn)}</pre>
|
||||
<ha-bar-button
|
||||
class=${ifDefined(btn.class)}
|
||||
label=${ifDefined(btn.label)}
|
||||
disabled=${ifDefined(btn.disabled)}
|
||||
>
|
||||
<ha-svg-icon .path=${btn.icon || mdiLightbulb}></ha-svg-icon>
|
||||
</ha-bar-button>
|
||||
</div>
|
||||
`
|
||||
)}
|
||||
</ha-card>
|
||||
|
||||
<ha-card>
|
||||
${repeat(
|
||||
buttonGroups,
|
||||
(group) => html`
|
||||
<div class="card-content">
|
||||
<pre>Config: ${JSON.stringify(group)}</pre>
|
||||
<ha-bar-button-group class=${ifDefined(group.class)}>
|
||||
<ha-bar-button>
|
||||
<ha-svg-icon
|
||||
.path=${mdiFanSpeed1}
|
||||
label="Speed 1"
|
||||
></ha-svg-icon>
|
||||
</ha-bar-button>
|
||||
<ha-bar-button>
|
||||
<ha-svg-icon
|
||||
.path=${mdiFanSpeed2}
|
||||
label="Speed 2"
|
||||
></ha-svg-icon>
|
||||
</ha-bar-button>
|
||||
<ha-bar-button>
|
||||
<ha-svg-icon
|
||||
.path=${mdiFanSpeed3}
|
||||
label="Speed 3"
|
||||
></ha-svg-icon>
|
||||
</ha-bar-button>
|
||||
</ha-bar-button-group>
|
||||
</div>
|
||||
`
|
||||
)}
|
||||
</ha-card>
|
||||
<ha-card>
|
||||
<div class="card-content">
|
||||
<p class="title"><b>Vertical</b></p>
|
||||
<div class="vertical-buttons">
|
||||
${repeat(
|
||||
buttonGroups,
|
||||
(group) => html`
|
||||
<ha-bar-button-group vertical class=${ifDefined(group.class)}>
|
||||
<ha-bar-button>
|
||||
<ha-svg-icon
|
||||
.path=${mdiFanSpeed1}
|
||||
label="Speed 1"
|
||||
></ha-svg-icon>
|
||||
</ha-bar-button>
|
||||
<ha-bar-button>
|
||||
<ha-svg-icon
|
||||
.path=${mdiFanSpeed2}
|
||||
label="Speed 2"
|
||||
></ha-svg-icon>
|
||||
</ha-bar-button>
|
||||
<ha-bar-button>
|
||||
<ha-svg-icon
|
||||
.path=${mdiFanSpeed3}
|
||||
label="Speed 3"
|
||||
></ha-svg-icon>
|
||||
</ha-bar-button>
|
||||
</ha-bar-button-group>
|
||||
`
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</ha-card>
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return css`
|
||||
ha-card {
|
||||
max-width: 600px;
|
||||
margin: 24px auto;
|
||||
}
|
||||
pre {
|
||||
margin-top: 0;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
label {
|
||||
font-weight: 600;
|
||||
}
|
||||
.custom {
|
||||
--button-bar-icon-color: var(--primary-color);
|
||||
--button-bar-background-color: var(--primary-color);
|
||||
--button-bar-background-opacity: 0.2;
|
||||
--button-bar-border-radius: 18px;
|
||||
height: 100px;
|
||||
width: 100px;
|
||||
}
|
||||
.custom-group {
|
||||
--button-bar-group-thickness: 100px;
|
||||
--button-bar-group-border-radius: 18px;
|
||||
--button-bar-group-spacing: 20px;
|
||||
}
|
||||
.custom-group ha-bar-button {
|
||||
--button-bar-border-radius: 18px;
|
||||
--mdc-icon-size: 32px;
|
||||
}
|
||||
.vertical-buttons {
|
||||
height: 300px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
}
|
||||
p.title {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.vertical-switches > *:not(:last-child) {
|
||||
margin-right: 4px;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-components-ha-bar-button": DemoHaBarButton;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
|
||||
@customElement("ha-bar-button-group")
|
||||
export class HaBarButtonGroup extends LitElement {
|
||||
@property({ type: Boolean, reflect: true })
|
||||
public vertical = false;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<div class="container">
|
||||
<slot></slot>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return css`
|
||||
:host {
|
||||
--button-bar-group-spacing: 12px;
|
||||
--button-bar-group-thickness: 40px;
|
||||
height: var(--button-bar-group-thickness);
|
||||
width: auto;
|
||||
display: block;
|
||||
}
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
::slotted(*) {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
::slotted(*:not(:last-child)) {
|
||||
margin-right: var(--button-bar-group-spacing);
|
||||
margin-inline-end: var(--button-bar-group-spacing);
|
||||
margin-inline-start: initial;
|
||||
direction: var(--direction);
|
||||
}
|
||||
:host([vertical]) {
|
||||
width: var(--button-bar-group-thickness);
|
||||
height: auto;
|
||||
}
|
||||
:host([vertical]) .container {
|
||||
flex-direction: column;
|
||||
}
|
||||
:host([vertical]) ::slotted(ha-bar-button:not(:last-child)) {
|
||||
margin-right: initial;
|
||||
margin-inline-end: initial;
|
||||
margin-bottom: var(--button-bar-group-spacing);
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-bar-button-group": HaBarButtonGroup;
|
||||
}
|
||||
}
|
|
@ -9,11 +9,9 @@ import {
|
|||
state,
|
||||
} from "lit/decorators";
|
||||
import { ifDefined } from "lit/directives/if-defined";
|
||||
import "../ha-icon";
|
||||
import "../ha-svg-icon";
|
||||
|
||||
@customElement("ha-tile-button")
|
||||
export class HaTileButton extends LitElement {
|
||||
@customElement("ha-bar-button")
|
||||
export class HaBarButton extends LitElement {
|
||||
@property({ type: Boolean, reflect: true }) disabled = false;
|
||||
|
||||
@property() public label?: string;
|
||||
|
@ -28,7 +26,7 @@ export class HaTileButton extends LitElement {
|
|||
type="button"
|
||||
class="button"
|
||||
aria-label=${ifDefined(this.label)}
|
||||
.title=${this.label}
|
||||
title=${ifDefined(this.label)}
|
||||
.disabled=${Boolean(this.disabled)}
|
||||
@focus=${this.handleRippleFocus}
|
||||
@blur=${this.handleRippleBlur}
|
||||
|
@ -81,9 +79,11 @@ export class HaTileButton extends LitElement {
|
|||
static get styles(): CSSResultGroup {
|
||||
return css`
|
||||
:host {
|
||||
--tile-button-icon-color: var(--primary-text-color);
|
||||
--tile-button-background-color: var(--disabled-color);
|
||||
--tile-button-background-opacity: 0.2;
|
||||
display: block;
|
||||
--button-bar-icon-color: var(--primary-text-color);
|
||||
--button-bar-background-color: var(--disabled-color);
|
||||
--button-bar-background-opacity: 0.2;
|
||||
--button-bar-border-radius: 10px;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
|
@ -97,7 +97,7 @@ export class HaTileButton extends LitElement {
|
|||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 10px;
|
||||
border-radius: var(--button-bar-border-radius);
|
||||
border: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
@ -106,7 +106,8 @@ export class HaTileButton extends LitElement {
|
|||
outline: none;
|
||||
overflow: hidden;
|
||||
background: none;
|
||||
--mdc-ripple-color: var(--tile-button-background-color);
|
||||
z-index: 1;
|
||||
--mdc-ripple-color: var(--button-bar-background-color);
|
||||
}
|
||||
.button::before {
|
||||
content: "";
|
||||
|
@ -115,22 +116,21 @@ export class HaTileButton extends LitElement {
|
|||
left: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
background-color: var(--tile-button-background-color);
|
||||
background-color: var(--button-bar-background-color);
|
||||
transition: background-color 180ms ease-in-out,
|
||||
opacity 180ms ease-in-out;
|
||||
opacity: var(--tile-button-background-opacity);
|
||||
opacity: var(--button-bar-background-opacity);
|
||||
}
|
||||
.button ::slotted(*) {
|
||||
--mdc-icon-size: 20px;
|
||||
transition: color 180ms ease-in-out;
|
||||
color: var(--tile-button-icon-color);
|
||||
color: var(--button-bar-icon-color);
|
||||
pointer-events: none;
|
||||
}
|
||||
.button:disabled {
|
||||
cursor: not-allowed;
|
||||
--tile-button-background-color: var(--disabled-color);
|
||||
--tile-button-icon-color: var(--disabled-text-color);
|
||||
--tile-button-background-opacity: 0.2;
|
||||
--button-bar-background-color: var(--disabled-color);
|
||||
--button-bar-icon-color: var(--disabled-text-color);
|
||||
--button-bar-background-opacity: 0.2;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
@ -138,6 +138,6 @@ export class HaTileButton extends LitElement {
|
|||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-tile-button": HaTileButton;
|
||||
"ha-bar-button": HaBarButton;
|
||||
}
|
||||
}
|
|
@ -7,7 +7,8 @@ import {
|
|||
computeOpenIcon,
|
||||
} from "../../../common/entity/cover_icon";
|
||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||
import "../../../components/tile/ha-tile-button";
|
||||
import "../../../components/ha-bar-button";
|
||||
import "../../../components/ha-bar-button-group";
|
||||
import {
|
||||
canClose,
|
||||
canOpen,
|
||||
|
@ -69,10 +70,10 @@ class HuiCoverOpenCloseTileFeature
|
|||
}
|
||||
|
||||
return html`
|
||||
<div class="container">
|
||||
<ha-bar-button-group>
|
||||
${supportsFeature(this.stateObj, CoverEntityFeature.OPEN)
|
||||
? html`
|
||||
<ha-tile-button
|
||||
<ha-bar-button
|
||||
.label=${this.hass.localize(
|
||||
"ui.dialogs.more_info_control.cover.open_cover"
|
||||
)}
|
||||
|
@ -82,12 +83,12 @@ class HuiCoverOpenCloseTileFeature
|
|||
<ha-svg-icon
|
||||
.path=${computeOpenIcon(this.stateObj)}
|
||||
></ha-svg-icon>
|
||||
</ha-tile-button>
|
||||
</ha-bar-button>
|
||||
`
|
||||
: null}
|
||||
${supportsFeature(this.stateObj, CoverEntityFeature.STOP)
|
||||
? html`
|
||||
<ha-tile-button
|
||||
<ha-bar-button
|
||||
.label=${this.hass.localize(
|
||||
"ui.dialogs.more_info_control.cover.stop_cover"
|
||||
)}
|
||||
|
@ -95,12 +96,12 @@ class HuiCoverOpenCloseTileFeature
|
|||
.disabled=${!canStop(this.stateObj)}
|
||||
>
|
||||
<ha-svg-icon .path=${mdiStop}></ha-svg-icon>
|
||||
</ha-tile-button>
|
||||
</ha-bar-button>
|
||||
`
|
||||
: null}
|
||||
${supportsFeature(this.stateObj, CoverEntityFeature.CLOSE)
|
||||
? html`
|
||||
<ha-tile-button
|
||||
<ha-bar-button
|
||||
.label=${this.hass.localize(
|
||||
"ui.dialogs.more_info_control.cover.close_cover"
|
||||
)}
|
||||
|
@ -110,29 +111,18 @@ class HuiCoverOpenCloseTileFeature
|
|||
<ha-svg-icon
|
||||
.path=${computeCloseIcon(this.stateObj)}
|
||||
></ha-svg-icon>
|
||||
</ha-tile-button>
|
||||
</ha-bar-button>
|
||||
`
|
||||
: undefined}
|
||||
</div>
|
||||
</ha-bar-button-group>
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return css`
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
padding: 0 12px 12px 12px;
|
||||
width: auto;
|
||||
}
|
||||
ha-tile-button {
|
||||
flex: 1;
|
||||
}
|
||||
ha-tile-button:not(:last-child) {
|
||||
margin-right: 12px;
|
||||
margin-inline-end: 12px;
|
||||
margin-inline-start: initial;
|
||||
direction: var(--direction);
|
||||
ha-bar-button-group {
|
||||
margin: 0 12px 12px 12px;
|
||||
--button-bar-group-spacing: 12px;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
|
|
@ -3,7 +3,8 @@ import { HassEntity } from "home-assistant-js-websocket";
|
|||
import { css, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||
import "../../../components/tile/ha-tile-button";
|
||||
import "../../../components/ha-bar-button";
|
||||
import "../../../components/ha-bar-button-group";
|
||||
import {
|
||||
canCloseTilt,
|
||||
canOpenTilt,
|
||||
|
@ -65,10 +66,10 @@ class HuiCoverTiltTileFeature
|
|||
}
|
||||
|
||||
return html`
|
||||
<div class="container">
|
||||
<ha-bar-button-group>
|
||||
${supportsFeature(this.stateObj, CoverEntityFeature.OPEN_TILT)
|
||||
? html`
|
||||
<ha-tile-button
|
||||
<ha-bar-button
|
||||
.label=${this.hass.localize(
|
||||
"ui.dialogs.more_info_control.cover.open_tilt_cover"
|
||||
)}
|
||||
|
@ -76,12 +77,12 @@ class HuiCoverTiltTileFeature
|
|||
.disabled=${!canOpenTilt(this.stateObj)}
|
||||
>
|
||||
<ha-svg-icon .path=${mdiArrowTopRight}></ha-svg-icon>
|
||||
</ha-tile-button>
|
||||
</ha-bar-button>
|
||||
`
|
||||
: null}
|
||||
${supportsFeature(this.stateObj, CoverEntityFeature.STOP_TILT)
|
||||
? html`
|
||||
<ha-tile-button
|
||||
<ha-bar-button
|
||||
.label=${this.hass.localize(
|
||||
"ui.dialogs.more_info_control.cover.stop_cover"
|
||||
)}
|
||||
|
@ -89,12 +90,12 @@ class HuiCoverTiltTileFeature
|
|||
.disabled=${!canStopTilt(this.stateObj)}
|
||||
>
|
||||
<ha-svg-icon .path=${mdiStop}></ha-svg-icon>
|
||||
</ha-tile-button>
|
||||
</ha-bar-button>
|
||||
`
|
||||
: null}
|
||||
${supportsFeature(this.stateObj, CoverEntityFeature.CLOSE_TILT)
|
||||
? html`
|
||||
<ha-tile-button
|
||||
<ha-bar-button
|
||||
.label=${this.hass.localize(
|
||||
"ui.dialogs.more_info_control.cover.close_tilt_cover"
|
||||
)}
|
||||
|
@ -102,29 +103,18 @@ class HuiCoverTiltTileFeature
|
|||
.disabled=${!canCloseTilt(this.stateObj)}
|
||||
>
|
||||
<ha-svg-icon .path=${mdiArrowBottomLeft}></ha-svg-icon>
|
||||
</ha-tile-button>
|
||||
</ha-bar-button>
|
||||
`
|
||||
: undefined}
|
||||
</div>
|
||||
</ha-bar-button-group>
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return css`
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
padding: 0 12px 12px 12px;
|
||||
width: auto;
|
||||
}
|
||||
ha-tile-button {
|
||||
flex: 1;
|
||||
}
|
||||
ha-tile-button:not(:last-child) {
|
||||
margin-right: 12px;
|
||||
margin-inline-end: 12px;
|
||||
margin-inline-start: initial;
|
||||
direction: var(--direction);
|
||||
ha-bar-button-group {
|
||||
margin: 0 12px 12px 12px;
|
||||
--button-bar-group-spacing: 12px;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
|
|
@ -11,7 +11,8 @@ import { HassEntity } from "home-assistant-js-websocket";
|
|||
import { css, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||
import "../../../components/tile/ha-tile-button";
|
||||
import "../../../components/ha-bar-button";
|
||||
import "../../../components/ha-bar-button-group";
|
||||
import { UNAVAILABLE } from "../../../data/entity";
|
||||
import {
|
||||
canReturnHome,
|
||||
|
@ -167,7 +168,7 @@ class HuiVacuumCommandTileFeature
|
|||
const stateObj = this.stateObj as VacuumEntity;
|
||||
|
||||
return html`
|
||||
<div class="container">
|
||||
<ha-bar-button-group>
|
||||
${VACUUM_COMMANDS.filter(
|
||||
(command) =>
|
||||
supportsVacuumCommand(stateObj, command) &&
|
||||
|
@ -175,7 +176,7 @@ class HuiVacuumCommandTileFeature
|
|||
).map((command) => {
|
||||
const button = VACUUM_COMMANDS_BUTTONS[command](stateObj);
|
||||
return html`
|
||||
<ha-tile-button
|
||||
<ha-bar-button
|
||||
.entry=${button}
|
||||
.label=${this.hass!.localize(
|
||||
// @ts-ignore
|
||||
|
@ -185,29 +186,18 @@ class HuiVacuumCommandTileFeature
|
|||
.disabled=${button.disabled || stateObj.state === UNAVAILABLE}
|
||||
>
|
||||
<ha-svg-icon .path=${button.icon}></ha-svg-icon>
|
||||
</ha-tile-button>
|
||||
</ha-bar-button>
|
||||
`;
|
||||
})}
|
||||
</div>
|
||||
</ha-bar-button-group>
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return css`
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
padding: 0 12px 12px 12px;
|
||||
width: auto;
|
||||
}
|
||||
ha-tile-button {
|
||||
flex: 1;
|
||||
}
|
||||
ha-tile-button:not(:last-child) {
|
||||
margin-right: 12px;
|
||||
margin-inline-end: 12px;
|
||||
margin-inline-start: initial;
|
||||
direction: var(--direction);
|
||||
ha-bar-button-group {
|
||||
margin: 0 12px 12px 12px;
|
||||
--button-bar-group-spacing: 12px;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue