"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.sliderProps = exports.default = void 0;
var _vue = require("vue");
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
require("../_util/type");
var _Marks = _interopRequireDefault(require("./Marks"));
var _useRange = _interopRequireDefault(require("./hooks/useRange"));
var _useOffset = _interopRequireDefault(require("./hooks/useOffset"));
var _lodash = require("lodash");
var _useDrag = _interopRequireDefault(require("./hooks/useDrag"));
var _context = require("./context");
var _classNames = _interopRequireDefault(require("../_util/classNames"));
var _Tracks = _interopRequireDefault(require("./Tracks"));
var _Steps = _interopRequireDefault(require("./Steps"));
var _Handles = _interopRequireDefault(require("./Handles"));
var _useControlledState = _interopRequireDefault(require("../_util/hooks/useControlledState"));
const sliderProps = () => ({
  prefixCls: {
    type: String,
    default: 'rc-slider'
  },
  className: String,
  style: Object,
  classNames: Object,
  styles: Object,
  id: String,
  // Status
  disabled: {
    type: Boolean,
    default: false
  },
  keyboard: {
    type: Boolean,
    default: true
  },
  autoFocus: {
    type: Boolean,
    default: undefined
  },
  onFocus: Function,
  onBlur: Function,
  // value
  range: {
    type: [Boolean, Object],
    default: undefined
  },
  /** @deprecated Use `range.minCount` or `range.maxCount` to handle this */
  count: Number,
  min: {
    type: Number,
    default: 0
  },
  max: {
    type: Number,
    default: 100
  },
  step: {
    type: Number,
    default: 1
  },
  value: [Number, Array],
  defaultValue: [Number, Array],
  onChange: Function,
  'onUpdate:value': Function,
  /** @deprecated It's always better to use `onChange` instead */
  onBeforeChange: Function,
  /** @deprecated Use `onChangeComplete` instead */
  onAfterChange: Function,
  onChangeComplete: Function,
  // Cross
  allowCross: {
    type: Boolean,
    default: true
  },
  pushable: [Boolean, Number],
  // Direction
  reverse: {
    type: Boolean,
    default: undefined
  },
  vertical: {
    type: Boolean,
    default: undefined
  },
  // Style
  included: {
    type: Boolean,
    default: true
  },
  startPoint: Number,
  /** @deprecated Please use `styles.track` instead */
  trackStyle: [Object, Array],
  /** @deprecated Please use `styles.handle` instead */
  handleStyle: [Object, Array],
  /** @deprecated Please use `styles.rail` instead */
  railStyle: Object,
  dotStyle: [Object, Function],
  activeDotStyle: [Object, Function],
  // Decorations
  marks: Object,
  dots: {
    type: Boolean,
    default: undefined
  },
  // Components
  handleRender: Function,
  activeHandleRender: Function,
  track: {
    type: Boolean,
    default: undefined
  },
  // Accessibility
  tabIndex: {
    type: [Number, Array],
    default: 0
  },
  ariaLabelForHandle: [String, Array],
  ariaLabelledByForHandle: [String, Array],
  ariaRequired: {
    type: Boolean,
    default: undefined
  },
  ariaValueTextFormatterForHandle: [Function, Array]
});
exports.sliderProps = sliderProps;
const Slider = (0, _vue.defineComponent)({
  name: 'ASlider',
  props: sliderProps(),
  setup(props, {
    expose
  }) {
    const handlesRef = (0, _vue.ref)();
    const containerRef = (0, _vue.ref)();
    const direction = (0, _vue.computed)(() => {
      const {
        vertical,
        reverse
      } = props;
      if (vertical) {
        return reverse ? 'ttb' : 'btt';
      }
      return reverse ? 'rtl' : 'ltr';
    });
    // ============================ Range =============================
    const range = (0, _useRange.default)((0, _vue.toRef)(props, 'range'));
    const mergedMin = (0, _vue.computed)(() => isFinite(props.min) ? props.min : 0);
    const mergedMax = (0, _vue.computed)(() => isFinite(props.max) ? props.max : 100);
    // ============================= Step =============================
    const mergedStep = (0, _vue.computed)(() => props.step !== null && props.step <= 0 ? 1 : props.step);
    // ============================= Push =============================
    const mergedPush = (0, _vue.computed)(() => {
      const pushable = props.pushable;
      if (typeof pushable === 'boolean') {
        return pushable ? mergedStep.value : false;
      }
      return pushable >= 0 ? pushable : false;
    });
    // ============================ Marks =============================
    const markList = (0, _vue.computed)(() => {
      const {
        marks
      } = props;
      return Object.keys(marks || {}).map(key => {
        const mark = marks[key];
        const markObj = {
          value: Number(key)
        };
        if (mark && typeof mark === 'object' && !(0, _vue.isVNode)(mark) && ('label' in mark || 'style' in mark)) {
          markObj.style = mark.style;
          markObj.label = mark.label;
        } else {
          markObj.label = mark;
        }
        return markObj;
      }).filter(({
        label
      }) => label || typeof label === 'number').sort((a, b) => a.value - b.value);
    });
    // ============================ Format ============================
    const [formatValue, offsetValues] = (0, _useOffset.default)(mergedMin, mergedMax, mergedStep, markList, (0, _vue.toRef)(props, 'allowCross'), mergedPush);
    // ============================ Values ============================
    const [mergedValue, setValue] = (0, _useControlledState.default)(props.defaultValue, (0, _vue.toRef)(props, 'value'));
    const rawValues = (0, _vue.computed)(() => {
      var _a;
      const valueList = mergedValue.value == null ? [] : Array.isArray(mergedValue.value) ? mergedValue.value : [mergedValue.value];
      const [val0 = mergedMin.value] = valueList;
      let returnValues = mergedValue.value === null ? [] : [val0];
      const [rangeEnabled] = range.value;
      const {
        count
      } = props;
      // Format as range
      if (rangeEnabled) {
        returnValues = [...valueList];
        // When count provided or value is `undefined`, we fill values
        if (count || mergedValue.value === undefined) {
          const pointCount = count >= 0 ? count + 1 : 2;
          returnValues = returnValues.slice(0, pointCount);
          // Fill with count
          while (returnValues.length < pointCount) {
            returnValues.push((_a = returnValues[returnValues.length - 1]) !== null && _a !== void 0 ? _a : mergedMin.value);
          }
        }
        returnValues.sort((a, b) => a - b);
      }
      // Align in range
      returnValues.forEach((val, index) => {
        returnValues[index] = formatValue(val);
      });
      return returnValues;
    });
    // =========================== onChange ===========================
    const getTriggerValue = triggerValues => range.value[0] ? triggerValues : triggerValues[0];
    const triggerChange = nextValues => {
      var _a, _b;
      // Order first
      const cloneNextValues = [...nextValues].sort((a, b) => a - b);
      // Trigger event if needed
      if (!(0, _lodash.isEqual)(cloneNextValues, rawValues.value)) {
        const nextValues = getTriggerValue(cloneNextValues);
        (_a = props.onChange) === null || _a === void 0 ? void 0 : _a.call(props, nextValues);
        (_b = props['onUpdate:value']) === null || _b === void 0 ? void 0 : _b.call(props, nextValues);
      }
      // We set this later since it will re-render component immediately
      setValue(cloneNextValues);
    };
    const finishChange = draggingDelete => {
      var _a, _b;
      // Trigger from `useDrag` will tell if it's a delete action
      if (draggingDelete) {
        handlesRef.value.hideHelp();
      }
      const finishValue = getTriggerValue(rawValues.value);
      (_a = props.onAfterChange) === null || _a === void 0 ? void 0 : _a.call(props, finishValue);
      (_b = props.onChangeComplete) === null || _b === void 0 ? void 0 : _b.call(props, finishValue);
    };
    const onDelete = index => {
      const [, rangeEditable,, minCount] = range.value;
      const {
        disabled,
        onBeforeChange
      } = props;
      if (disabled || !rangeEditable || rawValues.value.length <= minCount) {
        return;
      }
      const cloneNextValues = [...rawValues.value];
      cloneNextValues.splice(index, 1);
      onBeforeChange === null || onBeforeChange === void 0 ? void 0 : onBeforeChange(getTriggerValue(cloneNextValues));
      triggerChange(cloneNextValues);
      const nextFocusIndex = Math.max(0, index - 1);
      handlesRef.value.hideHelp();
      handlesRef.value.focus(nextFocusIndex);
    };
    const rangeEnabled = (0, _vue.computed)(() => range.value[0]);
    const rangeEditable = (0, _vue.computed)(() => range.value[1]);
    const rangeDraggableTrack = (0, _vue.computed)(() => range.value[2]);
    const minCount = (0, _vue.computed)(() => range.value[3]);
    const maxCount = (0, _vue.computed)(() => range.value[4]);
    const [draggingIndex, draggingValue, draggingDelete, cacheValues, onStartDrag] = (0, _useDrag.default)(containerRef, direction, rawValues, mergedMin, mergedMax, formatValue, triggerChange, finishChange, offsetValues, rangeEditable, minCount);
    /**
     * When `rangeEditable` will insert a new value in the values array.
     * Else it will replace the value in the values array.
     */
    const changeToCloseValue = (newValue, e) => {
      var _a, _b, _c, _d, _e;
      if (!props.disabled) {
        const cloneNextValues = [...rawValues.value];
        let valueIndex = 0;
        let valueBeforeIndex = 0;
        let valueDist = mergedMax.value - mergedMin.value;
        rawValues.value.forEach((val, index) => {
          const dist = Math.abs(newValue - val);
          if (dist <= valueDist) {
            valueDist = dist;
            valueIndex = index;
          }
          if (val < newValue) {
            valueBeforeIndex = index;
          }
        });
        let focusIndex = valueIndex;
        if (rangeEditable.value && valueDist !== 0 && (!maxCount.value || rawValues.value.length < maxCount.value)) {
          cloneNextValues.splice(valueBeforeIndex + 1, 0, newValue);
          focusIndex = valueBeforeIndex + 1;
        } else {
          cloneNextValues[valueIndex] = newValue;
        }
        // Fill value to match default 2 (only when `rawValues` is empty)
        if (rangeEnabled.value && !rawValues.value.length && props.count === undefined) {
          cloneNextValues.push(newValue);
        }
        const nextValue = getTriggerValue(cloneNextValues);
        (_a = props.onBeforeChange) === null || _a === void 0 ? void 0 : _a.call(props, nextValue);
        triggerChange(cloneNextValues);
        if (e) {
          (_c = (_b = document.activeElement) === null || _b === void 0 ? void 0 : _b.blur) === null || _c === void 0 ? void 0 : _c.call(_b);
          handlesRef.value.focus(focusIndex);
          onStartDrag(e, focusIndex, cloneNextValues);
        } else {
          (_d = props.onAfterChange) === null || _d === void 0 ? void 0 : _d.call(props, nextValue);
          (_e = props.onChangeComplete) === null || _e === void 0 ? void 0 : _e.call(props, nextValue);
        }
      }
    };
    // ============================ Click =============================
    const onSliderMouseDown = e => {
      e.preventDefault();
      const {
        width,
        height,
        left,
        top,
        bottom,
        right
      } = containerRef.value.getBoundingClientRect();
      const {
        clientX,
        clientY
      } = e;
      let percent;
      switch (direction.value) {
        case 'btt':
          percent = (bottom - clientY) / height;
          break;
        case 'ttb':
          percent = (clientY - top) / height;
          break;
        case 'rtl':
          percent = (right - clientX) / width;
          break;
        default:
          percent = (clientX - left) / width;
      }
      const nextValue = mergedMin.value + percent * (mergedMax.value - mergedMin.value);
      changeToCloseValue(formatValue(nextValue), e);
    };
    // =========================== Keyboard ===========================
    const keyboardValue = (0, _vue.ref)(null);
    const onHandleOffsetChange = (offset, valueIndex) => {
      var _a;
      if (!props.disabled) {
        const next = offsetValues(rawValues.value, offset, valueIndex);
        (_a = props.onBeforeChange) === null || _a === void 0 ? void 0 : _a.call(props, getTriggerValue(rawValues.value));
        triggerChange(next.values);
        keyboardValue.value = next.value;
      }
    };
    (0, _vue.watch)(() => keyboardValue.value, nextKeyboardValue => {
      if (nextKeyboardValue !== null) {
        const valueIndex = rawValues.value.indexOf(nextKeyboardValue);
        if (valueIndex >= 0) {
          handlesRef.value.focus(valueIndex);
        }
      }
      keyboardValue.value = null;
    });
    // ============================= Drag =============================
    const mergedDraggableTrack = (0, _vue.computed)(() => {
      if (rangeDraggableTrack.value && mergedStep.value === null) {
        return false;
      }
      return rangeDraggableTrack.value;
    });
    const onStartMove = (e, valueIndex) => {
      var _a;
      onStartDrag(e, valueIndex);
      (_a = props.onBeforeChange) === null || _a === void 0 ? void 0 : _a.call(props, getTriggerValue(rawValues.value));
    };
    // Auto focus for updated handle
    const dragging = (0, _vue.computed)(() => draggingIndex.value !== -1);
    (0, _vue.watch)(() => dragging.value, nextDragging => {
      if (!nextDragging) {
        const valueIndex = rawValues.value.lastIndexOf(draggingValue.value);
        handlesRef.value.focus(valueIndex);
      }
    });
    // =========================== Included ===========================
    const sortedCacheValues = (0, _vue.computed)(() => {
      const cloneCacheValues = [...cacheValues.value];
      return cloneCacheValues.sort((a, b) => a - b);
    });
    const included = (0, _vue.computed)(() => {
      if (!rangeEnabled.value) {
        return [mergedMin.value, sortedCacheValues.value[0]];
      }
      return [sortedCacheValues.value[0], sortedCacheValues.value[sortedCacheValues.value.length - 1]];
    });
    // ============================= Expose =============================
    expose({
      focus: () => {
        handlesRef.value.focus(0);
      },
      blur: () => {
        var _a;
        const {
          activeElement
        } = document;
        if ((_a = containerRef.value) === null || _a === void 0 ? void 0 : _a.contains(activeElement)) {
          activeElement === null || activeElement === void 0 ? void 0 : activeElement.blur();
        }
      }
    });
    // ========================== Auto Focus ==========================
    (0, _vue.onMounted)(() => {
      if (props.autoFocus) {
        handlesRef.value.focus(0);
      }
    });
    // =========================== Context ============================
    const context = (0, _vue.computed)(() => ({
      min: mergedMin.value,
      max: mergedMax.value,
      direction: direction.value,
      disabled: props.disabled,
      keyboard: props.keyboard,
      step: mergedStep.value,
      included: props.included,
      includedStart: included.value[0],
      includedEnd: included.value[1],
      range: rangeEnabled.value,
      tabIndex: props.tabIndex,
      ariaLabelForHandle: props.ariaLabelForHandle,
      ariaLabelledByForHandle: props.ariaLabelledByForHandle,
      ariaRequired: props.ariaRequired,
      ariaValueTextFormatterForHandle: props.ariaValueTextFormatterForHandle,
      styles: props.styles || {},
      classNames: props.classNames || {}
    }));
    (0, _context.useProvideSlider)(context);
    // ============================ Render ============================
    return () => {
      const {
        prefixCls,
        className,
        classNames,
        disabled,
        vertical,
        style,
        id,
        railStyle,
        styles,
        track,
        trackStyle,
        startPoint,
        dots,
        dotStyle,
        activeDotStyle,
        handleStyle,
        onFocus,
        onBlur,
        handleRender,
        activeHandleRender
      } = props;
      return (0, _vue.createVNode)("div", {
        "ref": containerRef,
        "class": (0, _classNames.default)(prefixCls, className, {
          [`${prefixCls}-disabled`]: disabled,
          [`${prefixCls}-vertical`]: vertical,
          [`${prefixCls}-horizontal`]: !vertical,
          [`${prefixCls}-with-marks`]: markList.value.length
        }),
        "style": style,
        "onMousedown": onSliderMouseDown,
        "id": id
      }, [(0, _vue.createVNode)("div", {
        "class": (0, _classNames.default)(`${prefixCls}-rail`, classNames === null || classNames === void 0 ? void 0 : classNames.rail),
        "style": (0, _extends2.default)((0, _extends2.default)({}, railStyle), (styles === null || styles === void 0 ? void 0 : styles.rail) || {})
      }, null), track !== false && (0, _vue.createVNode)(_Tracks.default, {
        "prefixCls": prefixCls,
        "style": trackStyle,
        "values": rawValues.value,
        "startPoint": startPoint,
        "onStartMove": mergedDraggableTrack.value ? onStartMove : undefined
      }, null), (0, _vue.createVNode)(_Steps.default, {
        "prefixCls": prefixCls,
        "marks": markList.value,
        "dots": dots,
        "style": dotStyle,
        "activeStyle": activeDotStyle
      }, null), (0, _vue.createVNode)(_Handles.default, {
        "ref": handlesRef,
        "prefixCls": prefixCls,
        "style": handleStyle,
        "values": cacheValues.value,
        "draggingIndex": draggingIndex.value,
        "draggingDelete": draggingDelete.value,
        "onStartMove": onStartMove,
        "onOffsetChange": onHandleOffsetChange,
        "onFocus": onFocus,
        "onBlur": onBlur,
        "handleRender": handleRender,
        "activeHandleRender": activeHandleRender,
        "onChangeComplete": finishChange,
        "onDelete": rangeEditable.value ? onDelete : undefined
      }, null), (0, _vue.createVNode)(_Marks.default, {
        "prefixCls": prefixCls,
        "marks": markList.value,
        "onClick": changeToCloseValue
      }, null)]);
    };
  }
});
var _default = exports.default = Slider;