/**
 * Copyright IBM Corp. 2026
 *
 * This source code is licensed under the Apache-2.0 license found in the
 * LICENSE file in the root directory of this source tree.
 */

import "../../globals/settings.js";
import { carbonElement } from "../../globals/decorators/carbon-element.js";
import { __decorate } from "../../_virtual/_@oxc-project_runtime@0.126.0/helpers/decorate.js";
import iconLoader from "../../globals/internal/icon-loader.js";
import if_non_empty_default from "../../globals/directives/if-non-empty.js";
import "../text-input/defs.js";
import text_input_default from "../text-input/text-input.js";
import { NUMBER_INPUT_VALIDATION_STATUS } from "./defs.js";
import number_input_default$1 from "./number-input.scss.js";
import { LitElement, html } from "lit";
import { property, query, state } from "lit/decorators.js";
import { classMap } from "lit/directives/class-map.js";
import WarningFilled16 from "@carbon/icons/es/warning--filled/16.js";
import WarningAltFilled16 from "@carbon/icons/es/warning--alt--filled/16.js";
import Add16 from "@carbon/icons/es/add/16.js";
import Subtract16 from "@carbon/icons/es/subtract/16.js";
import { NumberFormatter, NumberParser } from "@carbon/utilities";
//#region src/components/number-input/number-input.ts
/**
* Copyright IBM Corp. 2019, 2026
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* Number input type
*/
let NUMBER_INPUT_TYPE = /* @__PURE__ */ function(NUMBER_INPUT_TYPE) {
	/**
	* Number type - uses native number input
	*/
	NUMBER_INPUT_TYPE["NUMBER"] = "number";
	/**
	* Text type - uses text input with locale-based formatting
	*/
	NUMBER_INPUT_TYPE["TEXT"] = "text";
	return NUMBER_INPUT_TYPE;
}({});
/**
* Number input.
*
* @element cds-custom-number-input
* @fires cds-custom-number-input
*   The name of the custom event fired after the value is changed upon a user gesture.
* @slot helper-text - The helper text.
* @slot label-text - The label text.
* @slot validity-message - The validity message. If present and non-empty, this input shows the UI of its invalid state.
*/
let CDSNumberInput = class CDSNumberInput extends text_input_default {
	constructor(..._args) {
		super(..._args);
		this._numberValue = NaN;
		this._previousNumberValue = NaN;
		this._inputValue = "";
		this._invalid = false;
		this._hasUserInteraction = false;
		this._numberFormatter = null;
		this._numberParser = null;
		this._preventWheel = (event) => {
			event.preventDefault();
		};
		this._min = "";
		this._max = "";
		this._step = "1";
		this.iconDescription = "";
		this.incrementButtonAssistiveText = "increase number input";
		this.decrementButtonAssistiveText = "decrease number input";
		this.hideSteppers = false;
		this.allowEmpty = false;
		this.defaultValue = "";
		this.disableWheel = false;
		this.isFluid = false;
		this.size = "md";
		this.type = "number";
		this.locale = "en-US";
		this.formatOptions = {};
		this.pattern = "[0-9]*";
		this.inputMode = "decimal";
		this.stepStartValue = 0;
		this.getDecimalPlaces = (num) => {
			const parts = num.toString().split(".");
			return parts[1] ? parts[1].length : 0;
		};
	}
	/**
	* Handles `input` event on the `input` in the shadow DOM.
	*/
	_handleInput(event) {
		const { target } = event;
		const { value } = target;
		if (this.type === "number") {
			const direction = this._value !== void 0 && Number(value) > Number(this._value) ? "up" : "down";
			this._dispatchInputEvent(value, direction);
			this._value = value;
			this.requestUpdate();
		} else if (this.type === "text") {
			const _value = this.allowEmpty && value === "" ? "" : value;
			const parsedValue = this._numberParser?.parse(_value) ?? NaN;
			this._hasUserInteraction = true;
			this._numberValue = parsedValue;
			this._inputValue = _value;
			if (!isNaN(parsedValue)) this._value = String(parsedValue);
			else if (this.allowEmpty && _value === "") this._value = "";
			if (this.validate) {
				const isValid = this.validate(_value, this.locale);
				this._invalid = isValid === false;
			} else this._invalid = false;
		}
	}
	/**
	* Handles `click` event on the down button in the shadow DOM.
	*/
	_handleUserInitiatedStepDown() {
		const { _input: input } = this;
		if (this._handleStep("down")) {
			const newValue = this.type === "text" ? this._numberValue : input.value;
			this._dispatchInputEvent(newValue, "down");
		}
	}
	/**
	* Handles `click` event on the up button in the shadow DOM.
	*/
	_handleUserInitiatedStepUp() {
		const { _input: input } = this;
		if (this._handleStep("up")) {
			const newValue = this.type === "text" ? this._numberValue : input.value;
			this._dispatchInputEvent(newValue, "up");
		}
	}
	/**
	* Handles `focus` event on the `input` in the shadow DOM.
	*/
	_handleFocus(event) {
		if (this.disableWheel) event.target.addEventListener("wheel", this._preventWheel, { passive: false });
	}
	/**
	* Handles `blur` event on the `input` in the shadow DOM.
	*/
	_handleBlur(event) {
		if (this.disableWheel) event.target.removeEventListener("wheel", this._preventWheel);
		if (this.type === "text") {
			const rawValue = event.target.value;
			if ((this.validate ? this.validate(rawValue, this.locale) : true) !== false) {
				const formattedValue = isNaN(this._numberValue) ? "" : this._numberFormatter?.format(this._numberValue) ?? "";
				this._inputValue = formattedValue;
				const parsedFormattedValue = this._numberParser?.parse(formattedValue) ?? NaN;
				if (!(isNaN(this._previousNumberValue) && isNaN(parsedFormattedValue)) && this._previousNumberValue !== parsedFormattedValue) {
					const direction = this._previousNumberValue < parsedFormattedValue ? "up" : "down";
					this._dispatchInputEvent(parsedFormattedValue, direction);
				}
				this._numberValue = parsedFormattedValue;
				this._previousNumberValue = parsedFormattedValue;
				this._value = String(parsedFormattedValue);
				this._invalid = false;
			} else {
				this._invalid = true;
				this._inputValue = rawValue;
			}
			this.requestUpdate();
		}
	}
	/**
	* Handles keyboard events for arrow up/down when type="text"
	*/
	_handleKeyDown(event) {
		if (this.readonly) return;
		if (this.type === "text") {
			if (event.key === "ArrowUp") {
				event.preventDefault();
				if (this._handleStep("up")) this._dispatchInputEvent(this._numberValue, "up");
			} else if (event.key === "ArrowDown") {
				event.preventDefault();
				if (this._handleStep("down")) this._dispatchInputEvent(this._numberValue, "down");
			}
		}
	}
	/**
	* The minimum value allowed in the input
	*/
	get min() {
		return this._min.toString();
	}
	set min(value) {
		const oldValue = this.min;
		this._min = value;
		this.requestUpdate("min", oldValue);
	}
	/**
	* The maximum value allowed in the input
	*/
	get max() {
		return this._max.toString();
	}
	set max(value) {
		const oldValue = this.max;
		this._max = value;
		this.requestUpdate("max", oldValue);
	}
	/**
	* The amount the value should increase or decrease by
	*/
	get step() {
		return this._step.toString();
	}
	set step(value) {
		const oldValue = this.step;
		this._step = value;
		this.requestUpdate("step", oldValue);
	}
	/**
	* Override value setter to handle formatting for type="text"
	*/
	get value() {
		return super.value;
	}
	set value(val) {
		const oldValue = this._value;
		if (this.type === "text") {
			if (val === "") {
				this._numberValue = NaN;
				this._previousNumberValue = NaN;
				this._inputValue = "";
				this._value = "";
				this.requestUpdate("value", oldValue);
				if (this._input) this._input.value = "";
				return;
			}
			if (!this._numberFormatter || !this._numberParser) this._initializeFormatters();
			let parsed;
			if (typeof val === "string") parsed = this._numberParser?.parse(val) ?? Number(val);
			else parsed = val;
			this._syncTextModeState(parsed, true);
			this.requestUpdate("value", oldValue);
		} else super.value = val;
	}
	/**
	* Initialize formatters when component connects
	*/
	connectedCallback() {
		super.connectedCallback();
		this._initializeFormatters();
	}
	/**
	* Called before updates to initialize default value
	*/
	willUpdate(changedProperties) {
		super.willUpdate?.(changedProperties);
		if (changedProperties.has("type")) {
			this._initializeFormatters();
			this._invalid = false;
			const currentValue = this._value || this.value;
			if (currentValue) {
				const parsed = this._numberParser?.parse(currentValue) ?? NaN;
				if (!isNaN(parsed)) {
					this._numberValue = parsed;
					this._previousNumberValue = parsed;
					if (this.type === "text") {
						this._inputValue = this._numberFormatter?.format(parsed) ?? "";
						this._value = currentValue;
					} else {
						const plainValue = parsed.toString();
						this._value = plainValue;
						super.value = plainValue;
					}
				}
			}
		}
		if (changedProperties.has("locale") || changedProperties.has("formatOptions")) {
			this._initializeFormatters();
			if (this.type === "text") {
				if (this._value) {
					const parsed = Number(this._value);
					if (!isNaN(parsed)) this._syncTextModeState(parsed);
				} else if (!isNaN(this._numberValue)) this._syncTextModeState(this._numberValue);
			}
		}
		if (!this._value && this.defaultValue && !this._hasUserInteraction) if (this.type === "text") {
			const parsed = this._numberParser?.parse(this.defaultValue) ?? NaN;
			this._syncTextModeState(parsed);
		} else this._value = this.defaultValue;
	}
	/**
	* Update formatters when properties change
	*/
	updated(changedProperties) {
		super.updated?.();
		if ((changedProperties.has("locale") || changedProperties.has("formatOptions")) && this.type === "text" && this._input) this._input.value = this._inputValue;
	}
	/**
	* Initialize NumberFormatter and NumberParser instances
	*/
	_initializeFormatters() {
		if (this.type === "text") try {
			this._numberFormatter = new NumberFormatter(this.locale, this.formatOptions);
			this._numberParser = new NumberParser(this.locale, this.formatOptions);
		} catch (error) {
			console.error("[NumberInput] Failed to initialize formatters:", error);
		}
	}
	/**
	* Get input validity
	*/
	_getInputValidity() {
		if (this.invalid || this._invalid) return false;
		const valueToCheck = this.validate && this.type === "text" ? this._inputValue : this.type === "text" ? this._numberValue : this.value;
		if (valueToCheck === "") return this.allowEmpty;
		let numericValue;
		if (typeof valueToCheck === "string") if (this.type === "number") numericValue = Number(valueToCheck);
		else numericValue = this._numberParser?.parse(valueToCheck) ?? NaN;
		else if (typeof valueToCheck === "number") numericValue = valueToCheck;
		else numericValue = NaN;
		if (isNaN(numericValue)) return this.allowEmpty;
		if (this.validate && this.type === "text") {
			if (this.validate(this._inputValue, this.locale) === false) return false;
		}
		if (this.max !== "" && numericValue > Number(this.max)) return false;
		if (this.min !== "" && numericValue < Number(this.min)) return false;
		return true;
	}
	/**
	* Clamp a value between min and max
	*/
	_clamp(value, min, max) {
		return Math.min(Math.max(value, min), max);
	}
	/**
	* Handle stepping up or down
	* @returns true if the value changed, false if it was clamped to the same value
	*/
	_handleStep(direction) {
		const currentValue = this.type === "number" ? Number(this._input.value) : this._numberValue;
		let rawValue;
		if (Number.isNaN(currentValue) || currentValue === null || currentValue === void 0) if (typeof this.stepStartValue === "number" && this.stepStartValue) rawValue = this.stepStartValue;
		else if (this.min && Number(this.min) < 0 && this.max && Number(this.max) > 0 || !this.max && !this.min || this.max) rawValue = direction === "up" ? 1 : -1;
		else if (this.min && Number(this.min) > 0 && this.max && Number(this.max) > 0 || this.min) rawValue = Number(this.min);
		else rawValue = 0;
		else if (direction === "up") rawValue = currentValue + Number(this.step);
		else rawValue = currentValue - Number(this.step);
		const precision = Math.max(this.getDecimalPlaces(currentValue), this.getDecimalPlaces(Number(this.step)));
		const floatValue = parseFloat(Number(rawValue).toFixed(precision));
		const newValue = this._clamp(floatValue, this.min !== "" ? Number(this.min) : -Infinity, this.max !== "" ? Number(this.max) : Infinity);
		const valueChanged = currentValue !== newValue;
		if (this.type === "number") {
			this._value = String(newValue);
			this.value = this._value;
		} else if (this.type === "text") {
			this._syncTextModeState(newValue);
			this.requestUpdate();
		}
		return valueChanged;
	}
	/**
	* Handles incrementing the value in the input
	*/
	stepUp() {
		this._handleStep("up");
	}
	/**
	* Handles decrementing the value in the input
	*/
	stepDown() {
		this._handleStep("down");
	}
	_getInputValue() {
		if (this.type === "text") return this._inputValue;
		return this._value || this.defaultValue || "";
	}
	_dispatchInputEvent(value, direction) {
		this.dispatchEvent(new CustomEvent(this.constructor.eventInput, {
			bubbles: true,
			composed: true,
			cancelable: false,
			detail: {
				value,
				direction
			}
		}));
	}
	/**
	* Synchronizes text mode state by formatting a numeric value and updating all related state variables.
	* This centralizes the logic for managing _numberValue, _inputValue, _previousNumberValue, and _value.
	*
	* @param numericValue - The numeric value to format and synchronize
	* @param updateInput - Whether to update the DOM input element (default: false)
	*/
	_syncTextModeState(numericValue, updateInput = false) {
		const formattedValue = isNaN(numericValue) ? "" : this._numberFormatter?.format(numericValue) ?? String(numericValue);
		const parsedValue = formattedValue ? this._numberParser?.parse(formattedValue) ?? numericValue : numericValue;
		this._numberValue = parsedValue;
		this._previousNumberValue = parsedValue;
		this._inputValue = formattedValue;
		this._value = String(parsedValue);
		if (updateInput && this._input) this._input.value = formattedValue;
	}
	render() {
		const { _handleInput: handleInput, _handleUserInitiatedStepDown: handleUserInitiatedStepDown, _handleUserInitiatedStepUp: handleUserInitiatedStepUp, _handleFocus: handleFocus, _handleBlur: handleBlur, _handleKeyDown: handleKeyDown } = this;
		const isValid = this._getInputValidity();
		const invalidIcon = iconLoader(WarningFilled16, { class: `cds-custom--number__invalid` });
		const warnIcon = iconLoader(WarningAltFilled16, { class: `cds-custom--number__invalid cds-custom--number__invalid--warning` });
		const normalizedProps = {
			disabled: this.disabled,
			invalid: !this.readonly && !isValid,
			warn: !this.readonly && isValid && this.warn,
			"slot-name": "",
			"slot-text": "",
			icon: null,
			buttonsDisabled: this.disabled || this.readonly
		};
		const wrapperClasses = classMap({
			[`cds-custom--number`]: true,
			[`cds-custom--number--${this.size}`]: this.size,
			[`cds-custom--number--nosteppers`]: this.hideSteppers,
			[`cds-custom--number--readonly`]: this.readonly
		});
		const inputWrapperClasses = classMap({
			[`cds-custom--number__input-wrapper`]: true,
			[`cds-custom--number__input-wrapper--warning`]: normalizedProps.warn,
			[`cds-custom--number__input-wrapper--decorator`]: this._hasAILabel
		});
		const labelClasses = classMap({
			[`cds-custom--label`]: true,
			[`cds-custom--label--disabled`]: normalizedProps.disabled,
			[`cds-custom--visually-hidden`]: this.hideLabel
		});
		const helperTextClasses = classMap({
			[`cds-custom--form__helper-text`]: true,
			[`cds-custom--form__helper-text--disabled`]: normalizedProps.disabled
		});
		const inputValue = this._getInputValue();
		const incrementButton = html`
      <button
        class="${"cds-custom"}--number__control-btn up-icon"
        aria-label="${this.iconDescription || this.incrementButtonAssistiveText}"
        aria-live="polite"
        aria-atomic="true"
        type="button"
        ?disabled=${normalizedProps.buttonsDisabled}
        @click=${handleUserInitiatedStepUp}>
        ${iconLoader(Add16)}
      </button>
      <div class="${"cds-custom"}--number__rule-divider"></div>
    `;
		const decrementButton = html`
      <button
        class="${"cds-custom"}--number__control-btn down-icon"
        aria-label="${this.iconDescription || this.decrementButtonAssistiveText}"
        aria-live="polite"
        aria-atomic="true"
        type="button"
        ?disabled=${normalizedProps.buttonsDisabled}
        @click=${handleUserInitiatedStepDown}>
        ${iconLoader(Subtract16)}
      </button>
      <div class="${"cds-custom"}--number__rule-divider"></div>
    `;
		const input = html`
      <input
        autocomplete="${if_non_empty_default(this.autocomplete)}"
        ?autofocus="${this.autofocus}"
        ?data-invalid="${normalizedProps.invalid}"
        ?disabled="${normalizedProps.disabled}"
        id="input"
        name="${if_non_empty_default(this.name)}"
        pattern="${this.type === "text" ? if_non_empty_default(this.pattern) : ""}"
        ?readonly="${this.readonly}"
        ?required="${this.required}"
        type="${this.type}"
        inputmode="${this.type === "text" ? this.inputMode : ""}"
        .value="${inputValue}"
        @input="${handleInput}"
        @focus="${handleFocus}"
        @blur="${handleBlur}"
        @keydown="${handleKeyDown}"
        min="${this.type === "number" ? if_non_empty_default(this.min) : ""}"
        max="${this.type === "number" ? if_non_empty_default(this.max) : ""}"
        step="${this.type === "number" ? if_non_empty_default(this.step) : ""}"
        role="alert"
        aria-atomic="true" />
    `;
		if (normalizedProps.invalid) {
			normalizedProps.icon = invalidIcon;
			normalizedProps["slot-name"] = "invalid-text";
			normalizedProps["slot-text"] = this.invalidText;
		} else if (normalizedProps.warn) {
			normalizedProps.icon = warnIcon;
			normalizedProps["slot-name"] = "warn-text";
			normalizedProps["slot-text"] = this.warnText;
		}
		const validationMessage = normalizedProps.invalid || normalizedProps.warn ? html`<div
            class="${"cds-custom"}--form-requirement"
            ?hidden="${!normalizedProps.invalid && !normalizedProps.warn}">
            <slot name="${normalizedProps["slot-name"]}">
              ${normalizedProps["slot-text"]}
            </slot>
          </div>` : null;
		const helper = this.helperText ? html`<div
          class="${helperTextClasses}"
          id="helper-text"
          ?hidden="${normalizedProps.invalid || normalizedProps.warn}">
          <slot name="helper-text"> ${this.helperText} </slot>
        </div>` : null;
		return html`
      <div class="${wrapperClasses}" ?data-invalid=${normalizedProps.invalid}>
        <label class="${labelClasses}" for="input">
          <slot name="label-text"> ${this.label} </slot>
        </label>
        <div class="${inputWrapperClasses}">
          ${normalizedProps.icon} ${input}
          <slot name="ai-label" @slotchange="${this._handleSlotChange}"></slot>
          <slot name="slug" @slotchange="${this._handleSlotChange}"></slot>
          <div class="${"cds-custom"}--number__controls">
            ${!this.hideSteppers ? html`${decrementButton} ${incrementButton}` : null}
          </div>
        </div>
        ${this.isFluid ? html`<hr class="${"cds-custom"}--number-input__divider" />` : null}
        ${validationMessage ?? helper}
      </div>
    `;
	}
	/**
	* The name of the custom event fired after the value is changed upon a user gesture.
	*/
	static get eventInput() {
		return `cds-custom-number-input`;
	}
	static {
		this.shadowRootOptions = {
			...LitElement.shadowRootOptions,
			delegatesFocus: true
		};
	}
	static {
		this.styles = number_input_default$1;
	}
};
__decorate([query("input")], CDSNumberInput.prototype, "_input", void 0);
__decorate([state()], CDSNumberInput.prototype, "_numberValue", void 0);
__decorate([state()], CDSNumberInput.prototype, "_previousNumberValue", void 0);
__decorate([state()], CDSNumberInput.prototype, "_inputValue", void 0);
__decorate([state()], CDSNumberInput.prototype, "_invalid", void 0);
__decorate([state()], CDSNumberInput.prototype, "_hasUserInteraction", void 0);
__decorate([property({ reflect: true })], CDSNumberInput.prototype, "min", null);
__decorate([property({ reflect: true })], CDSNumberInput.prototype, "max", null);
__decorate([property({ reflect: true })], CDSNumberInput.prototype, "step", null);
__decorate([property({ attribute: "icon-description" })], CDSNumberInput.prototype, "iconDescription", void 0);
__decorate([property({ attribute: "increment-button-assistive-text" })], CDSNumberInput.prototype, "incrementButtonAssistiveText", void 0);
__decorate([property({ attribute: "decrement-button-assistive-text" })], CDSNumberInput.prototype, "decrementButtonAssistiveText", void 0);
__decorate([property({
	type: Boolean,
	attribute: "hide-steppers",
	reflect: true
})], CDSNumberInput.prototype, "hideSteppers", void 0);
__decorate([property({
	type: Boolean,
	attribute: "allow-empty",
	reflect: true
})], CDSNumberInput.prototype, "allowEmpty", void 0);
__decorate([property({ attribute: "default-value" })], CDSNumberInput.prototype, "defaultValue", void 0);
__decorate([property({
	type: Boolean,
	attribute: "disable-wheel",
	reflect: true
})], CDSNumberInput.prototype, "disableWheel", void 0);
__decorate([property({ type: Boolean })], CDSNumberInput.prototype, "isFluid", void 0);
__decorate([property({ reflect: true })], CDSNumberInput.prototype, "size", void 0);
__decorate([property({ reflect: true })], CDSNumberInput.prototype, "type", void 0);
__decorate([property({ reflect: true })], CDSNumberInput.prototype, "locale", void 0);
__decorate([property({
	type: Object,
	attribute: "format-options"
})], CDSNumberInput.prototype, "formatOptions", void 0);
__decorate([property({ reflect: true })], CDSNumberInput.prototype, "pattern", void 0);
__decorate([property({
	reflect: true,
	attribute: "input-mode"
})], CDSNumberInput.prototype, "inputMode", void 0);
__decorate([property({
	type: Number,
	attribute: "step-start-value"
})], CDSNumberInput.prototype, "stepStartValue", void 0);
__decorate([property({ reflect: true })], CDSNumberInput.prototype, "value", null);
CDSNumberInput = __decorate([carbonElement(`cds-custom-number-input`)], CDSNumberInput);
var number_input_default = CDSNumberInput;
//#endregion
export { NUMBER_INPUT_TYPE, NUMBER_INPUT_VALIDATION_STATUS, number_input_default as default };

//# sourceMappingURL=number-input.js.map