mirror of
https://github.com/GeorgeSG/lovelace-time-picker-card.git
synced 2026-09-06 02:18:44 +00:00
Extract time-unit component. Refactor styles. Refactor error partial.
This commit is contained in:
@@ -0,0 +1,109 @@
|
|||||||
|
import {
|
||||||
|
css,
|
||||||
|
CSSResult,
|
||||||
|
customElement,
|
||||||
|
html,
|
||||||
|
LitElement,
|
||||||
|
property,
|
||||||
|
TemplateResult,
|
||||||
|
} from 'lit-element';
|
||||||
|
import { TimeUnit } from '../models';
|
||||||
|
import { Direction } from '../types';
|
||||||
|
|
||||||
|
@customElement('time-unit')
|
||||||
|
export class TimeUnitComponent extends LitElement {
|
||||||
|
@property() private unit?: TimeUnit;
|
||||||
|
|
||||||
|
render(): TemplateResult {
|
||||||
|
if (!this.unit) {
|
||||||
|
throw new Error('Missing unit in time-unit. This should never happen :)');
|
||||||
|
}
|
||||||
|
|
||||||
|
return html`<div class="time-unit">
|
||||||
|
${this.renderStepChanger(Direction.UP)}
|
||||||
|
<input
|
||||||
|
class="time-input"
|
||||||
|
type="number"
|
||||||
|
placeholder="MM"
|
||||||
|
min="0"
|
||||||
|
max="60"
|
||||||
|
.value="${this.unit.toString()}"
|
||||||
|
@change=${this.onInputChange}
|
||||||
|
/>
|
||||||
|
${this.renderStepChanger(Direction.DOWN)}
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
onInputChange({ target: { value } }: { target: HTMLInputElement }): void {
|
||||||
|
this.unit!.setStringValue(value);
|
||||||
|
this.emitUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
onStepChangerClick(direction: Direction): void {
|
||||||
|
this.unit!.stepUpdate(direction);
|
||||||
|
this.emitUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private emitUpdate(): void {
|
||||||
|
const event = new CustomEvent('update');
|
||||||
|
this.dispatchEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
private renderStepChanger(direction: Direction): TemplateResult {
|
||||||
|
return html`
|
||||||
|
<div class="time-picker-icon" @click=${() => this.onStepChangerClick(direction)}>
|
||||||
|
<ha-icon .icon="mdi:arrow-${direction}"></ha-icon>
|
||||||
|
<mwc-ripple id="ripple"></mwc-ripple>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResult {
|
||||||
|
return css`
|
||||||
|
.time-unit {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-picker-icon {
|
||||||
|
width: 30px;
|
||||||
|
padding: 8px;
|
||||||
|
text-align: center;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-input {
|
||||||
|
width: 30px;
|
||||||
|
padding: 8px 8px 6px;
|
||||||
|
background: var(--time-picker-card-background-color);
|
||||||
|
border: 0;
|
||||||
|
border-bottom: 2px solid var(--time-picker-card-background-color);
|
||||||
|
color: var(--text-color, #fff);
|
||||||
|
text-align: center;
|
||||||
|
font-size: 1em;
|
||||||
|
-moz-appearance: textfield;
|
||||||
|
|
||||||
|
transition: border-color 0.2s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-input:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-input:invalid {
|
||||||
|
box-shadow: none;
|
||||||
|
outline: none;
|
||||||
|
border: 0;
|
||||||
|
border-bottom: 2px solid red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-input::-webkit-inner-spin-button,
|
||||||
|
.time-input::-webkit-outer-spin-button {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,7 @@
|
|||||||
import * as pkg from '../package.json';
|
import * as pkg from '../package.json';
|
||||||
|
|
||||||
export const CARD_VERSION = pkg.version;
|
export const CARD_VERSION = pkg.version;
|
||||||
|
|
||||||
|
export const STYLE_VARIABLES = {
|
||||||
|
'--time-picker-card-background-color': 'rgb(37, 47, 68)',
|
||||||
|
};
|
||||||
|
|||||||
+10
-45
@@ -1,51 +1,16 @@
|
|||||||
import { html, TemplateResult } from 'lit-element';
|
import { html, TemplateResult } from 'lit-element';
|
||||||
import { TimePickerCardConfig, Direction } from './types';
|
import { TimePickerCardConfig } from './types';
|
||||||
import { TimeUnit } from './models';
|
import { LovelaceCard } from 'custom-card-helpers';
|
||||||
|
|
||||||
export class Partial {
|
export class Partial {
|
||||||
static unit(unit: TimeUnit, serviceFn: () => Promise<void>): TemplateResult {
|
|
||||||
const onInputChange = ({ target: { value } }: { target: HTMLInputElement }): void => {
|
|
||||||
unit.setStringValue(value);
|
|
||||||
serviceFn();
|
|
||||||
};
|
|
||||||
|
|
||||||
return html`
|
|
||||||
<div class="time-unit">
|
|
||||||
${Partial.stepChanger(Direction.UP, unit, serviceFn)}
|
|
||||||
<input
|
|
||||||
class="time-input"
|
|
||||||
type="number"
|
|
||||||
placeholder="MM"
|
|
||||||
min="0"
|
|
||||||
max="60"
|
|
||||||
.value="${unit.toString()}"
|
|
||||||
@change=${onInputChange}
|
|
||||||
/>
|
|
||||||
${Partial.stepChanger(Direction.DOWN, unit, serviceFn)}
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static stepChanger(
|
|
||||||
direction: Direction,
|
|
||||||
unit: TimeUnit,
|
|
||||||
serviceFn: () => Promise<void>
|
|
||||||
): TemplateResult {
|
|
||||||
const onIconClick = (): void => {
|
|
||||||
unit.stepUpdate(direction);
|
|
||||||
serviceFn();
|
|
||||||
};
|
|
||||||
|
|
||||||
return html`
|
|
||||||
<div class="time-picker-icon" @click=${onIconClick}>
|
|
||||||
<ha-icon .icon="mdi:arrow-${direction}"></ha-icon>
|
|
||||||
<mwc-ripple id="ripple"></mwc-ripple>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static error(error: string, origConfig: TimePickerCardConfig): TemplateResult {
|
static error(error: string, origConfig: TimePickerCardConfig): TemplateResult {
|
||||||
const config = { error, origConfig };
|
const errorCard = document.createElement('hui-error-card') as LovelaceCard;
|
||||||
return html`<hui-error-card ._config="${config}"></hui-error-card>`;
|
errorCard.setConfig({
|
||||||
|
type: 'error',
|
||||||
|
error,
|
||||||
|
origConfig,
|
||||||
|
});
|
||||||
|
|
||||||
|
return html`${errorCard}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,60 +0,0 @@
|
|||||||
import { css } from 'lit-element';
|
|
||||||
|
|
||||||
export const styleVariables = {
|
|
||||||
'--time-picker-card-background-color': 'rgb(37, 47, 68)',
|
|
||||||
};
|
|
||||||
|
|
||||||
export const styles = css`
|
|
||||||
.time-picker-ha-card {
|
|
||||||
padding: 16px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.time-unit {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
padding: 0 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.time-picker-icon {
|
|
||||||
width: 30px;
|
|
||||||
padding: 8px;
|
|
||||||
text-align: center;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.time-input {
|
|
||||||
width: 30px;
|
|
||||||
padding: 8px;
|
|
||||||
background: var(--time-picker-card-background-color);
|
|
||||||
border: 0;
|
|
||||||
color: var(--text-color, #fff);
|
|
||||||
text-align: center;
|
|
||||||
font-size: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type='number']::-webkit-inner-spin-button,
|
|
||||||
input[type='number']::-webkit-outer-spin-button {
|
|
||||||
-webkit-appearance: none;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type='number'] {
|
|
||||||
-moz-appearance: textfield;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type='number']:focus {
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type='number']:invalid {
|
|
||||||
box-shadow: none;
|
|
||||||
outline: none;
|
|
||||||
border: 0;
|
|
||||||
border-bottom: 2px solid red;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
+30
-13
@@ -1,10 +1,18 @@
|
|||||||
import { HomeAssistant } from 'custom-card-helpers';
|
import { HomeAssistant } from 'custom-card-helpers';
|
||||||
import { HassEntity } from 'home-assistant-js-websocket';
|
import { HassEntity } from 'home-assistant-js-websocket';
|
||||||
import { CSSResult, customElement, html, LitElement, property, TemplateResult } from 'lit-element';
|
import {
|
||||||
import { CARD_VERSION } from './const';
|
css,
|
||||||
|
CSSResult,
|
||||||
|
customElement,
|
||||||
|
html,
|
||||||
|
LitElement,
|
||||||
|
property,
|
||||||
|
TemplateResult,
|
||||||
|
} from 'lit-element';
|
||||||
|
import './components/time-unit';
|
||||||
|
import { CARD_VERSION, STYLE_VARIABLES } from './const';
|
||||||
import { Hour, Minute } from './models';
|
import { Hour, Minute } from './models';
|
||||||
import { Partial } from './partials';
|
import { Partial } from './partials';
|
||||||
import { styles, styleVariables } from './styles';
|
|
||||||
import { TimePickerCardConfig } from './types';
|
import { TimePickerCardConfig } from './types';
|
||||||
|
|
||||||
console.info(
|
console.info(
|
||||||
@@ -20,6 +28,14 @@ export class TimePickerCard extends LitElement {
|
|||||||
@property() private hour?: Hour;
|
@property() private hour?: Hour;
|
||||||
@property() private minute?: Minute;
|
@property() private minute?: Minute;
|
||||||
|
|
||||||
|
connectedCallback(): void {
|
||||||
|
super.connectedCallback();
|
||||||
|
|
||||||
|
Object.entries(STYLE_VARIABLES).forEach(([variable, value]) =>
|
||||||
|
this.style.setProperty(variable, value)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
private get entity(): HassEntity | undefined {
|
private get entity(): HassEntity | undefined {
|
||||||
if (!this.config) {
|
if (!this.config) {
|
||||||
return;
|
return;
|
||||||
@@ -44,15 +60,14 @@ export class TimePickerCard extends LitElement {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setStyleVarialbes();
|
|
||||||
this.hour = new Hour(this.entity?.attributes.hour ?? 0, this.config.hour_step);
|
this.hour = new Hour(this.entity?.attributes.hour ?? 0, this.config.hour_step);
|
||||||
this.minute = new Minute(this.entity?.attributes.minute ?? 0, this.config.minute_step);
|
this.minute = new Minute(this.entity?.attributes.minute ?? 0, this.config.minute_step);
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-card class="time-picker-ha-card">
|
<ha-card class="time-picker-ha-card">
|
||||||
${Partial.unit(this.hour, this.serviceFn.bind(this))}
|
<time-unit .unit=${this.hour} @update=${this.serviceFn.bind(this)}></time-unit>
|
||||||
<div class="time-separator">:</div>
|
<div class="time-separator">:</div>
|
||||||
${Partial.unit(this.minute, this.serviceFn.bind(this))}
|
<time-unit .unit=${this.minute} @update=${this.serviceFn.bind(this)}></time-unit>
|
||||||
</ha-card>
|
</ha-card>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
@@ -86,13 +101,15 @@ export class TimePickerCard extends LitElement {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private setStyleVarialbes(): void {
|
|
||||||
Object.entries(styleVariables).forEach(([variable, value]) =>
|
|
||||||
this.style.setProperty(variable, value)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
static get styles(): CSSResult {
|
static get styles(): CSSResult {
|
||||||
return styles;
|
return css`
|
||||||
|
.time-picker-ha-card {
|
||||||
|
padding: 16px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-1
@@ -1,4 +1,6 @@
|
|||||||
export interface TimePickerCardConfig {
|
import { LovelaceCardConfig } from 'custom-card-helpers';
|
||||||
|
|
||||||
|
export interface TimePickerCardConfig extends LovelaceCardConfig {
|
||||||
entity: string;
|
entity: string;
|
||||||
hour_step?: number;
|
hour_step?: number;
|
||||||
minute_step?: number;
|
minute_step?: number;
|
||||||
|
|||||||
Reference in New Issue
Block a user