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;
};
import { html, LitElement, nothing } from 'lit';
import { property, state } from 'lit/decorators.js';
import { activateI18n } from '../i18n.js';
import { messages as daMessages } from './locales/da/messages.mjs';
import { messages as enMessages } from './locales/en/messages.mjs';
import { messages as fiMessages } from './locales/fi/messages.mjs';
import { messages as nbMessages } from './locales/nb/messages.mjs';
import { messages as svMessages } from './locales/sv/messages.mjs';
import { reset } from '../styles.js';
import { wSliderStyles } from './styles/w-slider.styles.js';
// Inspo:
//   https://css-tricks.com/multi-thumb-sliders-particular-two-thumb-case/
//   https://css-tricks.com/multi-thumb-sliders-general-case/
//   https://css-tricks.com/lets-make-a-multi-thumb-slider-that-calculates-the-width-between-thumbs/
/**
 * Parent component for sliders (both single and range sliders). Used in combination with a `<w-slider-thumb>`.
 *
 * [See Storybook for usage examples](https://warp-ds.github.io/elements/?path=/docs/forms-slider-and-range-slider--docs)
 *
 * @slot - For single sliders place a `<w-slider-thumb>` in the default slot.
 * @slot label - Label for the slider or range slider as a whole.
 * @slot description - Optional description between the label and slider.
 * @slot from - Range sliders need to place a `<w-slider-thumb>` in the from and to slots.
 * @slot to - Range sliders need to place a `<w-slider-thumb>` in the from and to slots.
 */
