mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-24 09:46:36 +00:00
Clearable time selector (#18590)
* initial attempt at clearable time selector * only show clear button if there is a value * use null instead of undefined. * leave event value undefined * move clear button into input Co-authored-by: Bram Kragten <mail@bramkragten.nl> --------- Co-authored-by: J. Nick Koston <nick@koston.org> Co-authored-by: Bram Kragten <mail@bramkragten.nl>
This commit is contained in:
parent
89d842c2a8
commit
0358fe5614
@ -1,10 +1,12 @@
|
||||
import "@material/mwc-list/mwc-list-item";
|
||||
import { css, html, LitElement, TemplateResult } from "lit";
|
||||
import { css, html, LitElement, TemplateResult, nothing } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { mdiClose } from "@mdi/js";
|
||||
import { ifDefined } from "lit/directives/if-defined";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { stopPropagation } from "../common/dom/stop_propagation";
|
||||
import "./ha-select";
|
||||
import "./ha-icon-button";
|
||||
import { HaTextField } from "./ha-textfield";
|
||||
import "./ha-input-helper-text";
|
||||
|
||||
@ -124,116 +126,128 @@ export class HaBaseTimeInput extends LitElement {
|
||||
*/
|
||||
@property() amPm: "AM" | "PM" = "AM";
|
||||
|
||||
@property({ type: Boolean, reflect: true }) public clearable?: boolean;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
${this.label
|
||||
? html`<label>${this.label}${this.required ? " *" : ""}</label>`
|
||||
: ""}
|
||||
<div class="time-input-wrap">
|
||||
${this.enableDay
|
||||
? html`
|
||||
<ha-textfield
|
||||
id="day"
|
||||
<div class="time-input-wrap-wrap">
|
||||
<div class="time-input-wrap">
|
||||
${this.enableDay
|
||||
? html`
|
||||
<ha-textfield
|
||||
id="day"
|
||||
type="number"
|
||||
inputmode="numeric"
|
||||
.value=${this.days.toFixed()}
|
||||
.label=${this.dayLabel}
|
||||
name="days"
|
||||
@change=${this._valueChanged}
|
||||
@focusin=${this._onFocus}
|
||||
no-spinner
|
||||
.required=${this.required}
|
||||
.autoValidate=${this.autoValidate}
|
||||
min="0"
|
||||
.disabled=${this.disabled}
|
||||
suffix=":"
|
||||
class="hasSuffix"
|
||||
>
|
||||
</ha-textfield>
|
||||
`
|
||||
: ""}
|
||||
|
||||
<ha-textfield
|
||||
id="hour"
|
||||
type="number"
|
||||
inputmode="numeric"
|
||||
.value=${this.hours.toFixed()}
|
||||
.label=${this.hourLabel}
|
||||
name="hours"
|
||||
@change=${this._valueChanged}
|
||||
@focusin=${this._onFocus}
|
||||
no-spinner
|
||||
.required=${this.required}
|
||||
.autoValidate=${this.autoValidate}
|
||||
maxlength="2"
|
||||
max=${ifDefined(this._hourMax)}
|
||||
min="0"
|
||||
.disabled=${this.disabled}
|
||||
suffix=":"
|
||||
class="hasSuffix"
|
||||
>
|
||||
</ha-textfield>
|
||||
<ha-textfield
|
||||
id="min"
|
||||
type="number"
|
||||
inputmode="numeric"
|
||||
.value=${this._formatValue(this.minutes)}
|
||||
.label=${this.minLabel}
|
||||
@change=${this._valueChanged}
|
||||
@focusin=${this._onFocus}
|
||||
name="minutes"
|
||||
no-spinner
|
||||
.required=${this.required}
|
||||
.autoValidate=${this.autoValidate}
|
||||
maxlength="2"
|
||||
max="59"
|
||||
min="0"
|
||||
.disabled=${this.disabled}
|
||||
.suffix=${this.enableSecond ? ":" : ""}
|
||||
class=${this.enableSecond ? "has-suffix" : ""}
|
||||
>
|
||||
</ha-textfield>
|
||||
${this.enableSecond
|
||||
? html`<ha-textfield
|
||||
id="sec"
|
||||
type="number"
|
||||
inputmode="numeric"
|
||||
.value=${this.days.toFixed()}
|
||||
.label=${this.dayLabel}
|
||||
name="days"
|
||||
.value=${this._formatValue(this.seconds)}
|
||||
.label=${this.secLabel}
|
||||
@change=${this._valueChanged}
|
||||
@focusin=${this._onFocus}
|
||||
name="seconds"
|
||||
no-spinner
|
||||
.required=${this.required}
|
||||
.autoValidate=${this.autoValidate}
|
||||
maxlength="2"
|
||||
max="59"
|
||||
min="0"
|
||||
.disabled=${this.disabled}
|
||||
suffix=":"
|
||||
class="hasSuffix"
|
||||
.suffix=${this.enableMillisecond ? ":" : ""}
|
||||
class=${this.enableMillisecond ? "has-suffix" : ""}
|
||||
>
|
||||
</ha-textfield>
|
||||
`
|
||||
: ""}
|
||||
</ha-textfield>`
|
||||
: ""}
|
||||
${this.enableMillisecond
|
||||
? html`<ha-textfield
|
||||
id="millisec"
|
||||
type="number"
|
||||
.value=${this._formatValue(this.milliseconds, 3)}
|
||||
.label=${this.millisecLabel}
|
||||
@change=${this._valueChanged}
|
||||
@focusin=${this._onFocus}
|
||||
name="milliseconds"
|
||||
no-spinner
|
||||
.required=${this.required}
|
||||
.autoValidate=${this.autoValidate}
|
||||
maxlength="3"
|
||||
max="999"
|
||||
min="0"
|
||||
.disabled=${this.disabled}
|
||||
>
|
||||
</ha-textfield>`
|
||||
: ""}
|
||||
${this.clearable && !this.required && !this.disabled
|
||||
? html`<ha-icon-button
|
||||
label="clear"
|
||||
@click=${this._clearValue}
|
||||
.path=${mdiClose}
|
||||
></ha-icon-button>`
|
||||
: nothing}
|
||||
</div>
|
||||
|
||||
<ha-textfield
|
||||
id="hour"
|
||||
type="number"
|
||||
inputmode="numeric"
|
||||
.value=${this.hours.toFixed()}
|
||||
.label=${this.hourLabel}
|
||||
name="hours"
|
||||
@change=${this._valueChanged}
|
||||
@focusin=${this._onFocus}
|
||||
no-spinner
|
||||
.required=${this.required}
|
||||
.autoValidate=${this.autoValidate}
|
||||
maxlength="2"
|
||||
max=${ifDefined(this._hourMax)}
|
||||
min="0"
|
||||
.disabled=${this.disabled}
|
||||
suffix=":"
|
||||
class="hasSuffix"
|
||||
>
|
||||
</ha-textfield>
|
||||
<ha-textfield
|
||||
id="min"
|
||||
type="number"
|
||||
inputmode="numeric"
|
||||
.value=${this._formatValue(this.minutes)}
|
||||
.label=${this.minLabel}
|
||||
@change=${this._valueChanged}
|
||||
@focusin=${this._onFocus}
|
||||
name="minutes"
|
||||
no-spinner
|
||||
.required=${this.required}
|
||||
.autoValidate=${this.autoValidate}
|
||||
maxlength="2"
|
||||
max="59"
|
||||
min="0"
|
||||
.disabled=${this.disabled}
|
||||
.suffix=${this.enableSecond ? ":" : ""}
|
||||
class=${this.enableSecond ? "has-suffix" : ""}
|
||||
>
|
||||
</ha-textfield>
|
||||
${this.enableSecond
|
||||
? html`<ha-textfield
|
||||
id="sec"
|
||||
type="number"
|
||||
inputmode="numeric"
|
||||
.value=${this._formatValue(this.seconds)}
|
||||
.label=${this.secLabel}
|
||||
@change=${this._valueChanged}
|
||||
@focusin=${this._onFocus}
|
||||
name="seconds"
|
||||
no-spinner
|
||||
.required=${this.required}
|
||||
.autoValidate=${this.autoValidate}
|
||||
maxlength="2"
|
||||
max="59"
|
||||
min="0"
|
||||
.disabled=${this.disabled}
|
||||
.suffix=${this.enableMillisecond ? ":" : ""}
|
||||
class=${this.enableMillisecond ? "has-suffix" : ""}
|
||||
>
|
||||
</ha-textfield>`
|
||||
: ""}
|
||||
${this.enableMillisecond
|
||||
? html`<ha-textfield
|
||||
id="millisec"
|
||||
type="number"
|
||||
.value=${this._formatValue(this.milliseconds, 3)}
|
||||
.label=${this.millisecLabel}
|
||||
@change=${this._valueChanged}
|
||||
@focusin=${this._onFocus}
|
||||
name="milliseconds"
|
||||
no-spinner
|
||||
.required=${this.required}
|
||||
.autoValidate=${this.autoValidate}
|
||||
maxlength="3"
|
||||
max="999"
|
||||
min="0"
|
||||
.disabled=${this.disabled}
|
||||
>
|
||||
</ha-textfield>`
|
||||
: ""}
|
||||
${this.format === 24
|
||||
? ""
|
||||
: html`<ha-select
|
||||
@ -249,13 +263,17 @@ export class HaBaseTimeInput extends LitElement {
|
||||
<mwc-list-item value="AM">AM</mwc-list-item>
|
||||
<mwc-list-item value="PM">PM</mwc-list-item>
|
||||
</ha-select>`}
|
||||
${this.helper
|
||||
? html`<ha-input-helper-text>${this.helper}</ha-input-helper-text>`
|
||||
: ""}
|
||||
</div>
|
||||
${this.helper
|
||||
? html`<ha-input-helper-text>${this.helper}</ha-input-helper-text>`
|
||||
: ""}
|
||||
`;
|
||||
}
|
||||
|
||||
private _clearValue(): void {
|
||||
fireEvent(this, "value-changed");
|
||||
}
|
||||
|
||||
private _valueChanged(ev: InputEvent) {
|
||||
const textField = ev.currentTarget as HaTextField;
|
||||
this[textField.name] =
|
||||
@ -302,18 +320,25 @@ export class HaBaseTimeInput extends LitElement {
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
:host([clearable]) {
|
||||
position: relative;
|
||||
}
|
||||
:host {
|
||||
display: block;
|
||||
}
|
||||
.time-input-wrap-wrap {
|
||||
display: flex;
|
||||
}
|
||||
.time-input-wrap {
|
||||
display: flex;
|
||||
border-radius: var(--mdc-shape-small, 4px) var(--mdc-shape-small, 4px) 0 0;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
direction: ltr;
|
||||
padding-right: 3px;
|
||||
}
|
||||
ha-textfield {
|
||||
width: 40px;
|
||||
width: 55px;
|
||||
text-align: center;
|
||||
--mdc-shape-small: 0;
|
||||
--text-field-appearance: none;
|
||||
@ -335,6 +360,21 @@ export class HaBaseTimeInput extends LitElement {
|
||||
--mdc-shape-small: 0;
|
||||
width: 85px;
|
||||
}
|
||||
:host([clearable]) .mdc-select__anchor {
|
||||
padding-inline-end: var(--select-selected-text-padding-end, 12px);
|
||||
}
|
||||
ha-icon-button {
|
||||
position: relative
|
||||
--mdc-icon-button-size: 36px;
|
||||
--mdc-icon-size: 20px;
|
||||
color: var(--secondary-text-color);
|
||||
direction: var(--direction);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background-color:var(--mdc-text-field-fill-color, whitesmoke);
|
||||
border-bottom-style: solid;
|
||||
border-bottom-width: 1px;
|
||||
}
|
||||
label {
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
|
@ -27,6 +27,7 @@ export class HaTimeSelector extends LitElement {
|
||||
.locale=${this.hass.locale}
|
||||
.disabled=${this.disabled}
|
||||
.required=${this.required}
|
||||
clearable
|
||||
.helper=${this.helper}
|
||||
.label=${this.label}
|
||||
enable-second
|
||||
|
@ -23,6 +23,8 @@ export class HaTimeInput extends LitElement {
|
||||
@property({ type: Boolean, attribute: "enable-second" })
|
||||
public enableSecond = false;
|
||||
|
||||
@property({ type: Boolean, reflect: true }) public clearable?: boolean;
|
||||
|
||||
protected render() {
|
||||
const useAMPM = useAmPm(this.locale);
|
||||
|
||||
@ -48,22 +50,26 @@ export class HaTimeInput extends LitElement {
|
||||
@value-changed=${this._timeChanged}
|
||||
.enableSecond=${this.enableSecond}
|
||||
.required=${this.required}
|
||||
.clearable=${this.clearable && this.value !== undefined}
|
||||
.helper=${this.helper}
|
||||
></ha-base-time-input>
|
||||
`;
|
||||
}
|
||||
|
||||
private _timeChanged(ev: CustomEvent<{ value: TimeChangedEvent }>) {
|
||||
private _timeChanged(ev: CustomEvent<{ value?: TimeChangedEvent }>) {
|
||||
ev.stopPropagation();
|
||||
const eventValue = ev.detail.value;
|
||||
|
||||
const useAMPM = useAmPm(this.locale);
|
||||
let value;
|
||||
let value: string | undefined;
|
||||
|
||||
// An undefined eventValue means the time selector is being cleared,
|
||||
// the `value` variable will (intentionally) be left undefined.
|
||||
if (
|
||||
!isNaN(eventValue.hours) ||
|
||||
!isNaN(eventValue.minutes) ||
|
||||
!isNaN(eventValue.seconds)
|
||||
eventValue !== undefined &&
|
||||
(!isNaN(eventValue.hours) ||
|
||||
!isNaN(eventValue.minutes) ||
|
||||
!isNaN(eventValue.seconds))
|
||||
) {
|
||||
let hours = eventValue.hours || 0;
|
||||
if (eventValue && useAMPM) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user