{"version":3,"file":"thumb.mjs","sources":["../../../../../src/components/ivue-scrollbar/thumb.vue"],"sourcesContent":["<template>\r\n  <transition :name=\"`${prefixCls}-fade`\">\r\n    <!-- v-show=\"always || visible\" -->\r\n    <div\r\n      :class=\"wrapperClasses\"\r\n      @mousedown=\"handleThumbWrapperMousedown\"\r\n      ref=\"thumbWrapper\"\r\n      v-show=\"always || data.visible\"\r\n    >\r\n      <div\r\n        :class=\"`${prefixCls}-thumb`\"\r\n        :style=\"thumbStyles\"\r\n        @mousedown=\"handleThumbMousedown\"\r\n        ref=\"thumb\"\r\n      ></div>\r\n    </div>\r\n  </transition>\r\n</template>\r\n\r\n<script lang=\"ts\">\r\nimport {\r\n  computed,\r\n  defineComponent,\r\n  ref,\r\n  reactive,\r\n  inject,\r\n  onBeforeUnmount,\r\n  onMounted,\r\n} from 'vue';\r\nimport { useEventListener } from '@vueuse/core';\r\n\r\nimport { on, off } from '../../utils/dom';\r\n\r\n// type\r\nimport { ScrollbarContextKey, ScrollbarContext } from './types/scrollbar';\r\nimport type { Props, Data } from './types/thumb';\r\n\r\nconst prefixCls = 'ivue-scrollbar';\r\n\r\n// 鼠标按下\r\nlet mousedown = false;\r\n// 鼠标离开\r\nlet mouseLeave = false;\r\n\r\n// 开始选择任意文本内容时触发\r\nlet originalOnSelectStart = document.onselectstart;\r\n\r\nexport default defineComponent({\r\n  name: 'ivue-scrollbar-thumb',\r\n  props: {\r\n    /**\r\n     * 偏移位置\r\n     *\r\n     * @type {Number}\r\n     */\r\n    move: {\r\n      type: Number,\r\n    },\r\n    /**\r\n     * 轴滚动比率\r\n     *\r\n     * @type {Number}\r\n     */\r\n    ratio: {\r\n      type: Number,\r\n      default: 1,\r\n    },\r\n    /**\r\n     * 滚动条总是显示\r\n     *\r\n     * @type {Boolean}\r\n     */\r\n    always: {\r\n      type: Boolean,\r\n      default: false,\r\n    },\r\n    /**\r\n     * 滚动条大小\r\n     *\r\n     * @type {String}\r\n     */\r\n    barSize: {\r\n      type: String,\r\n      default: '',\r\n    },\r\n    /**\r\n     * 方向\r\n     *\r\n     * @type {Boolean}\r\n     */\r\n    vertical: {\r\n      type: Boolean,\r\n      default: false,\r\n    },\r\n  },\r\n  setup(props: Props) {\r\n    // IvueScrollbar\r\n    const IvueScrollbar = inject(ScrollbarContextKey) as ScrollbarContext;\r\n\r\n    // dom\r\n    const thumbWrapper = ref<HTMLDivElement>();\r\n    const thumb = ref<HTMLDivElement>();\r\n\r\n    // data\r\n    const data = reactive<Data>({\r\n      /**\r\n       * 状态\r\n       *\r\n       * @type {Object}\r\n       */\r\n      thumbState: {},\r\n      /**\r\n       * 显示\r\n       *\r\n       * @type {Boolean}\r\n       */\r\n      visible: false,\r\n    });\r\n\r\n    // wrapperClasses\r\n    const wrapperClasses = computed(() => {\r\n      return [\r\n        `${prefixCls}-bar`,\r\n        props.vertical ? 'is-vertical' : 'is-horizontal',\r\n      ];\r\n    });\r\n\r\n    // 滑动块样式\r\n    const thumbStyles = computed(() => {\r\n      let obj = {};\r\n\r\n      // 垂直\r\n      if (props.vertical) {\r\n        obj = {\r\n          height: props.barSize,\r\n          transform: `translateY(${props.move}%)`,\r\n        };\r\n      }\r\n      // 横向\r\n      else {\r\n        obj = {\r\n          width: props.barSize,\r\n          transform: `translateX(${props.move}%)`,\r\n        };\r\n      }\r\n\r\n      return obj;\r\n    });\r\n\r\n    // 偏移比率\r\n    const offsetRatio = computed(() => {\r\n      // thumbWrapper height = scrollbarWrapper height - 4\r\n\r\n      // 垂直\r\n      // offsetRatioY = 滚动条的原始高度 / 表格滚动高度 / ratioY / 滚动块高度\r\n      if (props.vertical) {\r\n        return (\r\n          thumbWrapper.value.offsetHeight ** 2 /\r\n          IvueScrollbar.scrollbarWrapper.scrollHeight /\r\n          props.ratio /\r\n          thumb.value.offsetHeight\r\n        );\r\n      }\r\n\r\n      // 横向\r\n      // offsetRatioX = 滚动条的原始宽度 / 表格滚动宽度 / ratioX / 滚动块宽度\r\n      if (!props.vertical) {\r\n        return (\r\n          thumbWrapper.value.offsetWidth ** 2 /\r\n          IvueScrollbar.scrollbarWrapper.scrollWidth /\r\n          props.ratio /\r\n          thumb.value.offsetWidth\r\n        );\r\n      }\r\n\r\n      return null;\r\n    });\r\n\r\n    // methods\r\n\r\n    // 滑动块拖动\r\n    const handleThumbMousedown = (event: MouseEvent) => {\r\n      event.stopPropagation();\r\n\r\n      // 防止中键和右键的点击事件;\r\n      if (event.ctrlKey || [1, 2].includes(event.button)) {\r\n        return;\r\n      }\r\n\r\n      // 取消所有选择\r\n      window.getSelection()?.removeAllRanges();\r\n\r\n      // 阻止监听同一事件的其他事件监听器被调用\r\n      event.stopImmediatePropagation();\r\n\r\n      // 鼠标按下\r\n      mousedown = true;\r\n\r\n      // 鼠标移动\r\n      on(document, 'mousemove', handleMousemove);\r\n\r\n      // 鼠标按下\r\n      on(document, 'mouseup', handleMouseup);\r\n\r\n      // 开始选择任意文本内容时触发\r\n      originalOnSelectStart = document.onselectstart;\r\n\r\n      // 禁止选中\r\n      document.onselectstart = () => false;\r\n\r\n      // 当前目标\r\n      const target = event.currentTarget as HTMLDivElement;\r\n\r\n      if (!target) {\r\n        return;\r\n      }\r\n\r\n      // 垂直\r\n      if (props.vertical) {\r\n        // 可偏移高度\r\n        data.thumbState['Y'] =\r\n          target.offsetHeight -\r\n          (event.clientY - target.getBoundingClientRect().top);\r\n      }\r\n\r\n      // 横向\r\n      if (!props.vertical) {\r\n        // 可偏移宽度\r\n        data.thumbState['X'] =\r\n          target.offsetWidth -\r\n          (event.clientX - target.getBoundingClientRect().left);\r\n      }\r\n    };\r\n\r\n    // 鼠标移动\r\n    const handleMousemove = (event: MouseEvent) => {\r\n      // 是否有dom\r\n      if (!thumbWrapper.value || !thumb.value) {\r\n        return;\r\n      }\r\n\r\n      // 鼠标按下\r\n      if (mousedown === false) {\r\n        return;\r\n      }\r\n\r\n      // 点击时的偏移位置\r\n      let clickOffset = 0;\r\n\r\n      // 拖动时的偏移位置\r\n      let moveOffset = 0;\r\n\r\n      // 点击位置\r\n      let clickPosition = 0;\r\n\r\n      // 垂直\r\n      if (props.vertical) {\r\n        // 是否可以偏移\r\n        clickOffset = data.thumbState['Y'];\r\n\r\n        if (!clickOffset) {\r\n          return;\r\n        }\r\n\r\n        // 拖动时的偏移位置\r\n        moveOffset =\r\n          (thumbWrapper.value.getBoundingClientRect().top - event.clientY) * -1;\r\n\r\n        // 在滚动条点击的位置\r\n        clickPosition = thumb.value.offsetHeight - clickOffset;\r\n\r\n        // 点击位置的百分比\r\n        const clickPositionPercentage =\r\n          // 拖动时的偏移位置 - 点击时的偏移位置 * 100 * 偏移比率\r\n          ((moveOffset - clickPosition) * 100 * offsetRatio.value) /\r\n          thumbWrapper.value.offsetHeight;\r\n\r\n        // 设置滚动高度\r\n        IvueScrollbar.scrollbarWrapper.scrollTop =\r\n          (clickPositionPercentage *\r\n            IvueScrollbar.scrollbarWrapper.scrollHeight) /\r\n          100;\r\n      }\r\n\r\n      // 横向\r\n      if (!props.vertical) {\r\n        // 是否可以偏移\r\n        clickOffset = data.thumbState['X'];\r\n\r\n        if (!clickOffset) {\r\n          return;\r\n        }\r\n\r\n        // 拖动时的偏移位置\r\n        moveOffset =\r\n          (thumbWrapper.value.getBoundingClientRect().left - event.clientX) *\r\n          -1;\r\n\r\n        // 在滚动条点击的位置\r\n        clickPosition = thumb.value.offsetWidth - clickOffset;\r\n\r\n        // 点击位置的百分比\r\n        const clickPositionPercentage =\r\n          ((moveOffset - clickPosition) * 100 * offsetRatio.value) /\r\n          thumbWrapper.value.offsetWidth;\r\n\r\n        // 设置滚动宽度\r\n        IvueScrollbar.scrollbarWrapper.scrollLeft =\r\n          (clickPositionPercentage *\r\n            IvueScrollbar.scrollbarWrapper.scrollWidth) /\r\n          100;\r\n      }\r\n    };\r\n\r\n    // 鼠标按下\r\n    const handleMouseup = () => {\r\n      mousedown = false;\r\n\r\n      // 垂直\r\n      if (props.vertical) {\r\n        data.thumbState['Y'] = 0;\r\n      }\r\n\r\n      // 横向\r\n      if (!props.vertical) {\r\n        data.thumbState['X'] = 0;\r\n      }\r\n\r\n      // 鼠标移动\r\n      off(document, 'mousemove', handleMousemove);\r\n\r\n      // 鼠标按下\r\n      off(document, 'mouseup', handleMouseup);\r\n\r\n      // 恢复选择任意文本内容时触发\r\n      handleRestoreSelectStart();\r\n\r\n      // 鼠标离开\r\n      if (mouseLeave) {\r\n        data.visible = false;\r\n      }\r\n    };\r\n\r\n    // 恢复选择\r\n    const handleRestoreSelectStart = () => {\r\n      if (document.onselectstart !== originalOnSelectStart) {\r\n        document.onselectstart = originalOnSelectStart;\r\n      }\r\n    };\r\n\r\n    // 滚动条点击\r\n    const handleThumbWrapperMousedown = (event: MouseEvent) => {\r\n      if (\r\n        !thumb.value ||\r\n        !thumbWrapper.value ||\r\n        !IvueScrollbar.scrollbarWrapper\r\n      ) {\r\n        return;\r\n      }\r\n\r\n      // 视口到鼠标点击的位置\r\n      let offset = 0;\r\n      // 滚动块的一半\r\n      let thumbHalf = 0;\r\n\r\n      // 元素的大小及其相对于视口的位置。\r\n      const boundingClientRect = (\r\n        event.target as HTMLElement\r\n      ).getBoundingClientRect();\r\n\r\n      // 滚动块的位置\r\n      let thumbPositionPercentage = 0;\r\n\r\n      // 垂直\r\n      if (props.vertical) {\r\n        // 视口到鼠标点击的位置\r\n        offset = Math.abs(boundingClientRect.top - event.clientY);\r\n\r\n        thumbHalf = thumb.value.offsetHeight / 2;\r\n\r\n        thumbPositionPercentage =\r\n          ((offset - thumbHalf) * 100 * offsetRatio.value) /\r\n          thumbWrapper.value.offsetHeight;\r\n\r\n        IvueScrollbar.scrollbarWrapper.scrollTop =\r\n          (thumbPositionPercentage *\r\n            IvueScrollbar.scrollbarWrapper.scrollHeight) /\r\n          100;\r\n      }\r\n\r\n      // 横向\r\n      if (!props.vertical) {\r\n        // 视口到鼠标点击的位置\r\n        offset = Math.abs(boundingClientRect.left - event.clientX);\r\n\r\n        thumbHalf = thumb.value.offsetWidth / 2;\r\n\r\n        thumbPositionPercentage =\r\n          ((offset - thumbHalf) * 100 * offsetRatio.value) /\r\n          thumbWrapper.value.offsetWidth;\r\n\r\n        IvueScrollbar.scrollbarWrapper.scrollLeft =\r\n          (thumbPositionPercentage *\r\n            IvueScrollbar.scrollbarWrapper.scrollWidth) /\r\n          100;\r\n      }\r\n    };\r\n\r\n    // 鼠标移动到表格\r\n    const handleMousemoveTable = () => {\r\n      mouseLeave = false;\r\n\r\n      data.visible = !!props.barSize;\r\n    };\r\n\r\n    // 鼠标移出到表格\r\n    const handleMouseleaveTable = () => {\r\n      mouseLeave = true;\r\n\r\n      data.visible = mousedown;\r\n    };\r\n\r\n    // onBeforeUnmount\r\n    onBeforeUnmount(() => {\r\n      handleRestoreSelectStart();\r\n\r\n      // 鼠标移动\r\n      off(document, 'mousemove', handleMousemove);\r\n\r\n      // 鼠标按下\r\n      off(document, 'mouseup', handleMouseup);\r\n    });\r\n\r\n    // onMounted\r\n    onMounted(() => {\r\n      // mousemove\r\n      useEventListener(\r\n        IvueScrollbar.scrollbar,\r\n        'mousemove',\r\n        handleMousemoveTable\r\n      );\r\n\r\n      // mouseleave\r\n      useEventListener(\r\n        IvueScrollbar.scrollbar,\r\n        'mouseleave',\r\n        handleMouseleaveTable\r\n      );\r\n    });\r\n\r\n    return {\r\n      prefixCls,\r\n      // dom\r\n      thumbWrapper,\r\n      thumb,\r\n\r\n      // data\r\n      data,\r\n\r\n      // computed\r\n      wrapperClasses,\r\n      thumbStyles,\r\n\r\n      // methods\r\n      handleThumbMousedown,\r\n      handleThumbWrapperMousedown,\r\n    };\r\n  },\r\n});\r\n</script>\r\n"],"names":["_openBlock","_createBlock","_Transition","_withCtx","_withDirectives","_createElementVNode","_normalizeClass","_normalizeStyle"],"mappings":";;;;AACmB,SAAA,YAAK,IAAS,EAAA,MAAA,EAAA,MAAA,EAAA,MAAA,EAAA,OAAA,QAAA,EAAA;EAA/B,OAAAA,SAAA,EAea,EAAAC,WAAA,CAAAC,UAAA,EAAA;AAAA,IAAA,IAAA,EAAA,GAAA,IAAA,CAAA,SAAA,CAAA,KAAA,CAAA;AAAA;;IAZH,OAAA,EAAAC,QAAA,MAAA;AAAA,MACIC,cAAA,CAAAC,mBAAA,KAAA,EAAA;AAAA,QACV,KAAA,EAAIC,cAAc,CAAA,IAAA,CAAA,cAAA,CAAA;AAAA,QAAA,WAAA,EAAA,MAAA,CAAA,CAAA,CAAA,KAAA,MAAA,CAAA,CAAA,CAAA,GAAA,CAAA,GAAA,IAAA,KAAA,IAAA,CAAA,2BAAA,IAAA,IAAA,CAAA,2BAAA,CAAA,GAAA,IAAA,CAAA,CAAA;AAAA,QAGlB,GAAA,EAAA,cAAA;AAAA,OAAA,EAAA;AAAA,2BAEQ,KAAE,EAAA;AAAA,UACP,KAAS,EAAAA,cAAA,CAAA,CAAA,EAAA,IAAA,CAAA,SAAA,CAAA,MAAA,CAAA,CAAA;AAAA,UACV,KAAA,EAAIC,cAAO,CAAA,IAAA,CAAA,WAAA,CAAA;AAAA,UAAA,WAAA,EAAA,MAAA,CAAA,CAAA,CAAA,KAAA,MAAA,CAAA,CAAA,CAAA,GAAA,CAAA,GAAA,IAAA,KAAA,IAAA,CAAA,oBAAA,IAAA,IAAA,CAAA,oBAAA,CAAA,GAAA,IAAA,CAAA,CAAA;AAAA;iBANL,EAAsB,CAAA;AAAA,OAAA,EAAA,EAAA,CAAA,EAAA;AAAA;;;;;;;;;;"}