var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
import { html, nothing } from 'lit';
import { property } from 'lit/decorators.js';
import { ifDefined } from 'lit/directives/if-defined.js';
import { live } from 'lit/directives/live.js';
import FormfieldWrapper from '../formfieldwrapper';
import { DEFAULTS as FORMFIELD_DEFAULTS, VALIDATION } from '../formfieldwrapper/formfieldwrapper.constants';
import { DataAriaLabelMixin } from '../../utils/mixins/DataAriaLabelMixin';
import { FormInternalsMixin } from '../../utils/mixins/FormInternalsMixin';
import { AutoFocusOnMountMixin } from '../../utils/mixins/AutoFocusOnMountMixin';
import { KeyToActionMixin, ACTIONS, NAV_MODES } from '../../utils/mixins/KeyToActionMixin';
import { CharacterLimitMixin } from '../../utils/mixins/CharacterLimitMixin';
import { AUTO_CAPITALIZE, AUTO_COMPLETE, DEFAULTS, PREFIX_TEXT_OPTIONS } from './input.constants';
import styles from './input.styles';
/**
 * mdc-input is a component that allows users to input text.
 *  It contains:
 * - label field - describe the input field.
 * - input field - contains the value
 * - help text or validation message - displayed below the input field.
 * - trailing button - it displays a clear the input field.
 * - prefix text - displayed before the input field.
 * - leading icon - displayed before the input field.
 * - clear-aria-label - aria label for the trailing button.
 * - all the attributes of the input field.
 *
 * @tagname mdc-input
 *
 * @event input - (React: onInput) This event is dispatched when the value of the input field changes (every press).
 * @event change - (React: onChange) This event is dispatched when the value of the input field changes (on blur).
 * @event focus - (React: onFocus) This event is dispatched when the input receives focus.
 * @event blur - (React: onBlur) This event is dispatched when the input loses focus.
 * @event clear - (React: onClear) This event is dispatched when the input text is cleared.
 * @event limitexceeded - (React: onLimitExceeded) This event is dispatched once when the character limit
 * exceeds or restored.
 *
 * @dependency mdc-icon
 * @dependency mdc-text
 * @dependency mdc-button
 * @dependency mdc-toggletip
 *
 * @slot label - Slot for the label element. If not provided, the `label` property will be used to render the label.
 * @slot toggletip - Slot for the toggletip info icon button. If not provided, the `toggletip-text` property will be used to render the info icon button and toggletip.
 * @slot help-icon - Slot for the helper/validation icon. If not provided, the icon will be rendered based on the `helpTextType` property.
 * @slot help-text - Slot for the helper/validation text. If not provided, the `helpText` property will be used to render the helper/validation text.
 * @slot input - Slot for the input element. If not provided, the input field will be rendered.
 * @slot input-leading-icon - Slot for the leading icon before the input field. If not provided, the `leadingIcon` property will be used to render the leading icon.
 * @slot input-prefix-text - Slot for the prefix text before the input field. If not provided, the `prefixText` property will be used to render the prefix text.
 * @slot trailing-button - Slot for the trailing button to clear the input field. If not provided, the clear button will be rendered when `trailingButton` property is set to true.
 *
 * @cssproperty --mdc-label-font-size - Font size for the label text.
 * @cssproperty --mdc-label-font-weight - Font weight for the label text.
 * @cssproperty --mdc-label-line-height - Line height for the label text.
 * @cssproperty --mdc-label-color - Color for the label text.
 * @cssproperty --mdc-help-text-font-size - Font size for the help text.
 * @cssproperty --mdc-help-text-font-weight - Font weight for the help text.
 * @cssproperty --mdc-help-text-line-height - Line height for the help text.
 * @cssproperty --mdc-help-text-color - Color for the help text.
 * @cssproperty --mdc-required-indicator-color - Color for the required indicator text.
 * @cssproperty --mdc-input-text-color - Text color for the input field
 * @cssproperty --mdc-input-border-color - Border color for the input container
 * @cssproperty --mdc-input-background-color - Background color for the input field
 * @cssproperty --mdc-input-height - Height for the input container
 * @cssproperty --mdc-input-support-text-color - Text color for the help text
 * @cssproperty --mdc-input-selection-text-color - Text color for the selected text
 * @cssproperty --mdc-input-selection-background-color - Background color for the selected text
 *
 * @csspart label - The label element.
 * @csspart label-text - The container for the label and required indicator elements.
 * @csspart required-indicator - The required indicator element that is displayed next to the label when the `required` property is set to true.
 * @csspart info-icon-btn - The info icon button element that is displayed next to the label when the `toggletip-text` property is set.
 * @csspart label-toggletip - The toggletip element that is displayed when the info icon button is clicked.
 * @csspart help-text - The helper/validation text element.
 * @csspart helper-icon - The helper/validation icon element that is displayed next to the helper/validation text.
 * @csspart help-text-container - The container for the helper/validation icon and text elements.
 * @csspart leading-icon - The leading icon element that is displayed before the input field.
 * @csspart prefix-text - The prefix text element that is displayed before the input field.
 * @csspart input-container - The container for the input field, leading icon, prefix text, and trailing button elements.
 * @csspart input-section - The container for the input field, leading icon, and prefix text elements.
 * @csspart input-text - The input field element.
 * @csspart trailing-button - The trailing button element that is displayed to clear the input field when the `trailingButton` property is set to true.
 * @csspart input-footer - The footer element that contains the helper text and character counter.
 * @csspart character-counter - The character counter element.
 */
