browsertrix/frontend/src/components/ui/time-input.ts
sua yoo 896c3cc91c
Fix scheduler date input and display (#1472)
Fixes #1255

### Changes

- Fixes incorrect time zone conversion when generating UTC schedule in
workflow.
- Fixes minute input display not prefixing single digits with `0`

Co-authored-by: emma <hi@emma.cafe>
2024-01-18 23:55:55 -08:00

182 lines
4.6 KiB
TypeScript

import { LitElement, html, css } from "lit";
import { property, customElement } from "lit/decorators.js";
import { msg, localized } from "@lit/localize";
import type { SlInput } from "@shoelace-style/shoelace";
export type TimeInputChangeEvent = CustomEvent<{
hour: number;
minute: number;
period: "AM" | "PM";
}>;
/**
* Usage:
* ```ts
* <btrix-time-input
* hour="1"
* minute="1"
* period="AM"
* @time-change=${console.log}
* ></btrix-time-input>
* ```
*
* @events
* time-change TimeInputChangeEvent
*/
@localized()
@customElement("btrix-time-input")
export class TimeInput extends LitElement {
// postcss-lit-disable-next-line
static styles = css`
fieldset {
all: unset;
}
label {
display: inline-block;
color: var(--sl-input-label-color);
font-size: var(--sl-input-label-font-size-medium);
margin-bottom: var(--sl-spacing-3x-small);
}
.flex {
display: flex;
align-items: center;
}
.separator {
padding: var(--sl-spacing-2x-small);
}
sl-input {
width: 4rem;
}
sl-input::part(input) {
text-align: center;
}
sl-select[name="period"] {
margin-left: var(--sl-spacing-x-small);
}
`;
@property({ type: Number })
hour: number = new Date().getHours() % 12 || 12;
@property({ type: Number })
minute: number = 0;
@property({ type: String })
period: "AM" | "PM" = new Date().getHours() > 11 ? "PM" : "AM";
@property({ type: Boolean })
disabled = false;
render() {
return html`
<fieldset name="time">
<label><slot name="label">${msg("Time")}</slot></label>
<div class="flex">
<div class="flex">
<sl-input
name="hour"
pattern="[0-9]*"
maxlength="2"
value=${this.hour}
?disabled=${this.disabled}
required
@sl-change=${async (e: Event) => {
e.stopPropagation();
const input = e.target as SlInput;
if (input.value) {
const int = +input.value.replace(/[^0-9]/g, "");
input.value = `${Math.min(12, Math.max(1, int))}`;
} else {
input.value = "12";
}
await input.updateComplete;
this.hour = +input.value;
this.dispatchChange();
}}
>
</sl-input>
<span class="separator">:</span>
<sl-input
name="minute"
pattern="[0-9]*"
maxlength="2"
value=${`${this.minute}`.length === 1
? `${0}${this.minute}`
: this.minute}
?disabled=${this.disabled}
required
@sl-change=${async (e: Event) => {
e.stopPropagation();
const input = e.target as SlInput;
if (input.value) {
const int = Math.min(
59,
Math.max(0, +input.value.replace(/[^0-9]/g, ""))
);
input.value = int < 10 ? `0${int}` : `${int}`;
} else {
input.value = "00";
}
await input.updateComplete;
this.minute = +input.value;
this.dispatchChange();
}}
>
</sl-input>
</div>
<sl-select
name="period"
value=${this.period}
?disabled=${this.disabled}
hoist
@sl-hide=${this.stopProp}
@sl-after-hide=${this.stopProp}
@sl-change=${(e: any) => {
e.stopPropagation();
this.period = e.target.value;
this.dispatchChange();
}}
>
<sl-option value="AM"
>${msg("AM", { desc: "Time AM/PM" })}</sl-option
>
<sl-option value="PM"
>${msg("PM", { desc: "Time AM/PM" })}</sl-option
>
</sl-select>
</div>
</fieldset>
`;
}
private async dispatchChange() {
await this.updateComplete;
this.dispatchEvent(
<TimeInputChangeEvent>new CustomEvent("time-change", {
detail: {
hour: this.hour,
minute: this.minute,
period: this.period,
},
})
);
}
/**
* Stop propgation of sl-select events.
* Prevents bug where sl-dialog closes when dropdown closes
* https://github.com/shoelace-style/shoelace/issues/170
*/
private stopProp(e: CustomEvent) {
e.stopPropagation();
}
}