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 { ifDefined } from 'lit/directives/if-defined.js';
import { property, query } from 'lit/decorators.js';
import FormfieldWrapper from '../formfieldwrapper';
import { DEFAULTS as FORMFIELD_DEFAULTS, VALIDATION } from '../formfieldwrapper/formfieldwrapper.constants';
import { AUTO_CAPITALIZE } from '../input/input.constants';
import { DataAriaLabelMixin } from '../../utils/mixins/DataAriaLabelMixin';
import { FormInternalsMixin } from '../../utils/mixins/FormInternalsMixin';
import { AutoFocusOnMountMixin } from '../../utils/mixins/AutoFocusOnMountMixin';
import { ACTIONS, KeyToActionMixin } from '../../utils/mixins/KeyToActionMixin';
import { KeyDownHandledMixin } from '../../utils/mixins/KeyDownHandledMixin';
import { CharacterLimitMixin } from '../../utils/mixins/CharacterLimitMixin';
import { AUTO_COMPLETE, WRAP, DEFAULTS } from './textarea.constants';
import styles from './textarea.styles';
/**
 * mdc-textarea component, which is used to get the multi-line text input from the user.
 * It contains:
 * - label: It is the title of the textarea field.
 * - required: A boolean attribute depicting that the textarea field is required.
 * - Textarea: It is the multi-line text input field.
 * - helper-text: It is the text that provides additional information about the textarea field.
 * - max-character-limit: It is the text that shows the character count of the textarea field.
 * - Error, Warning, Success, Priority Help Text type: It is the text that provides additional information
 *   about the textarea field based on the validation state.
 * - limitexceeded: It is the event that is dispatched when the character limit exceeds or restored.
 *   This event exposes 3 properties:
 *   - currentCharacterCount - the current number of characters in the textarea field,
 *   - maxCharacterLimit - the maximum number of characters allowed in the textarea field,
 *   - value - the current value of the textarea field,
 *
 * **Note**: Consumers must set the help-text-type with 'error' and
 * help-text attribute with the error message using limitexceeded event.
 * The same help-text value will be used for the validation message to be displayed.
 *
 * ### Accessibility
 *
 * #### Resize
 *
 * Accessible text area resizing can be turned on with the `resizable`.
 * It is strongly recommended to set the `resize-button-aria-label` attribute as well to describe what it is and what are the shortcuts (up/down arrows) of the button.
 *
 * #### Best practices
 *
 * - Always provide a `label` for screen readers to identify the textarea's purpose
 * - Use `help-text` to provide additional context or instructions
 * - When using `max-character-limit`, consider providing `character-limit-announcement` for screen reader updates
 * - Use appropriate `help-text-type` (error, warning, success) to convey validation state
 * - Ensure `validation-message` is set for form validation errors
 *
 * @tagname mdc-textarea
 *
 * @event input - (React: onInput) This event is dispatched when the value of the textarea field changes (every press).
 * @event change - (React: onChange) This event is dispatched when the value of the textarea field changes (on blur).
 * @event focus - (React: onFocus) This event is dispatched when the textarea receives focus.
 * @event blur - (React: onBlur) This event is dispatched when the textarea loses focus.
 * @event limitexceeded - (React: onLimitExceeded) This event is dispatched once when the character limit
 * exceeds or restored.
 *
 * @dependency mdc-button
 * @dependency mdc-icon
 * @dependency mdc-text
 * @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.
 *
 * @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 textarea - The textarea element.
 * @csspart textarea-container - The container element that wraps the textarea and resize button.
 * @csspart textarea-footer - The footer element that contains the character counter.
 * @csspart character-counter - The character counter element.
 * @csspart resize-button - The resize button element (shown when `resizable` is true).
 * @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.
 *
 * @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-textarea-text-color - Text color for the textarea field
 * @cssproperty --mdc-textarea-background-color - Background color for the textarea field
 * @cssproperty --mdc-textarea-border-color - Border color for the textarea field
 * @cssproperty --mdc-textarea-text-secondary-normal - Text color for the character counter
 * @cssproperty --mdc-textarea-text-font-size - Font size for the textarea field
 * @cssproperty --mdc-textarea-text-line-height - Line height for the textarea field
 * @cssproperty --mdc-textarea-container-background-color - Background color for the textarea container
 */
