UNPKG

@esri/calcite-components

Version:

Web Components for Esri's Calcite Design System.

812 lines (811 loc) • 36.1 kB
/*! 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 })} @focus=${this.timePartFocusHandler} @keydown=${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 })} @focus=${this.timePartFocusHandler} @keydown=${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 })} @focus=${this.timePartFocusHandler} @keydown=${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 })} @focus=${this.timePartFocusHandler} @keydown=${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} @calcitePopoverBeforeClose=${this.popoverBeforeCloseHandler} @calcitePopoverBeforeOpen=${this.popoverBeforeOpenHandler} @calcitePopoverClose=${this.popoverCloseHandler} @calcitePopoverOpen=${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} @calciteTimePickerChange=${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" })} @focus=${this.timePartFocusHandler} @keydown=${isInteractive ? handleMeridiemKeyDownEvent : void 0} role=spinbutton tabindex=0 ${ref(this.setMeridiemEl)}>${localizedMeridiem || "--"}</span>`; } renderToggleIcon(open) { return html`<span class=${safeClassMap(CSS.toggleIcon)} @click=${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 };