import { __decorate } from "tslib";
import { property } from 'lit/decorators.js';
import { $ } from '@mdui/jq/$.js';
import '@mdui/jq/methods/index.js';
import { isArrayLike } from '@mdui/jq/shared/helper.js';
import { booleanConverter } from '@mdui/shared/helpers/decorator.js';
import './index.js';
/**
 * hover, pressed, dragged 三个属性用于添加到 rippleTarget 属性指定的元素上，供 CSS 选择题添加样式
 *
 * TODO: dragged 功能
 *
 * NOTE:
 * 不支持触控的屏幕上事件顺序为：pointerdown -> (8ms) -> mousedown -> pointerup -> (1ms) -> mouseup -> (1ms) -> click
 *
 * 支持触控的屏幕上事件顺序为：pointerdown -> (8ms) -> touchstart -> pointerup -> (1ms) -> touchend -> (5ms) -> mousedown -> mouseup -> click
 * pointermove 比较灵敏，有可能触发了 pointermove 但没有触发 touchmove
 *
 * 支持触摸笔的屏幕上事件顺序为：todo
 */
export const RippleMixin = (superclass) => {
    class Mixin extends superclass {
        constructor() {
            super(...arguments);
            /**
             * 是否禁用涟漪动画
             */
            this.noRipple = false;
            /**
             * 当前激活的是第几个 <mdui-ripple>。仅一个组件中有多个 <mdui-ripple> 时可以使用该属性
             * 若值为 undefined，则组件中所有 <mdui-ripple> 都激活
             */
            this.rippleIndex = undefined;
            /**
             * 获取当前激活的是第几个 <mdui-ripple>。仅一个组件中有多个 <mdui-ripple> 时可以使用该属性
             * 若值为 undefined，则组件中所有 <mdui-ripple> 都激活
             * 可在子类中手动指定该方法，指定需要激活的 ripple
             */
            this.getRippleIndex = () => this.rippleIndex;
        }
        /**
         * 子类要添加该属性，指向 <mdui-ripple> 元素
         * 如果一个组件中包含多个 <mdui-ripple> 元素，则这里可以是一个数组或 NodeList
         */
        get rippleElement() {
            throw new Error('Must implement rippleElement getter!');
        }
        /**
         * 子类要实现该属性，表示是否禁用 ripple
         * 如果一个组件中包含多个 <mdui-ripple> 元素，则这里可以是一个数组；也可以是单个值，同时控制多个 <mdui-ripple> 元素
         */
        get rippleDisabled() {
            throw new Error('Must implement rippleDisabled getter!');
        }
        /**
         * 当前 <mdui-ripple> 元素相对于哪个元素存在，即 hover、pressed、dragged 属性要添加到哪个元素上，默认为 :host
         * 如果需要修改该属性，则子类可以实现该属性
         * 如果一个组件中包含多个 <mdui-ripple> 元素，则这里可以是一个数组；也可以是单个值，同时控制多个 <mdui-ripple> 元素
         */
        get rippleTarget() {
            return this;
        }
        firstUpdated(changedProperties) {
            super.firstUpdated(changedProperties);
            const $rippleTarget = $(this.rippleTarget);
            // 监听到事件时，是在第几个 <mdui-ripple> 上触发的事件，记录到 this.rippleIndex 中
            const setRippleIndex = (event) => {
                if (isArrayLike(this.rippleTarget)) {
                    this.rippleIndex = $rippleTarget.index(event.target);
                }
            };
            const rippleTargetArr = isArrayLike(this.rippleTarget)
                ? this.rippleTarget
                : [this.rippleTarget];
            rippleTargetArr.forEach((rippleTarget) => {
                rippleTarget.addEventListener('pointerdown', (event) => {
                    setRippleIndex(event);
                    this.startPress(event);
                });
                rippleTarget.addEventListener('pointerenter', (event) => {
                    setRippleIndex(event);
                    this.startHover(event);
                });
                rippleTarget.addEventListener('pointerleave', (event) => {
                    setRippleIndex(event);
                    this.endHover(event);
                });
                rippleTarget.addEventListener('focus', (event) => {
                    setRippleIndex(event);
                    this.startFocus();
                });
                rippleTarget.addEventListener('blur', (event) => {
                    setRippleIndex(event);
                    this.endFocus();
                });
            });
        }
        /**
         * 若存在多个 <mdui-ripple>，但 rippleTarget 为同一个，则 hover 状态无法在多个 <mdui-ripple> 之间切换
         * 所以把 startHover 和 endHover 设置为 protected，供子类调用
         * 子类中，在 getRippleIndex() 的返回值变更前调用 endHover(event)，变更后调用 startHover(event)
         */
        startHover(event) {
            if (event.pointerType !== 'mouse' || this.isRippleDisabled()) {
                return;
            }
            this.getRippleTarget().setAttribute('hover', '');
            this.getRippleElement().startHover();
        }
        endHover(event) {
            if (event.pointerType !== 'mouse' || this.isRippleDisabled()) {
                return;
            }
            this.getRippleTarget().removeAttribute('hover');
            this.getRippleElement().endHover();
        }
        /**
         * 当前激活的 <mdui-ripple> 元素是否被禁用
         */
        isRippleDisabled() {
            const disabled = this.rippleDisabled;
            if (!Array.isArray(disabled)) {
                return disabled;
            }
            const rippleIndex = this.getRippleIndex();
            if (rippleIndex !== undefined) {
                return disabled[rippleIndex];
            }
            return disabled.length ? disabled[0] : false;
        }
        /**
         * 获取当前激活的 <mdui-ripple> 元素实例
         */
        getRippleElement() {
            const ripple = this.rippleElement;
            if (!isArrayLike(ripple)) {
                return ripple;
            }
            const rippleIndex = this.getRippleIndex();
            if (rippleIndex !== undefined) {
                return ripple[rippleIndex];
            }
            return ripple[0];
        }
        /**
         * 获取当前激活的 <mdui-ripple> 元素相对于哪个元素存在
         */
        getRippleTarget() {
            const target = this.rippleTarget;
            if (!isArrayLike(target)) {
                return target;
            }
            const rippleIndex = this.getRippleIndex();
            if (rippleIndex !== undefined) {
                return target[rippleIndex];
            }
            return target[0];
        }
        startFocus() {
            if (this.isRippleDisabled()) {
                return;
            }
            this.getRippleElement().startFocus();
        }
        endFocus() {
            if (this.isRippleDisabled()) {
                return;
            }
            this.getRippleElement().endFocus();
        }
        startPress(event) {
            // 为鼠标时操作，仅响应鼠标左键点击
            if (this.isRippleDisabled() || event.button) {
                return;
            }
            const target = this.getRippleTarget();
            target.setAttribute('pressed', '');
            // 手指触摸触发涟漪
            if (['touch', 'pen'].includes(event.pointerType)) {
                let hidden = false;
                // 手指触摸后，延迟一段时间触发涟漪，避免手指滑动时也触发涟漪
                let timer = setTimeout(() => {
                    timer = 0;
                    this.getRippleElement().startPress(event);
                }, 70);
                const hideRipple = () => {
                    // 如果手指没有移动，且涟漪动画还没有开始，则开始涟漪动画
                    if (timer) {
                        clearTimeout(timer);
                        timer = 0;
                        this.getRippleElement().startPress(event);
                    }
                    if (!hidden) {
                        hidden = true;
                        this.endPress();
                    }
                    target.removeEventListener('pointerup', hideRipple);
                    target.removeEventListener('pointercancel', hideRipple);
                };
                // 手指移动后，移除涟漪动画
                const touchMove = () => {
                    if (timer) {
                        clearTimeout(timer);
                        timer = 0;
                    }
                    target.removeEventListener('touchmove', touchMove);
                };
                // pointermove 事件过于灵敏，可能在未触发 touchmove 的情况下，触发了 pointermove 事件，导致正常的点击操作没有显示涟漪
                // 因此这里监听 touchmove 事件
                target.addEventListener('touchmove', touchMove);
                target.addEventListener('pointerup', hideRipple);
                target.addEventListener('pointercancel', hideRipple);
            }
            // 鼠标点击触发涟漪，点击后立即触发涟漪（仅鼠标左键能触发涟漪）
            if (event.pointerType === 'mouse' && event.button === 0) {
                const hideRipple = () => {
                    this.endPress();
                    target.removeEventListener('pointerup', hideRipple);
                    target.removeEventListener('pointercancel', hideRipple);
                    target.removeEventListener('pointerleave', hideRipple);
                };
                this.getRippleElement().startPress(event);
                target.addEventListener('pointerup', hideRipple);
                target.addEventListener('pointercancel', hideRipple);
                target.addEventListener('pointerleave', hideRipple);
            }
        }
        endPress() {
            if (this.isRippleDisabled()) {
                return;
            }
            this.getRippleTarget().removeAttribute('pressed');
            this.getRippleElement().endPress();
        }
        startDrag() {
            if (this.isRippleDisabled()) {
                return;
            }
            this.getRippleElement().startDrag();
        }
        endDrag() {
            if (this.isRippleDisabled()) {
                return;
            }
            this.getRippleElement().endDrag();
        }
    }
    __decorate([
        property({
            type: Boolean,
            reflect: true,
            converter: booleanConverter,
            attribute: 'no-ripple',
        })
    ], Mixin.prototype, "noRipple", void 0);
    return Mixin;
};