class Textarea extends CharacterLimitMixin(KeyDownHandledMixin(KeyToActionMixin(AutoFocusOnMountMixin(FormInternalsMixin(DataAriaLabelMixin(FormfieldWrapper)))))) {
    constructor() {
        super(...arguments);
        /**
         * The rows attribute specifies the visible number of lines in a text area.
         * @default 5
         */
        this.rows = DEFAULTS.ROWS;
        /**
         * The wrap attribute specifies how the text in a text area is to be wrapped when submitted in a form.
         * @default 'soft'
         */
        this.wrap = WRAP.SOFT;
        /**
         * The autocapitalize attribute of the textarea field.
         * @default 'off'
         */
        this.autocapitalize = AUTO_CAPITALIZE.OFF;
        /**
         * The autocomplete attribute of the textarea field.
         * @default 'off'
         */
        this.autocomplete = AUTO_COMPLETE.OFF;
        /**
         * Controls whether the textarea is resizable via the resize button.
         * When set to false, the resize button will be hidden.
         * @default false
         */
        this.resizable = false;
        /** @internal */
        this.resizeStartY = 0;
        /** @internal */
        this.resizeStartRows = 0;
        /**
         * Handles the start of pointer-based resizing.
         * @param event - The pointer event.
         */
        this.handlePointerDown = (event) => {
            if (this.readonly) {
                return;
            }
            const resizeButton = event.currentTarget;
            if (!resizeButton)
                return;
            event.preventDefault();
            this.resizeStartY = event.clientY;
            this.resizeStartRows = this.rows || DEFAULTS.ROWS;
            resizeButton.setPointerCapture(event.pointerId);
            resizeButton.addEventListener('pointermove', this.handlePointerMove);
            resizeButton.addEventListener('pointerup', this.handlePointerUp);
            resizeButton.addEventListener('lostpointercapture', this.handlePointerUp);
        };
        /**
         * Handles pointer movement during resizing.
         * @param event - The pointer event.
         */
        this.handlePointerMove = (event) => {
            if (!this.textarea)
                return;
            const deltaY = event.clientY - this.resizeStartY;
            const lineHeight = parseFloat(window.getComputedStyle(this.textarea).lineHeight);
            const rowsChange = Math.round(deltaY / lineHeight);
            this.rows = Math.max(1, this.resizeStartRows + rowsChange);
        };
        /**
         * Handles the end of pointer-based resizing.
         * @param event - The pointer event.
         */
        this.handlePointerUp = (event) => {
            const resizeButton = event.currentTarget;
            if (!resizeButton)
                return;
            if (event.type === 'pointerup' && resizeButton.hasPointerCapture(event.pointerId)) {
                resizeButton.releasePointerCapture(event.pointerId);
            }
            resizeButton.removeEventListener('pointermove', this.handlePointerMove);
            resizeButton.removeEventListener('pointerup', this.handlePointerUp);
            resizeButton.removeEventListener('lostpointercapture', this.handlePointerUp);
        };
    }
    get textarea() {
        return this.inputElement;
    }
    connectedCallback() {
        var _a;
        super.connectedCallback();
        // Set the default value to the textarea field if the value is set through the text content directly
        this.value = ((_a = this.textContent) === null || _a === void 0 ? void 0 : _a.trim()) || this.value;
        this.updateComplete
            .then(() => {
            if (this.textarea) {
                this.textarea.checkValidity();
                this.setTextareaValidity();
                this.internals.setFormValue(this.textarea.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);
    }
    setTextareaValidity() {
        if (this.required && this.validationMessage && this.value === '') {
            this.textarea.setCustomValidity(this.validationMessage);
        }
        else if (this.maxCharacterLimit &&
            this.value.length > this.maxCharacterLimit &&
            this.helpTextType === VALIDATION.ERROR &&
            this.helpText) {
            // Set custom validity if the character limit is exceeded to stop form submission
            // helptext and helptexttype will be set by the consumers.
            this.textarea.setCustomValidity(this.helpText);
        }
        else {
            this.textarea.setCustomValidity('');
        }
        this.setValidity();
    }
    /** @internal */
    formResetCallback() {
        this.value = '';
        this.requestUpdate();
    }
    /** @internal */
    formStateRestoreCallback(state) {
        this.value = state;
    }
    /**
     * Handles the value change of the textarea field.
     * Sets the form value and updates the validity of the textarea field.
     * @returns void
     */
    handleValueChange() {
        this.updateComplete
            .then(() => {
            this.setTextareaValidity();
        })
            .catch(error => {
            if (this.onerror) {
                this.onerror(error);
            }
        });
    }
    updated(changedProperties) {
        super.updated(changedProperties);
        if (changedProperties.has('value')) {
            this.handleValueChange();
            this.handleCharacterOverflowStateChange();
        }
        // When helpText gets changed, we need to re-announce the max length warning
        if (changedProperties.has('helpText')) {
            this.announceCharacterLimitWarning();
        }
    }
    /**
     * Updates the value of the textarea field.
     * Sets the form value.
     * @returns void
     */
    updateValue() {
        this.value = this.textarea.value;
        this.internals.setFormValue(this.textarea.value);
        this.announceCharacterLimitWarning();
    }
    /**
     * Handles the change event of the textarea field.
     * Updates the value and sets the validity of the textarea 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.
     */
    onChange(event) {
        this.updateValue();
        const EventConstructor = event.constructor;
        this.dispatchEvent(new EventConstructor(event.type, event));
    }
    renderTextareaFooter() {
        if (!this.helpText && !this.maxCharacterLimit) {
            return nothing;
        }
        return html ` <div part="textarea-footer">${this.renderHelperText()} ${this.renderCharacterCounter()}</div> `;
    }
    /**
     * Handles the resize button keydown event for keyboard-based resizing.
     * @param event - The keyboard event.
     */
    handleResizeKeyDown(event) {
        if (this.readonly) {
            return;
        }
        const currentRows = this.rows || DEFAULTS.ROWS;
        let newRows;
        const action = this.getActionForKeyEvent(event);
        if (action === ACTIONS.UP) {
            newRows = Math.max(1, currentRows - 1);
            if (newRows !== currentRows) {
                this.keyDownEventHandled();
            }
        }
        else if (action === ACTIONS.DOWN) {
            newRows = currentRows + 1;
            this.keyDownEventHandled();
        }
        if (newRows !== undefined) {
            event.preventDefault();
            event.stopPropagation();
            this.rows = newRows;
        }
    }
    render() {
        var _a, _b;
        return html `
      ${this.renderLabel()}
      <div class="mdc-focus-ring" part="textarea-container">
        <textarea
          aria-label="${(_a = this.dataAriaLabel) !== null && _a !== void 0 ? _a : ''}"
          part="textarea"
          id="${this.inputId}"
          name="${this.name}"
          .value="${this.value}"
          ?disabled="${this.disabled}"
          ?readonly="${this.readonly}"
          ?required="${this.required}"
          placeholder=${ifDefined(this.placeholder)}
          rows=${ifDefined(this.rows)}
          cols=${ifDefined(this.cols)}
          wrap=${ifDefined(this.wrap)}
          autocapitalize=${this.autocapitalize}
          autocomplete=${this.autocomplete}
          minlength=${ifDefined(this.minlength)}
          maxlength=${ifDefined(this.maxlength)}
          dirname=${ifDefined(this.dirname)}
          @input=${this.updateValue}
          @change=${this.onChange}
          aria-describedby="${ifDefined(this.helpText ? FORMFIELD_DEFAULTS.HELPER_TEXT_ID : '')}"
          aria-invalid="${this.helpTextType === 'error' ? 'true' : 'false'}"
        ></textarea>
        <mdc-screenreaderannouncer
          identity="${this.inputId}"
          announcement="${ifDefined(this.characterLimitAriaLiveAnnouncer)}"
          data-aria-live="polite"
          delay="500"
        ></mdc-screenreaderannouncer>
        ${this.resizable
            ? html `
              <mdc-button
                part="resize-button"
                class="own-focus-ring"
                variant="tertiary"
                size="24"
                prefix-icon="resize-corner-regular"
                aria-label=${(_b = this.resizeButtonAriaLabel) !== null && _b !== void 0 ? _b : ''}
                ?disabled="${this.disabled || this.readonly}"
                @keydown=${this.handleResizeKeyDown}
                @pointerdown=${this.handlePointerDown}
              ></mdc-button>
            `
            : nothing}
      </div>
      ${this.renderTextareaFooter()}
    `;
    }
}
Textarea.styles = [...FormfieldWrapper.styles, ...styles];
__decorate([
    property({ type: String }),
    __metadata("design:type", String)
], Textarea.prototype, "placeholder", void 0);
__decorate([
    property({ type: Number, reflect: true }),
    __metadata("design:type", Number)
], Textarea.prototype, "rows", void 0);
__decorate([
    property({ type: Number }),
    __metadata("design:type", Number)
], Textarea.prototype, "cols", void 0);
__decorate([
    property({ type: String }),
    __metadata("design:type", String)
], Textarea.prototype, "wrap", void 0);
__decorate([
    property({ type: String }),
    __metadata("design:type", String)
], Textarea.prototype, "autocapitalize", void 0);
__decorate([
    property({ type: String }),
    __metadata("design:type", String)
], Textarea.prototype, "autocomplete", void 0);
__decorate([
    property({ type: String }),
    __metadata("design:type", String)
], Textarea.prototype, "dirname", void 0);
__decorate([
    property({ type: Number }),
    __metadata("design:type", Number)
], Textarea.prototype, "maxlength", void 0);
__decorate([
    property({ type: Number }),
    __metadata("design:type", Number)
], Textarea.prototype, "minlength", void 0);
__decorate([
    property({ type: Boolean, reflect: true }),
    __metadata("design:type", Boolean)
], Textarea.prototype, "resizable", void 0);
__decorate([
    property({ type: String, attribute: 'resize-button-aria-label' }),
    __metadata("design:type", String)
], Textarea.prototype, "resizeButtonAriaLabel", void 0);
__decorate([
    query('textarea'),
    __metadata("design:type", HTMLTextAreaElement)
], Textarea.prototype, "inputElement", void 0);
export default Textarea;
