/**
 * amis-ui v6.12.0-beta.0
 * Copyright 2018-2025 fex
 */

import { __extends, __decorate, __metadata, __assign } from 'tslib';
import range from 'lodash/range';
import keys from 'lodash/keys';
import isString from 'lodash/isString';
import difference from 'lodash/difference';
import React__default from 'react';
import TooltipWrapper from './TooltipWrapper.js';
import { Overlay, autobind, camel, stripNumber, safeAdd, safeSub, themeable, uncontrollable } from 'amis-core';
import { findDOMNode } from 'react-dom';
import { Icon } from './icons.js';

/**
 * @file Range
 * @description
 * @author fex
 */
// 小数或者整数
var MARKS_REG = /^[0-9]+(\.?[0-9]+)?%$/;
/**
 * 滑块值 -> position.left
 * @param value 滑块值
 * @param min 最小值
 * @param max 最大值
 * @returns position.left
 */
var valueToOffsetLeft = function (value, min, max) {
    // 最多保留两位小数，且尽量取整
    return Math.floor(((value - min) * 10000) / (max - min)) / 100 + '%';
};
/**
 * 滑块handle
 * 双滑块涉及两个handle，单独抽一个组件
 */
var HandleItem = /** @class */ (function (_super) {
    __extends(HandleItem, _super);
    function HandleItem(props) {
        var _this = _super.call(this, props) || this;
        _this.handleRef = React__default.createRef();
        _this.state = {
            isDrag: false,
            labelActive: false
        };
        return _this;
    }
    /**
     * mouseDown事件
     * 防止拖动过快，全局监听 mousemove、mouseup
     */
    HandleItem.prototype.onMouseDown = function () {
        this.setState({
            isDrag: true,
            labelActive: true
        });
        window.addEventListener('mousemove', this.onMouseMove);
        window.addEventListener('mouseup', this.onMouseUp);
    };
    /**
     * mouseMove事件
     * 触发公共onchange事件
     */
    HandleItem.prototype.onMouseMove = function (e) {
        var isDrag = this.state.isDrag;
        var _a = this.props.type, type = _a === void 0 ? 'min' : _a;
        if (!isDrag) {
            return;
        }
        this.props.onChange(e.pageX, type);
    };
    /**
     * mouseUp事件
     * 移除全局 mousemove、mouseup
     */
    HandleItem.prototype.onMouseUp = function () {
        this.setState({
            isDrag: false
        });
        var onAfterChange = this.props.onAfterChange;
        onAfterChange && onAfterChange();
        window.removeEventListener('mousemove', this.onMouseMove);
        window.removeEventListener('mouseup', this.onMouseUp);
    };
    /**
     * mouseEnter事件
     * 鼠标移入 -> 展示label
     */
    HandleItem.prototype.onMouseEnter = function () {
        this.setState({
            labelActive: true
        });
    };
    /**
     * mouseLeave事件
     * 鼠标移出 & !isDrag -> 隐藏label
     */
    HandleItem.prototype.onMouseLeave = function () {
        var isDrag = this.state.isDrag;
        if (isDrag) {
            return;
        }
        this.setState({
            labelActive: false
        });
    };
    HandleItem.prototype.onTouchStart = function () {
        this.setState({
            isDrag: true,
            labelActive: true
        });
    };
    HandleItem.prototype.onTouchMove = function (e) {
        var isDrag = this.state.isDrag;
        var _a = this.props.type, type = _a === void 0 ? 'min' : _a;
        if (!isDrag) {
            return;
        }
        this.props.onChange(e.touches[0].clientX, type);
    };
    HandleItem.prototype.onTouchEnd = function () {
        this.setState({
            labelActive: false
        });
    };
    HandleItem.prototype.render = function () {
        var _this = this;
        var _a = this.props, cx = _a.classnames, disabled = _a.disabled, value = _a.value, min = _a.min, max = _a.max, tooltipVisible = _a.tooltipVisible, tipFormatter = _a.tipFormatter, unit = _a.unit, _b = _a.tooltipPlacement, tooltipPlacement = _b === void 0 ? 'auto' : _b, mobileUI = _a.mobileUI;
        var _c = this.state, isDrag = _c.isDrag, labelActive = _c.labelActive;
        var style = {
            left: valueToOffsetLeft(value, min, max),
            zIndex: isDrag ? 2 : 1
        };
        return disabled ? (React__default.createElement("div", { className: cx('InputRange-handle'), style: style },
            React__default.createElement("div", { className: cx('InputRange-handle-icon') },
                React__default.createElement(Icon, { icon: "slider-handle", className: "icon" })))) : (React__default.createElement("div", { className: cx('InputRange-handle', {
                'is-mobile': mobileUI
            }), style: style, ref: this.handleRef },
            React__default.createElement("div", { className: cx(isDrag ? 'InputRange-handle-drage' : 'InputRange-handle-icon'), onMouseDown: this.onMouseDown, onMouseEnter: this.onMouseEnter, onMouseLeave: this.onMouseLeave, onTouchStart: this.onTouchStart, onTouchMove: this.onTouchMove, onTouchEnd: this.onTouchEnd },
                React__default.createElement(Icon, { icon: "slider-handle", className: "icon" })),
            React__default.createElement(Overlay, { placement: tooltipPlacement, target: function () { return findDOMNode(_this); }, container: function () { return findDOMNode(_this); }, rootClose: false, show: true },
                React__default.createElement(Label, { show: labelActive, classPrefix: this.props.classPrefix, classnames: cx, value: value, tooltipVisible: tooltipVisible, tipFormatter: tipFormatter, unit: unit, placement: tooltipPlacement }))));
    };
    __decorate([
        autobind,
        __metadata("design:type", Function),
        __metadata("design:paramtypes", []),
        __metadata("design:returntype", void 0)
    ], HandleItem.prototype, "onMouseDown", null);
    __decorate([
        autobind,
        __metadata("design:type", Function),
        __metadata("design:paramtypes", [MouseEvent]),
        __metadata("design:returntype", void 0)
    ], HandleItem.prototype, "onMouseMove", null);
    __decorate([
        autobind,
        __metadata("design:type", Function),
        __metadata("design:paramtypes", []),
        __metadata("design:returntype", void 0)
    ], HandleItem.prototype, "onMouseUp", null);
    __decorate([
        autobind,
        __metadata("design:type", Function),
        __metadata("design:paramtypes", []),
        __metadata("design:returntype", void 0)
    ], HandleItem.prototype, "onMouseEnter", null);
    __decorate([
        autobind,
        __metadata("design:type", Function),
        __metadata("design:paramtypes", []),
        __metadata("design:returntype", void 0)
    ], HandleItem.prototype, "onMouseLeave", null);
    __decorate([
        autobind,
        __metadata("design:type", Function),
        __metadata("design:paramtypes", []),
        __metadata("design:returntype", void 0)
    ], HandleItem.prototype, "onTouchStart", null);
    __decorate([
        autobind,
        __metadata("design:type", Function),
        __metadata("design:paramtypes", [Object]),
        __metadata("design:returntype", void 0)
    ], HandleItem.prototype, "onTouchMove", null);
    __decorate([
        autobind,
        __metadata("design:type", Function),
        __metadata("design:paramtypes", []),
        __metadata("design:returntype", void 0)
    ], HandleItem.prototype, "onTouchEnd", null);
    return HandleItem;
}(React__default.Component));
/**
 * 滑块标签
 */