class WarpSlider extends LitElement {
    static { this.shadowRootOptions = {
        ...LitElement.shadowRootOptions,
        delegatesFocus: true,
    }; }
    static { this.styles = [reset, wSliderStyles]; }
    constructor() {
        super();
        this.disabled = false;
        this.allowValuesOutsideRange = false;
        this.error = '';
        this.helpText = '';
        this.invalid = false;
        /** Ensures a child slider thumb has a value before allowing the containing form to submit. */
        this.required = false;
        /** Suffix used in text input fields and for the min and max values of the slider. */
        this.suffix = '';
        this._invalidMessage = '';
        this._hasInternalError = false;
        activateI18n(enMessages, nbMessages, fiMessages, daMessages, svMessages);
    }
    #syncSliderThumbs() {
        const sliderThumbs = this.querySelectorAll('w-slider-thumb');
        let usedNamedSlots = false;
        for (const thumb of sliderThumbs.values()) {
            // Set attributes that should be in sync between slider thumbs
            thumb.min = this.edgeMin;
            thumb.max = this.edgeMax;
            thumb.step = this.step;
            thumb.suffix = this.suffix;
            thumb.required = this.required;
            thumb.formatter = this.formatter;
            thumb.allowValuesOutsideRange = this.allowValuesOutsideRange;
            if (!thumb.ariaLabel) {
                if (!thumb.slot) {
                    thumb.ariaLabel = this.label;
                }
                if (thumb.slot === 'from') {
                    thumb.ariaLabel = `${this.label} min`;
                }
                if (thumb.slot === 'to') {
                    thumb.ariaLabel = `${this.label} max`;
                }
            }
            if (thumb.slot === 'from' || thumb.slot === 'to') {
                usedNamedSlots = true;
            }
            thumb.disabled = this.disabled;
            thumb.invalid = this.invalid || this._hasInternalError;
            this.#updateActiveTrack(thumb);
        }
        // Missing a CSS-only way to detect if something is slotted in the named slots
        const fieldset = this.shadowRoot.querySelector('fieldset');
        if (usedNamedSlots) {
            fieldset.style.setProperty('--active-range-inline-start-padding', 'var(--w-slider-thumb-size)');
            fieldset.style.setProperty('--active-range-inline-end-padding', 'var(--w-slider-thumb-size)');
        }
        else {
            fieldset.style.setProperty('--active-range-border-radius', '4px');
        }
    }
    get edgeMin() {
        return this.allowValuesOutsideRange ? (Number(this.min) - 1).toString() : this.min;
    }
    get edgeMax() {
        return this.allowValuesOutsideRange ? (Number(this.max) + 1).toString() : this.max;
    }
    async connectedCallback() {
        super.connectedCallback();
        await this.updateComplete;
        if (this.step) {
            this.style.setProperty('--step', String(this.step));
        }
        this.style.setProperty('--min', this.edgeMin);
        this.style.setProperty('--max', this.max);
        if (this.markers) {
            this.style.setProperty('--markers', String(this.markers));
        }
        if (this.allowValuesOutsideRange) {
            this.style.setProperty('--over-under-offset', '1');
        }
        this.#syncSliderThumbs();
    }
    updated(changedProperties) {
        if (changedProperties.has('disabled') ||
            changedProperties.has('invalid') ||
            changedProperties.has('required') ||
            changedProperties.has('min') ||
            changedProperties.has('step') ||
            changedProperties.has('max') ||
            changedProperties.has('suffix') ||
            changedProperties.has('formatter') ||
            changedProperties.has('_invalidMessage') ||
            changedProperties.has('_hasInternalError')) {
            this.#syncSliderThumbs();
        }
    }
    #onInput(e) {
        const input = e.target;
        this.#updateActiveTrack(input);
        const isRangeSlider = input.slot;
        if (isRangeSlider) {
            this.#doValidation();
        }
    }
    #doValidation() {
        // In a range slider changing the value in one input can change the validity
        // of the second input. Specifically, what was a value outside the mininum or
        // maximum can become inside those limits when the limits change, by changing
        // the from or to values. Check to see if a field is invalid, but should be
        // valid based on those rules.
        let from;
        let to;
        const sliderThumbs = this.querySelectorAll('w-slider-thumb');
        for (const thumb of sliderThumbs.values()) {
            if (thumb.slot === 'from')
                from = thumb;
            if (thumb.slot === 'to')
                to = thumb;
        }
        if (!from || !to) {
            // Not a range slider, nothing to do here.
            return;
        }
        if (!from.invalid && !to.invalid) {
            // Both are valid, nothing to do here
            return;
        }
    }
    #onSliderValidity(e) {
        e.stopPropagation();
        const didHaveInternalError = this._hasInternalError || this.invalid;
        const triggeredThumb = e.target;
        this._hasInternalError = Boolean(e.detail.invalid) || this.invalid;
        this._invalidMessage = e.detail.invalid;
        if (didHaveInternalError === true && this._hasInternalError === false) {
            const sliderThumbs = this.querySelectorAll('w-slider-thumb');
            for (const thumb of sliderThumbs.values()) {
                if (thumb !== triggeredThumb) {
                    thumb.updateFieldAfterValidation();
                }
            }
        }
    }
    #getEdgeValue(boundary, input) {
        if (input.value === undefined || input.value === null) {
            input.value = this.allowValuesOutsideRange ? '' : boundary;
        }
        // Use boundary for CSS positioning when value is empty
        return input.value === '' ? boundary : input.value;
    }
    /**
     * We use CSS variables to fill the active track with a background color.
     */
    #updateActiveTrack(input) {
        const slotName = input.slot;
        if (!slotName) {
            this.style.setProperty('--from', '0');
        }
        if (slotName === 'from') {
            this.style.setProperty('--from', this.#getEdgeValue(this.edgeMin, input));
        }
        if (!slotName || slotName === 'to') {
            this.style.setProperty('--to', this.#getEdgeValue(this.edgeMax, input));
        }
    }
    get componentHasError() {
        return this.invalid || this._hasInternalError;
    }
    get errorText() {
        if (!this.componentHasError) {
            return '';
        }
        return this.error || this._invalidMessage;
    }
    render() {
        return html `
      <fieldset
        id="fieldset"
        class="w-slider"
        @input="${this.#onInput}"
        @slidervalidity="${this.#onSliderValidity}"
        aria-invalid="${this.errorText ? 'true' : nothing}"
      >
        ${this.label
            ? html `<legend class="w-slider__label">
              <slot id="label" name="label">${this.label}</slot>
            </legend>`
            : nothing}
        <slot class="w-slider__description" name="description"></slot>
        ${this.markers ? html `<div class="w-slider__markers"></div>` : nothing}
        <div class="w-slider__range">
          <div class="w-slider__active-range"></div>
        </div>
        <slot
          class="w-slider__slider"
          @slotchange=${this.#syncSliderThumbs}
        ></slot>
        <slot
          class="w-slider__slider"
          name="from"
          @slotchange=${this.#syncSliderThumbs}
        ></slot>
        <slot
          class="w-slider__slider"
          name="to"
          @slotchange=${this.#syncSliderThumbs}
        ></slot>
        ${this.errorText
            ? html `<p class="w-slider__error" aria-describes="fieldset">
              ${this.errorText}
            </p>`
            : this.helpText
                ? html `<p class="w-slider__help-text" aria-describes="fieldset">
                ${this.helpText}
              </p>`
                : nothing}
      </fieldset>
    `;
    }
}
__decorate([
    property({ reflect: true })
], WarpSlider.prototype, "label", void 0);
__decorate([
    property({ type: Boolean, reflect: true })
], WarpSlider.prototype, "disabled", void 0);
__decorate([
    property({ type: Boolean, attribute: 'allow-values-outside-range' })
], WarpSlider.prototype, "allowValuesOutsideRange", void 0);
__decorate([
    property({ type: String, reflect: true })
], WarpSlider.prototype, "error", void 0);
__decorate([
    property({ type: String, reflect: true, attribute: 'help-text' })
], WarpSlider.prototype, "helpText", void 0);
__decorate([
    property({ type: Boolean, reflect: true })
], WarpSlider.prototype, "invalid", void 0);
__decorate([
    property({ type: Boolean, reflect: true })
], WarpSlider.prototype, "required", void 0);
__decorate([
    property({ reflect: true })
], WarpSlider.prototype, "min", void 0);
__decorate([
    property({ reflect: true })
], WarpSlider.prototype, "max", void 0);
__decorate([
    property({ type: Number, reflect: true })
], WarpSlider.prototype, "markers", void 0);
__decorate([
    property({ type: Number, reflect: true })
], WarpSlider.prototype, "step", void 0);
__decorate([
    property({ reflect: true })
], WarpSlider.prototype, "suffix", void 0);
__decorate([
    property({ attribute: false })
], WarpSlider.prototype, "formatter", void 0);
__decorate([
    state()
], WarpSlider.prototype, "_invalidMessage", void 0);
__decorate([
    state()
], WarpSlider.prototype, "_hasInternalError", void 0);
if (!customElements.get('w-slider')) {
    customElements.define('w-slider', WarpSlider);
}
export { WarpSlider };
