@esri/calcite-components
Version:
Web Components for Esri's Calcite Design System.
535 lines (534 loc) • 37.3 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 CSS_UTILITY, c as customElement } from "../../chunks/runtime.js";
import { live } from "lit-html/directives/live.js";
import { keyed } from "lit-html/directives/keyed.js";
import { html, nothing } from "lit";
import { createRef, ref } from "lit-html/directives/ref.js";
import { LitElement, createEvent, stringOrBoolean, safeClassMap } from "@arcgis/lumina";
import { useWatchAttributes } from "@arcgis/lumina/controllers";
import { v as setRequestedIcon, i as isPrimaryPointerButton, g as getElementDir } from "../../chunks/dom.js";
import { c as connectForm, i as internalHiddenInputInputEvent, 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 { n as numberKeys } from "../../chunks/key.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 { i as isValidNumber, n as numberStringFormatter, B as BigDecimal, p as parseNumberString, s as sanitizeNumberString, c as addLocalizedTrailingDecimalZeros } from "../../chunks/locale.js";
import { V as Validation } from "../../chunks/Validation.js";
import { s as syncHiddenFormInput } from "../../chunks/input.js";
import { u as useT9n } from "../../chunks/useT9n.js";
import { css } from "@lit/reactive-element/css-tag.js";
const CSS = {
loader: "loader",
clearButton: "clear-button",
clearable: "clearable",
inputIcon: "icon",
prefix: "prefix",
suffix: "suffix",
numberButtonWrapper: "number-button-wrapper",
buttonItemHorizontal: "number-button-item--horizontal",
wrapper: "element-wrapper",
inputWrapper: "wrapper",
actionWrapper: "action-wrapper",
numberButtonItem: "number-button-item",
hasSuffix: "has-suffix",
hasPrefix: "has-prefix"
};
const IDS = {
validationMessage: "inputNumberValidationMessage"
};
const SLOTS = {
action: "action"
};
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:block}:host([scale=s]) input,:host([scale=s]) .prefix,:host([scale=s]) .suffix{padding-inline:.5rem;font-size:var(--calcite-font-size--2);line-height:1rem;block-size:var(--calcite-input-number-height, 1.5rem)}:host([scale=s]) .number-button-wrapper,:host([scale=s]) .action-wrapper calcite-button,:host([scale=s]) .action-wrapper calcite-button button{block-size:var(--calcite-input-number-height, 1.5rem)}:host([scale=s]) .clear-button{min-block-size:1.5rem;min-inline-size:1.5rem}:host([scale=m]) input,:host([scale=m]) .prefix,:host([scale=m]) .suffix{padding-inline:.75rem;font-size:var(--calcite-font-size--1);line-height:1rem;block-size:var(--calcite-input-number-height, 2rem)}:host([scale=m]) .number-button-wrapper,:host([scale=m]) .action-wrapper calcite-button,:host([scale=m]) .action-wrapper calcite-button button{block-size:var(--calcite-input-number-height, 2rem)}:host([scale=m]) .clear-button{min-block-size:2rem;min-inline-size:2rem}:host([scale=l]) input,:host([scale=l]) .prefix,:host([scale=l]) .suffix{padding-inline:1rem;font-size:var(--calcite-font-size-0);line-height:1.25rem;block-size:var(--calcite-input-number-height, 2.75rem)}:host([scale=l]) .number-button-wrapper,:host([scale=l]) .action-wrapper calcite-button,:host([scale=l]) .action-wrapper calcite-button button{block-size:var(--calcite-input-number-height, 2.75rem)}:host([scale=l]) .clear-button{min-block-size:2.75rem;min-inline-size:2.75rem}:host([disabled]) ::slotted([calcite-hydrated][disabled]),:host([disabled]) [calcite-hydrated][disabled]{opacity:1}.interaction-container{display:contents}input{transition:var(--calcite-animation-timing),block-size 0,outline-offset 0s;-webkit-appearance:none;position:relative;margin:0;box-sizing:border-box;display:flex;max-block-size:100%;inline-size:100%;max-inline-size:100%;flex:1 1 0%;text-overflow:ellipsis;font-family:inherit;font-weight:var(--calcite-font-weight-normal);background-color:var(--calcite-input-number-background-color, var(--calcite-color-foreground-1));color:var(--calcite-input-number-text-color, var(--calcite-color-text-1));border-radius:var(--calcite-input-number-corner-radius, var(--calcite-corner-radius-sharp))}input:placeholder-shown{text-overflow:ellipsis}input{border-width:1px;border-style:solid;border-color:var(--calcite-input-number-border-color, var(--calcite-color-border-input))}input::placeholder,input:-ms-input-placeholder,input::-ms-input-placeholder{font-weight:var(--calcite-font-weight-normal);color:var(--calcite-input-number-placeholder-text-color, var(--calcite-color-text-3))}input:focus{border-color:var(--calcite-color-brand);color:var(--calcite-input-number-text-color-focus, var(--calcite-color-text-1))}input[readonly]{font-weight:var(--calcite-font-weight-medium);background-color:var(--calcite-input-number-background-color, var(--calcite-color-background))}input[readonly]:focus{color:var(--calcite-input-number-text-color-focus, var(--calcite-color-text-1))}calcite-icon{color:var(--calcite-input-actions-icon-color, var(--calcite-color-text-3))}input{outline-color:transparent}input:focus{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))))}:host([status=invalid]) input{border-color:var(--calcite-color-status-danger)}:host([status=invalid]) input:focus{outline:2px solid var(--calcite-color-status-danger);outline-offset:calc(-2px*(1 - (2*clamp(0,var(--calcite-offset-invert-focus),1))))}:host([scale=s]) .icon{inset-inline-start:.5rem}:host([scale=m]) .icon{inset-inline-start:.75rem}:host([scale=l]) .icon{inset-inline-start:1rem}:host([icon][scale=s]) input{padding-inline-start:2rem}:host([icon][scale=m]) input{padding-inline-start:2.5rem}:host([icon][scale=l]) input{padding-inline-start:3.5rem}.element-wrapper{position:relative;order:3;display:inline-flex;flex:1 1 0%;align-items:center}.icon{pointer-events:none;position:absolute;z-index:var(--calcite-z-index);display:block;transition-property:background-color,block-size,border-color,box-shadow,color,inset-block-end,inset-block-start,inset-inline-end,inset-inline-start,inset-size,opacity,outline-color,transform;transition-duration:var(--calcite-animation-timing);transition-timing-function:ease-in-out}.clear-button{pointer-events:initial;order:4;margin:0;box-sizing:border-box;display:flex;min-block-size:100%;cursor:pointer;align-items:center;justify-content:center;align-self:stretch;border-width:1px;border-style:solid;outline-color:transparent;border-color:var(--calcite-input-number-border-color, var(--calcite-color-border-input));background-color:var(--calcite-input-actions-background-color, var(--calcite-color-foreground-1));border-inline-start-width:0px}.clear-button:hover{transition-property:background-color,block-size,border-color,box-shadow,color,inset-block-end,inset-block-start,inset-inline-end,inset-inline-start,inset-size,opacity,outline-color,transform;transition-duration:var(--calcite-animation-timing);transition-timing-function:ease-in-out;background-color:var(--calcite-input-actions-background-color-hover, var(--calcite-color-foreground-2))}.clear-button:hover calcite-icon{transition-property:background-color,block-size,border-color,box-shadow,color,inset-block-end,inset-block-start,inset-inline-end,inset-inline-start,inset-size,opacity,outline-color,transform;transition-duration:var(--calcite-animation-timing);transition-timing-function:ease-in-out;color:var(--calcite-input-actions-icon-color-hover, var(--calcite-color-text-1))}.clear-button:active{background-color:var(--calcite-input-actions-background-color-press, var(--calcite-color-foreground-3))}.clear-button:active calcite-icon{color:var(--calcite-input-actions-icon-color-press, var(--calcite-color-text-1))}.clear-button:focus{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))))}.clear-button:disabled{opacity:var(--calcite-opacity-disabled)}.loader{inset-block-start:1px;inset-inline:1px;pointer-events:none;position:absolute;display:block}.loader calcite-progress{--calcite-progress-background-color: var(--calcite-input-loading-background-color);--calcite-progress-fill-color: var(--calcite-input-loading-fill-color)}.action-wrapper{order:7;display:flex}.prefix,.suffix{box-sizing:border-box;display:flex;block-size:auto;min-block-size:100%;-webkit-user-select:none;user-select:none;align-content:center;align-items:center;overflow-wrap:break-word;border-width:1px;border-style:solid;font-weight:var(--calcite-font-weight-medium);line-height:1;border-color:var(--calcite-input-number-border-color, var(--calcite-color-border-input))}.prefix{order:2;border-inline-end-width:0px;inline-size:var(--calcite-input-prefix-size, auto);background-color:var(--calcite-input-prefix-background-color, var(--calcite-color-background));color:var(--calcite-input-prefix-text-color, var(--calcite-color-text-2))}.suffix{order:5;border-inline-start-width:0px;inline-size:var(--calcite-input-suffix-size, auto);background-color:var(--calcite-input-suffix-background-color, var(--calcite-color-background));color:var(--calcite-input-suffix-text-color, var(--calcite-color-text-2))}:host([alignment=start]) input{text-align:start}:host([alignment=end]) input{text-align:end}.number-button-wrapper{pointer-events:none;order:6;box-sizing:border-box;display:flex;flex-direction:column;transition-property:background-color,block-size,border-color,box-shadow,color,inset-block-end,inset-block-start,inset-inline-end,inset-inline-start,inset-size,opacity,outline-color,transform;transition-duration:var(--calcite-animation-timing);transition-timing-function:ease-in-out}:host([number-button-type=vertical]) .wrapper{flex-direction:row;display:flex}:host([number-button-type=vertical]) input{order:2}:host([number-button-type=horizontal]) .calcite--rtl .number-button-item[data-adjustment=down] calcite-icon{transform:rotate(-90deg)}:host([number-button-type=horizontal]) .calcite--rtl .number-button-item[data-adjustment=up] calcite-icon{transform:rotate(-90deg)}.number-button-item.number-button-item--horizontal[data-adjustment=down],.number-button-item.number-button-item--horizontal[data-adjustment=up]{order:1;max-block-size:100%;min-block-size:100%;align-self:stretch}.number-button-item.number-button-item--horizontal[data-adjustment=down] calcite-icon,.number-button-item.number-button-item--horizontal[data-adjustment=up] calcite-icon{transform:rotate(90deg)}.number-button-item.number-button-item--horizontal[data-adjustment=down]{border-inline-start-width:1px;border-inline-end-width:0px}.number-button-item.number-button-item--horizontal[data-adjustment=up]{order:5}:host([number-button-type=vertical]) .number-button-item[data-adjustment=down]{border-block-start-width:0px}.number-button-item{max-block-size:50%;min-block-size:50%;pointer-events:initial;margin:0;box-sizing:border-box;display:flex;cursor:pointer;align-items:center;align-self:center;border-width:1px;border-style:solid;padding-block:0px;padding-inline:.5rem;transition-property:background-color,block-size,border-color,box-shadow,color,inset-block-end,inset-block-start,inset-inline-end,inset-inline-start,inset-size,opacity,outline-color,transform;transition-duration:var(--calcite-animation-timing);transition-timing-function:ease-in-out;border-inline-start-width:0px;border-color:var(--calcite-input-number-border-color, var(--calcite-color-border-input));background-color:var(--calcite-input-actions-background-color, var(--calcite-color-foreground-1))}.number-button-item calcite-icon{pointer-events:none;transition-property:background-color,block-size,border-color,box-shadow,color,inset-block-end,inset-block-start,inset-inline-end,inset-inline-start,inset-size,opacity,outline-color,transform;transition-duration:var(--calcite-animation-timing);transition-timing-function:ease-in-out}.number-button-item:disabled{pointer-events:none}.number-button-item:hover{background-color:var(--calcite-input-actions-background-color-hover, var(--calcite-color-foreground-2))}.number-button-item:hover calcite-icon{color:var(--calcite-input-actions-icon-color-hover, var(--calcite-color-text-1))}.number-button-item:active{background-color:var(--calcite-input-actions-background-color-press, var(--calcite-color-foreground-3))}.number-button-item:active calcite-icon{color:var(--calcite-input-actions-icon-color-press, var(--calcite-color-text-1))}.prefix,:host([number-button-type=horizontal]) .number-button-item[data-adjustment=down]{border-start-start-radius:var(--calcite-input-number-corner-radius, var(--calcite-corner-radius-sharp));border-end-start-radius:var(--calcite-input-number-corner-radius, var(--calcite-corner-radius-sharp))}:host([read-only]) .suffix,:host([read-only]) .wrapper:not(.has-suffix) .clear-button,:host([number-button-type=horizontal]) .number-button-item[data-adjustment=up]{border-end-end-radius:var(--calcite-input-number-corner-radius, var(--calcite-corner-radius-sharp));border-start-end-radius:var(--calcite-input-number-corner-radius, var(--calcite-corner-radius-sharp))}:host([number-button-type=vertical]) .number-button-item[data-adjustment=down]{border-block-start-width:0px;border-end-end-radius:var(--calcite-input-number-corner-radius, var(--calcite-corner-radius-sharp))}:host([number-button-type=vertical]) .number-button-item[data-adjustment=up]{border-start-end-radius:var(--calcite-input-number-corner-radius, var(--calcite-corner-radius-sharp))}:host(:not([read-only])[number-button-type=horizontal]) .prefix,:host(:not([read-only])[number-button-type=horizontal]) input,.has-prefix input{border-start-start-radius:0;border-end-start-radius:0}.has-suffix input,:host .clearable input,:host(:not([read-only])) input,:host .suffix,:host .clear-button{border-start-end-radius:0;border-end-end-radius:0}:host .wrapper{position:relative;display:flex;flex-direction:row;align-items:center}:host(.no-bottom-border) input{border-block-end-width:0px}:host(.border-top-color-one) input{border-block-start-color:var(--calcite-color-border-1)}input.inline-child{background-color:transparent;transition-property:background-color,block-size,border-color,box-shadow,color,inset-block-end,inset-block-start,inset-inline-end,inset-inline-start,inset-size,opacity,outline-color,transform;transition-duration:var(--calcite-animation-timing);transition-timing-function:ease-in-out}input.inline-child .editing-enabled{background-color:inherit}input.inline-child:not(.editing-enabled){display:flex;cursor:pointer;text-overflow:ellipsis;border-color:transparent;padding-inline-start:0}.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}::slotted(input[slot=hidden-form-input]){margin:0 ;opacity:0 ;outline:none ;padding:0 ;position:absolute ;inset:0 ;transform:none ;-webkit-appearance:none ;z-index:-1 }:host([hidden]){display:none}[hidden]{display:none}::placeholder{font-weight:var(--calcite-font-weight-normal);color:var(--calcite-input-placeholder-text-color, var(--calcite-color-text-3))}`;
class InputNumber extends LitElement {
constructor() {
super();
this.actionWrapperEl = createRef();
this.attributeWatch = useWatchAttributes(["autofocus", "enterkeyhint", "inputmode"], this.handleGlobalAttributesChanged);
this.inputWrapperEl = createRef();
this.onHiddenFormInputInput = (event) => {
if (event.target.name === this.name) {
this.setNumberValue({
value: event.target.value,
origin: "direct"
});
}
this.setFocus();
event.stopPropagation();
};
this.previousValueOrigin = "initial";
this.userChangedValue = false;
this._value = "";
this.messages = useT9n();
this.slottedActionElDisabledInternally = false;
this.alignment = "start";
this.clearable = false;
this.disabled = false;
this.editingEnabled = false;
this.groupSeparator = false;
this.iconFlipRtl = false;
this.integer = false;
this.loading = false;
this.localeFormat = false;
this.numberButtonType = "vertical";
this.readOnly = false;
this.required = false;
this.scale = "m";
this.status = "idle";
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.calciteInputNumberChange = createEvent({ cancelable: false });
this.calciteInputNumberInput = createEvent();
this.calciteInternalInputNumberBlur = createEvent({ cancelable: false });
this.calciteInternalInputNumberFocus = createEvent({ cancelable: false });
this.listen("click", this.clickHandler);
this.listen("keydown", this.keyDownHandler);
}
static {
this.properties = { displayedValue: [16, {}, { state: true }], slottedActionElDisabledInternally: [16, {}, { state: true }], alignment: [3, {}, { reflect: true }], autocomplete: [0, {}, { attribute: false }], clearable: [7, {}, { reflect: true, type: Boolean }], disabled: [7, {}, { reflect: true, type: Boolean }], editingEnabled: [7, {}, { reflect: true, type: Boolean }], form: [3, {}, { reflect: true }], groupSeparator: [7, {}, { reflect: true, type: Boolean }], icon: [3, { converter: stringOrBoolean }, { reflect: true }], iconFlipRtl: [7, {}, { reflect: true, type: Boolean }], integer: [5, {}, { type: Boolean }], label: 1, loading: [7, {}, { reflect: true, type: Boolean }], localeFormat: [5, {}, { type: Boolean }], max: [11, {}, { reflect: true, type: Number }], maxLength: [11, {}, { reflect: true, type: Number }], messageOverrides: [0, {}, { attribute: false }], min: [11, {}, { reflect: true, type: Number }], minLength: [11, {}, { reflect: true, type: Number }], name: [3, {}, { reflect: true }], numberButtonType: [3, {}, { reflect: true }], numberingSystem: [3, {}, { reflect: true }], placeholder: 1, prefixText: 1, readOnly: [7, {}, { reflect: true, type: Boolean }], required: [7, {}, { reflect: true, type: Boolean }], scale: [3, {}, { reflect: true }], status: [3, {}, { reflect: true }], step: [3, {}, { reflect: true }], suffixText: 1, validationIcon: [3, { converter: stringOrBoolean }, { reflect: true }], validationMessage: 1, validity: [0, {}, { attribute: false }], value: 1 };
}
static {
this.styles = styles;
}
get value() {
return this._value;
}
set value(value) {
const oldValue = this._value;
if (value !== oldValue) {
this._value = value;
this.valueWatcher(value, oldValue);
if (value && this._value === "") {
this.setNumberValue({
origin: "reset",
value: oldValue
});
}
}
}
async selectText() {
this.childNumberEl?.select();
}
async setFocus() {
await componentFocusable(this);
this.childNumberEl?.focus();
}
connectedCallback() {
super.connectedCallback();
this.inlineEditableEl = this.el.closest("calcite-inline-editable");
if (this.inlineEditableEl) {
this.editingEnabled = this.inlineEditableEl.editingEnabled || false;
}
connectLabel(this);
connectForm(this);
this.el.addEventListener(internalHiddenInputInputEvent, this.onHiddenFormInputInput);
}
async load() {
this.maxString = this.max?.toString();
this.minString = this.min?.toString();
this.requestedIcon = setRequestedIcon({}, this.icon, "number");
this.setPreviousEmittedNumberValue(this.value);
this.setPreviousNumberValue(this.value);
this.warnAboutInvalidNumberValue(this.value);
if (this.value === "Infinity" || this.value === "-Infinity") {
this.displayedValue = this.value;
this.previousEmittedNumberValue = this.value;
} else {
this.setNumberValue({
origin: "connected",
value: isValidNumber(this.value) ? this.value : ""
});
}
}
willUpdate(changes) {
if (changes.has("max")) {
this.maxString = this.max?.toString() || null;
}
if (changes.has("min")) {
this.minString = this.min?.toString() || null;
}
if (changes.has("icon")) {
this.requestedIcon = setRequestedIcon({}, this.icon, "number");
}
if (changes.has("messages")) {
numberStringFormatter.numberFormatOptions = {
locale: this.messages._lang,
numberingSystem: this.numberingSystem,
useGrouping: false
};
}
}
updated() {
updateHostInteraction(this);
}
disconnectedCallback() {
super.disconnectedCallback();
disconnectLabel(this);
disconnectForm(this);
this.el.removeEventListener(internalHiddenInputInputEvent, this.onHiddenFormInputInput);
}
get isClearable() {
return this.clearable && this.value.length > 0;
}
handleGlobalAttributesChanged() {
this.requestUpdate();
}
valueWatcher(newValue, previousValue) {
if (!this.userChangedValue) {
if (newValue === "Infinity" || newValue === "-Infinity") {
this.displayedValue = newValue;
this.previousEmittedNumberValue = newValue;
return;
}
this.setNumberValue({
origin: "direct",
previousValue,
value: newValue == null || newValue == "" ? "" : isValidNumber(newValue) ? newValue : this.previousValue || ""
});
this.warnAboutInvalidNumberValue(newValue);
}
this.userChangedValue = false;
}
keyDownHandler(event) {
if (this.readOnly || this.disabled || event.defaultPrevented) {
return;
}
if (this.isClearable && event.key === "Escape") {
this.clearInputValue(event);
event.preventDefault();
}
if (event.key === "Enter") {
if (submitForm(this)) {
event.preventDefault();
}
}
}
onLabelClick() {
this.setFocus();
}
incrementOrDecrementNumberValue(direction, inputMax, inputMin, nativeEvent) {
const { value } = this;
if (value === "Infinity" || value === "-Infinity") {
return;
}
const adjustment = direction === "up" ? 1 : -1;
const stepHandleInteger = this.integer && this.step !== "any" ? Math.round(this.step) : this.step;
const inputStep = stepHandleInteger === "any" ? 1 : Math.abs(stepHandleInteger || 1);
const inputVal = new BigDecimal(value !== "" ? value : "0");
const nudgedValue = inputVal.add(`${inputStep * adjustment}`);
const nudgedValueBelowInputMin = () => typeof inputMin === "number" && !isNaN(inputMin) && nudgedValue.subtract(`${inputMin}`).isNegative;
const nudgedValueAboveInputMax = () => typeof inputMax === "number" && !isNaN(inputMax) && !nudgedValue.subtract(`${inputMax}`).isNegative;
const finalValue = nudgedValueBelowInputMin() ? `${inputMin}` : nudgedValueAboveInputMax() ? `${inputMax}` : nudgedValue.toString();
this.setNumberValue({
committing: true,
nativeEvent,
origin: "user",
value: finalValue
});
}
clearInputValue(nativeEvent) {
this.setNumberValue({
committing: true,
nativeEvent,
origin: "user",
value: ""
});
}
emitChangeIfUserModified() {
if (this.previousValueOrigin === "user" && this.value !== this.previousEmittedNumberValue) {
this.calciteInputNumberChange.emit();
this.setPreviousEmittedNumberValue(this.value);
}
}
inputNumberBlurHandler() {
window.clearInterval(this.nudgeNumberValueIntervalId);
this.calciteInternalInputNumberBlur.emit();
this.emitChangeIfUserModified();
}
clickHandler(event) {
if (this.disabled) {
return;
}
const composedPath = event.composedPath();
if (!composedPath.includes(this.inputWrapperEl.value) || composedPath.includes(this.actionWrapperEl.value)) {
return;
}
this.setFocus();
}
inputNumberFocusHandler() {
this.calciteInternalInputNumberFocus.emit();
}
inputNumberInputHandler(nativeEvent) {
if (this.disabled || this.readOnly) {
return;
}
if (this.value === "Infinity" || this.value === "-Infinity") {
return;
}
const value = nativeEvent.target.value;
numberStringFormatter.numberFormatOptions = {
locale: this.messages._lang,
numberingSystem: this.numberingSystem,
useGrouping: this.groupSeparator
};
const delocalizedValue = numberStringFormatter.delocalize(value);
if (nativeEvent.inputType === "insertFromPaste") {
if (!isValidNumber(delocalizedValue) || this.integer && (delocalizedValue.includes("e") || delocalizedValue.includes("."))) {
nativeEvent.preventDefault();
}
this.setNumberValue({
nativeEvent,
origin: "user",
value: parseNumberString(delocalizedValue)
});
this.childNumberEl.value = this.displayedValue;
} else {
this.setNumberValue({
nativeEvent,
origin: "user",
value: delocalizedValue
});
}
}
inputNumberKeyDownHandler(event) {
if (this.disabled || this.readOnly) {
return;
}
if (this.value === "Infinity" || this.value === "-Infinity") {
event.preventDefault();
if (event.key === "Backspace" || event.key === "Delete") {
this.clearInputValue(event);
}
return;
}
if (event.key === "ArrowUp") {
event.preventDefault();
this.nudgeNumberValue("up", event);
return;
}
if (event.key === "ArrowDown") {
event.preventDefault();
this.nudgeNumberValue("down", event);
return;
}
const supportedKeys = [
...numberKeys,
"ArrowLeft",
"ArrowRight",
"Backspace",
"Delete",
"Enter",
"Escape",
"Tab"
];
if (event.altKey || event.ctrlKey || event.metaKey) {
return;
}
const isShiftTabEvent = event.shiftKey && event.key === "Tab";
if (supportedKeys.includes(event.key) || isShiftTabEvent) {
if (event.key === "Enter") {
this.emitChangeIfUserModified();
}
return;
}
numberStringFormatter.numberFormatOptions = {
locale: this.messages._lang,
numberingSystem: this.numberingSystem,
useGrouping: this.groupSeparator
};
if (event.key === numberStringFormatter.decimal && !this.integer) {
if (!this.value && !this.childNumberEl.value) {
return;
}
if (this.value && this.childNumberEl.value.indexOf(numberStringFormatter.decimal) === -1) {
return;
}
}
if (/[eE]/.test(event.key) && !this.integer) {
if (!this.value && !this.childNumberEl.value) {
return;
}
if (this.value && !/[eE]/.test(this.childNumberEl.value)) {
return;
}
}
if (event.key === "-") {
if (!this.value && !this.childNumberEl.value) {
return;
}
if (this.value && this.childNumberEl.value.split("-").length <= 2) {
return;
}
}
event.preventDefault();
}
nudgeNumberValue(direction, nativeEvent) {
if (nativeEvent instanceof KeyboardEvent && nativeEvent.repeat) {
return;
}
const inputMax = this.maxString ? parseFloat(this.maxString) : null;
const inputMin = this.minString ? parseFloat(this.minString) : null;
const valueNudgeDelayInMs = 150;
this.incrementOrDecrementNumberValue(direction, inputMax, inputMin, nativeEvent);
if (this.nudgeNumberValueIntervalId) {
window.clearInterval(this.nudgeNumberValueIntervalId);
}
let firstValueNudge = true;
this.nudgeNumberValueIntervalId = window.setInterval(() => {
if (firstValueNudge) {
firstValueNudge = false;
return;
}
this.incrementOrDecrementNumberValue(direction, inputMax, inputMin, nativeEvent);
}, valueNudgeDelayInMs);
}
nudgeButtonPointerUpHandler(event) {
if (!isPrimaryPointerButton(event)) {
return;
}
window.clearInterval(this.nudgeNumberValueIntervalId);
}
nudgeButtonPointerOutHandler() {
window.clearInterval(this.nudgeNumberValueIntervalId);
}
nudgeButtonPointerDownHandler(event) {
if (!isPrimaryPointerButton(event)) {
return;
}
event.preventDefault();
const direction = event.target.dataset.adjustment;
if (!this.disabled) {
this.nudgeNumberValue(direction, event);
}
}
syncHiddenFormInput(input) {
syncHiddenFormInput("number", this, input);
}
setChildNumberElRef(el) {
this.childNumberEl = el;
}
setInputNumberValue(newInputValue) {
if (!this.childNumberEl) {
return;
}
this.childNumberEl.value = newInputValue;
}
setPreviousEmittedNumberValue(value) {
this.previousEmittedNumberValue = this.normalizeValue(value);
}
normalizeValue(value) {
return isValidNumber(value) ? value : "";
}
setPreviousNumberValue(value) {
this.previousValue = this.normalizeValue(value);
}
setNumberValue({ committing = false, nativeEvent, origin, previousValue, value }) {
numberStringFormatter.numberFormatOptions = {
locale: this.messages._lang,
numberingSystem: this.numberingSystem,
useGrouping: this.groupSeparator
};
const isValueDeleted = this.previousValue?.length > value.length || this.value?.length > value.length;
const valueHandleInteger = this.integer ? value.replace(/[e.]/g, "") : value;
const hasTrailingDecimalSeparator = valueHandleInteger.charAt(valueHandleInteger.length - 1) === ".";
const hasLeadingMinusSign = valueHandleInteger.charAt(0) === "-";
const hasLeadingZeros = valueHandleInteger.match(/^-?(0+)\d/);
const sanitizedValue = hasTrailingDecimalSeparator && isValueDeleted ? valueHandleInteger : sanitizeNumberString(valueHandleInteger);
const newValue = value && !sanitizedValue ? isValidNumber(this.previousValue) ? this.previousValue : "" : sanitizedValue;
let newLocalizedValue = numberStringFormatter.localize(newValue);
if (origin !== "connected" && !hasTrailingDecimalSeparator) {
newLocalizedValue = addLocalizedTrailingDecimalZeros(newLocalizedValue, newValue, numberStringFormatter);
}
if (hasTrailingDecimalSeparator && isValueDeleted) {
newLocalizedValue = `${newLocalizedValue}${numberStringFormatter.decimal}`;
}
if (hasLeadingZeros) {
newLocalizedValue = `${hasLeadingMinusSign ? newLocalizedValue.charAt(0) : ""}${numberStringFormatter.localize("0").repeat(hasLeadingZeros[1].length)}${hasLeadingMinusSign ? newLocalizedValue.slice(1) : newLocalizedValue}`;
}
this.displayedValue = newLocalizedValue;
this.setPreviousNumberValue(previousValue ?? this.value);
this.previousValueOrigin = origin;
this.userChangedValue = origin === "user" && this.value !== newValue;
const validNewValue = ["-", "."].includes(newValue) ? "" : newValue;
this.value = validNewValue;
const localizedCharAllowlist = /* @__PURE__ */ new Set([
"e",
"E",
numberStringFormatter.decimal,
numberStringFormatter.minusSign,
numberStringFormatter.group,
...numberStringFormatter.digits
]);
const childInputValue = this.childNumberEl?.value;
if (childInputValue) {
const sanitizedChildInputValue = Array.from(childInputValue).filter((char) => localizedCharAllowlist.has(char)).join("");
if (sanitizedChildInputValue !== childInputValue) {
this.setInputNumberValue(sanitizedChildInputValue);
}
}
if (origin === "direct") {
this.setInputNumberValue(newLocalizedValue);
this.setPreviousEmittedNumberValue(validNewValue);
}
if (nativeEvent) {
const calciteInputNumberInputEvent = this.calciteInputNumberInput.emit();
if (calciteInputNumberInputEvent.defaultPrevented) {
this.value = this.previousValue;
this.displayedValue = numberStringFormatter.localize(this.previousValue);
} else if (committing) {
this.emitChangeIfUserModified();
}
}
}
inputNumberKeyUpHandler() {
window.clearInterval(this.nudgeNumberValueIntervalId);
}
warnAboutInvalidNumberValue(value) {
if (value && !isValidNumber(value)) {
console.warn(`The specified value "${value}" cannot be parsed, or is out of range.`);
}
}
render() {
const dir = getElementDir(this.el);
const loader = html`<div class=${safeClassMap(CSS.loader)}><calcite-progress .label=${this.messages.loading} type=indeterminate></calcite-progress></div>`;
const inputClearButton = html`<button .ariaLabel=${this.messages.clear} class=${safeClassMap(CSS.clearButton)} .disabled=${this.disabled || this.readOnly} @click=${this.clearInputValue} tabindex=-1 type=button><calcite-icon icon=x .scale=${getIconScale(this.scale)}></calcite-icon></button>`;
const iconEl = html`<calcite-icon class=${safeClassMap(CSS.inputIcon)} .flipRtl=${this.iconFlipRtl} .icon=${this.requestedIcon} .scale=${getIconScale(this.scale)}></calcite-icon>`;
const isHorizontalNumberButton = this.numberButtonType === "horizontal";
const numberButtonsHorizontalUp = html`<button aria-hidden=true class=${safeClassMap({
[CSS.numberButtonItem]: true,
[CSS.buttonItemHorizontal]: isHorizontalNumberButton
})} data-adjustment=up .disabled=${this.disabled || this.readOnly} @pointerdown=${this.nudgeButtonPointerDownHandler} @pointerout=${this.nudgeButtonPointerOutHandler} @pointerup=${this.nudgeButtonPointerUpHandler} tabindex=-1 type=button><calcite-icon icon=chevron-up .scale=${getIconScale(this.scale)}></calcite-icon></button>`;
const numberButtonsHorizontalDown = html`<button aria-hidden=true class=${safeClassMap({
[CSS.numberButtonItem]: true,
[CSS.buttonItemHorizontal]: isHorizontalNumberButton
})} data-adjustment=down .disabled=${this.disabled || this.readOnly} @pointerdown=${this.nudgeButtonPointerDownHandler} @pointerout=${this.nudgeButtonPointerOutHandler} @pointerup=${this.nudgeButtonPointerUpHandler} tabindex=-1 type=button><calcite-icon icon=chevron-down .scale=${getIconScale(this.scale)}></calcite-icon></button>`;
const numberButtonsVertical = html`<div class=${safeClassMap(CSS.numberButtonWrapper)}>${numberButtonsHorizontalUp}${numberButtonsHorizontalDown}</div>`;
const prefixText = html`<div class=${safeClassMap(CSS.prefix)}>${this.prefixText}</div>`;
const suffixText = html`<div class=${safeClassMap(CSS.suffix)}>${this.suffixText}</div>`;
const childEl = keyed("localized-input", html`<input aria-errormessage=${IDS.validationMessage} .ariaInvalid=${this.status === "invalid"} .ariaLabel=${getLabelText(this)} autocomplete=${this.autocomplete ?? nothing} .autofocus=${this.el.autofocus} value=${this.defaultValue ?? nothing} .disabled=${this.disabled} enterkeyhint=${this.el.enterKeyHint ?? nothing} inputmode=${this.el.inputMode || "decimal"} maxlength=${this.maxLength ?? nothing} minlength=${this.minLength ?? nothing} name=${nothing} @blur=${this.inputNumberBlurHandler} @focus=${this.inputNumberFocusHandler} @input=${this.inputNumberInputHandler} @keydown=${this.inputNumberKeyDownHandler} @keyup=${this.inputNumberKeyUpHandler} placeholder=${(this.placeholder || "") ?? nothing} .readOnly=${this.readOnly} type=text .value=${live(this.displayedValue ?? "")} ${ref(this.setChildNumberElRef)}>`);
return InteractiveContainer({ disabled: this.disabled, children: html`<div class=${safeClassMap({
[CSS.inputWrapper]: true,
[CSS_UTILITY.rtl]: dir === "rtl",
[CSS.hasSuffix]: this.suffixText,
[CSS.hasPrefix]: this.prefixText,
[CSS.clearable]: this.isClearable
})} ${ref(this.inputWrapperEl)}>${this.numberButtonType === "horizontal" && !this.readOnly ? numberButtonsHorizontalDown : null}${this.prefixText ? prefixText : null}<div class=${safeClassMap(CSS.wrapper)}>${childEl}${this.isClearable ? inputClearButton : null}${this.requestedIcon ? iconEl : null}${this.loading ? loader : null}</div><div class=${safeClassMap(CSS.actionWrapper)} ${ref(this.actionWrapperEl)}><slot name=${SLOTS.action}></slot></div>${this.numberButtonType === "vertical" && !this.readOnly ? numberButtonsVertical : null}${this.suffixText ? suffixText : null}${this.numberButtonType === "horizontal" && !this.readOnly ? numberButtonsHorizontalUp : null}${HiddenFormInputSlot({ component: this })}</div>${this.validationMessage && this.status === "invalid" ? Validation({ icon: this.validationIcon, id: IDS.validationMessage, message: this.validationMessage, scale: this.scale, status: this.status }) : null}` });
}
}
customElement("calcite-input-number", InputNumber);
export {
InputNumber
};