var Label = /** @class */ (function (_super) {
    __extends(Label, _super);
    function Label() {
        return _super !== null && _super.apply(this, arguments) || this;
    }
    Label.prototype.render = function () {
        var _a = this.props, cx = _a.classnames, value = _a.value, show = _a.show, tooltipVisible = _a.tooltipVisible, tipFormatter = _a.tipFormatter, _b = _a.unit, unit = _b === void 0 ? '' : _b, _c = _a.positionLeft, positionLeft = _c === void 0 ? 0 : _c, _d = _a.positionTop, positionTop = _d === void 0 ? 0 : _d;
        var placement = this.props.placement;
        if (placement === 'auto') {
            positionLeft >= 0 && positionTop >= 0 && (placement = 'top');
            positionLeft >= 0 && positionTop < 0 && (placement = 'bottom');
            positionLeft < 0 && positionTop >= 0 && (placement = 'left');
            positionLeft < 0 && positionTop < 0 && (placement = 'right');
        }
        // tooltipVisible 优先级 比show高
        // tooltipVisible 为 true时，tipFormatter才生效
        var isShow = tooltipVisible !== undefined
            ? tooltipVisible && tipFormatter
                ? tipFormatter(value)
                : tooltipVisible
            : show;
        return (React__default.createElement("div", { className: cx('InputRange-label', "pos-".concat(camel(placement)), {
                'InputRange-label-visible': isShow
            }) },
            React__default.createElement("span", null, value + unit)));
    };
    return Label;
}(React__default.Component));
var Range = /** @class */ (function (_super) {
    __extends(Range, _super);
    function Range() {
        var _this = _super !== null && _super.apply(this, arguments) || this;
        _this.multipleValue = {
            min: _this.props.value.min,
            max: _this.props.value.max
        };
        _this.trackRef = React__default.createRef();
        return _this;
    }
    /**
     * 接收组件value变换
     * value变换 -> Range.updateValue
     * @param value
     */
    Range.prototype.updateValue = function (value) {
        var onChange = this.props.onChange;
        onChange && onChange(value);
    };
    /**
     * 获取 坐标、宽高
     */
    Range.prototype.getBoundingClient = function (dom) {
        var _a = dom === null || dom === void 0 ? void 0 : dom.getBoundingClientRect(), x = _a.x, y = _a.y, width = _a.width, height = _a.height;
        return { x: x, y: y, width: width, height: height };
    };
    /**
     * 坐标 -> 滑块值
     * @param pageX target.target 坐标
     * @returns 滑块值
     */
    Range.prototype.pageXToValue = function (pageX) {
        var _a = this.getBoundingClient(this.trackRef.current), x = _a.x, width = _a.width;
        var _b = this.props, max = _b.max, min = _b.min;
        return ((pageX - x) * (max - min)) / width + min;
    };
    /**
     * 滑块改变事件
     * @param pageX target.pageX 坐标
     * @param type min max
     * @returns void
     */
    Range.prototype.onChange = function (pageX, type) {
        var _a;
        if (type === void 0) { type = 'min'; }
        var _b = this.props, max = _b.max, min = _b.min, step = _b.step, multiple = _b.multiple, originValue = _b.value;
        var value = this.pageXToValue(pageX);
        if (value > max || value < min) {
            return;
        }
        var result = stripNumber(this.getStepValue(value, step));
        // 值相同 不更新
        if (result === originValue) {
            return;
        }
        if (multiple) {
            this.updateValue(__assign(__assign({}, originValue), (_a = {}, _a[type] = result, _a)));
        }
        else {
            this.updateValue(result);
        }
    };
    /**
     * 获取step为单位的value
     * @param value 拖拽后计算的value
     * @param step 步长
     * @returns step为单位的value
     */
    Range.prototype.getStepValue = function (value, step) {
        var surplus = value % step;
        var curValue = 0;
        var closeNum = value - surplus;
        // 余数 >= 步长一半 -> 向上取，value为正值的时候，使用 safeAdd，否则使用 safeSub
        // 余数 <  步长一半 -> 向下取，使用 closeNum
        if (Math.abs(surplus) >= step / 2) {
            curValue = value >= 0 ? safeAdd(closeNum, step) : safeSub(closeNum, step);
        }
        else {
            curValue = closeNum;
        }
        /*
        let result = 0;
        let closeNum = Math.floor(value - surplus);
        // 余数 >= 步长一半 -> 向上取
        // 余数 <  步长一半 -> 向下取
        const _value = Math.abs(surplus) >= step / 2 ? value : safeSub(value, step);
        while (result <= _value) {
          if (step < 1 || result === 0 || result === closeNum) {
            result = safeAdd(result, step);
          } else {
            result = closeNum;
          }
        }
        return result;
        */
        return curValue;
    };
    /**
     * 点击滑轨 -> 触发onchange 改变value
     * @param e event
     * @returns void
     */
    Range.prototype.onClickTrack = function (e) {
        if (!!this.props.disabled) {
            return;
        }
        var value = this.props.value;
        var _value = this.pageXToValue(e.pageX);
        var type = Math.abs(_value - value.min) >
            Math.abs(_value - value.max)
            ? 'max'
            : 'min';
        this.onChange(e.pageX, type);
    };
    /**
     * 设置步长
     * @returns ReactNode
     */
    Range.prototype.renderSteps = function () {
        var _a = this.props, max = _a.max, min = _a.min, step = _a.step, showSteps = _a.showSteps, cx = _a.classnames, parts = _a.parts;
        var isShowSteps = showSteps;
        // 只要设置了 parts 就展示分隔
        if (parts > 1 || Array.isArray(parts)) {
            isShowSteps = true;
        }
        // 总区间
        var section = max - min;
        // 总区间被平均分为多少块
        var steps = parts > 1 ? parts : Math.floor(section / step);
        // 平均分 每块的长度
        var partLength = section / steps;
        // parts为数组时，以0为起点(传入的值 - min)
        var partLengthList = Array.isArray(parts)
            ? parts.map(function (item) { return item - min; })
            : range(steps - 1).map(function (item) { return (item + 1) * partLength; });
        return (isShowSteps && (React__default.createElement("div", null, partLengthList.map(function (item) { return (React__default.createElement("span", { key: item, className: cx('InputRange-track-dot'), style: { left: (item * 100) / (max - min) + '%' } })); }))));
    };
    /**
     * 双滑块改变最大值、最小值
     * @param pageX 拖拽后的pageX
     * @param type 'min' | 'max'
     */
    Range.prototype.onGetChangeValue = function (pageX, type) {
        var _a = this.props, max = _a.max, min = _a.min;
        var value = this.pageXToValue(pageX);
        if (value > max || value < min) {
            return;
        }
        this.multipleValue[type] = stripNumber(this.getStepValue(value, this.props.step));
        var _min = Math.min(this.multipleValue.min, this.multipleValue.max);
        var _max = Math.max(this.multipleValue.min, this.multipleValue.max);
        this.updateValue({ max: _max, min: _min });
    };
    /**
     * 计算每个标记 position.left
     * @param value 滑块值
     * @returns
     */
    Range.prototype.getOffsetLeft = function (value, getNumber) {
        var _a = this.props, max = _a.max, min = _a.min;
        if (isString(value) && MARKS_REG.test(value)) {
            return getNumber ? parseFloat(value) : value;
        }
        value = Math.min(Math.max(+value, min), max);
        return getNumber
            ? ((value - min) * 100) / (max - min)
            : ((value - min) * 100) / (max - min) + '%';
    };
    /**
     * 计算每个刻度标记mark 的最大宽度 max-width
     * 通过给父元素div的width赋值，从而继承
     * @param value 刻度标记key
     * @param marks 整个刻度标记对象
     * @returns 刻度标记的最大宽度 string
     */
    Range.prototype.getMarkMaxWidth = function (value, marks) {
        var _a = this.props, max = _a.max, min = _a.min, mobileUI = _a.mobileUI;
        var curNum = isString(value) ? parseInt(value, 10) : value;
        // 给最大宽度赋初始值 默认最大
        var maxWidth = Math.abs(max - min);
        // 遍历刻度标记masks 寻找距离当前节点最近的刻度标记 并记录差值
        keys(marks).forEach(function (mKey) {
            var mNum = isString(mKey) ? parseInt(mKey, 10) : mKey;
            if (mKey !== value && !isNaN(mNum)) {
                maxWidth = Math.min(Math.abs(curNum - mNum), maxWidth);
            }
        });
        // 差值的1/2 即为此刻度标记的最大宽度
        return mobileUI
            ? Math.floor(maxWidth) + '%'
            : Math.floor(maxWidth / 2) + '%';
    };
    Range.prototype.render = function () {
        var _this = this;
        var _a = this.props, cx = _a.classnames, marks = _a.marks, multiple = _a.multiple, value = _a.value, max = _a.max, min = _a.min, disabled = _a.disabled, tooltipVisible = _a.tooltipVisible, unit = _a.unit, tooltipPlacement = _a.tooltipPlacement, tipFormatter = _a.tipFormatter, onAfterChange = _a.onAfterChange, mobileUI = _a.mobileUI;
        // trace
        var traceActiveStyle = {
            width: valueToOffsetLeft(multiple
                ? value.max - value.min + min
                : value, min, max),
            left: valueToOffsetLeft(multiple ? value.min : min, min, max)
        };
        // handle 双滑块
        var diff = difference(Object.values(value), Object.values(this.multipleValue));
        if (diff && !!diff.length) {
            this.multipleValue = {
                min: value.min,
                max: value.max
            };
        }
        var sortMaks = marks
            ? keys(marks).sort(function (a, b) {
                return _this.getOffsetLeft(a, true) -
                    _this.getOffsetLeft(b, true);
            })
            : [];
        return (React__default.createElement("div", { className: cx('InputRange-wrap') },
            React__default.createElement("div", { ref: this.trackRef, className: cx('InputRange-track', 'InputRange-track--background'), onClick: this.onClickTrack },
                React__default.createElement("div", { className: cx('InputRange-track-active'), style: traceActiveStyle }),
                this.renderSteps(),
                multiple ? (['min', 'max'].map(function (type) { return (React__default.createElement(HandleItem, { key: type, value: _this.multipleValue[type], type: type, min: min, max: max, classPrefix: _this.props.classPrefix, classnames: cx, disabled: disabled, tooltipVisible: tooltipVisible, tipFormatter: tipFormatter, unit: unit, mobileUI: mobileUI, tooltipPlacement: tooltipPlacement, onAfterChange: onAfterChange, onChange: _this.onGetChangeValue.bind(_this) })); })) : (React__default.createElement(HandleItem, { value: +value, min: min, max: max, classPrefix: this.props.classPrefix, classnames: cx, disabled: disabled, tooltipVisible: tooltipVisible, tipFormatter: tipFormatter, unit: unit, mobileUI: mobileUI, tooltipPlacement: tooltipPlacement, onAfterChange: onAfterChange, onChange: this.onChange.bind(this) })),
                marks && (React__default.createElement("div", { className: cx('InputRange-marks') }, sortMaks.map(function (key) {
                    var _a, _b, _c;
                    var offsetLeft = _this.getOffsetLeft(key);
                    var markMaxWidth = _this.getMarkMaxWidth(key, marks);
                    if (MARKS_REG.test(offsetLeft)) {
                        return (React__default.createElement("div", { key: key, style: { left: offsetLeft, width: markMaxWidth } },
                            React__default.createElement(TooltipWrapper, { placement: 'bottom', tooltip: ((_a = marks[key]) === null || _a === void 0 ? void 0 : _a.label) || marks[key], trigger: ['hover', 'focus'], rootClose: false },
                                React__default.createElement("span", { style: (_b = marks[key]) === null || _b === void 0 ? void 0 : _b.style }, ((_c = marks[key]) === null || _c === void 0 ? void 0 : _c.label) || marks[key]))));
                    }
                    else {
                        return null;
                    }
                }))))));
    };
    __decorate([
        autobind,
        __metadata("design:type", Function),
        __metadata("design:paramtypes", [Object]),
        __metadata("design:returntype", void 0)
    ], Range.prototype, "updateValue", null);
    __decorate([
        autobind,
        __metadata("design:type", Function),
        __metadata("design:paramtypes", [Element]),
        __metadata("design:returntype", void 0)
    ], Range.prototype, "getBoundingClient", null);
    __decorate([
        autobind,
        __metadata("design:type", Function),
        __metadata("design:paramtypes", [Number, String]),
        __metadata("design:returntype", void 0)
    ], Range.prototype, "onChange", null);
    __decorate([
        autobind,
        __metadata("design:type", Function),
        __metadata("design:paramtypes", [Object]),
        __metadata("design:returntype", void 0)
    ], Range.prototype, "onClickTrack", null);
    __decorate([
        autobind,
        __metadata("design:type", Function),
        __metadata("design:paramtypes", []),
        __metadata("design:returntype", void 0)
    ], Range.prototype, "renderSteps", null);
    __decorate([
        autobind,
        __metadata("design:type", Function),
        __metadata("design:paramtypes", [Number, Object]),
        __metadata("design:returntype", void 0)
    ], Range.prototype, "onGetChangeValue", null);
    __decorate([
        autobind,
        __metadata("design:type", Function),
        __metadata("design:paramtypes", [Object, Boolean]),
        __metadata("design:returntype", void 0)
    ], Range.prototype, "getOffsetLeft", null);
    __decorate([
        autobind,
        __metadata("design:type", Function),
        __metadata("design:paramtypes", [Object, Object]),
        __metadata("design:returntype", void 0)
    ], Range.prototype, "getMarkMaxWidth", null);
    return Range;
}(React__default.Component));
var Range$1 = themeable(uncontrollable(Range, {
    value: 'onChange'
}));

export { Range, Range$1 as default };
