/**
 * Copyright Aquera Inc 2023
 *
 * This source code is licensed under the BSD-3-Clause license found in the
 * LICENSE file in the root directory of this source tree.
 */
import { __decorate } from "tslib";
import { html } from 'lit';
import { customElement, property } from 'lit/decorators.js';
import { styles } from './nile-textarea.css';
import { classMap } from 'lit/directives/class-map.js';
import { query, state } from 'lit/decorators.js';
import { defaultValue } from '../internal/default-value';
import { HasSlotController } from '../internal/slot';
import { ifDefined } from 'lit/directives/if-defined.js';
import { live } from 'lit/directives/live.js';
import { watch } from '../internal/watch';
import NileElement from '../internal/nile-element';
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
/**
 * @summary Textareas collect data from the user and allow multiple lines of text.
 * @tag nile-text-area

 *
 * @slot label - The textarea's label. Alternatively, you can use the `label` attribute.
 * @slot help-text - Text that describes how to use the input. Alternatively, you can use the `help-text` attribute.
 *
 * @event nile-blur - Emitted when the control loses focus.
 * @event nile-change - Emitted when an alteration to the control's value is committed by the user.
 * @event nile-focus - Emitted when the control gains focus.
 * @event nile-input - Emitted when the control receives input.
 *
 * @csspart form-control - The form control that wraps the label, input, and help text.
 * @csspart form-control-label - The label's wrapper.
 * @csspart form-control-input - The input's wrapper.
 * @csspart form-control-help-text - The help text's wrapper.
 * @csspart base - The component's base wrapper.
 * @csspart textarea - The internal `<textarea>` control.
 */
