import { addResizeListener, removeResizeListener } from '../utils/resize-event';
import { isArray, toObject, addUnit, isString, isNumber } from '../utils/util';
import { defineComponent, ref, inject, computed, onMounted, onBeforeUnmount, openBlock, createBlock, Transition, withCtx, withDirectives, createVNode, vShow, provide, nextTick, resolveComponent, resolveDynamicComponent, renderSlot, Fragment, createCommentVNode } from 'vue';
import { on, off } from '../utils/dom';
import { warn } from '../utils/error';

const BAR_MAP = {
  vertical: {
    offset: "offsetHeight",
    scroll: "scrollTop",
    scrollSize: "scrollHeight",
    size: "height",
    key: "vertical",
    axis: "Y",
    client: "clientY",
    direction: "top"
  },
  horizontal: {
    offset: "offsetWidth",
    scroll: "scrollLeft",
    scrollSize: "scrollWidth",
    size: "width",
    key: "horizontal",
    axis: "X",
    client: "clientX",
    direction: "left"
  }
};
function renderThumbStyle({ move, size, bar }) {
  const style = {};
  const translate = `translate${bar.axis}(${move}%)`;
  style[bar.size] = size;
  style.transform = translate;
  style.msTransform = translate;
  style.webkitTransform = translate;
  return style;
}

var __pow = Math.pow;
var script = defineComponent({
  name: "Bar",
  props: {
    vertical: Boolean,
    size: String,
    move: Number,
    ratio: Number,
    always: Boolean
  },
  setup(props) {
    const instance = ref(null);
    const thumb = ref(null);
    const scrollbar = inject("scrollbar", {});
    const wrap = inject("scrollbar-wrap", {});
    const bar = computed(() => BAR_MAP[props.vertical ? "vertical" : "horizontal"]);
    const barStore = ref({});
    const cursorDown = ref(null);
    const cursorLeave = ref(null);
    const visible = ref(false);
    let onselectstartStore = null;
    const offsetRatio = computed(() => {
      return __pow(instance.value[bar.value.offset], 2) / wrap.value[bar.value.scrollSize] / props.ratio / thumb.value[bar.value.offset];
    });
    const clickThumbHandler = (e) => {
      e.stopPropagation();
      if (e.ctrlKey || [1, 2].includes(e.button)) {
        return;
      }
      window.getSelection().removeAllRanges();
      startDrag(e);
      barStore.value[bar.value.axis] = e.currentTarget[bar.value.offset] - (e[bar.value.client] - e.currentTarget.getBoundingClientRect()[bar.value.direction]);
    };
    const clickTrackHandler = (e) => {
      const offset = Math.abs(e.target.getBoundingClientRect()[bar.value.direction] - e[bar.value.client]);
      const thumbHalf = thumb.value[bar.value.offset] / 2;
      const thumbPositionPercentage = (offset - thumbHalf) * 100 * offsetRatio.value / instance.value[bar.value.offset];
      wrap.value[bar.value.scroll] = thumbPositionPercentage * wrap.value[bar.value.scrollSize] / 100;
    };
    const startDrag = (e) => {
      e.stopImmediatePropagation();
      cursorDown.value = true;
      on(document, "mousemove", mouseMoveDocumentHandler);
      on(document, "mouseup", mouseUpDocumentHandler);
      onselectstartStore = document.onselectstart;
      document.onselectstart = () => false;
    };
    const mouseMoveDocumentHandler = (e) => {
      if (cursorDown.value === false)
        return;
      const prevPage = barStore.value[bar.value.axis];
      if (!prevPage)
        return;
      const offset = (instance.value.getBoundingClientRect()[bar.value.direction] - e[bar.value.client]) * -1;
      const thumbClickPosition = thumb.value[bar.value.offset] - prevPage;
      const thumbPositionPercentage = (offset - thumbClickPosition) * 100 * offsetRatio.value / instance.value[bar.value.offset];
      wrap.value[bar.value.scroll] = thumbPositionPercentage * wrap.value[bar.value.scrollSize] / 100;
    };
    const mouseUpDocumentHandler = () => {
      cursorDown.value = false;
      barStore.value[bar.value.axis] = 0;
      off(document, "mousemove", mouseMoveDocumentHandler);
      document.onselectstart = onselectstartStore;
      if (cursorLeave.value) {
        visible.value = false;
      }
    };
    const thumbStyle = computed(() => renderThumbStyle({
      size: props.size,
      move: props.move,
      bar: bar.value
    }));
    const mouseMoveScrollbarHandler = () => {
      cursorLeave.value = false;
      visible.value = !!props.size;
    };
    const mouseLeaveScrollbarHandler = () => {
      cursorLeave.value = true;
      visible.value = cursorDown.value;
    };
    onMounted(() => {
      on(scrollbar.value, "mousemove", mouseMoveScrollbarHandler);
      on(scrollbar.value, "mouseleave", mouseLeaveScrollbarHandler);
    });
    onBeforeUnmount(() => {
      off(document, "mouseup", mouseUpDocumentHandler);
      off(scrollbar.value, "mousemove", mouseMoveScrollbarHandler);
      off(scrollbar.value, "mouseleave", mouseLeaveScrollbarHandler);
    });
    return {
      instance,
      thumb,
      bar,
      clickTrackHandler,
      clickThumbHandler,
      thumbStyle,
      visible
    };
  }
});