class Input extends CharacterLimitMixin(KeyToActionMixin(AutoFocusOnMountMixin(FormInternalsMixin(DataAriaLabelMixin(FormfieldWrapper))))) {
    constructor() {
        super(...arguments);
        /**
         * The placeholder text that is displayed when the input field is empty.
         */
        this.placeholder = '';
        /**
         * The trailing button when set to true, shows a clear button that clears the input field.
         * @default false
         */
        this.trailingButton = false;
        /**
         * The autocapitalize attribute of the input field.
         * @default 'off'
         */
        this.autocapitalize = AUTO_CAPITALIZE.OFF;
        /**
         * The autocomplete attribute of the input field.
         * @default 'off'
         */
        this.autocomplete = AUTO_COMPLETE.OFF;
        /**
         * Aria label for the trailing button. If trailing button is set to true, this label is used for the clear button.
         * @default ''
         */
        this.clearAriaLabel = '';
        /**
         * Defines a id pointing to the element which describes the input element.
         * The AriaDescribedby attribute to be set for accessibility.
         * @default null
         */
        this.dataAriaDescribedby = null;
    }
    connectedCallback() {
        super.connectedCallback();
        this.updateComplete
            .then(() => {
            if (this.inputElement) {
                this.setInputValidity();
                this.internals.setFormValue(this.inputElement.value);
            }
        })
            .catch(error => {
            if (this.onerror) {
                this.onerror(error);
            }
        });
    }
    firstUpdated(_changedProperties) {
        // set the element to auto focus if autoFocusOnMount is set to true
        // before running the super method, so that the AutoFocusOnMountMixin can use it
        // to focus the correct element
        if (this.inputElement && this.autoFocusOnMount) {
            this.elementToAutoFocus = this.inputElement;
        }
        super.firstUpdated(_changedProperties);
    }
    /** @internal */
    formResetCallback() {
        this.value = '';
        this.inputElement.value = '';
        this.setInputValidity();
        this.requestUpdate();
    }
    /** @internal */
    formStateRestoreCallback(state) {
        this.value = state;
    }
    /**
     * This function is called when the attribute changes.
     * It updates the validity of the input field based on the input field's validity.
     *
     * @param name - attribute name
     * @param old - old value
     * @param value - new value
     */
    attributeChangedCallback(name, old, value) {
        super.attributeChangedCallback(name, old, value);
        if (name === 'validation-message') {
            this.updateComplete
                .then(() => {
                this.setInputValidity();
            })
                .catch(error => {
                if (this.onerror) {
                    this.onerror(error);
                }
            });
        }
    }
    setInputValidity() {
        this.inputElement.setCustomValidity('');
        if (!this.inputElement.validity.valid && this.validationMessage) {
            this.inputElement.setCustomValidity(this.validationMessage);
        }
        else if (this.maxCharacterLimit &&
            this.value.length > this.maxCharacterLimit &&
            this.helpTextType === VALIDATION.ERROR &&
            this.helpText) {
            this.inputElement.setCustomValidity(this.helpText);
        }
        this.setValidity();
    }
    updated(changedProperties) {
        super.updated(changedProperties);
        if (changedProperties.has('value')) {
            this.handleCharacterOverflowStateChange();
        }
        if (changedProperties.has('helpText')) {
            this.announceCharacterLimitWarning();
        }
    }
    /**
     * Updates the value of the input field.
     * Sets the form value.
     * @returns void
     */
    updateValue() {
        this.value = this.inputElement.value;
        this.internals.setFormValue(this.inputElement.value);
        this.announceCharacterLimitWarning();
    }
    /**
     * Handles the input event of the input field.
     * Updates the value and sets the validity of the input field.
     * @internal
     */
    onInput(event) {
        this.updateValue();
        this.setInputValidity();
        this.checkValidity();
        // Some versions of the 'input' event are not composed.
        // If the event is not composed, we need to re-dispatch the same event to ensure it is propagated correctly.
        if (!event.composed) {
            const EventConstructor = event.constructor;
            this.dispatchEvent(new EventConstructor(event.type, event));
        }
    }
    /**
     * Handles the change event of the input field.
     * Updates the value and sets the validity of the input field.
     *
     * The 'change' event does not bubble up through the shadow DOM as it was not composed.
     * Therefore, we need to re-dispatch the same event to ensure it is propagated correctly.
     * Read more: https://developer.mozilla.org/en-US/docs/Web/API/Event/composed
     *
     * @param event - Event which contains information about the value change.
     * @internal
     */
    onChange(event) {
        this.updateValue();
        this.setInputValidity();
        const EventConstructor = event.constructor;
        this.dispatchEvent(new EventConstructor(event.type, event));
    }
    /**
     * Handles the keydown event of the input field.
     * If the key pressed is 'Enter', it submits the form.
     * @param event - Keyboard event
     */
    handleKeyDown(event) {
        var _a;
        if (this.getActionForKeyEvent(event) === ACTIONS.ENTER && this.getKeyboardNavMode() === NAV_MODES.DEFAULT) {
            (_a = this.form) === null || _a === void 0 ? void 0 : _a.requestSubmit();
        }
    }
    /**
     * Renders the leading icon before the input field.
     * If the leading icon is not set, it will not be displayed.
     *
     * @returns void
     */
    renderLeadingIcon() {
        if (!this.leadingIcon) {
            return nothing;
        }
        return html `
      <mdc-icon
        part="leading-icon"
        name=${this.leadingIcon}
        size="${DEFAULTS.ICON_SIZE_VALUE}"
        length-unit="${DEFAULTS.ICON_SIZE_UNIT}"
      >
      </mdc-icon>
    `;
    }
    /**
     * Renders the prefix text before the input field.
     * If the prefix text is more than 10 characters,
     * - it will not be displayed.
     * - the validation messsage will be displayed.
     *
     *  Note: We are setting aria-hidden so that the screen reader does not read the prefix text.
     *  The consumers should set the appropriate aria-label for the input field using 'data-aria-label' attribute.
     * @returns void
     */
    renderPrefixText() {
        if (!this.prefixText) {
            return nothing;
        }
        return html `
      <mdc-text
        part="prefix-text"
        tagname="${DEFAULTS.PREFIX_TEXT_TAG}"
        type="${DEFAULTS.PREFIX_TEXT_TYPE}"
        aria-hidden="true"
      >
        ${this.prefixText.slice(0, PREFIX_TEXT_OPTIONS.MAX_LENGTH)}
      </mdc-text>
    `;
    }
    /**
     * Clears the input field.
     */
    clearInputText() {
        var _a;
        this.value = '';
        // focus the input field after clearing the text
        (_a = this.inputElement) === null || _a === void 0 ? void 0 : _a.focus();
        this.dispatchEvent(new CustomEvent('clear', { bubbles: true, composed: true }));
    }
    /**
     * Renders the trailing button to clear the input field if the trailingButton is set to true.
     * @returns void
     */
    renderTrailingButton(show = false) {
        const showBtn = show || (this.value && this.trailingButton);
        if (!showBtn) {
            return nothing;
        }
        return html `
      <mdc-button
        part="trailing-button"
        class="own-focus-ring ${!showBtn ? 'hidden' : ''}"
        prefix-icon="${DEFAULTS.CLEAR_BUTTON_ICON}"
        variant="${DEFAULTS.CLEAR_BUTTON_VARIANT}"
        size="${DEFAULTS.CLEAR_BUTTON_SIZE}"
        aria-label="${this.clearAriaLabel}"
        @click=${this.clearInputText}
        ?disabled=${this.disabled || this.readonly || !showBtn}
      ></mdc-button>
    `;
    }
    renderInputElement(type, hidePlaceholder = false) {
        var _a, _b;
        const placeholderText = hidePlaceholder ? '' : this.placeholder;
        return html `<input
      aria-label="${(_a = this.dataAriaLabel) !== null && _a !== void 0 ? _a : ''}"
      part="input-text"
      id="${this.inputId}"
      name="${this.name}"
      .value="${live(this.value)}"
      ?disabled="${this.disabled}"
      ?readonly="${this.readonly}"
      ?required="${this.required}"
      type="${type}"
      aria-describedby="${ifDefined(this.helpText ? FORMFIELD_DEFAULTS.HELPER_TEXT_ID : ((_b = this.dataAriaDescribedby) !== null && _b !== void 0 ? _b : ''))}"
      aria-invalid="${this.helpTextType === 'error' ? 'true' : 'false'}"
      placeholder=${ifDefined(placeholderText)}
      minlength=${ifDefined(this.minlength)}
      maxlength=${ifDefined(this.maxlength)}
      autocapitalize=${this.autocapitalize}
      autocomplete=${this.autocomplete}
      dirname=${ifDefined(this.dirname)}
      pattern=${ifDefined(this.pattern)}
      list=${ifDefined(this.list)}
      size=${ifDefined(this.size)}
      @input=${this.onInput}
      @change=${this.onChange}
      @keydown=${this.handleKeyDown}
    />`;
    }
    renderInputFooter() {
        if (!this.helpText && !this.maxCharacterLimit) {
            return nothing;
        }
        return html ` <div part="input-footer">${this.renderHelperText()} ${this.renderCharacterCounter()}</div> `;
    }
    render() {
        return html `
      ${this.renderLabel()}
      <div class="mdc-focus-ring" part="input-container">
        <slot name="input-leading-icon">${this.renderLeadingIcon()}</slot>
        <div part="input-section">
          <slot name="input-prefix-text">${this.renderPrefixText()}</slot>
          <slot name="input">${this.renderInputElement(DEFAULTS.INPUT_TYPE)}</slot>
        </div>
        <slot name="trailing-button">${this.renderTrailingButton()}</slot>
      </div>
      <mdc-screenreaderannouncer
        identity="${this.inputId}"
        announcement="${ifDefined(this.characterLimitAriaLiveAnnouncer)}"
        data-aria-live="polite"
        delay="500"
      ></mdc-screenreaderannouncer>
      ${this.renderInputFooter()}
    `;
    }
}
Input.styles = [...FormfieldWrapper.styles, ...styles];
Input.shadowRootOptions = { ...FormfieldWrapper.shadowRootOptions, delegatesFocus: true };
__decorate([
    property({ type: String }),
    __metadata("design:type", Object)
], Input.prototype, "placeholder", void 0);
__decorate([
    property({ type: String, attribute: 'prefix-text' }),
    __metadata("design:type", String)
], Input.prototype, "prefixText", void 0);
__decorate([
    property({ type: String, attribute: 'leading-icon' }),
    __metadata("design:type", String)
], Input.prototype, "leadingIcon", void 0);
__decorate([
    property({ type: Boolean, attribute: 'trailing-button' }),
    __metadata("design:type", Object)
], Input.prototype, "trailingButton", void 0);
__decorate([
    property({ type: Number }),
    __metadata("design:type", Number)
], Input.prototype, "maxlength", void 0);
__decorate([
    property({ type: Number }),
    __metadata("design:type", Number)
], Input.prototype, "minlength", void 0);
__decorate([
    property({ type: String }),
    __metadata("design:type", String)
], Input.prototype, "autocapitalize", void 0);
__decorate([
    property({ type: String }),
    __metadata("design:type", String)
], Input.prototype, "autocomplete", void 0);
__decorate([
    property({ type: String }),
    __metadata("design:type", String)
], Input.prototype, "dirname", void 0);
__decorate([
    property({ type: String }),
    __metadata("design:type", String)
], Input.prototype, "pattern", void 0);
__decorate([
    property({ type: String }),
    __metadata("design:type", String)
], Input.prototype, "list", void 0);
__decorate([
    property({ type: Number }),
    __metadata("design:type", Object)
], Input.prototype, "size", void 0);
__decorate([
    property({ type: String, attribute: 'clear-aria-label' }),
    __metadata("design:type", Object)
], Input.prototype, "clearAriaLabel", void 0);
__decorate([
    property({ type: String, reflect: true, attribute: 'data-aria-describedby' }),
    __metadata("design:type", Object)
], Input.prototype, "dataAriaDescribedby", void 0);
export default Input;