let NileTextarea = class NileTextarea extends NileElement {
    constructor() {
        super(...arguments);
        this.hasSlotController = new HasSlotController(this, 'help-text', 'label');
        this.hasFocus = false;
        this.title = ''; // make reactive to pass through
        /** The name of the textarea, submitted as a name/value pair with form data. */
        this.name = '';
        /** The current value of the textarea, submitted as a name/value pair with form data. */
        this.value = '';
        /** The textarea's size. */
        this.size = 'medium';
        /** Draws a filled textarea. */
        this.filled = false;
        /** The textarea's label. If you need to display HTML, use the `label` slot instead. */
        this.label = '';
        this.helpText = '';
        this.errorMessage = '';
        /** Placeholder text to show as a hint when the input is empty. */
        this.placeholder = '';
        /** The number of rows to display by default. */
        this.rows = 4;
        /** Controls how the textarea can be resized. */
        this.resize = 'vertical';
        /** Disables the textarea. */
        this.disabled = false;
        /** Makes the textarea readonly. */
        this.readonly = false;
        /**
         * By default, form controls are associated with the nearest containing `<form>` element. This attribute allows you
         * to place the form control outside of a form and associate it with the form that has this `id`. The form must be in
         * the same document or shadow root for this to work.
         */
        this.form = '';
        /** Makes the textarea a required field. */
        this.required = false;
        /** Sets the input to a warning state, changing its visual appearance. */
        this.warning = false;
        /** Sets the input to an error state, changing its visual appearance. */
        this.error = false;
        /** Sets the input to a success state, changing its visual appearance. */
        this.success = false;
        /** Enables spell checking on the textarea. */
        this.spellcheck = true;
        /** The default value of the form control. Primarily used for resetting the form control. */
        this.defaultValue = '';
        this.fullHeight = false;
        this.checkNonPrintableChar = false;
        this.hasPrintableCharacters = false;
    }
    ;
    connectedCallback() {
        super.connectedCallback();
        this.resizeObserver = new ResizeObserver(() => this.setTextareaHeight());
        this.updateComplete.then(() => {
            this.setTextareaHeight();
            this.resizeObserver.observe(this.input);
        });
        if (this.fullHeight) {
            requestAnimationFrame(() => {
                let parentHeight = this.parentElement?.getBoundingClientRect().height;
                if (parentHeight) {
                    parentHeight = parentHeight - 65;
                    if (parentHeight < 12) {
                        parentHeight = 12;
                    }
                    this.shadowRoot?.querySelector('textarea')?.style.setProperty('height', `${parentHeight}px`);
                }
            });
        }
        this.emit('nile-init');
    }
    disconnectedCallback() {
        super.disconnectedCallback();
        if (this.input) {
            this.resizeObserver.unobserve(this.input);
        }
        this.emit('nile-destroy');
    }
    handleBlur() {
        this.hasFocus = false;
        this.emit('nile-blur', { value: this.value });
    }
    handleChange() {
        this.value = this.input.value;
        this.setTextareaHeight();
        this.emit('nile-change', { value: this.value });
    }
    handleFocus() {
        this.hasFocus = true;
        this.emit('nile-focus', { value: this.value });
    }
    handleInput() {
        this.value = this.input.value;
        this.emit('nile-input', { value: this.value });
    }
    setTextareaHeight() {
        if (this.resize === 'auto') {
            this.input.style.height = 'auto';
            this.input.style.height = `${this.input.scrollHeight}px`;
        }
        else {
            this.input.style.height = undefined;
        }
    }
    handleRowsChange() {
        this.setTextareaHeight();
    }
    async handleValueChange() {
        await this.updateComplete;
        this.setTextareaHeight();
        if (this.checkNonPrintableChar) {
            this.markNonPrintableCharacters();
        }
    }
    /** checks non printable characters in the value. */
    markNonPrintableCharacters() {
        let markedValue = '';
        this.hasPrintableCharacters = false;
        if (this.value) {
            for (let i = 0, n = this.value.length; i < n; i++) {
                const charCode = this.value.charCodeAt(i);
                if ((charCode > 255 && charCode !== 9109) || [129, 143, 144, 157, 160].includes(charCode)) {
                    markedValue += `<span class="input__srtiked-text">${this.value.charAt(i)}</span>`;
                    this.hasPrintableCharacters = true;
                }
                else {
                    markedValue += this.value.charAt(i);
                }
            }
        }
        this.markedValue = markedValue;
        this.emit('nile-value-marked', { value: this.markedValue, hasNonPrintableCharacters: this.hasPrintableCharacters });
    }
    /** Removes all non printable characters from the value. */
    removeAllNonPrintableCharacters() {
        let cleanedValue = '';
        if (this.value) {
            for (let i = 0, n = this.value.length; i < n; i++) {
                const charCode = this.value.charCodeAt(i);
                // Consider a character printable if it's not in the specified non-printable ranges
                if (!(charCode > 255 && charCode !== 9109) && ![129, 143, 144, 157, 160].includes(charCode)) {
                    cleanedValue += this.value.charAt(i);
                }
            }
        }
        this.value = cleanedValue;
        this.emit('nile-npchar-removed', { value: this.value });
    }
    /** Sets focus on the textarea. */
    focus(options) {
        this.input.focus(options);
    }
    /** Removes focus from the textarea. */
    blur() {
        this.input.blur();
    }
    /** Selects all the text in the textarea. */
    select() {
        this.input.select();
    }
    /** Gets or sets the textarea's scroll position. */
    scrollPosition(position) {
        if (position) {
            if (typeof position.top === 'number')
                this.input.scrollTop = position.top;
            if (typeof position.left === 'number')
                this.input.scrollLeft = position.left;
            return undefined;
        }
        return {
            top: this.input.scrollTop,
            left: this.input.scrollTop
        };
    }
    /** Sets the start and end positions of the text selection (0-based). */
    setSelectionRange(selectionStart, selectionEnd, selectionDirection = 'none') {
        this.input.setSelectionRange(selectionStart, selectionEnd, selectionDirection);
    }
    /** Replaces a range of text with a new string. */
    setRangeText(replacement, start, end, selectMode) {
        // @ts-expect-error - start, end, and selectMode are optional
        this.input.setRangeText(replacement, start, end, selectMode);
        if (this.value !== this.input.value) {
            this.value = this.input.value;
        }
        if (this.value !== this.input.value) {
            this.value = this.input.value;
            this.setTextareaHeight();
        }
    }
    render() {
        const hasLabelSlot = this.hasSlotController.test('label');
        const hasHelpTextSlot = this.hasSlotController.test('help-text');
        const hasLabel = this.label ? true : !!hasLabelSlot;
        const hasHelpText = this.helpText ? true : false;
        const hasErrorMessage = this.errorMessage ? true : false;
        return html `
      <div
        part="form-control"
        class=${classMap({
            'form-control': true,
            'form-control--medium': this.size === 'medium',
            'form-control--has-label': hasLabel,
            'form-control--has-help-text': hasHelpText,
        })}
      >
        <label
          part="form-control-label"
          class="form-control__label"
          for="input"
          aria-hidden=${hasLabel ? 'false' : 'true'}
        >
          <slot name="label">${this.label}</slot>
        </label>

        <nile-popup
          ?active=${this.hasPrintableCharacters}
          placement="bottom-start"
          distance="5"
          strategy="fixed"
        >
          <div part="form-control-input" class="form-control-input" slot="anchor">
            <div
              part="base"
              class=${classMap({
            textarea: true,
            'textarea--medium': this.size === 'medium',
            'textarea--warning': this.warning,
            'textarea--error': this.error,
            'textarea--success': this.success,
            'textarea--standard': !this.filled,
            'textarea--disabled': this.disabled,
            'textarea--focused': this.hasFocus,
            'textarea--empty': !this.value,
            'textarea--resize-none': this.resize === 'none',
            'textarea--resize-vertical': this.resize === 'vertical',
            'textarea--resize-auto': this.resize === 'auto',
        })}
            >
              <textarea
                part="textarea"
                id="input"
                class="textarea__control"
                title=${this
            .title /* An empty title prevents browser validation tooltips from appearing on hover */}
                name=${ifDefined(this.name)}
                .value=${live(this.value)}
                ?disabled=${this.disabled}
                ?readonly=${this.readonly}
                ?required=${this.required}
                placeholder=${ifDefined(this.placeholder)}
                rows=${ifDefined(this.rows)}
                minlength=${ifDefined(this.minlength)}
                maxlength=${ifDefined(this.maxlength)}
                autocapitalize=${ifDefined(this.autocapitalize)}
                autocorrect=${ifDefined(this.autocorrect)}
                ?autofocus=${this.autofocus}
                spellcheck=${ifDefined(this.spellcheck)}
                enterkeyhint=${ifDefined(this.enterkeyhint)}
                inputmode=${ifDefined(this.inputmode)}
                aria-describedby="help-text"
                @change=${this.handleChange}
                @input=${this.handleInput}
                @focus=${this.handleFocus}
                @blur=${this.handleBlur}
              ></textarea>
            </div>

            ${hasHelpText
            ? html `
                <nile-form-help-text>${this.helpText}</nile-form-help-text>
              `
            : ``}
          ${hasErrorMessage
            ? html `
                <nile-form-error-message
                  >${this.errorMessage}</nile-form-error-message
                >
              `
            : ``}

          </div>
          <div class="input__non-printable">
            Non-printable character detected.
            <nile-badge
              variant="error"
              @click=${this.removeAllNonPrintableCharacters}
              class="input__remove-non-printable"
            >
              Remove All
            </nile-badge>

            <div class="input__srtiked-text-container">
              ${unsafeHTML(this.markedValue)}
            </div>
          </div>
        </nile-popup>
      </div>
    `;
    }
};
NileTextarea.styles = styles;
__decorate([
    query('.textarea__control')
], NileTextarea.prototype, "input", void 0);
__decorate([
    state()
], NileTextarea.prototype, "hasFocus", void 0);
__decorate([
    property()
], NileTextarea.prototype, "title", void 0);
__decorate([
    property()
], NileTextarea.prototype, "name", void 0);
__decorate([
    property()
], NileTextarea.prototype, "value", void 0);
__decorate([
    property({ reflect: true })
], NileTextarea.prototype, "size", void 0);
__decorate([
    property({ type: Boolean, reflect: true })
], NileTextarea.prototype, "filled", void 0);
__decorate([
    property()
], NileTextarea.prototype, "label", void 0);
__decorate([
    property({ attribute: 'help-text', reflect: true })
], NileTextarea.prototype, "helpText", void 0);
__decorate([
    property({ attribute: 'error-message', reflect: true })
], NileTextarea.prototype, "errorMessage", void 0);
__decorate([
    property()
], NileTextarea.prototype, "placeholder", void 0);
__decorate([
    property({ type: Number })
], NileTextarea.prototype, "rows", void 0);
__decorate([
    property()
], NileTextarea.prototype, "resize", void 0);
__decorate([
    property({ type: Boolean, reflect: true })
], NileTextarea.prototype, "disabled", void 0);
__decorate([
    property({ type: Boolean, reflect: true })
], NileTextarea.prototype, "readonly", void 0);
__decorate([
    property({ reflect: true })
], NileTextarea.prototype, "form", void 0);
__decorate([
    property({ type: Boolean, reflect: true })
], NileTextarea.prototype, "required", void 0);
__decorate([
    property({ type: Number })
], NileTextarea.prototype, "minlength", void 0);
__decorate([
    property({ type: Number })
], NileTextarea.prototype, "maxlength", void 0);
__decorate([
    property({ type: Boolean })
], NileTextarea.prototype, "warning", void 0);
__decorate([
    property({ type: Boolean })
], NileTextarea.prototype, "error", void 0);
__decorate([
    property({ type: Boolean })
], NileTextarea.prototype, "success", void 0);
__decorate([
    property()
], NileTextarea.prototype, "autocapitalize", void 0);
__decorate([
    property()
], NileTextarea.prototype, "autocorrect", void 0);
__decorate([
    property()
], NileTextarea.prototype, "autocomplete", void 0);
__decorate([
    property({ type: Boolean })
], NileTextarea.prototype, "autofocus", void 0);
__decorate([
    property()
], NileTextarea.prototype, "enterkeyhint", void 0);
__decorate([
    property({
        type: Boolean,
        converter: {
            // Allow "true|false" attribute values but keep the property boolean
            fromAttribute: value => (!value || value === 'false' ? false : true),
            toAttribute: value => (value ? 'true' : 'false')
        }
    })
], NileTextarea.prototype, "spellcheck", void 0);
__decorate([
    property()
], NileTextarea.prototype, "inputmode", void 0);
__decorate([
    defaultValue()
], NileTextarea.prototype, "defaultValue", void 0);
__decorate([
    property({ type: Boolean, reflect: true })
], NileTextarea.prototype, "fullHeight", void 0);
__decorate([
    property({ type: Boolean })
], NileTextarea.prototype, "checkNonPrintableChar", void 0);
__decorate([
    state()
], NileTextarea.prototype, "hasPrintableCharacters", void 0);
__decorate([
    state()
], NileTextarea.prototype, "markedValue", void 0);
__decorate([
    watch('rows', { waitUntilFirstUpdate: true })
], NileTextarea.prototype, "handleRowsChange", null);
__decorate([
    watch('value', { waitUntilFirstUpdate: true })
], NileTextarea.prototype, "handleValueChange", null);
NileTextarea = __decorate([
    customElement('nile-textarea')
], NileTextarea);
export { NileTextarea };
export default NileTextarea;
//# sourceMappingURL=nile-textarea.js.map