@esri/calcite-components
Version:
Web Components for Esri's Calcite Design System.
812 lines (811 loc) • 36.1 kB
JavaScript
/*! All material copyright ESRI, All Rights Reserved, unless otherwise specified.
See https://github.com/Esri/calcite-design-system/blob/dev/LICENSE.md for details.
v3.2.1 */
import { c as customElement } from "../../chunks/runtime.js";
import { ref } from "lit-html/directives/ref.js";
import { nothing, html } from "lit";
import { createEvent, LitElement, stringOrBoolean, safeClassMap } from "@arcgis/lumina";
import { c as connectForm, d as disconnectForm, s as submitForm, H as HiddenFormInputSlot } from "../../chunks/form.js";
import { u as updateHostInteraction, I as InteractiveContainer } from "../../chunks/interactive.js";
import { c as connectLabel, d as disconnectLabel, g as getLabelText } from "../../chunks/label.js";
import { c as componentFocusable, g as getIconScale } from "../../chunks/component.js";
import { d as decimalPlaces, g as getDecimals } from "../../chunks/math.js";
import { V as Validation } from "../../chunks/Validation.js";
import { h as focusFirstTabbable, g as getElementDir } from "../../chunks/dom.js";
import { s as syncHiddenFormInput } from "../../chunks/input.js";
import { u as useT9n } from "../../chunks/useT9n.js";
import { i as isValidNumber } from "../../chunks/locale.js";
import { toFunction, GenericController } from "@arcgis/lumina/controllers";
import { f as formatTimePart, d as getLocaleHourFormat, e as getMeridiemOrder, t as toISOTimeString, i as isValidTime, p as parseTimeString, h as localizeTimeString, g as getMeridiem, a as getLocalizedTimePartSuffix, b as getLocalizedDecimalSeparator, c as localizeTimePart, j as getLocalizedMeridiem, m as maxTenthForMinuteAndSecond } from "../../chunks/time.js";
import { c as capitalizeWord } from "../../chunks/text.js";
import { n as numberKeys } from "../../chunks/key.js";
import { css } from "@lit/reactive-element/css-tag.js";
class TimeController extends GenericController {
constructor() {
super(...arguments);
this.localizedDecimalSeparator = ".";
this.userChangedValue = false;
this.handleHourKeyDownEvent = (event) => {
const key = event.key;
if (numberKeys.includes(key)) {
const keyAsNumber = parseInt(key);
let newHour;
if (isValidNumber(this.hour)) {
switch (this.hourFormat) {
case "12":
newHour = this.hour === "01" && keyAsNumber >= 0 && keyAsNumber <= 2 ? `1${keyAsNumber}` : keyAsNumber;
break;
case "24":
if (this.hour === "01") {
newHour = `1${keyAsNumber}`;
} else if (this.hour === "02" && keyAsNumber >= 0 && keyAsNumber <= 3) {
newHour = `2${keyAsNumber}`;
} else {
newHour = keyAsNumber;
}
break;
}
} else {
newHour = keyAsNumber;
}
this.setValuePart("hour", newHour);
} else {
switch (key) {
case "Backspace":
case "Delete":
event.preventDefault();
this.setValuePart("hour", null);
break;
case "ArrowDown":
event.preventDefault();
this.decrementHour();
break;
case "ArrowUp":
event.preventDefault();
this.incrementHour();
break;
case " ":
case "Spacebar":
event.preventDefault();
break;
}
}
};
this.handleMinuteKeyDownEvent = (event) => {
const key = event.key;
if (numberKeys.includes(key)) {
const keyAsNumber = parseInt(key);
let newMinute;
if (isValidNumber(this.minute) && this.minute.startsWith("0")) {
const minuteAsNumber = parseInt(this.minute);
newMinute = minuteAsNumber > maxTenthForMinuteAndSecond ? keyAsNumber : `${minuteAsNumber}${keyAsNumber}`;
} else {
newMinute = keyAsNumber;
}
this.setValuePart("minute", newMinute);
} else {
switch (key) {
case "Backspace":
case "Delete":
event.preventDefault();
this.setValuePart("minute", null);
break;
case "ArrowDown":
event.preventDefault();
this.decrementMinute();
break;
case "ArrowUp":
event.preventDefault();
this.incrementMinute();
break;
case " ":
case "Spacebar":
event.preventDefault();
break;
}
}
};
this.handleSecondKeyDownEvent = (event) => {
const key = event.key;
if (numberKeys.includes(key)) {
const keyAsNumber = parseInt(key);
let newSecond;
if (isValidNumber(this.second) && this.second.startsWith("0")) {
const secondAsNumber = parseInt(this.second);
newSecond = secondAsNumber > maxTenthForMinuteAndSecond ? keyAsNumber : `${secondAsNumber}${keyAsNumber}`;
} else {
newSecond = keyAsNumber;
}
this.setValuePart("second", newSecond);
} else {
switch (key) {
case "Backspace":
case "Delete":
event.preventDefault();
this.setValuePart("second", null);
break;
case "ArrowDown":
event.preventDefault();
this.decrementSecond();
break;
case "ArrowUp":
event.preventDefault();
this.incrementSecond();
break;
case " ":
case "Spacebar":
event.preventDefault();
break;
}
}
};
this.handleFractionalSecondKeyDownEvent = (event) => {
const { key } = event;
if (numberKeys.includes(key)) {
const stepPrecision = decimalPlaces(this.component.step);
const fractionalSecondAsInteger = parseInt(this.fractionalSecond);
const fractionalSecondAsIntegerLength = fractionalSecondAsInteger.toString().length;
let newFractionalSecondAsIntegerString;
if (fractionalSecondAsIntegerLength >= stepPrecision) {
newFractionalSecondAsIntegerString = key.padStart(stepPrecision, "0");
} else if (fractionalSecondAsIntegerLength < stepPrecision) {
newFractionalSecondAsIntegerString = `${fractionalSecondAsInteger}${key}`.padStart(stepPrecision, "0");
}
this.setValuePart("fractionalSecond", parseFloat(`0.${newFractionalSecondAsIntegerString}`));
} else {
switch (key) {
case "Backspace":
case "Delete":
event.preventDefault();
this.setValuePart("fractionalSecond", null);
break;
case "ArrowDown":
event.preventDefault();
this.nudgeFractionalSecond("down");
break;
case "ArrowUp":
event.preventDefault();
this.nudgeFractionalSecond("up");
break;
case " ":
event.preventDefault();
break;
}
}
};
this.handleMeridiemKeyDownEvent = (event) => {
switch (event.key) {
case "a":
this.setValuePart("meridiem", "AM");
break;
case "p":
this.setValuePart("meridiem", "PM");
break;
case "Backspace":
case "Delete":
event.preventDefault();
this.setValuePart("meridiem");
break;
case "ArrowUp":
case "ArrowDown":
event.preventDefault();
this.toggleMeridiem(event.key);
break;
case " ":
case "Spacebar":
event.preventDefault();
break;
}
};
this.calciteTimeChange = createEvent();
}
//#endregion
//#region Private Methods
hostConnected() {
this.setHourFormat();
this.setMeridiemOrder();
this.setValue(this.component.value);
}
hostUpdate(changes) {
let updateHourFormat = false;
let updateMeridiemOrder = false;
let updateValue = false;
if (changes.has("hourFormat")) {
updateHourFormat = true;
updateValue = true;
}
if (changes.has("messages") && changes.get("messages")?._lang !== this.component.messages._lang) {
updateHourFormat = true;
updateMeridiemOrder = true;
updateValue = true;
}
if (changes.has("numberingSystem")) {
updateValue = true;
}
if (changes.has("step")) {
const oldStep = this.component.step;
const newStep = changes.get("step");
if (oldStep >= 60 && newStep > 0 && newStep < 60 || newStep >= 60 && oldStep > 0 && oldStep < 60) {
updateValue = true;
}
}
if (updateHourFormat) {
this.setHourFormat();
}
if (updateMeridiemOrder) {
this.setMeridiemOrder();
}
if (updateValue) {
this.setValue(this.component.value);
}
}
decrementHour() {
const newHour = !this.hour ? 0 : this.hour === "00" ? 23 : parseInt(this.hour) - 1;
this.setValuePart("hour", newHour);
}
decrementMinute() {
this.decrementMinuteOrSecond("minute");
}
decrementMinuteOrSecond(key) {
let newValue;
if (isValidNumber(this[key])) {
const valueAsNumber = parseInt(this[key]);
newValue = valueAsNumber === 0 ? 59 : valueAsNumber - 1;
} else {
newValue = 59;
}
this.setValuePart(key, newValue);
}
decrementSecond() {
this.decrementMinuteOrSecond("second");
}
incrementHour() {
const newHour = isValidNumber(this.hour) ? this.hour === "23" ? 0 : parseInt(this.hour) + 1 : 1;
this.setValuePart("hour", newHour);
}
incrementMinute() {
this.incrementMinuteOrSecond("minute");
}
incrementMinuteOrSecond(key) {
const newValue = isValidNumber(this[key]) ? this[key] === "59" ? 0 : parseInt(this[key]) + 1 : 0;
this.setValuePart(key, newValue);
}
incrementSecond() {
this.incrementMinuteOrSecond("second");
}
nudgeFractionalSecond(direction) {
const stepDecimal = getDecimals(this.component.step);
const stepPrecision = decimalPlaces(this.component.step);
const fractionalSecondAsInteger = parseInt(this.fractionalSecond);
const fractionalSecondAsFloat = parseFloat(`0.${this.fractionalSecond}`);
let nudgedValue;
let nudgedValueRounded;
let nudgedValueRoundedDecimals;
let newFractionalSecond;
if (direction === "up") {
nudgedValue = isNaN(fractionalSecondAsInteger) ? 0 : fractionalSecondAsFloat + stepDecimal;
nudgedValueRounded = parseFloat(nudgedValue.toFixed(stepPrecision));
nudgedValueRoundedDecimals = getDecimals(nudgedValueRounded);
newFractionalSecond = nudgedValueRounded < 1 && decimalPlaces(nudgedValueRoundedDecimals) > 0 ? formatTimePart(nudgedValueRoundedDecimals, stepPrecision) : "".padStart(stepPrecision, "0");
}
if (direction === "down") {
nudgedValue = isNaN(fractionalSecondAsInteger) || fractionalSecondAsInteger === 0 ? 1 - stepDecimal : fractionalSecondAsFloat - stepDecimal;
nudgedValueRounded = parseFloat(nudgedValue.toFixed(stepPrecision));
nudgedValueRoundedDecimals = getDecimals(nudgedValueRounded);
newFractionalSecond = nudgedValueRounded < 1 && decimalPlaces(nudgedValueRoundedDecimals) > 0 && Math.sign(nudgedValueRoundedDecimals) === 1 ? formatTimePart(nudgedValueRoundedDecimals, stepPrecision) : "".padStart(stepPrecision, "0");
}
this.setValuePart("fractionalSecond", newFractionalSecond);
}
setHourFormat() {
const { hourFormat, messages } = this.component;
const locale = messages._lang;
this.hourFormat = hourFormat === "user" ? getLocaleHourFormat(locale) : hourFormat;
}
setMeridiemOrder() {
const { messages } = this.component;
const locale = messages._lang;
this.meridiemOrder = getMeridiemOrder(locale);
}
toggleMeridiem(direction) {
let newMeridiem;
if (!this.meridiem) {
newMeridiem = direction === "ArrowDown" ? "PM" : "AM";
} else {
newMeridiem = this.meridiem === "AM" ? "PM" : "AM";
}
this.setValuePart("meridiem", newMeridiem);
}
setValue(value, userChangedValue = false) {
const { messages, numberingSystem, step, value: previousValue } = this.component;
const locale = messages._lang;
const hour12 = this.hourFormat === "12";
const newValue = toISOTimeString(value, step);
if (isValidTime(value)) {
const { hour, minute, second, fractionalSecond } = parseTimeString(newValue, step);
const {
hour: localizedHour,
hourSuffix: localizedHourSuffix,
minute: localizedMinute,
minuteSuffix: localizedMinuteSuffix,
second: localizedSecond,
secondSuffix: localizedSecondSuffix,
decimalSeparator: localizedDecimalSeparator,
fractionalSecond: localizedFractionalSecond,
meridiem: localizedMeridiem
} = localizeTimeString({
hour12,
locale,
numberingSystem,
parts: true,
step,
value: newValue
});
this.hour = hour;
this.minute = minute;
this.second = second;
this.fractionalSecond = fractionalSecond;
this.localizedHour = localizedHour;
this.localizedHourSuffix = localizedHourSuffix;
this.localizedMinute = localizedMinute;
this.localizedMinuteSuffix = localizedMinuteSuffix;
this.localizedSecond = localizedSecond;
this.localizedDecimalSeparator = localizedDecimalSeparator;
this.localizedFractionalSecond = localizedFractionalSecond;
this.localizedSecondSuffix = localizedSecondSuffix;
if (localizedMeridiem) {
this.meridiem = getMeridiem(this.hour);
this.localizedMeridiem = localizedMeridiem;
}
} else {
this.hour = null;
this.minute = null;
this.second = null;
this.fractionalSecond = null;
this.meridiem = null;
this.localizedHour = null;
this.localizedHourSuffix = getLocalizedTimePartSuffix("hour", locale, numberingSystem);
this.localizedMinute = null;
this.localizedMinuteSuffix = getLocalizedTimePartSuffix("minute", locale, numberingSystem);
this.localizedSecond = null;
this.localizedDecimalSeparator = getLocalizedDecimalSeparator(locale, numberingSystem);
this.localizedFractionalSecond = null;
this.localizedSecondSuffix = getLocalizedTimePartSuffix("second", locale, numberingSystem);
this.localizedMeridiem = null;
}
if (newValue !== previousValue) {
this.userChangedValue = userChangedValue;
this.component.value = newValue ?? "";
} else {
this.component.requestUpdate();
}
}
setValuePart(key, value) {
const { hourFormat } = this;
const { messages, numberingSystem, step } = this.component;
const locale = messages._lang;
const hour12 = hourFormat === "12";
const previousValue = this.component.value;
if (key === "meridiem") {
const oldMeridiem = this.meridiem;
this.meridiem = value;
this.localizedMeridiem = localizeTimePart({
hour12,
locale,
numberingSystem,
part: "meridiem",
value: this.meridiem
});
if (isValidNumber(this.hour)) {
const hourAsNumber = parseInt(this.hour);
switch (value) {
case "AM":
if (hourAsNumber >= 12) {
this.hour = formatTimePart(hourAsNumber - 12);
}
break;
case "PM":
if (hourAsNumber < 12) {
this.hour = formatTimePart(hourAsNumber + 12);
}
break;
default:
this.component.value = "";
break;
}
this.localizedHour = localizeTimePart({
hour12,
locale,
numberingSystem,
part: "hour",
value: this.hour
});
}
if (oldMeridiem !== this.meridiem) {
this.component.requestUpdate();
}
} else if (key === "fractionalSecond") {
const oldFractionalSecond = this.fractionalSecond;
const stepPrecision = decimalPlaces(step);
if (typeof value === "number") {
this.fractionalSecond = value === 0 ? "".padStart(stepPrecision, "0") : formatTimePart(value, stepPrecision);
} else {
this.fractionalSecond = value;
}
this.localizedFractionalSecond = localizeTimePart({
value: this.fractionalSecond,
part: "fractionalSecond",
locale,
numberingSystem,
hour12
});
if (oldFractionalSecond !== this.fractionalSecond) {
this.component.requestUpdate();
}
} else {
const oldValue = this[key];
this[key] = typeof value === "number" ? formatTimePart(value) : value;
this[`localized${capitalizeWord(key)}`] = localizeTimePart({
value: this[key],
part: key,
locale,
numberingSystem,
hour12
});
if (oldValue !== this[key]) {
this.component.requestUpdate();
}
}
const { hour, minute, second, fractionalSecond } = this;
const newValue = toISOTimeString({ hour, minute, second, fractionalSecond }, step);
if (previousValue !== newValue) {
if (key === "hour" && hourFormat === "12") {
this.meridiem = getMeridiem(hour);
this.localizedMeridiem = getLocalizedMeridiem({ locale, meridiem: this.meridiem });
}
this.userChangedValue = true;
this.calciteTimeChange.emit(newValue ?? "");
}
}
//#endregion
}
const useTime = toFunction(TimeController);
const styles = css`:host([disabled]){cursor:default;-webkit-user-select:none;user-select:none;opacity:var(--calcite-opacity-disabled)}:host([disabled]) *,:host([disabled]) ::slotted(*){pointer-events:none}:host{display:inline-block}:host([disabled]) ::slotted([calcite-hydrated][disabled]),:host([disabled]) [calcite-hydrated][disabled]{opacity:1}.interaction-container{display:contents}::slotted(input[slot=hidden-form-input]){margin:0!important;opacity:0!important;outline:none!important;padding:0!important;position:absolute!important;inset:0!important;transform:none!important;-webkit-appearance:none!important;z-index:-1!important}.container{--calcite-icon-color: var(--calcite-color-text-3);align-items:center;background-color:var(--calcite-color-foreground-1);border:1px solid var(--calcite-color-border-input);box-sizing:border-box;display:flex;color:var(--calcite-color-text-1);flex-wrap:nowrap;font-weight:var(--calcite-font-weight-normal);inline-size:100%;padding-block:var(--calcite-spacing-base);-webkit-user-select:none;user-select:none}.container:focus-within{border-color:var(--calcite-color-brand);outline:2px solid var(--calcite-color-focus, var(--calcite-ui-focus-color, var(--calcite-color-brand)));outline-offset:calc(-2px*(1 - (2*clamp(0,var(--calcite-offset-invert-focus),1))))}.container.read-only{background-color:var(--calcite-color-background);font-weight:var(--calcite-font-weight-medium)}.input-container{display:flex;flex-grow:1}.input{align-items:center;display:flex;block-size:100%;justify-content:center;min-inline-size:max-content}.input.empty{inline-size:var(--calcite-spacing-xl)}.input:focus,.input:hover:focus{background-color:Highlight;color:HighlightText;outline:2px solid transparent;outline-offset:2px}.toggle-icon{align-items:center;block-size:24px;cursor:pointer;display:flex;inline-size:24px;justify-content:center}.toggle-icon:hover{--calcite-icon-color: var(--calcite-color-text-1)}.meridiem--start{margin-inline-end:var(--calcite-spacing-xxs)}.meridiem--end{margin-inline-start:var(--calcite-spacing-xxs)}:host([scale=s]) .container{block-size:1.5rem;font-size:var(--calcite-font-size-sm);gap:var(--calcite-spacing-sm);padding-inline-start:var(--calcite-spacing-sm);padding-inline-end:var(--calcite-spacing-xxs)}:host([scale=s]) .input-container{line-height:1rem}:host([scale=m]) .container{block-size:2rem;font-size:var(--calcite-font-size);gap:var(--calcite-spacing-md);padding-inline-start:var(--calcite-spacing-md);padding-inline-end:var(--calcite-spacing-sm)}:host([scale=m]) .input-container{line-height:1.5rem}:host([scale=l]) .container{block-size:2.75rem;font-size:var(--calcite-font-size-md);gap:var(--calcite-spacing-lg);padding-inline:var(--calcite-spacing-lg)}:host([scale=l]) .input-container{line-height:2.25rem}:host([status=invalid]) .container{border-color:var(--calcite-color-status-danger)}:host([status=invalid]) .container:focus-within{outline:2px solid var(--calcite-color-status-danger);outline-offset:calc(-2px*(1 - (2*clamp(0,var(--calcite-offset-invert-focus),1))))}.validation-container{display:flex;flex-direction:column;align-items:flex-start;align-self:stretch}:host([scale=m]) .validation-container,:host([scale=l]) .validation-container{padding-block-start:.5rem}:host([scale=s]) .validation-container{padding-block-start:.25rem}:host([hidden]){display:none}[hidden]{display:none}`;
const CSS = {
clockIcon: "clock-icon",
container: "container",
decimalSeparator: "decimal-separator",
empty: "empty",
fractionalSecond: "fractional-second",
hour: "hour",
hourSuffix: "hour-suffix",
input: "input",
inputContainer: "input-container",
meridiem: "meridiem",
meridiemStart: "meridiem--start",
meridiemEnd: "meridiem--end",
minute: "minute",
minuteSuffix: "minute-suffix",
readOnly: "read-only",
second: "second",
secondSuffix: "second-suffix",
toggleIcon: "toggle-icon"
};
const IDS = {
validationMessage: "inputTimePickerValidationMessage"
};
class InputTimePicker extends LitElement {
constructor() {
super();
this.messages = useT9n();
this.time = useTime(this);
this.disabled = false;
this.focusTrapDisabled = false;
this.hourFormat = "user";
this.open = false;
this.overlayPositioning = "absolute";
this.placement = "auto";
this.readOnly = false;
this.required = false;
this.scale = "m";
this.status = "idle";
this.step = 60;
this.validity = {
valid: false,
badInput: false,
customError: false,
patternMismatch: false,
rangeOverflow: false,
rangeUnderflow: false,
stepMismatch: false,
tooLong: false,
tooShort: false,
typeMismatch: false,
valueMissing: false
};
this.calciteInputTimePickerBeforeClose = createEvent({ cancelable: false });
this.calciteInputTimePickerBeforeOpen = createEvent({ cancelable: false });
this.calciteInputTimePickerChange = createEvent();
this.calciteInputTimePickerClose = createEvent({ cancelable: false });
this.calciteInputTimePickerOpen = createEvent({ cancelable: false });
this.listen("blur", this.blurHandler);
this.listen("keydown", this.keyDownHandler);
this.listen("calciteTimeChange", this.timeChangeHandler);
}
static {
this.properties = { disabled: [7, {}, { reflect: true, type: Boolean }], focusTrapDisabled: [7, {}, { reflect: true, type: Boolean }], form: [3, {}, { reflect: true }], hourFormat: [3, {}, { reflect: true }], max: [3, {}, { reflect: true }], messageOverrides: [0, {}, { attribute: false }], min: [3, {}, { reflect: true }], name: 1, numberingSystem: [3, {}, { reflect: true }], open: [7, {}, { reflect: true, type: Boolean }], overlayPositioning: 1, placement: [3, {}, { reflect: true }], readOnly: [7, {}, { reflect: true, type: Boolean }], required: [7, {}, { reflect: true, type: Boolean }], scale: [3, {}, { reflect: true }], status: [3, {}, { reflect: true }], step: [11, {}, { reflect: true, type: Number }], validationIcon: [3, { converter: stringOrBoolean }, { reflect: true }], validationMessage: 1, validity: [0, {}, { attribute: false }], value: 1 };
}
static {
this.shadowRootOptions = { mode: "open", delegatesFocus: true };
}
static {
this.styles = styles;
}
async reposition(delayed = false) {
this.popoverEl?.reposition(delayed);
}
async setFocus() {
await componentFocusable(this);
focusFirstTabbable(this.el);
}
connectedCallback() {
super.connectedCallback();
connectLabel(this);
connectForm(this);
}
willUpdate(changes) {
if (changes.has("open") && (this.hasUpdated || this.open !== false)) {
this.openHandler();
}
if (changes.has("disabled") && (this.hasUpdated || this.disabled !== false)) {
if (!this.disabled) {
this.open = false;
}
}
if (changes.has("readOnly") && (this.hasUpdated || this.readOnly !== false)) {
if (!this.readOnly) {
this.open = false;
}
}
if (changes.has("value")) {
if (this.hasUpdated) {
if (!this.time.userChangedValue) {
this.previousEmittedValue = this.value;
}
this.time.setValue(this.value);
} else {
this.previousEmittedValue = this.value;
}
}
}
updated() {
updateHostInteraction(this);
}
disconnectedCallback() {
super.disconnectedCallback();
disconnectLabel(this);
disconnectForm(this);
}
blurHandler() {
this.changeEventHandler();
}
changeEventHandler() {
const { previousEmittedValue, value } = this;
if (previousEmittedValue !== value) {
const changeEvent = this.calciteInputTimePickerChange.emit();
if (changeEvent.defaultPrevented) {
this.time.setValue(this.previousEmittedValue);
} else {
this.previousEmittedValue = value;
}
}
}
keyDownHandler(event) {
const { defaultPrevented, key } = event;
const { hourFormat, meridiemOrder } = this.time;
if (defaultPrevented) {
return;
}
if (key === "Enter") {
if (submitForm(this)) {
event.preventDefault();
}
this.changeEventHandler();
} else if (this.open && this.focusTrapDisabled && key === "Escape") {
this.open = false;
event.preventDefault();
} else {
const showFractionalSecond = decimalPlaces(this.step) > 0;
const showSecond = this.step < 60;
switch (this.activeEl) {
case this.hourEl:
if (key === "ArrowRight") {
this.setFocusPart("minute");
} else if (key === "ArrowLeft" && hourFormat === "12" && meridiemOrder === 0) {
this.setFocusPart("meridiem");
}
break;
case this.minuteEl:
switch (key) {
case "ArrowLeft":
this.setFocusPart("hour");
break;
case "ArrowRight":
if (this.step !== 60) {
this.setFocusPart("second");
} else if (hourFormat === "12") {
this.setFocusPart("meridiem");
}
break;
}
break;
case this.secondEl:
switch (key) {
case "ArrowLeft":
this.setFocusPart("minute");
break;
case "ArrowRight":
if (decimalPlaces(this.step) > 0) {
this.setFocusPart("fractionalSecond");
} else if (hourFormat === "12") {
this.setFocusPart("meridiem");
}
break;
}
break;
case this.fractionalSecondEl:
switch (key) {
case "ArrowLeft":
this.setFocusPart("second");
break;
case "ArrowRight":
if (hourFormat === "12" && meridiemOrder !== 0) {
this.setFocusPart("meridiem");
}
break;
}
break;
case this.meridiemEl:
if (key === "ArrowLeft" && meridiemOrder !== 0) {
if (showFractionalSecond) {
this.setFocusPart("fractionalSecond");
} else if (showSecond) {
this.setFocusPart("second");
} else {
this.setFocusPart("minute");
}
} else if (key === "ArrowRight" && meridiemOrder === 0) {
this.setFocusPart("hour");
}
break;
}
}
}
onLabelClick() {
this.setFocus();
}
openHandler() {
if (this.disabled || this.readOnly) {
return;
}
if (this.popoverEl) {
this.popoverEl.open = this.open;
}
}
popoverBeforeOpenHandler(event) {
event.stopPropagation();
this.calciteInputTimePickerBeforeOpen.emit();
}
popoverOpenHandler(event) {
event.stopPropagation();
this.calciteInputTimePickerOpen.emit();
}
popoverBeforeCloseHandler(event) {
event.stopPropagation();
this.calciteInputTimePickerBeforeClose.emit();
}
popoverCloseHandler(event) {
event.stopPropagation();
this.calciteInputTimePickerClose.emit();
this.open = false;
}
setCalcitePopoverEl(el) {
this.popoverEl = el;
this.openHandler();
}
setContainerEl(el) {
this.containerEl = el;
}
async setFocusPart(target) {
this[`${target || "hour"}El`]?.focus();
}
setFractionalSecondEl(el) {
this.fractionalSecondEl = el;
}
setHourEl(el) {
this.hourEl = el;
}
setMinuteEl(el) {
this.minuteEl = el;
}
setSecondEl(el) {
this.secondEl = el;
}
setMeridiemEl(el) {
this.meridiemEl = el;
}
syncHiddenFormInput(input) {
syncHiddenFormInput("time", this, input);
}
timeChangeHandler(event) {
event.stopPropagation();
if (this.disabled) {
return;
}
const newValue = event.detail;
if (newValue !== this.value) {
this.value = newValue;
}
}
timePartFocusHandler(event) {
this.activeEl = event.currentTarget;
}
timePickerChangeHandler(event) {
event.stopPropagation();
this.time.setValue(event.target.value, true);
}
toggleIconClickHandler() {
this.open = !this.open;
}
render() {
const { messages, readOnly, scale } = this;
const { fractionalSecond, handleHourKeyDownEvent, handleMinuteKeyDownEvent, handleSecondKeyDownEvent, handleFractionalSecondKeyDownEvent, hour, hourFormat, localizedDecimalSeparator, localizedFractionalSecond, localizedHour, localizedHourSuffix, localizedMinute, localizedMinuteSuffix, localizedSecond, localizedSecondSuffix, meridiemOrder, minute, second } = this.time;
const emptyPlaceholder = "--";
const fractionalSecondIsNumber = isValidNumber(fractionalSecond);
const hourIsNumber = isValidNumber(hour);
const minuteIsNumber = isValidNumber(minute);
const secondIsNumber = isValidNumber(second);
const showFractionalSecond = decimalPlaces(this.step) > 0;
const showMeridiem = hourFormat === "12";
const showSecond = this.step < 60;
const meridiemStart = meridiemOrder === 0 || getElementDir(this.el) === "rtl";
const isInteractive = !this.disabled && !this.readOnly;
return InteractiveContainer({ disabled: this.disabled, children: html`<div aria-label=${getLabelText(this) ?? nothing} class=${safeClassMap({
[CSS.container]: true,
[CSS.readOnly]: readOnly
})} role=combobox ${ref(this.setContainerEl)}><calcite-icon class=${safeClassMap(CSS.clockIcon)} icon=clock .scale=${scale === "l" ? "m" : "s"}></calcite-icon><div class=${safeClassMap(CSS.inputContainer)} dir=ltr>${showMeridiem && meridiemStart && this.renderMeridiem("start") || ""}<span aria-label=${this.messages.hour ?? nothing} aria-valuemax=23 aria-valuemin=1 aria-valuenow=${hourIsNumber && parseInt(hour) || "0"} aria-valuetext=${hour ?? nothing} class=${safeClassMap({
[CSS.empty]: !localizedHour,
[CSS.hour]: true,
[CSS.input]: true
})} =${this.timePartFocusHandler} =${isInteractive ? handleHourKeyDownEvent : void 0} role=spinbutton tabindex=0 ${ref(this.setHourEl)}>${localizedHour || emptyPlaceholder}</span><span class=${safeClassMap(CSS.hourSuffix)}>${localizedHourSuffix}</span><span aria-label=${this.messages.minute ?? nothing} aria-valuemax=12 aria-valuemin=1 aria-valuenow=${minuteIsNumber && parseInt(minute) || "0"} aria-valuetext=${minute ?? nothing} class=${safeClassMap({
[CSS.empty]: !localizedMinute,
[CSS.input]: true,
[CSS.minute]: true
})} =${this.timePartFocusHandler} =${isInteractive ? handleMinuteKeyDownEvent : void 0} role=spinbutton tabindex=0 ${ref(this.setMinuteEl)}>${localizedMinute || emptyPlaceholder}</span>${showSecond && html`<span class=${safeClassMap(CSS.minuteSuffix)}>${localizedMinuteSuffix}</span>` || ""}${showSecond && html`<span aria-label=${this.messages.second ?? nothing} aria-valuemax=59 aria-valuemin=0 aria-valuenow=${secondIsNumber && parseInt(second) || "0"} aria-valuetext=${second ?? nothing} class=${safeClassMap({
[CSS.empty]: !localizedSecond,
[CSS.input]: true,
[CSS.second]: true
})} =${this.timePartFocusHandler} =${isInteractive ? handleSecondKeyDownEvent : void 0} role=spinbutton tabindex=0 ${ref(this.setSecondEl)}>${localizedSecond || emptyPlaceholder}</span>` || ""}${showFractionalSecond && html`<span class=${safeClassMap(CSS.decimalSeparator)}>${localizedDecimalSeparator}</span>` || ""}${showFractionalSecond && html`<span aria-label=${this.messages.fractionalSecond ?? nothing} aria-valuemax=999 aria-valuemin=1 aria-valuenow=${fractionalSecondIsNumber && parseInt(fractionalSecond) || "0"} aria-valuetext=${localizedFractionalSecond ?? nothing} class=${safeClassMap({
[CSS.empty]: !localizedFractionalSecond,
[CSS.fractionalSecond]: true,
[CSS.input]: true
})} =${this.timePartFocusHandler} =${isInteractive ? handleFractionalSecondKeyDownEvent : void 0} role=spinbutton tabindex=0 ${ref(this.setFractionalSecondEl)}>${localizedFractionalSecond || "".padStart(decimalPlaces(this.step), "-")}</span>` || ""}${localizedSecondSuffix && html`<span class=${safeClassMap(CSS.secondSuffix)}>${localizedSecondSuffix}</span>` || ""}${showMeridiem && !meridiemStart && this.renderMeridiem("end") || ""}</div>${!this.readOnly && this.renderToggleIcon(this.open) || ""}</div><calcite-popover auto-close .focusTrapDisabled=${this.focusTrapDisabled} .focusTrapOptions=${{ initialFocus: false }} .label=${messages.chooseTime} lang=${this.messages._lang ?? nothing} =${this.popoverBeforeCloseHandler} =${this.popoverBeforeOpenHandler} =${this.popoverCloseHandler} =${this.popoverOpenHandler} .overlayPositioning=${this.overlayPositioning} .placement=${this.placement} .referenceElement=${this.containerEl} trigger-disabled ${ref(this.setCalcitePopoverEl)}><calcite-time-picker .hourFormat=${this.time.hourFormat} lang=${this.messages._lang ?? nothing} .messageOverrides=${this.messageOverrides} .numberingSystem=${this.numberingSystem} =${this.timePickerChangeHandler} .scale=${this.scale} .step=${this.step} tabindex=${(this.open ? void 0 : -1) ?? nothing} .value=${this.value}></calcite-time-picker></calcite-popover>${HiddenFormInputSlot({ component: this })}${this.validationMessage && this.status === "invalid" ? Validation({ icon: this.validationIcon, id: IDS.validationMessage, message: this.validationMessage, scale: this.scale, status: this.status }) : null}` });
}
renderMeridiem(position) {
const { handleMeridiemKeyDownEvent, localizedMeridiem, meridiem } = this.time;
const isInteractive = !this.disabled && !this.readOnly;
return html`<span aria-label=${this.messages.meridiem ?? nothing} aria-valuemax=2 aria-valuemin=1 aria-valuenow=${meridiem === "PM" && "2" || "1"} aria-valuetext=${meridiem ?? nothing} class=${safeClassMap({
[CSS.empty]: !localizedMeridiem,
[CSS.input]: true,
[CSS.meridiem]: true,
[CSS.meridiemStart]: position === "start",
[CSS.meridiemEnd]: position === "end"
})} =${this.timePartFocusHandler} =${isInteractive ? handleMeridiemKeyDownEvent : void 0} role=spinbutton tabindex=0 ${ref(this.setMeridiemEl)}>${localizedMeridiem || "--"}</span>`;
}
renderToggleIcon(open) {
return html`<span class=${safeClassMap(CSS.toggleIcon)} =${this.toggleIconClickHandler}><calcite-icon .icon=${open ? "chevron-up" : "chevron-down"} .scale=${getIconScale(this.scale)}></calcite-icon></span>`;
}
}
customElement("calcite-input-time-picker", InputTimePicker);
export {
InputTimePicker
};