1
0

Scrollbar.tsx 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. import { addResizeListener, removeResizeListener } from '/@/utils/event/resizeEvent';
  2. import scrollbarWidth from '/@/utils/scrollbarWidth';
  3. import { toObject } from './util';
  4. import Bar from './Bar';
  5. import { isString } from '/@/utils/is';
  6. import {
  7. defineComponent,
  8. PropType,
  9. unref,
  10. reactive,
  11. ref,
  12. provide,
  13. onMounted,
  14. nextTick,
  15. onBeforeUnmount,
  16. } from 'vue';
  17. import { getSlot } from '/@/utils/helper/tsxHelper';
  18. import './index.less';
  19. import { useExpose } from '/@/hooks/core/useExpose';
  20. import { ScrollbarType } from './types';
  21. export default defineComponent({
  22. name: 'Scrollbar',
  23. props: {
  24. native: Boolean as PropType<boolean>,
  25. wrapStyle: {
  26. type: Object as PropType<any>,
  27. },
  28. wrapClass: { type: String as PropType<string>, required: false },
  29. viewClass: { type: String as PropType<string> },
  30. viewStyle: { type: Object as PropType<any> },
  31. noresize: Boolean as PropType<boolean>,
  32. tag: {
  33. type: String as PropType<string>,
  34. default: 'div',
  35. },
  36. },
  37. setup(props, { slots }) {
  38. const resizeRef = ref<Nullable<HTMLDivElement>>(null);
  39. const wrapElRef = ref<Nullable<HTMLDivElement>>(null);
  40. provide('scroll-bar-wrap', wrapElRef);
  41. const state = reactive({
  42. sizeWidth: '0',
  43. sizeHeight: '0',
  44. moveX: 0,
  45. moveY: 0,
  46. });
  47. function handleScroll() {
  48. const warpEl = unref(wrapElRef);
  49. if (!warpEl) return;
  50. const { scrollTop, scrollLeft, clientHeight, clientWidth } = warpEl;
  51. state.moveY = (scrollTop * 100) / clientHeight;
  52. state.moveX = (scrollLeft * 100) / clientWidth;
  53. }
  54. function update() {
  55. const warpEl = unref(wrapElRef);
  56. if (!warpEl) return;
  57. const { scrollHeight, scrollWidth, clientHeight, clientWidth } = warpEl;
  58. const heightPercentage = (clientHeight * 100) / scrollHeight;
  59. const widthPercentage = (clientWidth * 100) / scrollWidth;
  60. state.sizeHeight = heightPercentage < 100 ? heightPercentage + '%' : '';
  61. state.sizeWidth = widthPercentage < 100 ? widthPercentage + '%' : '';
  62. }
  63. onMounted(() => {
  64. useExpose<ScrollbarType>({
  65. wrap: unref(wrapElRef),
  66. });
  67. const { native, noresize } = props;
  68. const resizeEl = unref(resizeRef);
  69. const warpEl = unref(wrapElRef);
  70. if (native || !resizeEl || !warpEl) return;
  71. nextTick(update);
  72. if (!noresize) {
  73. addResizeListener(resizeEl, update);
  74. addResizeListener(warpEl, update);
  75. }
  76. });
  77. onBeforeUnmount(() => {
  78. const { native, noresize } = props;
  79. const resizeEl = unref(resizeRef);
  80. const warpEl = unref(wrapElRef);
  81. if (native || !resizeEl || !warpEl) return;
  82. if (!noresize) {
  83. removeResizeListener(resizeEl, update);
  84. removeResizeListener(warpEl, update);
  85. }
  86. });
  87. return () => {
  88. const { native, tag, viewClass, viewStyle, wrapClass, wrapStyle } = props;
  89. let style: any = wrapStyle;
  90. const gutter = scrollbarWidth();
  91. if (gutter) {
  92. const gutterWith = `-${gutter}px`;
  93. const gutterStyle = `margin-bottom: ${gutterWith}; margin-right: ${gutterWith};`;
  94. if (Array.isArray(wrapStyle)) {
  95. style = toObject(wrapStyle);
  96. style.marginRight = style.marginBottom = gutterWith;
  97. } else if (isString(wrapStyle)) {
  98. style += gutterStyle;
  99. } else {
  100. style = gutterStyle;
  101. }
  102. }
  103. const Tag = tag as any;
  104. const view = (
  105. <Tag class={['scrollbar__view', viewClass]} style={viewStyle} ref={resizeRef}>
  106. {getSlot(slots)}
  107. </Tag>
  108. );
  109. const wrap = (
  110. <div
  111. ref={wrapElRef}
  112. style={style}
  113. onScroll={handleScroll}
  114. class={[wrapClass, 'scrollbar__wrap', gutter ? '' : 'scrollbar__wrap--hidden-default']}
  115. >
  116. {[view]}
  117. </div>
  118. );
  119. let nodes: any[] = [];
  120. const { moveX, sizeWidth, moveY, sizeHeight } = state;
  121. if (!native) {
  122. nodes = [
  123. wrap,
  124. /* eslint-disable */
  125. <Bar move={moveX} size={sizeWidth}></Bar>,
  126. <Bar vertical move={moveY} size={sizeHeight}></Bar>,
  127. ];
  128. } else {
  129. nodes = [
  130. <div ref="wrap" class={[wrapClass, 'scrollbar__wrap']} style={style}>
  131. {[view]}
  132. </div>,
  133. ];
  134. }
  135. return <div class="scrollbar">{nodes}</div>;
  136. };
  137. },
  138. });