import { ComputedRef, Ref, ShallowRef, onBeforeUnmount, onMounted, watch } from 'vue';
import { AreaSelectionConfig, StkTableColumn } from './types';
import { VirtualScrollStore, VirtualScrollXStore } from './useVirtualScroll';
/** 翻页按键 */
const ScrollCodes = {
ArrowUp: 'ArrowUp',
ArrowRight: 'ArrowRight',
ArrowDown: 'ArrowDown',
ArrowLeft: 'ArrowLeft',
PageUp: 'PageUp',
PageDown: 'PageDown',
Home: 'Home',
End: 'End',
} as const;
type ScrollCodes = (typeof ScrollCodes)[keyof typeof ScrollCodes];
/** 所有翻页按键数组 */
const ScrollCodesValues = Object.values(ScrollCodes);
/**
* 按下键盘箭头滚动。只有悬浮在表体上才能生效键盘。
*
* 在低版本浏览器中,虚拟滚动时,使用键盘滚动,等选中的行消失在视口外时,滚动会失效。
*/
export function useKeyboardArrowScroll
>(
targetElement: Ref,
props: any,
scrollTo: (y: number | null, x: number | null) => void,
virtualScroll: Ref,
virtualScrollX: Ref,
tableHeaders: ShallowRef[][]>,
virtual_on: ComputedRef,
areaSelectionConfig: ComputedRef,
) {
/** 检测鼠标是否悬浮在表格体上 */
let isMouseOver = false;
watch(virtual_on, val => {
removeListeners();
if (val) {
addEventListeners();
}
});
onMounted(addEventListeners);
onBeforeUnmount(removeListeners);
function addEventListeners() {
window.addEventListener('keydown', handleKeydown);
targetElement.value?.addEventListener('mouseenter', handleMouseEnter);
targetElement.value?.addEventListener('mouseleave', handleMouseLeave);
targetElement.value?.addEventListener('mousedown', handleMouseDown);
}
function removeListeners() {
window.removeEventListener('keydown', handleKeydown);
targetElement.value?.removeEventListener('mouseenter', handleMouseEnter);
targetElement.value?.removeEventListener('mouseleave', handleMouseLeave);
targetElement.value?.removeEventListener('mousedown', handleMouseDown);
}
/** 键盘按下事件 */
function handleKeydown(e: KeyboardEvent) {
if (!virtual_on.value) return; // 非虚拟滚动使用浏览器默认滚动
// 如果单元格选区键盘控制已启用,则不处理滚动,交给 useAreaSelection 处理
if (areaSelectionConfig.value.keyboard) return;
const keyCode = e.code;
if (!ScrollCodesValues.includes(keyCode as any)) return;
if (!isMouseOver) return; // 不悬浮还是要触发键盘事件的
e.preventDefault(); // 不触发键盘默认的箭头事件
const { scrollTop, rowHeight, containerHeight, scrollHeight } = virtualScroll.value;
const { scrollLeft } = virtualScrollX.value;
const { headless, headerRowHeight } = props;
// 这里不用virtualScroll 中的pageSize,因为我需要上一页的最后一条放在下一页的第一条
const headerHeight = headless ? 0 : tableHeaders.value.length * (headerRowHeight || rowHeight);
/** 表体的page */
const bodyPageSize = Math.floor((containerHeight - headerHeight) / rowHeight);
if (keyCode === ScrollCodes.ArrowUp) {
scrollTo(scrollTop - rowHeight, null);
} else if (keyCode === ScrollCodes.ArrowRight) {
scrollTo(null, scrollLeft + 50);
} else if (keyCode === ScrollCodes.ArrowDown) {
scrollTo(scrollTop + rowHeight, null);
} else if (keyCode === ScrollCodes.ArrowLeft) {
scrollTo(null, scrollLeft - 50);
} else if (keyCode === ScrollCodes.PageUp) {
scrollTo(scrollTop - rowHeight * bodyPageSize + headerHeight, null);
} else if (keyCode === ScrollCodes.PageDown) {
scrollTo(scrollTop + rowHeight * bodyPageSize - headerHeight, null);
} else if (keyCode === ScrollCodes.Home) {
scrollTo(0, null);
} else if (keyCode === ScrollCodes.End) {
scrollTo(scrollHeight, null);
}
}
function handleMouseEnter() {
isMouseOver = true;
}
function handleMouseLeave() {
isMouseOver = false;
}
/**
* 兜底。
* 是否存在不触发mouseEnter的时候?
*/
function handleMouseDown() {
if (!isMouseOver) isMouseOver = true;
}
}