import { __decorate } from "tslib";
import { html } from 'lit';
import { customElement, property, state } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js';
import { map } from 'lit/directives/map.js';
import { createRef, ref } from 'lit/directives/ref.js';
import { styleMap } from 'lit/directives/style-map.js';
import { when } from 'lit/directives/when.js';
import { $ } from '@mdui/jq/$.js';
import '@mdui/jq/methods/css.js';
import { FormController, formResets } from '@mdui/shared/controllers/form.js';
import { defaultValue } from '@mdui/shared/decorators/default-value.js';
import { SliderBase } from '../slider/slider-base.js';
/**
 * @summary 范围滑块组件
 *
 * ```html
 * <mdui-range-slider></mdui-range-slider>
 * ```
 *
 * @event focus - 获得焦点时触发
 * @event blur - 失去焦点时触发
 * @event change - 值发生变更，且失去焦点时，将触发该事件
 * @event input - 值变更时触发
 * @event invalid - 表单字段验证未通过时触发
 *
 * @csspart track-inactive - 未激活状态的轨道
 * @csspart track-active - 已激活状态的轨道
 * @csspart handle - 操作杆
 * @csspart label - 提示文本
 * @csspart tickmark - 刻度标记
 */
let RangeSlider = class RangeSlider extends SliderBase {
    constructor() {
        super(...arguments);
        /**
         * 默认值。在重置表单时，将重置为该默认值。此属性只能通过 JavaScript 属性设置
         */
        this.defaultValue = [];
        /**
         * 当前操作的是哪一个 handle
         */
        this.currentHandle = 'start';
        this.rippleStartRef = createRef();
        this.rippleEndRef = createRef();
        this.handleStartRef = createRef();
        this.handleEndRef = createRef();
        this.formController = new FormController(this);
        this._value = [];
        this.getRippleIndex = () => {
            if (this.hoverHandle) {
                return this.hoverHandle === 'start' ? 0 : 1;
            }
            return this.currentHandle === 'start' ? 0 : 1;
        };
    }
    /**
     * 滑块的值，为数组格式，将于表单数据一起提交。
     *
     * **NOTE**：该属性无法通过 HTML 属性设置初始值，如果要修改该值，只能通过修改 JavaScript 属性值实现。
     */
    get value() {
        return this._value;
    }
    set value(_value) {
        const oldValue = [...this._value];
        this._value = [this.fixValue(_value[0]), this.fixValue(_value[1])];
        this.requestUpdate('value', oldValue);
        this.updateComplete.then(() => {
            this.updateStyle();
            // reset 引起的值变更，不执行验证；直接修改值引起的变更，需要进行验证
            const form = this.formController.getForm();
            if (form && formResets.get(form)?.has(this)) {
                this.invalid = false;
                formResets.get(form).delete(this);
            }
            else {
                this.invalid = !this.inputRef.value.checkValidity();
            }
        });
    }
    get rippleElement() {
        return [this.rippleStartRef.value, this.rippleEndRef.value];
    }
    connectedCallback() {
        super.connectedCallback();
        if (!this.value.length) {
            this.value = [this.min, this.max];
        }
        this.value[0] = this.fixValue(this.value[0]);
        this.value[1] = this.fixValue(this.value[1]);
        if (!this.defaultValue.length) {
            this.defaultValue = [...this.value];
        }
    }
    firstUpdated(changedProperties) {
        super.firstUpdated(changedProperties);
        // 在轨道上点击时，计算出点击位置在 <input type="range"> 元素上的值
        // 若该值在 this.value 的两个值中间位置的左侧，则表示操作的是左侧的值，否则操作的是右侧的值
        const getCurrentHandle = (event) => {
            const $this = $(this);
            // 计算出鼠标悬浮位置的值，<mdui-range-slider> 元素的左右两侧有内边距，计算时要去除内边距
            const paddingLeft = parseFloat($this.css('padding-left'));
            const paddingRight = parseFloat($this.css('padding-right'));
            const percent = (event.offsetX - paddingLeft) /
                (this.clientWidth - paddingLeft - paddingRight);
            const pointerValue = (this.max - this.min) * percent + this.min;
            // 计算 this.value 两个值中间位置的值
            const middleValue = (this.value[1] - this.value[0]) / 2 + this.value[0];
            return pointerValue > middleValue ? 'end' : 'start';
        };
        const onTouchStart = () => {
            if (!this.disabled) {
                this.labelVisible = true;
            }
        };
        const onTouchEnd = () => {
            if (!this.disabled) {
                this.labelVisible = false;
            }
        };
        this.addEventListener('touchstart', onTouchStart);
        this.addEventListener('mousedown', onTouchStart);
        this.addEventListener('touchend', onTouchEnd);
        this.addEventListener('mouseup', onTouchEnd);
        // 按下鼠标时，计算当前操作的是起始值还是结束值
        this.addEventListener('pointerdown', (event) => {
            this.currentHandle = getCurrentHandle(event);
        });
        // 移动鼠标时，修改 mdui-ripple 的 hover 状态
        this.addEventListener('pointermove', (event) => {
            const currentHandle = getCurrentHandle(event);
            if (this.hoverHandle !== currentHandle) {
                this.endHover(event);
                this.hoverHandle = currentHandle;
                this.startHover(event);
            }
        });
        this.updateStyle();
    }
    /**
     * <input /> 用于提供拖拽操作
     * <input class="invalid" /> 用于提供 html5 自带的表单错误提示
     */
    render() {
        return html `<label class="${classMap({ invalid: this.invalid })}"><input ${ref(this.inputRef)} type="range" step="${this.step}" min="${this.min}" max="${this.max}" ?disabled="${this.disabled}" @input="${this.onInput}" @change="${this.onChange}"><div part="track-inactive" class="track-inactive"></div><div ${ref(this.trackActiveRef)} part="track-active" class="track-active"></div><div ${ref(this.handleStartRef)} part="handle" class="handle start" style="${styleMap({
            'z-index': this.currentHandle === 'start' ? '2' : '1',
        })}"><div class="elevation"></div><mdui-ripple ${ref(this.rippleStartRef)} .noRipple="${this.noRipple}"></mdui-ripple>${this.renderLabel(this.value[0])}</div><div ${ref(this.handleEndRef)} part="handle" class="handle end" style="${styleMap({
            'z-index': this.currentHandle === 'end' ? '2' : '1',
        })}"><div class="elevation"></div><mdui-ripple ${ref(this.rippleEndRef)} .noRipple="${this.noRipple}"></mdui-ripple>${this.renderLabel(this.value[1])}</div>${when(this.tickmarks, () => map(this.getCandidateValues(), (value) => html `<div part="tickmark" class="tickmark ${classMap({
            active: value > this.value[0] && value < this.value[1],
        })}" style="${styleMap({
            left: `${((value - this.min) / this.max) * 100}%`,
            display: value === this.value[0] || value === this.value[1]
                ? 'none'
                : 'block',
        })}"></div>`))}</label>`;
    }
    updateStyle() {
        const getPercent = (value) => ((value - this.min) / (this.max - this.min)) * 100;
        const startPercent = getPercent(this.value[0]);
        const endPercent = getPercent(this.value[1]);
        this.trackActiveRef.value.style.width = `${endPercent - startPercent}%`;
        this.trackActiveRef.value.style.left = `${startPercent}%`;
        this.handleStartRef.value.style.left = `${startPercent}%`;
        this.handleEndRef.value.style.left = `${endPercent}%`;
    }
    onInput() {
        const isStart = this.currentHandle === 'start';
        const value = parseFloat(this.inputRef.value.value);
        const startValue = this.value[0];
        const endValue = this.value[1];
        const doInput = () => {
            this.updateStyle();
        };
        if (isStart) {
            if (value <= endValue) {
                this.value = [value, endValue];
                doInput();
            }
            else if (startValue !== endValue) {
                this.value = [endValue, endValue];
                doInput();
            }
        }
        else {
            if (value >= startValue) {
                this.value = [startValue, value];
                doInput();
            }
            else if (startValue !== endValue) {
                this.value = [startValue, startValue];
                doInput();
            }
        }
    }
};
RangeSlider.styles = [SliderBase.styles];
__decorate([
    defaultValue()
], RangeSlider.prototype, "defaultValue", void 0);
__decorate([
    state()
], RangeSlider.prototype, "currentHandle", void 0);
__decorate([
    property({ type: Array, attribute: false })
], RangeSlider.prototype, "value", null);
RangeSlider = __decorate([
    customElement('mdui-range-slider')
], RangeSlider);
export { RangeSlider };
