mirror of
https://github.com/home-assistant/frontend.git
synced 2025-11-08 18:39:40 +00:00
Compare commits
20 Commits
20251029.1
...
feature/ti
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0ce17e3dea | ||
|
|
ef9029829a | ||
|
|
8bc53c61f2 | ||
|
|
711dcdbff0 | ||
|
|
6834e458d7 | ||
|
|
d597239925 | ||
|
|
004b3ce025 | ||
|
|
43e7b55e99 | ||
|
|
ea5fe14a64 | ||
|
|
807fbf8bb6 | ||
|
|
44cd425ce8 | ||
|
|
af01f66329 | ||
|
|
d5892b372c | ||
|
|
8656df6129 | ||
|
|
8c543ee67c | ||
|
|
4789d8c793 | ||
|
|
f08bbe7c1e | ||
|
|
9f1ee988bc | ||
|
|
eba0fa35d3 | ||
|
|
5b8c5375b4 |
121
src/components/ha-numeric-arrow-input.ts
Normal file
121
src/components/ha-numeric-arrow-input.ts
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
import { css, html, LitElement } from "lit";
|
||||||
|
import { customElement, property, query } from "lit/decorators";
|
||||||
|
import memoizeOne from "memoize-one";
|
||||||
|
import { mdiMinus, mdiPlus } from "@mdi/js";
|
||||||
|
import { fireEvent } from "../common/dom/fire_event";
|
||||||
|
import type { HaIconButton } from "./ha-icon-button";
|
||||||
|
import "./ha-textfield";
|
||||||
|
import "./ha-icon-button";
|
||||||
|
import { clampValue } from "../data/number";
|
||||||
|
|
||||||
|
@customElement("ha-numeric-arrow-input")
|
||||||
|
export class HaNumericArrowInput extends LitElement {
|
||||||
|
@property({ attribute: false }) public disabled = false;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public required = false;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public min?: number;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public max?: number;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public step?: number;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public padStart?: number;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public labelUp = "Increase";
|
||||||
|
|
||||||
|
@property({ attribute: false }) public labelDown = "Decrease";
|
||||||
|
|
||||||
|
@property({ attribute: false }) public value = 0;
|
||||||
|
|
||||||
|
@query("ha-icon-button[data-direction='up']")
|
||||||
|
private _upButton!: HaIconButton;
|
||||||
|
|
||||||
|
@query("ha-icon-button[data-direction='down']")
|
||||||
|
private _downButton!: HaIconButton;
|
||||||
|
|
||||||
|
private _paddedValue = memoizeOne((value: number, padStart?: number) =>
|
||||||
|
value.toString().padStart(padStart ?? 0, "0")
|
||||||
|
);
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return html`<div
|
||||||
|
class="numeric-arrow-input-container"
|
||||||
|
@keydown=${this._keyDown}
|
||||||
|
>
|
||||||
|
<ha-icon-button
|
||||||
|
data-direction="up"
|
||||||
|
.disabled=${this.disabled}
|
||||||
|
.label=${this.labelUp}
|
||||||
|
.path=${mdiPlus}
|
||||||
|
@click=${this._up}
|
||||||
|
></ha-icon-button>
|
||||||
|
<span class="numeric-arrow-input-value"
|
||||||
|
>${this._paddedValue(this.value, this.padStart)}</span
|
||||||
|
>
|
||||||
|
<ha-icon-button
|
||||||
|
data-direction="down"
|
||||||
|
.disabled=${this.disabled}
|
||||||
|
.label=${this.labelDown}
|
||||||
|
.path=${mdiMinus}
|
||||||
|
@click=${this._down}
|
||||||
|
></ha-icon-button>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _keyDown(ev: KeyboardEvent) {
|
||||||
|
if (ev.key === "ArrowUp") {
|
||||||
|
this._upButton.focus();
|
||||||
|
this._up();
|
||||||
|
}
|
||||||
|
if (ev.key === "ArrowDown") {
|
||||||
|
this._downButton.focus();
|
||||||
|
this._down();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _up() {
|
||||||
|
const newValue = this.value + (this.step ?? 1);
|
||||||
|
fireEvent(
|
||||||
|
this,
|
||||||
|
"value-changed",
|
||||||
|
clampValue({ value: newValue, min: this.min, max: this.max })
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _down() {
|
||||||
|
const newValue = this.value - (this.step ?? 1);
|
||||||
|
fireEvent(
|
||||||
|
this,
|
||||||
|
"value-changed",
|
||||||
|
clampValue({ value: newValue, min: this.min, max: this.max })
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static styles = css`
|
||||||
|
.numeric-arrow-input-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.numeric-arrow-input-container ha-icon-button {
|
||||||
|
--mdc-icon-button-size: 24px;
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.numeric-arrow-input-value {
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-numeric-arrow-input": HaNumericArrowInput;
|
||||||
|
}
|
||||||
|
}
|
||||||
281
src/components/ha-time-picker.ts
Normal file
281
src/components/ha-time-picker.ts
Normal file
@@ -0,0 +1,281 @@
|
|||||||
|
import type { PropertyValues } from "lit";
|
||||||
|
import { css, html, LitElement, nothing } from "lit";
|
||||||
|
import { customElement, property, state } from "lit/decorators";
|
||||||
|
import { clampValue } from "../data/number";
|
||||||
|
import { useAmPm } from "../common/datetime/use_am_pm";
|
||||||
|
import { fireEvent } from "../common/dom/fire_event";
|
||||||
|
import type { FrontendLocaleData } from "../data/translation";
|
||||||
|
import type { HomeAssistant } from "../types";
|
||||||
|
import type { ClampedValue } from "../data/number";
|
||||||
|
import "./ha-base-time-input";
|
||||||
|
import "./ha-button";
|
||||||
|
import "./ha-numeric-arrow-input";
|
||||||
|
|
||||||
|
@customElement("ha-time-picker")
|
||||||
|
export class HaTimePicker extends LitElement {
|
||||||
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public locale!: FrontendLocaleData;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public value?: string;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public disabled = false;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public required = false;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public enableSeconds = false;
|
||||||
|
|
||||||
|
@state() private _hours = 0;
|
||||||
|
|
||||||
|
@state() private _minutes = 0;
|
||||||
|
|
||||||
|
@state() private _seconds = 0;
|
||||||
|
|
||||||
|
@state() private _useAmPm = false;
|
||||||
|
|
||||||
|
@state() private _isPm = false;
|
||||||
|
|
||||||
|
protected firstUpdated(changedProperties: PropertyValues) {
|
||||||
|
super.firstUpdated(changedProperties);
|
||||||
|
this._useAmPm = useAmPm(this.locale);
|
||||||
|
|
||||||
|
let hours = NaN;
|
||||||
|
let minutes = NaN;
|
||||||
|
let seconds = NaN;
|
||||||
|
let isPm = false;
|
||||||
|
|
||||||
|
if (this.value) {
|
||||||
|
const parts = this.value?.split(":") || [];
|
||||||
|
minutes = parts[1] ? Number(parts[1]) : 0;
|
||||||
|
seconds = parts[2] ? Number(parts[2]) : 0;
|
||||||
|
const hour24 = parts[0] ? Number(parts[0]) : 0;
|
||||||
|
|
||||||
|
if (this._useAmPm) {
|
||||||
|
if (hour24 === 0) {
|
||||||
|
hours = 12;
|
||||||
|
isPm = false;
|
||||||
|
} else if (hour24 < 12) {
|
||||||
|
hours = hour24;
|
||||||
|
isPm = false;
|
||||||
|
} else if (hour24 === 12) {
|
||||||
|
hours = 12;
|
||||||
|
isPm = true;
|
||||||
|
} else {
|
||||||
|
hours = hour24 - 12;
|
||||||
|
isPm = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
hours = hour24;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this._hours = hours;
|
||||||
|
this._minutes = minutes;
|
||||||
|
this._seconds = seconds;
|
||||||
|
this._isPm = isPm;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render() {
|
||||||
|
return html`<div class="time-picker-container">
|
||||||
|
<ha-numeric-arrow-input
|
||||||
|
.disabled=${this.disabled}
|
||||||
|
.required=${this.required}
|
||||||
|
.min=${this._useAmPm ? 1 : 0}
|
||||||
|
.max=${this._useAmPm ? 12 : 23}
|
||||||
|
.step=${1}
|
||||||
|
.padStart=${2}
|
||||||
|
.value=${this._hours}
|
||||||
|
@value-changed=${this._hoursChanged}
|
||||||
|
.labelUp=${
|
||||||
|
// TODO: Localize
|
||||||
|
"Increase hours"
|
||||||
|
}
|
||||||
|
.labelDown=${
|
||||||
|
// TODO: Localize
|
||||||
|
"Decrease hours"
|
||||||
|
}
|
||||||
|
></ha-numeric-arrow-input>
|
||||||
|
<span class="time-picker-separator">:</span>
|
||||||
|
<ha-numeric-arrow-input
|
||||||
|
.disabled=${this.disabled}
|
||||||
|
.required=${this.required}
|
||||||
|
.min=${0}
|
||||||
|
.max=${59}
|
||||||
|
.step=${1}
|
||||||
|
.padStart=${2}
|
||||||
|
.labelUp=${
|
||||||
|
// TODO: Localize
|
||||||
|
"Increase minutes"
|
||||||
|
}
|
||||||
|
.labelDown=${
|
||||||
|
// TODO: Localize
|
||||||
|
"Decrease minutes"
|
||||||
|
}
|
||||||
|
.value=${this._minutes}
|
||||||
|
@value-changed=${this._minutesChanged}
|
||||||
|
></ha-numeric-arrow-input>
|
||||||
|
${this.enableSeconds
|
||||||
|
? html`
|
||||||
|
<span class="time-picker-separator">:</span>
|
||||||
|
<ha-numeric-arrow-input
|
||||||
|
.disabled=${this.disabled}
|
||||||
|
.required=${this.required}
|
||||||
|
.min=${0}
|
||||||
|
.max=${59}
|
||||||
|
.step=${1}
|
||||||
|
.padStart=${2}
|
||||||
|
.labelUp=${
|
||||||
|
// TODO: Localize
|
||||||
|
"Increase seconds"
|
||||||
|
}
|
||||||
|
.labelDown=${
|
||||||
|
// TODO: Localize
|
||||||
|
"Decrease seconds"
|
||||||
|
}
|
||||||
|
.value=${this._seconds}
|
||||||
|
@value-changed=${this._secondsChanged}
|
||||||
|
></ha-numeric-arrow-input>
|
||||||
|
`
|
||||||
|
: nothing}
|
||||||
|
${this._useAmPm
|
||||||
|
? html`
|
||||||
|
<ha-button @click=${this._toggleAmPm}>
|
||||||
|
${this._isPm ? "PM" : "AM"}
|
||||||
|
</ha-button>
|
||||||
|
`
|
||||||
|
: nothing}
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected updated(changedProperties: PropertyValues) {
|
||||||
|
super.updated(changedProperties);
|
||||||
|
|
||||||
|
if (changedProperties.has("_hours")) {
|
||||||
|
this._timeUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changedProperties.has("_minutes")) {
|
||||||
|
this._timeUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changedProperties.has("_seconds")) {
|
||||||
|
this._timeUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changedProperties.has("_useAmPm")) {
|
||||||
|
this._timeUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changedProperties.has("_isPm")) {
|
||||||
|
this._timeUpdated();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _hoursChanged(ev: CustomEvent<ClampedValue>) {
|
||||||
|
ev.stopPropagation?.();
|
||||||
|
this._hours = ev.detail.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _minutesChanged(ev: CustomEvent<ClampedValue>) {
|
||||||
|
ev.stopPropagation?.();
|
||||||
|
this._minutes = ev.detail.value;
|
||||||
|
if (ev.detail.clamped) {
|
||||||
|
if (ev.detail.value === 0) {
|
||||||
|
this._hoursChanged({
|
||||||
|
detail: clampValue({
|
||||||
|
value: this._hours - 1,
|
||||||
|
min: this._useAmPm ? 1 : 0,
|
||||||
|
max: this._useAmPm ? 12 : 23,
|
||||||
|
}),
|
||||||
|
} as CustomEvent<ClampedValue>);
|
||||||
|
this._minutes = 59;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ev.detail.value === 59) {
|
||||||
|
this._hoursChanged({
|
||||||
|
detail: clampValue({
|
||||||
|
value: this._hours + 1,
|
||||||
|
min: this._useAmPm ? 1 : 0,
|
||||||
|
max: this._useAmPm ? 12 : 23,
|
||||||
|
}),
|
||||||
|
} as CustomEvent<ClampedValue>);
|
||||||
|
const hourMax = this._useAmPm ? 12 : 23;
|
||||||
|
if (this._hours < hourMax) {
|
||||||
|
this._minutes = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _secondsChanged(ev: CustomEvent<ClampedValue>) {
|
||||||
|
ev.stopPropagation?.();
|
||||||
|
this._seconds = ev.detail.value;
|
||||||
|
if (ev.detail.clamped) {
|
||||||
|
if (ev.detail.value === 0) {
|
||||||
|
this._minutesChanged({
|
||||||
|
detail: clampValue({ value: this._minutes - 1, min: 0, max: 59 }),
|
||||||
|
} as CustomEvent<ClampedValue>);
|
||||||
|
this._seconds = 59;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ev.detail.value === 59) {
|
||||||
|
this._minutesChanged({
|
||||||
|
detail: clampValue({ value: this._minutes + 1, min: 0, max: 59 }),
|
||||||
|
} as CustomEvent<ClampedValue>);
|
||||||
|
const hourMax = this._useAmPm ? 12 : 23;
|
||||||
|
if (!(this._hours === hourMax && this._minutes === 59)) {
|
||||||
|
this._seconds = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _toggleAmPm() {
|
||||||
|
this._isPm = !this._isPm;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _timeUpdated() {
|
||||||
|
let hour24 = this._hours;
|
||||||
|
|
||||||
|
if (this._useAmPm) {
|
||||||
|
if (this._hours === 12) {
|
||||||
|
hour24 = this._isPm ? 12 : 0;
|
||||||
|
} else {
|
||||||
|
hour24 = this._isPm ? this._hours + 12 : this._hours;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeParts = [
|
||||||
|
hour24.toString().padStart(2, "0"),
|
||||||
|
this._minutes.toString().padStart(2, "0"),
|
||||||
|
this._seconds.toString().padStart(2, "0"),
|
||||||
|
];
|
||||||
|
|
||||||
|
const time = timeParts.join(":");
|
||||||
|
if (time === this.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.value = time;
|
||||||
|
fireEvent(this, "change");
|
||||||
|
fireEvent(this, "value-changed", { value: time });
|
||||||
|
}
|
||||||
|
|
||||||
|
static styles = css`
|
||||||
|
.time-picker-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-picker-separator {
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-time-picker": HaTimePicker;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,3 +12,35 @@ export const getNumberDeviceClassConvertibleUnits = (
|
|||||||
type: "number/device_class_convertible_units",
|
type: "number/device_class_convertible_units",
|
||||||
device_class: deviceClass,
|
device_class: deviceClass,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export interface ClampedValue {
|
||||||
|
clamped: boolean;
|
||||||
|
value: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clamp a value between a minimum and maximum value
|
||||||
|
* @param value - The value to clamp
|
||||||
|
* @param min - The minimum value
|
||||||
|
* @param max - The maximum value
|
||||||
|
* @returns The clamped value
|
||||||
|
*/
|
||||||
|
export const clampValue = ({
|
||||||
|
value,
|
||||||
|
min,
|
||||||
|
max,
|
||||||
|
}: {
|
||||||
|
value: number;
|
||||||
|
min?: number;
|
||||||
|
max?: number;
|
||||||
|
}): ClampedValue => {
|
||||||
|
if (max !== undefined && value > max) {
|
||||||
|
return { clamped: true, value: max };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (min !== undefined && value < min) {
|
||||||
|
return { clamped: true, value: min };
|
||||||
|
}
|
||||||
|
|
||||||
|
return { clamped: false, value };
|
||||||
|
};
|
||||||
|
|||||||
@@ -79,6 +79,13 @@ export const demoPanels: Panels = {
|
|||||||
config: null,
|
config: null,
|
||||||
url_path: "energy",
|
url_path: "energy",
|
||||||
},
|
},
|
||||||
|
"time-picker": {
|
||||||
|
component_name: "time-picker",
|
||||||
|
icon: "hass:clock-outline",
|
||||||
|
title: "time_picker",
|
||||||
|
config: null,
|
||||||
|
url_path: "time-picker",
|
||||||
|
},
|
||||||
// config: {
|
// config: {
|
||||||
// component_name: "config",
|
// component_name: "config",
|
||||||
// icon: "hass:cog",
|
// icon: "hass:cog",
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ const COMPONENTS = {
|
|||||||
my: () => import("../panels/my/ha-panel-my"),
|
my: () => import("../panels/my/ha-panel-my"),
|
||||||
profile: () => import("../panels/profile/ha-panel-profile"),
|
profile: () => import("../panels/profile/ha-panel-profile"),
|
||||||
todo: () => import("../panels/todo/ha-panel-todo"),
|
todo: () => import("../panels/todo/ha-panel-todo"),
|
||||||
|
"time-picker": () => import("../panels/time-picker/ha-panel-time-picker"),
|
||||||
"media-browser": () =>
|
"media-browser": () =>
|
||||||
import("../panels/media-browser/ha-panel-media-browser"),
|
import("../panels/media-browser/ha-panel-media-browser"),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -54,6 +54,10 @@ class DeveloperToolsRouter extends HassRouterPage {
|
|||||||
tag: "developer-tools-debug",
|
tag: "developer-tools-debug",
|
||||||
load: () => import("./debug/developer-tools-debug"),
|
load: () => import("./debug/developer-tools-debug"),
|
||||||
},
|
},
|
||||||
|
"time-picker": {
|
||||||
|
tag: "developer-tools-time-picker",
|
||||||
|
load: () => import("../time-picker/ha-panel-time-picker"),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -80,6 +80,13 @@ class PanelDeveloperTools extends LitElement {
|
|||||||
<sl-tab slot="nav" panel="assist" .active=${page === "assist"}
|
<sl-tab slot="nav" panel="assist" .active=${page === "assist"}
|
||||||
>Assist</sl-tab
|
>Assist</sl-tab
|
||||||
>
|
>
|
||||||
|
<sl-tab
|
||||||
|
slot="nav"
|
||||||
|
panel="time-picker"
|
||||||
|
.active=${page === "time-picker"}
|
||||||
|
>
|
||||||
|
Time Picker
|
||||||
|
</sl-tab>
|
||||||
</sl-tab-group>
|
</sl-tab-group>
|
||||||
</div>
|
</div>
|
||||||
<developer-tools-router
|
<developer-tools-router
|
||||||
|
|||||||
180
src/panels/time-picker/ha-panel-time-picker.ts
Normal file
180
src/panels/time-picker/ha-panel-time-picker.ts
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
import { html, LitElement, css } from "lit";
|
||||||
|
import { customElement, property, state } from "lit/decorators";
|
||||||
|
import type { HomeAssistant } from "../../types";
|
||||||
|
import "../../components/ha-time-picker";
|
||||||
|
import "../../components/ha-card";
|
||||||
|
import "../../components/ha-button";
|
||||||
|
import "../../components/ha-alert";
|
||||||
|
import "../../components/ha-selector/ha-selector";
|
||||||
|
|
||||||
|
@customElement("developer-tools-time-picker")
|
||||||
|
export class DeveloperToolsTimePicker extends LitElement {
|
||||||
|
@property({ attribute: false })
|
||||||
|
public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property({ type: Boolean, reflect: true })
|
||||||
|
public narrow = false;
|
||||||
|
|
||||||
|
@state()
|
||||||
|
private _timeValue = "14:15:00";
|
||||||
|
|
||||||
|
@state()
|
||||||
|
private _timeValue2 = "09:05:05";
|
||||||
|
|
||||||
|
static get styles() {
|
||||||
|
return css`
|
||||||
|
:host {
|
||||||
|
display: block;
|
||||||
|
padding: 16px;
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header h1 {
|
||||||
|
margin: 0 0 8px 0;
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
font-size: 28px;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header p {
|
||||||
|
margin: 0;
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section {
|
||||||
|
margin-bottom: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section h2 {
|
||||||
|
margin: 0 0 16px 0;
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.example {
|
||||||
|
background: var(--card-background-color);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 24px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
border: 1px solid var(--divider-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.example h3 {
|
||||||
|
margin: 0 0 16px 0;
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-picker-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.value-display {
|
||||||
|
background: var(--secondary-background-color);
|
||||||
|
padding: 8px 12px;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
min-width: 100px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.controls {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-toggle {
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
:host {
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-picker-container {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.controls {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render() {
|
||||||
|
return html`
|
||||||
|
<div class="header">
|
||||||
|
<h1>Time picker demo</h1>
|
||||||
|
<p>
|
||||||
|
This page demonstrates the ha-time-picker component with various
|
||||||
|
configurations and use cases.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="section">
|
||||||
|
<h2>Time picker</h2>
|
||||||
|
<div class="example">
|
||||||
|
<div class="time-picker-container">
|
||||||
|
<ha-time-picker
|
||||||
|
.locale=${this.hass.locale}
|
||||||
|
.value=${this._timeValue}
|
||||||
|
@value-changed=${this._onTimeChanged}
|
||||||
|
></ha-time-picker>
|
||||||
|
<div class="value-display">${this._timeValue}</div>
|
||||||
|
</div>
|
||||||
|
<p>Current value: ${this._timeValue}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="section">
|
||||||
|
<h2>Time picker with seconds</h2>
|
||||||
|
<div class="example">
|
||||||
|
<div class="time-picker-container">
|
||||||
|
<ha-time-picker
|
||||||
|
.locale=${this.hass.locale}
|
||||||
|
.value=${this._timeValue2}
|
||||||
|
.enableSeconds=${true}
|
||||||
|
@value-changed=${this._onTime2Changed}
|
||||||
|
></ha-time-picker>
|
||||||
|
<div class="value-display">${this._timeValue2}</div>
|
||||||
|
</div>
|
||||||
|
<p>Current value: ${this._timeValue2}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _onTimeChanged(ev: CustomEvent) {
|
||||||
|
this._timeValue = ev.detail.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _onTime2Changed(ev: CustomEvent) {
|
||||||
|
this._timeValue2 = ev.detail.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"developer-tools-time-picker": DeveloperToolsTimePicker;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user