ant-design-vue/components/vc-table/Header/DragHandle.tsx

181 lines
4.8 KiB
Vue
Raw Normal View History

2021-10-27 14:58:55 +08:00
import addEventListenerWrap from '../../vc-util/Dom/addEventListener';
import type { EventHandler } from '../../_util/EventInterface';
import raf from '../../_util/raf';
import {
defineComponent,
onUnmounted,
nextTick,
watch,
computed,
ref,
watchEffect,
getCurrentInstance,
onMounted,
} from 'vue';
import type { PropType } from 'vue';
import devWarning from '../../vc-util/devWarning';
import type { ColumnType } from '../interface';
import { useInjectTableContext } from '../../table/context';
import supportsPassive from '../../_util/supportsPassive';
const events = {
mouse: {
start: 'mousedown',
move: 'mousemove',
stop: 'mouseup',
},
touch: {
start: 'touchstart',
move: 'touchmove',
stop: 'touchend',
},
};
type HandleEvent = MouseEvent & TouchEvent;
const defaultMinWidth = 50;
export default defineComponent({
name: 'DragHandle',
props: {
prefixCls: String,
width: {
type: Number,
required: true,
},
minWidth: {
type: Number,
default: defaultMinWidth,
},
maxWidth: {
type: Number,
default: Infinity,
},
column: {
type: Object as PropType<ColumnType<any>>,
default: undefined as ColumnType<any>,
},
},
setup(props) {
let startX = 0;
let moveEvent = { remove: () => {} };
let stopEvent = { remove: () => {} };
const removeEvents = () => {
moveEvent.remove();
stopEvent.remove();
};
onUnmounted(() => {
removeEvents();
});
watchEffect(() => {
devWarning(!isNaN(props.width), 'Table', 'width must be a number when use resizable');
});
const { onResizeColumn } = useInjectTableContext();
const minWidth = computed(() => {
return typeof props.minWidth === 'number' && !isNaN(props.minWidth)
? props.minWidth
: defaultMinWidth;
});
const maxWidth = computed(() => {
return typeof props.maxWidth === 'number' && !isNaN(props.maxWidth)
? props.maxWidth
: Infinity;
});
const instance = getCurrentInstance();
// eslint-disable-next-line vue/no-setup-props-destructure
let baseWidth = props.width;
onMounted(() => {
nextTick(() => {
baseWidth = instance.vnode.el?.parentNode?.getBoundingClientRect().width;
});
});
const dragging = ref(false);
let rafId: number;
const updateWidth = (e: HandleEvent) => {
let pageX = 0;
if (e.touches) {
if (e.touches.length) {
// touchmove
pageX = e.touches[0].pageX;
} else {
// touchend
pageX = e.changedTouches[0].pageX;
}
} else {
pageX = e.pageX;
}
const tmpDeltaX = startX - pageX;
let w = Math.max(baseWidth - tmpDeltaX, minWidth.value);
w = Math.min(w, maxWidth.value);
raf.cancel(rafId);
rafId = raf(() => {
onResizeColumn(w, props.column.__originColumn__);
});
};
const handleMove = (e: HandleEvent) => {
updateWidth(e);
};
const handleStop = (e: HandleEvent) => {
dragging.value = false;
updateWidth(e);
nextTick(() => {
baseWidth = instance.vnode.el?.parentNode?.getBoundingClientRect().width;
});
removeEvents();
};
const handleStart = (e: HandleEvent, eventsFor: any) => {
dragging.value = true;
removeEvents();
if (e instanceof MouseEvent && e.which !== 1) {
return;
}
if (e.stopPropagation) e.stopPropagation();
startX = e.touches ? e.touches[0].pageX : e.pageX;
moveEvent = addEventListenerWrap(document.documentElement, eventsFor.move, handleMove);
stopEvent = addEventListenerWrap(document.documentElement, eventsFor.stop, handleStop);
};
const handleDown: EventHandler = (e: HandleEvent) => {
e.stopPropagation();
e.preventDefault();
handleStart(e, events.mouse);
};
const handleTouchDown: EventHandler = (e: HandleEvent) => {
e.stopPropagation();
e.preventDefault();
handleStart(e, events.touch);
};
const handleClick: EventHandler = (e: HandleEvent) => {
e.stopPropagation();
e.preventDefault();
};
watch(
() => props.width,
() => {
if (!dragging.value) {
baseWidth = props.width;
}
},
{ immediate: true },
);
return () => {
const { prefixCls } = props;
const touchEvents = {
[supportsPassive ? 'onTouchstartPassive' : 'onTouchstart']: e => handleTouchDown(e),
};
return (
<div
class={`${prefixCls}-resize-handle ${dragging.value ? 'dragging' : ''}`}
onMousedown={handleDown}
{...touchEvents}
onClick={handleClick}
>
<div class={`${prefixCls}-resize-handle-line`}></div>
</div>
);
};
},
});