function render(_ctx, _cache, $props, $setup, $data, $options) {
  return openBlock(), createBlock(Transition, { name: "el-scrollbar-fade" }, {
    default: withCtx(() => [
      withDirectives(createVNode("div", {
        ref: "instance",
        class: ["el-scrollbar__bar", "is-" + _ctx.bar.key],
        onMousedown: _cache[2] || (_cache[2] = (...args) => _ctx.clickTrackHandler && _ctx.clickTrackHandler(...args))
      }, [
        createVNode("div", {
          ref: "thumb",
          class: "el-scrollbar__thumb",
          style: _ctx.thumbStyle,
          onMousedown: _cache[1] || (_cache[1] = (...args) => _ctx.clickThumbHandler && _ctx.clickThumbHandler(...args))
        }, null, 36)
      ], 34), [
        [vShow, _ctx.always || _ctx.visible]
      ])
    ]),
    _: 1
  });
}

script.render = render;
script.__file = "packages/scrollbar/src/bar.vue";

var __pow$1 = Math.pow;
var script$1 = defineComponent({
  name: "ElScrollbar",
  components: { Bar: script },
  props: {
    height: {
      type: [String, Number],
      default: ""
    },
    maxHeight: {
      type: [String, Number],
      default: ""
    },
    native: {
      type: Boolean,
      default: false
    },
    wrapStyle: {
      type: [String, Array],
      default: ""
    },
    wrapClass: {
      type: [String, Array],
      default: ""
    },
    viewClass: {
      type: [String, Array],
      default: ""
    },
    viewStyle: {
      type: [String, Array],
      default: ""
    },
    noresize: Boolean,
    tag: {
      type: String,
      default: "div"
    },
    always: {
      type: Boolean,
      default: false
    },
    minSize: {
      type: Number,
      default: 20
    }
  },
  emits: ["scroll"],
  setup(props, { emit }) {
    const sizeWidth = ref("0");
    const sizeHeight = ref("0");
    const moveX = ref(0);
    const moveY = ref(0);
    const scrollbar = ref(null);
    const wrap = ref(null);
    const resize = ref(null);
    const ratioY = ref(1);
    const ratioX = ref(1);
    const SCOPE = "ElScrollbar";
    const GAP = 4;
    provide("scrollbar", scrollbar);
    provide("scrollbar-wrap", wrap);
    const handleScroll = () => {
      if (wrap.value) {
        const offsetHeight = wrap.value.offsetHeight - GAP;
        const offsetWidth = wrap.value.offsetWidth - GAP;
        moveY.value = wrap.value.scrollTop * 100 / offsetHeight * ratioY.value;
        moveX.value = wrap.value.scrollLeft * 100 / offsetWidth * ratioX.value;
        emit("scroll", {
          scrollTop: wrap.value.scrollTop,
          scrollLeft: wrap.value.scrollLeft
        });
      }
    };
    const setScrollTop = (value) => {
      if (!isNumber(value)) {
        if (process.env.NODE_ENV !== "production") {
          warn(SCOPE, "value must be a number");
        }
        return;
      }
      wrap.value.scrollTop = value;
    };
    const setScrollLeft = (value) => {
      if (!isNumber(value)) {
        if (process.env.NODE_ENV !== "production") {
          warn(SCOPE, "value must be a number");
        }
        return;
      }
      wrap.value.scrollLeft = value;
    };
    const update = () => {
      if (!wrap.value)
        return;
      const offsetHeight = wrap.value.offsetHeight - GAP;
      const offsetWidth = wrap.value.offsetWidth - GAP;
      const originalHeight = __pow$1(offsetHeight, 2) / wrap.value.scrollHeight;
      const originalWidth = __pow$1(offsetWidth, 2) / wrap.value.scrollWidth;
      const height = Math.max(originalHeight, props.minSize);
      const width = Math.max(originalWidth, props.minSize);
      ratioY.value = originalHeight / (offsetHeight - originalHeight) / (height / (offsetHeight - height));
      ratioX.value = originalWidth / (offsetWidth - originalWidth) / (width / (offsetWidth - width));
      sizeHeight.value = height + GAP < offsetHeight ? height + "px" : "";
      sizeWidth.value = width + GAP < offsetWidth ? width + "px" : "";
    };
    const style = computed(() => {
      let style2 = props.wrapStyle;
      if (isArray(style2)) {
        style2 = toObject(style2);
        style2.height = addUnit(props.height);
        style2.maxHeight = addUnit(props.maxHeight);
      } else if (isString(style2)) {
        style2 += addUnit(props.height) ? `height: ${addUnit(props.height)};` : "";
        style2 += addUnit(props.maxHeight) ? `max-height: ${addUnit(props.maxHeight)};` : "";
      }
      return style2;
    });
    onMounted(() => {
      if (!props.native) {
        nextTick(update);
      }
      if (!props.noresize) {
        addResizeListener(resize.value, update);
        addEventListener("resize", update);
      }
    });
    onBeforeUnmount(() => {
      if (!props.noresize) {
        removeResizeListener(resize.value, update);
        removeEventListener("resize", update);
      }
    });
    return {
      moveX,
      moveY,
      ratioX,
      ratioY,
      sizeWidth,
      sizeHeight,
      style,
      scrollbar,
      wrap,
      resize,
      update,
      handleScroll,
      setScrollTop,
      setScrollLeft
    };
  }
});

const _hoisted_1 = {
  ref: "scrollbar",
  class: "el-scrollbar"
};
function render$1(_ctx, _cache, $props, $setup, $data, $options) {
  const _component_bar = resolveComponent("bar");
  return openBlock(), createBlock("div", _hoisted_1, [
    createVNode("div", {
      ref: "wrap",
      class: [
        _ctx.wrapClass,
        "el-scrollbar__wrap",
        _ctx.native ? "" : "el-scrollbar__wrap--hidden-default"
      ],
      style: _ctx.style,
      onScroll: _cache[1] || (_cache[1] = (...args) => _ctx.handleScroll && _ctx.handleScroll(...args))
    }, [
      (openBlock(), createBlock(resolveDynamicComponent(_ctx.tag), {
        ref: "resize",
        class: ["el-scrollbar__view", _ctx.viewClass],
        style: _ctx.viewStyle
      }, {
        default: withCtx(() => [
          renderSlot(_ctx.$slots, "default")
        ]),
        _: 3
      }, 8, ["class", "style"]))
    ], 38),
    !_ctx.native ? (openBlock(), createBlock(Fragment, { key: 0 }, [
      createVNode(_component_bar, {
        move: _ctx.moveX,
        ratio: _ctx.ratioX,
        size: _ctx.sizeWidth,
        always: _ctx.always
      }, null, 8, ["move", "ratio", "size", "always"]),
      createVNode(_component_bar, {
        move: _ctx.moveY,
        ratio: _ctx.ratioY,
        size: _ctx.sizeHeight,
        vertical: "",
        always: _ctx.always
      }, null, 8, ["move", "ratio", "size", "always"])
    ], 64)) : createCommentVNode("v-if", true)
  ], 512);
}

script$1.render = render$1;
script$1.__file = "packages/scrollbar/src/index.vue";

script$1.install = (app) => {
  app.component(script$1.name, script$1);
};
const _Scrollbar = script$1;

export default _Scrollbar;
