Merge branch 'next' of github.com:vueComponent/ant-design-vue into next
						commit
						8cb7c65684
					
				
							
								
								
									
										8
									
								
								.jest.js
								
								
								
								
							
							
						
						
									
										8
									
								
								.jest.js
								
								
								
								
							| 
						 | 
				
			
			@ -13,12 +13,13 @@ if (process.env.WORKFLOW === 'true') {
 | 
			
		|||
module.exports = {
 | 
			
		||||
  testURL: 'http://localhost/',
 | 
			
		||||
  setupFiles: ['./tests/setup.js'],
 | 
			
		||||
  moduleFileExtensions: ['js', 'jsx', 'json', 'vue', 'md', 'jpg'],
 | 
			
		||||
  moduleFileExtensions: ['js', 'jsx', 'ts', 'tsx', 'json', 'vue', 'md', 'jpg'],
 | 
			
		||||
  modulePathIgnorePatterns: ['/_site/'],
 | 
			
		||||
  testPathIgnorePatterns: testPathIgnorePatterns,
 | 
			
		||||
  transform: {
 | 
			
		||||
    '^.+\\.(vue|md)$': '<rootDir>/node_modules/vue-jest',
 | 
			
		||||
    '^.+\\.(js|jsx)$': '<rootDir>/node_modules/babel-jest',
 | 
			
		||||
    '^.+\\.(ts|tsx)$': '<rootDir>/node_modules/ts-jest',
 | 
			
		||||
    '^.+\\.svg$': '<rootDir>/node_modules/jest-transform-stub',
 | 
			
		||||
  },
 | 
			
		||||
  testRegex: libDir === 'dist' ? 'demo\\.test\\.js$' : '.*\\.test\\.js$',
 | 
			
		||||
| 
						 | 
				
			
			@ -45,4 +46,9 @@ module.exports = {
 | 
			
		|||
  ],
 | 
			
		||||
  testEnvironment: 'jest-environment-jsdom-fifteen',
 | 
			
		||||
  transformIgnorePatterns,
 | 
			
		||||
  globals: {
 | 
			
		||||
    'ts-jest': {
 | 
			
		||||
      babelConfig: true,
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,7 +2,7 @@ import { getOptionProps } from './props-util';
 | 
			
		|||
 | 
			
		||||
export default {
 | 
			
		||||
  methods: {
 | 
			
		||||
    setState(state = {}, callback: () => any) {
 | 
			
		||||
    setState(state = {}, callback: () => void) {
 | 
			
		||||
      let newState = typeof state === 'function' ? state(this.$data, this.$props) : state;
 | 
			
		||||
      if (this.getDerivedStateFromProps) {
 | 
			
		||||
        const s = this.getDerivedStateFromProps(getOptionProps(this), {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -16,7 +16,7 @@ export type ClassValue =
 | 
			
		|||
  | boolean;
 | 
			
		||||
 | 
			
		||||
function classNames(...args: ClassValue[]): string {
 | 
			
		||||
  const classes = [];
 | 
			
		||||
  const classes: string[] = [];
 | 
			
		||||
  for (let i = 0; i < args.length; i++) {
 | 
			
		||||
    const value = args[i];
 | 
			
		||||
    if (!value) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,4 @@
 | 
			
		|||
import { PropType } from 'vue';
 | 
			
		||||
import { PropType, VNodeProps } from 'vue';
 | 
			
		||||
 | 
			
		||||
export type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
 | 
			
		||||
// https://stackoverflow.com/questions/46176165/ways-to-get-string-literal-type-of-array-values-without-enum-overhead
 | 
			
		||||
| 
						 | 
				
			
			@ -25,6 +25,8 @@ export type EventHandlers<E> = {
 | 
			
		|||
 | 
			
		||||
export type Data = Record<string, unknown>;
 | 
			
		||||
 | 
			
		||||
export type Key = string | number;
 | 
			
		||||
 | 
			
		||||
export declare type DefaultFactory<T> = (props: Data) => T | null | undefined;
 | 
			
		||||
export declare interface PropOptions<T = any, D = T> {
 | 
			
		||||
  type?: PropType<T> | true | null;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,11 +1,13 @@
 | 
			
		|||
import { PropType } from 'vue';
 | 
			
		||||
import isPlainObject from 'lodash-es/isPlainObject';
 | 
			
		||||
import { toType, getType, isFunction, validateType, isInteger, isArray, warn } from './utils';
 | 
			
		||||
interface BaseTypes {
 | 
			
		||||
  type: any;
 | 
			
		||||
  def: Function;
 | 
			
		||||
  validator: Function;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// interface BaseTypes {
 | 
			
		||||
//   type: any;
 | 
			
		||||
//   def: Function;
 | 
			
		||||
//   validator: Function;
 | 
			
		||||
// }
 | 
			
		||||
 | 
			
		||||
const PropTypes = {
 | 
			
		||||
  get any() {
 | 
			
		||||
    return toType('any', {
 | 
			
		||||
| 
						 | 
				
			
			@ -16,7 +18,7 @@ const PropTypes = {
 | 
			
		|||
  get func() {
 | 
			
		||||
    return {
 | 
			
		||||
      type: Function,
 | 
			
		||||
    } as BaseTypes;
 | 
			
		||||
    };
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  get bool() {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,7 +5,6 @@ import {
 | 
			
		|||
  defineComponent,
 | 
			
		||||
  SetupContext,
 | 
			
		||||
  App,
 | 
			
		||||
  watchEffect,
 | 
			
		||||
  VNodeTypes,
 | 
			
		||||
  CSSProperties,
 | 
			
		||||
} from 'vue';
 | 
			
		||||
| 
						 | 
				
			
			@ -44,29 +43,8 @@ const Tag = defineComponent({
 | 
			
		|||
    const { getPrefixCls } = inject('configProvider', defaultConfigProvider);
 | 
			
		||||
 | 
			
		||||
    const visible = ref(true);
 | 
			
		||||
 | 
			
		||||
    const props = attrs as TagProps;
 | 
			
		||||
 | 
			
		||||
    watchEffect(() => {
 | 
			
		||||
      if ('visible' in props) {
 | 
			
		||||
        visible.value = props.visible!;
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    const handleCloseClick = (e: MouseEvent) => {
 | 
			
		||||
      e.stopPropagation();
 | 
			
		||||
      if (props.onClose) {
 | 
			
		||||
        props.onClose(e);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (e.defaultPrevented) {
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      if (!('visible' in props)) {
 | 
			
		||||
        visible.value = false;
 | 
			
		||||
      }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    return () => {
 | 
			
		||||
      const {
 | 
			
		||||
        prefixCls: customizePrefixCls,
 | 
			
		||||
| 
						 | 
				
			
			@ -79,6 +57,24 @@ const Tag = defineComponent({
 | 
			
		|||
        ...restProps
 | 
			
		||||
      } = props;
 | 
			
		||||
 | 
			
		||||
      if ('visible' in props) {
 | 
			
		||||
        visible.value = props.visible!;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      const handleCloseClick = (e: MouseEvent) => {
 | 
			
		||||
        e.stopPropagation();
 | 
			
		||||
        if (props.onClose) {
 | 
			
		||||
          props.onClose(e);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (e.defaultPrevented) {
 | 
			
		||||
          return;
 | 
			
		||||
        }
 | 
			
		||||
        if (!('visible' in props)) {
 | 
			
		||||
          visible.value = false;
 | 
			
		||||
        }
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      const isPresetColor = (): boolean => {
 | 
			
		||||
        if (!color) {
 | 
			
		||||
          return false;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,19 +1,24 @@
 | 
			
		|||
// based on rc-resize-observer 0.1.3
 | 
			
		||||
import { defineComponent, PropType } from 'vue';
 | 
			
		||||
import ResizeObserver from 'resize-observer-polyfill';
 | 
			
		||||
import BaseMixin from '../_util/BaseMixin';
 | 
			
		||||
import { findDOMNode } from '../_util/props-util';
 | 
			
		||||
 | 
			
		||||
// Still need to be compatible with React 15, we use class component here
 | 
			
		||||
const VueResizeObserver = {
 | 
			
		||||
const VueResizeObserver = defineComponent({
 | 
			
		||||
  name: 'ResizeObserver',
 | 
			
		||||
  mixins: [BaseMixin],
 | 
			
		||||
  props: {
 | 
			
		||||
    disabled: Boolean,
 | 
			
		||||
    onResize: Function,
 | 
			
		||||
    onResize: Function as PropType<
 | 
			
		||||
      (size: { width: number; height: number; offsetWidth: number; offsetHeight: number }) => void
 | 
			
		||||
    >,
 | 
			
		||||
  },
 | 
			
		||||
  data() {
 | 
			
		||||
  beforeCreate() {
 | 
			
		||||
    this.currentElement = null;
 | 
			
		||||
    this.resizeObserver = null;
 | 
			
		||||
  },
 | 
			
		||||
  data() {
 | 
			
		||||
    return {
 | 
			
		||||
      width: 0,
 | 
			
		||||
      height: 0,
 | 
			
		||||
| 
						 | 
				
			
			@ -54,7 +59,7 @@ const VueResizeObserver = {
 | 
			
		|||
      }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    handleResize(entries) {
 | 
			
		||||
    handleResize(entries: ResizeObserverEntry[]) {
 | 
			
		||||
      const { target } = entries[0];
 | 
			
		||||
      const { width, height } = target.getBoundingClientRect();
 | 
			
		||||
      /**
 | 
			
		||||
| 
						 | 
				
			
			@ -82,8 +87,8 @@ const VueResizeObserver = {
 | 
			
		|||
  },
 | 
			
		||||
 | 
			
		||||
  render() {
 | 
			
		||||
    return this.$slots.default && this.$slots.default()[0];
 | 
			
		||||
    return this.$slots.default?.()[0];
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export default VueResizeObserver;
 | 
			
		||||
| 
						 | 
				
			
			@ -1,10 +1,23 @@
 | 
			
		|||
import classNames from '../_util/classNames';
 | 
			
		||||
import ResizeObserver from '../vc-resize-observer';
 | 
			
		||||
import { CSSProperties, FunctionalComponent } from 'vue';
 | 
			
		||||
 | 
			
		||||
const Filter = ({ height, offset, prefixCls, onInnerResize }, { slots }) => {
 | 
			
		||||
interface FillerProps {
 | 
			
		||||
  prefixCls?: string;
 | 
			
		||||
  /** Virtual filler height. Should be `count * itemMinHeight` */
 | 
			
		||||
  height: number;
 | 
			
		||||
  /** Set offset of visible items. Should be the top of start item position */
 | 
			
		||||
  offset?: number;
 | 
			
		||||
  onInnerResize?: () => void;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const Filter: FunctionalComponent<FillerProps> = (
 | 
			
		||||
  { height, offset, prefixCls, onInnerResize },
 | 
			
		||||
  { slots },
 | 
			
		||||
) => {
 | 
			
		||||
  let outerStyle = {};
 | 
			
		||||
 | 
			
		||||
  let innerStyle = {
 | 
			
		||||
  let innerStyle: CSSProperties = {
 | 
			
		||||
    display: 'flex',
 | 
			
		||||
    flexDirection: 'column',
 | 
			
		||||
  };
 | 
			
		||||
| 
						 | 
				
			
			@ -43,15 +56,8 @@ const Filter = ({ height, offset, prefixCls, onInnerResize }, { slots }) => {
 | 
			
		|||
    </div>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Filter.displayName = 'Filter';
 | 
			
		||||
Filter.inheritAttrs = false;
 | 
			
		||||
Filter.props = {
 | 
			
		||||
  prefixCls: String,
 | 
			
		||||
  /** Virtual filler height. Should be `count * itemMinHeight` */
 | 
			
		||||
  height: Number,
 | 
			
		||||
  /** Set offset of visible items. Should be the top of start item position */
 | 
			
		||||
  offset: Number,
 | 
			
		||||
  onInnerResize: Function,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default Filter;
 | 
			
		||||
| 
						 | 
				
			
			@ -1,17 +0,0 @@
 | 
			
		|||
import { cloneVNode } from 'vue';
 | 
			
		||||
 | 
			
		||||
function Item({ setRef }, { slots }) {
 | 
			
		||||
  const children = slots?.default();
 | 
			
		||||
  return children && children.length
 | 
			
		||||
    ? cloneVNode(children[0], {
 | 
			
		||||
        ref: setRef,
 | 
			
		||||
      })
 | 
			
		||||
    : children;
 | 
			
		||||
}
 | 
			
		||||
Item.props = {
 | 
			
		||||
  setRef: {
 | 
			
		||||
    type: Function,
 | 
			
		||||
    default: () => {},
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
export default Item;
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,17 @@
 | 
			
		|||
import { cloneVNode, FunctionalComponent } from 'vue';
 | 
			
		||||
 | 
			
		||||
export interface ItemProps {
 | 
			
		||||
  setRef: (element: HTMLElement) => void;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const Item: FunctionalComponent<ItemProps> = ({ setRef }, { slots }) => {
 | 
			
		||||
  const children = slots.default?.();
 | 
			
		||||
 | 
			
		||||
  return children && children.length
 | 
			
		||||
    ? cloneVNode(children[0], {
 | 
			
		||||
        ref: setRef,
 | 
			
		||||
      })
 | 
			
		||||
    : children;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default Item;
 | 
			
		||||
| 
						 | 
				
			
			@ -1,3 +1,16 @@
 | 
			
		|||
import {
 | 
			
		||||
  ref,
 | 
			
		||||
  defineComponent,
 | 
			
		||||
  PropType,
 | 
			
		||||
  watchEffect,
 | 
			
		||||
  Component,
 | 
			
		||||
  computed,
 | 
			
		||||
  nextTick,
 | 
			
		||||
  onBeforeUnmount,
 | 
			
		||||
  reactive,
 | 
			
		||||
  CSSProperties,
 | 
			
		||||
} from 'vue';
 | 
			
		||||
import { Key } from '../_util/type';
 | 
			
		||||
import Filler from './Filler';
 | 
			
		||||
import Item from './Item';
 | 
			
		||||
import ScrollBar from './ScrollBar';
 | 
			
		||||
| 
						 | 
				
			
			@ -7,18 +20,24 @@ import useFrameWheel from './hooks/useFrameWheel';
 | 
			
		|||
import useMobileTouchMove from './hooks/useMobileTouchMove';
 | 
			
		||||
import useOriginScroll from './hooks/useOriginScroll';
 | 
			
		||||
import PropTypes from '../_util/vue-types';
 | 
			
		||||
import { computed, nextTick, onBeforeUnmount, reactive, watchEffect } from 'vue';
 | 
			
		||||
import classNames from '../_util/classNames';
 | 
			
		||||
import createRef from '../_util/createRef';
 | 
			
		||||
import { RenderFunc, SharedConfig } from './interface';
 | 
			
		||||
 | 
			
		||||
const EMPTY_DATA = [];
 | 
			
		||||
 | 
			
		||||
const ScrollStyle = {
 | 
			
		||||
const ScrollStyle: CSSProperties = {
 | 
			
		||||
  overflowY: 'auto',
 | 
			
		||||
  overflowAnchor: 'none',
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function renderChildren(list, startIndex, endIndex, setNodeRef, renderFunc, { getKey }) {
 | 
			
		||||
function renderChildren<T>(
 | 
			
		||||
  list: T[],
 | 
			
		||||
  startIndex: number,
 | 
			
		||||
  endIndex: number,
 | 
			
		||||
  setNodeRef: (item: T, element: HTMLElement) => void,
 | 
			
		||||
  renderFunc: RenderFunc<T>,
 | 
			
		||||
  { getKey }: SharedConfig<T>,
 | 
			
		||||
) {
 | 
			
		||||
  return list.slice(startIndex, endIndex + 1).map((item, index) => {
 | 
			
		||||
    const eleIndex = startIndex + index;
 | 
			
		||||
    const node = renderFunc(item, eleIndex, {
 | 
			
		||||
| 
						 | 
				
			
			@ -33,43 +52,58 @@ function renderChildren(list, startIndex, endIndex, setNodeRef, renderFunc, { ge
 | 
			
		|||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const ListProps = {
 | 
			
		||||
  prefixCls: PropTypes.string,
 | 
			
		||||
  data: PropTypes.array,
 | 
			
		||||
  height: PropTypes.number,
 | 
			
		||||
  itemHeight: PropTypes.number,
 | 
			
		||||
  /** If not match virtual scroll condition, Set List still use height of container. */
 | 
			
		||||
  fullHeight: PropTypes.bool.def(true),
 | 
			
		||||
  itemKey: PropTypes.any,
 | 
			
		||||
  component: PropTypes.any,
 | 
			
		||||
  /** Set `false` will always use real scroll instead of virtual one */
 | 
			
		||||
  virtual: PropTypes.bool,
 | 
			
		||||
  children: PropTypes.func,
 | 
			
		||||
  onScroll: PropTypes.func,
 | 
			
		||||
};
 | 
			
		||||
export interface ListState<T = object> {
 | 
			
		||||
  scrollTop: number;
 | 
			
		||||
  scrollMoving: boolean;
 | 
			
		||||
  mergedData: T[];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const List = {
 | 
			
		||||
  props: ListProps,
 | 
			
		||||
const List = defineComponent({
 | 
			
		||||
  inheritAttrs: false,
 | 
			
		||||
  name: 'List',
 | 
			
		||||
  props: {
 | 
			
		||||
    prefixCls: PropTypes.string,
 | 
			
		||||
    data: PropTypes.array,
 | 
			
		||||
    height: PropTypes.number,
 | 
			
		||||
    itemHeight: PropTypes.number,
 | 
			
		||||
    /** If not match virtual scroll condition, Set List still use height of container. */
 | 
			
		||||
    fullHeight: PropTypes.bool,
 | 
			
		||||
    itemKey: {
 | 
			
		||||
      type: [String, Number, Function] as PropType<Key | ((item: object) => Key)>,
 | 
			
		||||
      required: true,
 | 
			
		||||
    },
 | 
			
		||||
    component: {
 | 
			
		||||
      type: [String, Object] as PropType<string | Component>,
 | 
			
		||||
    },
 | 
			
		||||
    /** Set `false` will always use real scroll instead of virtual one */
 | 
			
		||||
    virtual: PropTypes.bool,
 | 
			
		||||
    children: PropTypes.func,
 | 
			
		||||
    onScroll: PropTypes.func,
 | 
			
		||||
  },
 | 
			
		||||
  setup(props) {
 | 
			
		||||
    // ================================= MISC =================================
 | 
			
		||||
 | 
			
		||||
    const inVirtual = computed(() => {
 | 
			
		||||
      const { height, itemHeight, data, virtual } = props;
 | 
			
		||||
      return virtual !== false && height && itemHeight && data && itemHeight * data.length > height;
 | 
			
		||||
      return !!(
 | 
			
		||||
        virtual !== false &&
 | 
			
		||||
        height &&
 | 
			
		||||
        itemHeight &&
 | 
			
		||||
        data &&
 | 
			
		||||
        itemHeight * data.length > height
 | 
			
		||||
      );
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    const state = reactive({
 | 
			
		||||
    const state = reactive<ListState>({
 | 
			
		||||
      scrollTop: 0,
 | 
			
		||||
      scrollMoving: false,
 | 
			
		||||
      mergedData: computed(() => props.data || EMPTY_DATA),
 | 
			
		||||
      mergedData: computed(() => props.data || EMPTY_DATA) as any,
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    const componentRef = createRef();
 | 
			
		||||
    const componentRef = ref<Element>();
 | 
			
		||||
 | 
			
		||||
    // =============================== Item Key ===============================
 | 
			
		||||
    const getKey = item => {
 | 
			
		||||
    const getKey = (item: Record<string, any>) => {
 | 
			
		||||
      if (typeof props.itemKey === 'function') {
 | 
			
		||||
        return props.itemKey(item);
 | 
			
		||||
      }
 | 
			
		||||
| 
						 | 
				
			
			@ -81,8 +115,8 @@ const List = {
 | 
			
		|||
    };
 | 
			
		||||
 | 
			
		||||
    // ================================ Scroll ================================
 | 
			
		||||
    function syncScrollTop(newTop) {
 | 
			
		||||
      let value;
 | 
			
		||||
    function syncScrollTop(newTop: number | ((prev: number) => number)) {
 | 
			
		||||
      let value: number;
 | 
			
		||||
      if (typeof newTop === 'function') {
 | 
			
		||||
        value = newTop(state.scrollTop);
 | 
			
		||||
      } else {
 | 
			
		||||
| 
						 | 
				
			
			@ -91,8 +125,8 @@ const List = {
 | 
			
		|||
 | 
			
		||||
      const alignedTop = keepInRange(value);
 | 
			
		||||
 | 
			
		||||
      if (componentRef.current) {
 | 
			
		||||
        componentRef.current.scrollTop = alignedTop;
 | 
			
		||||
      if (componentRef.value) {
 | 
			
		||||
        componentRef.value.scrollTop = alignedTop;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      state.scrollTop = alignedTop;
 | 
			
		||||
| 
						 | 
				
			
			@ -112,9 +146,9 @@ const List = {
 | 
			
		|||
        };
 | 
			
		||||
      }
 | 
			
		||||
      let itemTop = 0;
 | 
			
		||||
      let startIndex;
 | 
			
		||||
      let startOffset;
 | 
			
		||||
      let endIndex;
 | 
			
		||||
      let startIndex: number | undefined;
 | 
			
		||||
      let startOffset: number | undefined;
 | 
			
		||||
      let endIndex: number | undefined;
 | 
			
		||||
      const dataLen = state.mergedData.length;
 | 
			
		||||
      for (let i = 0; i < dataLen; i += 1) {
 | 
			
		||||
        const item = state.mergedData[i];
 | 
			
		||||
| 
						 | 
				
			
			@ -122,7 +156,7 @@ const List = {
 | 
			
		|||
 | 
			
		||||
        const cacheHeight = heights[key];
 | 
			
		||||
        const currentItemBottom =
 | 
			
		||||
          itemTop + (cacheHeight === undefined ? props.itemHeight : cacheHeight);
 | 
			
		||||
          itemTop + (cacheHeight === undefined ? props.itemHeight! : cacheHeight);
 | 
			
		||||
 | 
			
		||||
        if (currentItemBottom >= state.scrollTop && startIndex === undefined) {
 | 
			
		||||
          startIndex = i;
 | 
			
		||||
| 
						 | 
				
			
			@ -130,7 +164,7 @@ const List = {
 | 
			
		|||
        }
 | 
			
		||||
 | 
			
		||||
        // Check item bottom in the range. We will render additional one item for motion usage
 | 
			
		||||
        if (currentItemBottom > state.scrollTop + props.height && endIndex === undefined) {
 | 
			
		||||
        if (currentItemBottom > state.scrollTop + props.height! && endIndex === undefined) {
 | 
			
		||||
          endIndex = i;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -157,9 +191,9 @@ const List = {
 | 
			
		|||
      };
 | 
			
		||||
    });
 | 
			
		||||
    // =============================== In Range ===============================
 | 
			
		||||
    const maxScrollHeight = computed(() => calRes.value.scrollHeight - props.height);
 | 
			
		||||
    const maxScrollHeight = computed(() => calRes.value.scrollHeight! - props.height!);
 | 
			
		||||
 | 
			
		||||
    function keepInRange(newScrollTop) {
 | 
			
		||||
    function keepInRange(newScrollTop: number) {
 | 
			
		||||
      let newTop = Math.max(newScrollTop, 0);
 | 
			
		||||
      if (!Number.isNaN(maxScrollHeight.value)) {
 | 
			
		||||
        newTop = Math.min(newTop, maxScrollHeight.value);
 | 
			
		||||
| 
						 | 
				
			
			@ -173,15 +207,15 @@ const List = {
 | 
			
		|||
    const originScroll = useOriginScroll(isScrollAtTop, isScrollAtBottom);
 | 
			
		||||
 | 
			
		||||
    // ================================ Scroll ================================
 | 
			
		||||
    function onScrollBar(newScrollTop) {
 | 
			
		||||
    function onScrollBar(newScrollTop: number) {
 | 
			
		||||
      const newTop = newScrollTop;
 | 
			
		||||
      syncScrollTop(newTop);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // This code may only trigger in test case.
 | 
			
		||||
    // But we still need a sync if some special escape
 | 
			
		||||
    function onFallbackScroll(e) {
 | 
			
		||||
      const { scrollTop: newScrollTop } = e.currentTarget;
 | 
			
		||||
    function onFallbackScroll(e: UIEvent) {
 | 
			
		||||
      const { scrollTop: newScrollTop } = e.currentTarget as Element;
 | 
			
		||||
      if (newScrollTop !== state.scrollTop) {
 | 
			
		||||
        syncScrollTop(newScrollTop);
 | 
			
		||||
      }
 | 
			
		||||
| 
						 | 
				
			
			@ -209,29 +243,29 @@ const List = {
 | 
			
		|||
        return false;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      onRawWheel({ preventDefault() {}, deltaY });
 | 
			
		||||
      onRawWheel({ preventDefault() {}, deltaY } as WheelEvent);
 | 
			
		||||
      return true;
 | 
			
		||||
    });
 | 
			
		||||
    // Firefox only
 | 
			
		||||
    function onMozMousePixelScroll(e) {
 | 
			
		||||
    function onMozMousePixelScroll(e: MouseEvent) {
 | 
			
		||||
      if (inVirtual.value) {
 | 
			
		||||
        e.preventDefault();
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    const removeEventListener = () => {
 | 
			
		||||
      if (componentRef.current) {
 | 
			
		||||
        componentRef.current.removeEventListener('wheel', onRawWheel);
 | 
			
		||||
        componentRef.current.removeEventListener('DOMMouseScroll', onFireFoxScroll);
 | 
			
		||||
        componentRef.current.removeEventListener('MozMousePixelScroll', onMozMousePixelScroll);
 | 
			
		||||
      if (componentRef.value) {
 | 
			
		||||
        componentRef.value.removeEventListener('wheel', onRawWheel);
 | 
			
		||||
        componentRef.value.removeEventListener('DOMMouseScroll', onFireFoxScroll);
 | 
			
		||||
        componentRef.value.removeEventListener('MozMousePixelScroll', onMozMousePixelScroll);
 | 
			
		||||
      }
 | 
			
		||||
    };
 | 
			
		||||
    watchEffect(() => {
 | 
			
		||||
      nextTick(() => {
 | 
			
		||||
        if (componentRef.current) {
 | 
			
		||||
        if (componentRef.value) {
 | 
			
		||||
          removeEventListener();
 | 
			
		||||
          componentRef.current.addEventListener('wheel', onRawWheel);
 | 
			
		||||
          componentRef.current.addEventListener('DOMMouseScroll', onFireFoxScroll);
 | 
			
		||||
          componentRef.current.addEventListener('MozMousePixelScroll', onMozMousePixelScroll);
 | 
			
		||||
          componentRef.value.addEventListener('wheel', onRawWheel);
 | 
			
		||||
          componentRef.value.addEventListener('DOMMouseScroll', onFireFoxScroll);
 | 
			
		||||
          componentRef.value.addEventListener('MozMousePixelScroll', onMozMousePixelScroll);
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
| 
						 | 
				
			
			@ -252,15 +286,15 @@ const List = {
 | 
			
		|||
    );
 | 
			
		||||
 | 
			
		||||
    const componentStyle = computed(() => {
 | 
			
		||||
      let cs = null;
 | 
			
		||||
      let cs: CSSProperties | null = null;
 | 
			
		||||
      if (props.height) {
 | 
			
		||||
        cs = { [props.fullHeight ? 'height' : 'maxHeight']: props.height + 'px', ...ScrollStyle };
 | 
			
		||||
 | 
			
		||||
        if (inVirtual.value) {
 | 
			
		||||
          cs.overflowY = 'hidden';
 | 
			
		||||
          cs!.overflowY = 'hidden';
 | 
			
		||||
 | 
			
		||||
          if (state.scrollMoving) {
 | 
			
		||||
            cs.pointerEvents = 'none';
 | 
			
		||||
            cs!.pointerEvents = 'none';
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
| 
						 | 
				
			
			@ -305,7 +339,6 @@ const List = {
 | 
			
		|||
      componentStyle,
 | 
			
		||||
      onFallbackScroll,
 | 
			
		||||
      onScrollBar,
 | 
			
		||||
      componentRef,
 | 
			
		||||
      inVirtual,
 | 
			
		||||
      collectHeight,
 | 
			
		||||
      sharedConfig,
 | 
			
		||||
| 
						 | 
				
			
			@ -332,7 +365,7 @@ const List = {
 | 
			
		|||
        <Component
 | 
			
		||||
          class={`${prefixCls}-holder`}
 | 
			
		||||
          style={componentStyle}
 | 
			
		||||
          ref={componentRef}
 | 
			
		||||
          ref="componentRef"
 | 
			
		||||
          onScroll={onFallbackScroll}
 | 
			
		||||
        >
 | 
			
		||||
          <Filler
 | 
			
		||||
| 
						 | 
				
			
			@ -364,6 +397,6 @@ const List = {
 | 
			
		|||
      </div>
 | 
			
		||||
    );
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export default List;
 | 
			
		||||
| 
						 | 
				
			
			@ -1,4 +1,4 @@
 | 
			
		|||
import { reactive } from 'vue';
 | 
			
		||||
import { defineComponent, PropType, reactive } from 'vue';
 | 
			
		||||
import classNames from '../_util/classNames';
 | 
			
		||||
import createRef from '../_util/createRef';
 | 
			
		||||
import raf from '../_util/raf';
 | 
			
		||||
| 
						 | 
				
			
			@ -6,29 +6,18 @@ import PropTypes from '../_util/vue-types';
 | 
			
		|||
 | 
			
		||||
const MIN_SIZE = 20;
 | 
			
		||||
 | 
			
		||||
// export interface ScrollBarProps {
 | 
			
		||||
//   prefixCls: string;
 | 
			
		||||
//   scrollTop: number;
 | 
			
		||||
//   scrollHeight: number;
 | 
			
		||||
//   height: number;
 | 
			
		||||
//   count: number;
 | 
			
		||||
//   onScroll: (scrollTop: number) => void;
 | 
			
		||||
//   onStartMove: () => void;
 | 
			
		||||
//   onStopMove: () => void;
 | 
			
		||||
// }
 | 
			
		||||
interface ScrollBarState {
 | 
			
		||||
  dragging: boolean;
 | 
			
		||||
  pageY: number | null;
 | 
			
		||||
  startTop: number | null;
 | 
			
		||||
  visible: boolean;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// interface ScrollBarState {
 | 
			
		||||
//   dragging: boolean;
 | 
			
		||||
//   pageY: number;
 | 
			
		||||
//   startTop: number;
 | 
			
		||||
//   visible: boolean;
 | 
			
		||||
// }
 | 
			
		||||
 | 
			
		||||
function getPageY(e) {
 | 
			
		||||
function getPageY(e: MouseEvent | TouchEvent) {
 | 
			
		||||
  return 'touches' in e ? e.touches[0].pageY : e.pageY;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
export default defineComponent({
 | 
			
		||||
  name: 'ScrollBar',
 | 
			
		||||
  inheritAttrs: false,
 | 
			
		||||
  props: {
 | 
			
		||||
| 
						 | 
				
			
			@ -37,9 +26,15 @@ export default {
 | 
			
		|||
    scrollHeight: PropTypes.number,
 | 
			
		||||
    height: PropTypes.number,
 | 
			
		||||
    count: PropTypes.number,
 | 
			
		||||
    onScroll: PropTypes.func,
 | 
			
		||||
    onStartMove: PropTypes.func,
 | 
			
		||||
    onStopMove: PropTypes.func,
 | 
			
		||||
    onScroll: {
 | 
			
		||||
      type: Function as PropType<(scrollTop: number) => void>,
 | 
			
		||||
    },
 | 
			
		||||
    onStartMove: {
 | 
			
		||||
      type: Function as PropType<() => void>,
 | 
			
		||||
    },
 | 
			
		||||
    onStopMove: {
 | 
			
		||||
      type: Function as PropType<() => void>,
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
  setup() {
 | 
			
		||||
    return {
 | 
			
		||||
| 
						 | 
				
			
			@ -47,7 +42,7 @@ export default {
 | 
			
		|||
      scrollbarRef: createRef(),
 | 
			
		||||
      thumbRef: createRef(),
 | 
			
		||||
      visibleTimeout: null,
 | 
			
		||||
      state: reactive({
 | 
			
		||||
      state: reactive<ScrollBarState>({
 | 
			
		||||
        dragging: false,
 | 
			
		||||
        pageY: null,
 | 
			
		||||
        startTop: null,
 | 
			
		||||
| 
						 | 
				
			
			@ -83,11 +78,11 @@ export default {
 | 
			
		|||
      }, 2000);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    onScrollbarTouchStart(e) {
 | 
			
		||||
    onScrollbarTouchStart(e: TouchEvent) {
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    onContainerMouseDown(e) {
 | 
			
		||||
    onContainerMouseDown(e: MouseEvent) {
 | 
			
		||||
      e.stopPropagation();
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
    },
 | 
			
		||||
| 
						 | 
				
			
			@ -114,7 +109,7 @@ export default {
 | 
			
		|||
    },
 | 
			
		||||
 | 
			
		||||
    // ======================= Thumb =======================
 | 
			
		||||
    onMouseDown(e) {
 | 
			
		||||
    onMouseDown(e: MouseEvent | TouchEvent) {
 | 
			
		||||
      const { onStartMove } = this.$props;
 | 
			
		||||
 | 
			
		||||
      Object.assign(this.state, {
 | 
			
		||||
| 
						 | 
				
			
			@ -129,7 +124,7 @@ export default {
 | 
			
		|||
      e.preventDefault();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    onMouseMove(e) {
 | 
			
		||||
    onMouseMove(e: MouseEvent | TouchEvent) {
 | 
			
		||||
      const { dragging, pageY, startTop } = this.state;
 | 
			
		||||
      const { onScroll } = this.$props;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -203,7 +198,7 @@ export default {
 | 
			
		|||
          bottom: 0,
 | 
			
		||||
          right: 0,
 | 
			
		||||
          position: 'absolute',
 | 
			
		||||
          display: visible ? null : 'none',
 | 
			
		||||
          display: visible ? undefined : 'none',
 | 
			
		||||
        }}
 | 
			
		||||
        onMousedown={this.onContainerMouseDown}
 | 
			
		||||
        onMousemove={this.delayHidden}
 | 
			
		||||
| 
						 | 
				
			
			@ -229,4 +224,4 @@ export default {
 | 
			
		|||
      </div>
 | 
			
		||||
    );
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
});
 | 
			
		||||
| 
						 | 
				
			
			@ -1,22 +1,33 @@
 | 
			
		|||
import { Ref } from 'vue';
 | 
			
		||||
import raf from '../../_util/raf';
 | 
			
		||||
import isFF from '../utils/isFirefox';
 | 
			
		||||
import useOriginScroll from './useOriginScroll';
 | 
			
		||||
 | 
			
		||||
export default function useFrameWheel(inVirtual, isScrollAtTop, isScrollAtBottom, onWheelDelta) {
 | 
			
		||||
interface FireFoxDOMMouseScrollEvent {
 | 
			
		||||
  detail: number;
 | 
			
		||||
  preventDefault: Function;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default function useFrameWheel(
 | 
			
		||||
  inVirtual: Ref<boolean>,
 | 
			
		||||
  isScrollAtTop: Ref<boolean>,
 | 
			
		||||
  isScrollAtBottom: Ref<boolean>,
 | 
			
		||||
  onWheelDelta: (offset: number) => void,
 | 
			
		||||
): [(e: WheelEvent) => void, (e: FireFoxDOMMouseScrollEvent) => void] {
 | 
			
		||||
  let offsetRef = 0;
 | 
			
		||||
  let nextFrame = null;
 | 
			
		||||
  let nextFrame: number | null | undefined = null;
 | 
			
		||||
 | 
			
		||||
  // Firefox patch
 | 
			
		||||
  let wheelValue = null;
 | 
			
		||||
  let wheelValue: null = null;
 | 
			
		||||
  let isMouseScroll = false;
 | 
			
		||||
 | 
			
		||||
  // Scroll status sync
 | 
			
		||||
  const originScroll = useOriginScroll(isScrollAtTop, isScrollAtBottom);
 | 
			
		||||
 | 
			
		||||
  function onWheel(event) {
 | 
			
		||||
  function onWheel(event: { preventDefault?: any; deltaY?: any }) {
 | 
			
		||||
    if (!inVirtual.value) return;
 | 
			
		||||
 | 
			
		||||
    raf.cancel(nextFrame);
 | 
			
		||||
    raf.cancel(nextFrame!);
 | 
			
		||||
 | 
			
		||||
    const { deltaY } = event;
 | 
			
		||||
    offsetRef += deltaY;
 | 
			
		||||
| 
						 | 
				
			
			@ -40,7 +51,7 @@ export default function useFrameWheel(inVirtual, isScrollAtTop, isScrollAtBottom
 | 
			
		|||
  }
 | 
			
		||||
 | 
			
		||||
  // A patch for firefox
 | 
			
		||||
  function onFireFoxScroll(event) {
 | 
			
		||||
  function onFireFoxScroll(event: { detail: any }) {
 | 
			
		||||
    if (!inVirtual.value) return;
 | 
			
		||||
 | 
			
		||||
    isMouseScroll = event.detail === wheelValue;
 | 
			
		||||
| 
						 | 
				
			
			@ -1,9 +1,15 @@
 | 
			
		|||
import { reactive, ref } from 'vue';
 | 
			
		||||
import { findDOMNode } from '../../_util/props-util';
 | 
			
		||||
import { reactive, Ref, ref, VNodeProps } from 'vue';
 | 
			
		||||
import { GetKey } from '../interface';
 | 
			
		||||
 | 
			
		||||
export default function useHeights(getKey, onItemAdd, onItemRemove) {
 | 
			
		||||
  const instance = new Map();
 | 
			
		||||
  const heights = reactive({});
 | 
			
		||||
type CacheMap = Record<string, number>;
 | 
			
		||||
 | 
			
		||||
export default function useHeights<T>(
 | 
			
		||||
  getKey: GetKey<T>,
 | 
			
		||||
  onItemAdd?: ((item: T) => void) | null,
 | 
			
		||||
  onItemRemove?: ((item: T) => void) | null,
 | 
			
		||||
): [(item: T, instance: HTMLElement) => void, () => void, CacheMap, Ref<number>] {
 | 
			
		||||
  const instance = new Map<VNodeProps['key'], HTMLElement>();
 | 
			
		||||
  const heights = reactive<CacheMap>({});
 | 
			
		||||
  let updatedMark = ref(0);
 | 
			
		||||
  let heightUpdateId = 0;
 | 
			
		||||
  function collectHeight() {
 | 
			
		||||
| 
						 | 
				
			
			@ -15,11 +21,10 @@ export default function useHeights(getKey, onItemAdd, onItemRemove) {
 | 
			
		|||
      let changed = false;
 | 
			
		||||
      instance.forEach((element, key) => {
 | 
			
		||||
        if (element && element.offsetParent) {
 | 
			
		||||
          const htmlElement = findDOMNode(element);
 | 
			
		||||
          const { offsetHeight } = htmlElement;
 | 
			
		||||
          if (heights[key] !== offsetHeight) {
 | 
			
		||||
          const { offsetHeight } = element;
 | 
			
		||||
          if (heights[key!] !== offsetHeight) {
 | 
			
		||||
            changed = true;
 | 
			
		||||
            heights[key] = htmlElement.offsetHeight;
 | 
			
		||||
            heights[key!] = element.offsetHeight;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
| 
						 | 
				
			
			@ -29,7 +34,7 @@ export default function useHeights(getKey, onItemAdd, onItemRemove) {
 | 
			
		|||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function setInstance(item, ins) {
 | 
			
		||||
  function setInstance(item: T, ins: HTMLElement) {
 | 
			
		||||
    const key = getKey(item);
 | 
			
		||||
    const origin = instance.get(key);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1,19 +1,28 @@
 | 
			
		|||
import { watch } from 'vue';
 | 
			
		||||
import { watch, Ref } from 'vue';
 | 
			
		||||
 | 
			
		||||
const SMOOTH_PTG = 14 / 15;
 | 
			
		||||
 | 
			
		||||
export default function useMobileTouchMove(inVirtual, listRef, callback) {
 | 
			
		||||
export default function useMobileTouchMove(
 | 
			
		||||
  inVirtual: Ref<boolean>,
 | 
			
		||||
  listRef: Ref<Element | undefined>,
 | 
			
		||||
  callback: (offsetY: number, smoothOffset?: boolean) => boolean,
 | 
			
		||||
) {
 | 
			
		||||
  let touched = false;
 | 
			
		||||
  let touchY = 0;
 | 
			
		||||
 | 
			
		||||
  let element = null;
 | 
			
		||||
  let element: HTMLElement | null = null;
 | 
			
		||||
 | 
			
		||||
  // Smooth scroll
 | 
			
		||||
  let interval = null;
 | 
			
		||||
  let interval: any = null;
 | 
			
		||||
 | 
			
		||||
  let cleanUpEvents;
 | 
			
		||||
  const cleanUpEvents = () => {
 | 
			
		||||
    if (element) {
 | 
			
		||||
      element.removeEventListener('touchmove', onTouchMove);
 | 
			
		||||
      element.removeEventListener('touchend', onTouchEnd);
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const onTouchMove = e => {
 | 
			
		||||
  const onTouchMove = (e: TouchEvent) => {
 | 
			
		||||
    if (touched) {
 | 
			
		||||
      const currentY = Math.ceil(e.touches[0].pageY);
 | 
			
		||||
      let offsetY = touchY - currentY;
 | 
			
		||||
| 
						 | 
				
			
			@ -41,31 +50,25 @@ export default function useMobileTouchMove(inVirtual, listRef, callback) {
 | 
			
		|||
    cleanUpEvents();
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const onTouchStart = e => {
 | 
			
		||||
  const onTouchStart = (e: TouchEvent) => {
 | 
			
		||||
    cleanUpEvents();
 | 
			
		||||
 | 
			
		||||
    if (e.touches.length === 1 && !touched) {
 | 
			
		||||
      touched = true;
 | 
			
		||||
      touchY = Math.ceil(e.touches[0].pageY);
 | 
			
		||||
 | 
			
		||||
      element = e.target;
 | 
			
		||||
      element.addEventListener('touchmove', onTouchMove);
 | 
			
		||||
      element.addEventListener('touchend', onTouchEnd);
 | 
			
		||||
      element = e.target as HTMLElement;
 | 
			
		||||
      element!.addEventListener('touchmove', onTouchMove);
 | 
			
		||||
      element!.addEventListener('touchend', onTouchEnd);
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  cleanUpEvents = () => {
 | 
			
		||||
    if (element) {
 | 
			
		||||
      element.removeEventListener('touchmove', onTouchMove);
 | 
			
		||||
      element.removeEventListener('touchend', onTouchEnd);
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
  watch(inVirtual, val => {
 | 
			
		||||
    listRef.current.removeEventListener('touchstart', onTouchStart);
 | 
			
		||||
    listRef.value.removeEventListener('touchstart', onTouchStart);
 | 
			
		||||
    cleanUpEvents();
 | 
			
		||||
    clearInterval(interval);
 | 
			
		||||
    if (val.value) {
 | 
			
		||||
      listRef.current.addEventListener('touchstart', onTouchStart);
 | 
			
		||||
    if (val) {
 | 
			
		||||
      listRef.value.addEventListener('touchstart', onTouchStart);
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,7 +1,9 @@
 | 
			
		|||
export default (isScrollAtTop, isScrollAtBottom) => {
 | 
			
		||||
import { Ref } from 'vue';
 | 
			
		||||
 | 
			
		||||
export default (isScrollAtTop: Ref<boolean>, isScrollAtBottom: Ref<boolean>) => {
 | 
			
		||||
  // Do lock for a wheel when scrolling
 | 
			
		||||
  let lock = false;
 | 
			
		||||
  let lockTimeout = null;
 | 
			
		||||
  let lockTimeout: any = null;
 | 
			
		||||
  function lockScroll() {
 | 
			
		||||
    clearTimeout(lockTimeout);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -11,7 +13,7 @@ export default (isScrollAtTop, isScrollAtBottom) => {
 | 
			
		|||
      lock = false;
 | 
			
		||||
    }, 50);
 | 
			
		||||
  }
 | 
			
		||||
  return (deltaY, smoothOffset = false) => {
 | 
			
		||||
  return (deltaY: number, smoothOffset = false) => {
 | 
			
		||||
    const originScroll =
 | 
			
		||||
      // Pass origin wheel when on the top
 | 
			
		||||
      (deltaY < 0 && isScrollAtTop.value) ||
 | 
			
		||||
| 
						 | 
				
			
			@ -1,41 +1,43 @@
 | 
			
		|||
/* eslint-disable no-param-reassign */
 | 
			
		||||
 | 
			
		||||
import { Data } from '../../_util/type';
 | 
			
		||||
import { Ref } from 'vue';
 | 
			
		||||
import raf from '../../_util/raf';
 | 
			
		||||
import { GetKey } from '../interface';
 | 
			
		||||
import { ListState } from '../List';
 | 
			
		||||
 | 
			
		||||
export default function useScrollTo(
 | 
			
		||||
  containerRef,
 | 
			
		||||
  state,
 | 
			
		||||
  heights,
 | 
			
		||||
  containerRef: Ref<Element | undefined>,
 | 
			
		||||
  state: ListState,
 | 
			
		||||
  heights: Data,
 | 
			
		||||
  props,
 | 
			
		||||
  getKey,
 | 
			
		||||
  collectHeight,
 | 
			
		||||
  syncScrollTop,
 | 
			
		||||
  getKey: GetKey,
 | 
			
		||||
  collectHeight: () => void,
 | 
			
		||||
  syncScrollTop: (newTop: number) => void,
 | 
			
		||||
) {
 | 
			
		||||
  let scroll = null;
 | 
			
		||||
  let scroll: number | null = null;
 | 
			
		||||
 | 
			
		||||
  return arg => {
 | 
			
		||||
    raf.cancel(scroll);
 | 
			
		||||
    raf.cancel(scroll!);
 | 
			
		||||
    const data = state.mergedData;
 | 
			
		||||
    const itemHeight = props.itemHeight;
 | 
			
		||||
    if (typeof arg === 'number') {
 | 
			
		||||
      syncScrollTop(arg);
 | 
			
		||||
    } else if (arg && typeof arg === 'object') {
 | 
			
		||||
      let index;
 | 
			
		||||
      let index: number;
 | 
			
		||||
      const { align } = arg;
 | 
			
		||||
 | 
			
		||||
      if ('index' in arg) {
 | 
			
		||||
        ({ index } = arg);
 | 
			
		||||
      } else {
 | 
			
		||||
        index = data.findIndex(item => getKey(item) === arg.key);
 | 
			
		||||
        index = data.findIndex((item: object) => getKey(item) === arg.key);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      const { offset = 0 } = arg;
 | 
			
		||||
 | 
			
		||||
      // We will retry 3 times in case dynamic height shaking
 | 
			
		||||
      const syncScroll = (times, targetAlign) => {
 | 
			
		||||
        if (times < 0 || !containerRef.current) return;
 | 
			
		||||
      const syncScroll = (times: number, targetAlign?: 'top' | 'bottom') => {
 | 
			
		||||
        if (times < 0 || !containerRef.value) return;
 | 
			
		||||
 | 
			
		||||
        const height = containerRef.current.clientHeight;
 | 
			
		||||
        const height = containerRef.value.clientHeight;
 | 
			
		||||
        let needCollectHeight = false;
 | 
			
		||||
        let newTargetAlign = targetAlign;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -51,7 +53,7 @@ export default function useScrollTo(
 | 
			
		|||
          for (let i = 0; i <= index; i += 1) {
 | 
			
		||||
            const key = getKey(data[i]);
 | 
			
		||||
            itemTop = stackTop;
 | 
			
		||||
            const cacheHeight = heights[key];
 | 
			
		||||
            const cacheHeight = heights[key!];
 | 
			
		||||
            itemBottom = itemTop + (cacheHeight === undefined ? itemHeight : cacheHeight);
 | 
			
		||||
 | 
			
		||||
            stackTop = itemBottom;
 | 
			
		||||
| 
						 | 
				
			
			@ -62,7 +64,7 @@ export default function useScrollTo(
 | 
			
		|||
          }
 | 
			
		||||
 | 
			
		||||
          // Scroll to
 | 
			
		||||
          let targetTop = null;
 | 
			
		||||
          let targetTop: number | null = null;
 | 
			
		||||
 | 
			
		||||
          switch (mergedAlign) {
 | 
			
		||||
            case 'top':
 | 
			
		||||
| 
						 | 
				
			
			@ -73,7 +75,7 @@ export default function useScrollTo(
 | 
			
		|||
              break;
 | 
			
		||||
 | 
			
		||||
            default: {
 | 
			
		||||
              const { scrollTop } = containerRef.current;
 | 
			
		||||
              const { scrollTop } = containerRef.value;
 | 
			
		||||
              const scrollBottom = scrollTop + height;
 | 
			
		||||
              if (itemTop < scrollTop) {
 | 
			
		||||
                newTargetAlign = 'top';
 | 
			
		||||
| 
						 | 
				
			
			@ -83,7 +85,7 @@ export default function useScrollTo(
 | 
			
		|||
            }
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          if (targetTop !== null && targetTop !== containerRef.current.scrollTop) {
 | 
			
		||||
          if (targetTop !== null && targetTop !== containerRef.value.scrollTop) {
 | 
			
		||||
            syncScrollTop(targetTop);
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,14 @@
 | 
			
		|||
import { CSSProperties, VNodeTypes } from 'vue';
 | 
			
		||||
import { Key } from '../_util/type';
 | 
			
		||||
 | 
			
		||||
export type RenderFunc<T> = (
 | 
			
		||||
  item: T,
 | 
			
		||||
  index: number,
 | 
			
		||||
  props: { style?: CSSProperties },
 | 
			
		||||
) => VNodeTypes;
 | 
			
		||||
 | 
			
		||||
export interface SharedConfig<T> {
 | 
			
		||||
  getKey: (item: T) => Key;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export type GetKey<T = object> = (item: T) => Key;
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,9 @@
 | 
			
		|||
import { defaultTo } from 'lodash-es';
 | 
			
		||||
import Empty from '../components/empty';
 | 
			
		||||
import '../components/empty/style';
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
  render() {
 | 
			
		||||
    return <Empty />;
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -130,7 +130,7 @@
 | 
			
		|||
    "html-webpack-plugin": "^3.2.0",
 | 
			
		||||
    "husky": "^4.0.0",
 | 
			
		||||
    "istanbul-instrumenter-loader": "^3.0.0",
 | 
			
		||||
    "jest": "^25.4.0",
 | 
			
		||||
    "jest": "^26.0.0",
 | 
			
		||||
    "jest-environment-jsdom-fifteen": "^1.0.2",
 | 
			
		||||
    "jest-serializer-vue": "^2.0.0",
 | 
			
		||||
    "jest-transform-stub": "^2.0.0",
 | 
			
		||||
| 
						 | 
				
			
			@ -168,6 +168,7 @@
 | 
			
		|||
    "stylelint-config-standard": "^19.0.0",
 | 
			
		||||
    "terser-webpack-plugin": "^3.0.3",
 | 
			
		||||
    "through2": "^3.0.0",
 | 
			
		||||
    "ts-jest": "^26.4.1",
 | 
			
		||||
    "ts-loader": "^8.0.2",
 | 
			
		||||
    "typescript": "^4.0.2",
 | 
			
		||||
    "umi-mock-middleware": "^1.0.0",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -12,7 +12,7 @@
 | 
			
		|||
    "jsx": "preserve",
 | 
			
		||||
    "noUnusedParameters": true,
 | 
			
		||||
    "noUnusedLocals": true,
 | 
			
		||||
    "noImplicitAny": true,
 | 
			
		||||
    "noImplicitAny": false,
 | 
			
		||||
    "target": "es6",
 | 
			
		||||
    "lib": ["dom", "es2017"],
 | 
			
		||||
    "skipLibCheck": true
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue