import { ComputedRef, Ref, ShallowRef, computed, onBeforeUnmount, onMounted, ref } from 'vue';
import { StkTableColumn, UniqKey } from './types';
import { getCalculatedColWidth } from './utils/constRefUtils';
type ColResizeState
> = {
/** 当前被拖动的列*/
currentCol: StkTableColumn | null;
/** 最后一个叶子列 */
lastCol: StkTableColumn | null;
/** 鼠标按下开始位置 */
startX: number;
/** 鼠标按下时鼠标对于表格的偏移量 */
startOffsetTableX: 0;
/** 是否反向计算,true:左增右减。false:左减右增 */
revertMoveX: boolean;
};
/** 列宽拖动 */
export function useColResize>(
props: any,
emits: any,
tableContainerRef: Ref,
tableHeaderLast: ShallowRef[]>,
colResizeIndicatorRef: Ref,
colKeyGen: ComputedRef<(p: any) => UniqKey>,
fixedCols: Ref[]>,
onColWidthChange?: () => void,
) {
/** 列宽是否在拖动 */
const isColResizing = ref(false);
/** 列宽调整状态 */
let colResizeState: ColResizeState = {
currentCol: null,
lastCol: null,
startX: 0,
startOffsetTableX: 0,
revertMoveX: false,
};
/** 是否可拖动 */
const colResizeOn = computed(() => {
if (Object.prototype.toString.call(props.colResizable) === '[object Object]') {
return (col: StkTableColumn) => !props.colResizable.disabled(col);
}
return () => props.colResizable;
});
onMounted(() => {
initColResizeEvent();
});
onBeforeUnmount(() => {
clearColResizeEvent();
});
/** 初始化列宽拖动事件 */
function initColResizeEvent() {
window.addEventListener('mousemove', onThResizeMouseMove);
window.addEventListener('mouseup', onThResizeMouseUp);
}
/** 清除列宽拖动事件 */
function clearColResizeEvent() {
window.removeEventListener('mousemove', onThResizeMouseMove);
window.removeEventListener('mouseup', onThResizeMouseUp);
}
/**
* 拖动开始
* @param e
* @param col 当前列配置
* @param leftHandle 是否是左侧的拖动条
*/
function onThResizeMouseDown(e: MouseEvent, col: StkTableColumn, leftHandle = false) {
if (!tableContainerRef.value) return;
e.stopPropagation();
e.preventDefault();
const { clientX } = e;
const { scrollLeft, scrollTop } = tableContainerRef.value;
const { left } = tableContainerRef.value.getBoundingClientRect();
const tableHeaderLastValue = tableHeaderLast.value;
let revertMoveX = false;
const colKey = colKeyGen.value;
const colKeyValue = colKey(col);
const colIndex = tableHeaderLastValue.findIndex(it => colKey(it) === colKeyValue);
const fixedIndex = fixedCols.value.indexOf(col);
const isColFixed = fixedIndex !== -1;
if (leftHandle) {
// 左侧拖动条
if (isColFixed && col.fixed === 'right') {
// 对于固定右侧的列,拖动左侧的把,需要反向计算
revertMoveX = true;
} else {
// 取上一列
if (colIndex - 1 >= 0) {
col = tableHeaderLastValue[colIndex - 1];
}
}
} else {
// 右侧拖动条
if (isColFixed && col.fixed === 'right') {
// 对于固定右侧的列,拖动右侧的把,需要拖动下一固定的列
col = fixedCols.value[fixedIndex + 1] || col;
}
}
const offsetTableX = clientX - left + scrollLeft;
// 记录拖动状态
isColResizing.value = true;
Object.assign(colResizeState, {
currentCol: col,
lastCol: findLastChildCol(col),
startX: clientX,
startOffsetTableX: offsetTableX,
revertMoveX,
});
// 展示指示线,更新其位置
if (colResizeIndicatorRef.value) {
const style = colResizeIndicatorRef.value.style;
style.display = 'block';
style.left = offsetTableX + 'px';
style.top = scrollTop + 'px';
}
}
/**
* @param {MouseEvent} e
*/
function onThResizeMouseMove(e: MouseEvent) {
if (!isColResizing.value) return;
e.stopPropagation();
e.preventDefault();
const { lastCol, startX, startOffsetTableX } = colResizeState;
const { clientX } = e;
let moveX = clientX - startX;
const currentColWidth = getCalculatedColWidth(lastCol);
const minWidth = lastCol?.minWidth ?? props.colMinWidth;
// 移动量不小于最小列宽
if (currentColWidth + moveX < minWidth) {
moveX = -currentColWidth;
}
const offsetTableX = startOffsetTableX + moveX;
if (!colResizeIndicatorRef.value) return;
colResizeIndicatorRef.value.style.left = offsetTableX + 'px';
}
/**
* @param {MouseEvent} e
*/
function onThResizeMouseUp(e: MouseEvent) {
if (!isColResizing.value) return;
const { startX, lastCol, revertMoveX } = colResizeState;
const { clientX } = e;
const moveX = revertMoveX ? startX - clientX : clientX - startX;
// 移动量不小于最小列宽
let width = getCalculatedColWidth(lastCol) + moveX;
if (width < props.colMinWidth) width = props.colMinWidth;
const colKey = colKeyGen.value;
const curCol = tableHeaderLast.value.find(it => colKey(it) === colKey(lastCol));
if (curCol) {
curCol.width = width + 'px';
onColWidthChange?.();
emits('update:columns', props.columns.slice());
emits('col-resize', { ...curCol });
}
// 隐藏指示线
if (colResizeIndicatorRef.value) {
const style = colResizeIndicatorRef.value.style;
style.display = 'none';
style.left = '0';
style.top = '0';
}
// 清除拖动状态
isColResizing.value = false;
colResizeState = {
currentCol: null,
lastCol: null,
startX: 0,
startOffsetTableX: 0,
revertMoveX: false,
};
}
/**获取最后一个叶子 */
function findLastChildCol(column: StkTableColumn | null) {
if (column?.children?.length) {
const lastChild = column.children.slice(-1)[0] as StkTableColumn;
return findLastChildCol(lastChild);
}
return column;
}
return [colResizeOn, isColResizing, onThResizeMouseDown] as const;
}