import { computed, ComputedRef, Ref, ShallowRef, shallowRef } from 'vue';
import { StkTableColumn, UniqKey } from './types';
import { VirtualScrollXStore } from './useVirtualScroll';
import { getCalculatedColWidth } from './utils/constRefUtils';
/**
* 固定列处理
* @returns
*/
export function useFixedCol
>(
props: any,
colKeyGen: ComputedRef<(col: StkTableColumn) => UniqKey>,
getFixedColPosition: ComputedRef<(col: StkTableColumn) => number>,
tableHeaders: ShallowRef[][]>,
tableHeadersForCalc: ShallowRef[][]>,
tableContainerRef: Ref,
) {
/** 保存需要出现阴影的列 */
const fixedShadowCols = shallowRef[]>([]);
/** 正在被固定的列 */
const fixedCols = shallowRef[]>([]);
/**
* 需要自行绘制左边框的右固定列。
* 单元格的左边框由左侧相邻单元格的 border-right 提供,右固定列吸附遮挡内容后两者不再相邻,因此需要自行绘制。
*/
const fixedBorderLeftCols = shallowRef[]>([]);
/** 固定列的class */
const fixedColClassMap = computed(() => {
const colMap = new Map();
const fixedShadowColsValue = fixedShadowCols.value;
const fixedColsValue = fixedCols.value;
const fixedBorderLeftColsValue = fixedBorderLeftCols.value;
const colKeyFn = colKeyGen.value;
const fixedColShadow = props.fixedColShadow;
const headers = tableHeaders.value;
for (let i = 0, len = headers.length; i < len; i++) {
const cols = headers[i];
for (let j = 0, colLen = cols.length; j < colLen; j++) {
const col = cols[j];
const fixed = col.fixed;
const showShadow = fixed && fixedColShadow && fixedShadowColsValue.includes(col);
const classList = [];
if (fixedColsValue.includes(col)) {
// 表示该列正在被固定
classList.push('fixed-cell--active');
}
if (fixed) {
classList.push('fixed-cell');
classList.push('fixed-cell--' + fixed);
}
if (showShadow) {
classList.push('fixed-cell--shadow');
}
if (fixed === 'right' && fixedBorderLeftColsValue.includes(col)) {
classList.push('fixed-cell--border-left');
}
colMap.set(colKeyFn(col), classList);
}
}
return colMap;
});
/**
* 返回所有父元素,包括自己
* @param col
* @param type 1-shadow(阴影) 2-active(被固定的列)
*
*/
// function getColAndParentCols(col: StkTableColumn | null, type: 1 | 2 = 1) {
// if (!col) return [];
// const colsTemp: StkTableColumn[] = [];
// let node: any = { __PARENT__: col };
// while ((node = node.__PARENT__)) {
// if (type === 1 && node.fixed) {
// // shadow
// colsTemp.push(node);
// }
// if (type === 2) {
// // active
// colsTemp.push(node);
// }
// }
// return colsTemp;
// }
/** 滚动条变化时,更新需要展示阴影的列 */
function updateFixedShadow(virtualScrollX?: Ref) {
const fixedColsTemp: StkTableColumn[] = [];
const getFixedColPositionValue = getFixedColPosition.value;
let clientWidth, scrollLeft;
if (virtualScrollX?.value) {
const { containerWidth: cw, scrollLeft: sl } = virtualScrollX.value;
clientWidth = cw;
scrollLeft = sl;
} else {
const { clientWidth: cw, scrollLeft: sl } = tableContainerRef.value as HTMLDivElement;
clientWidth = cw;
scrollLeft = sl;
}
/*******
* 根据横向滚动位置,计算出哪个列需要展示阴影
*****/
/** 左侧需要展示阴影的列 */
const leftShadowCol: StkTableColumn[] = [];
/** 右侧展示阴影的列 */
const rightShadowCol: StkTableColumn[] = [];
const len = tableHeadersForCalc.value.length;
for (let level = 0; level < len; level++) {
const row = tableHeadersForCalc.value[level];
/**
* 最右侧连续 fixed:right 列的起始下标。
* 这些列的固定偏移只由其右侧固定列宽度决定,不依赖中间列的声明宽度,
* 且 sticky/relative 在无横向溢出时偏移为0无副作用,因此无需判断滚动位置,始终固定。
*/
let rightSuffixStart = row.length;
while (rightSuffixStart > 0 && row[rightSuffixStart - 1].fixed === 'right') {
rightSuffixStart--;
}
/**
* 左侧第n个fixed:left 计算要加上前面所有left 的列宽。
*/
let left = 0;
for (let i = 0, rowLen = row.length; i < rowLen; i++) {
const col = row[i];
const position = getFixedColPositionValue(col);
const isFixedLeft = col.fixed === 'left';
const isFixedRight = col.fixed === 'right';
if (isFixedLeft && position + scrollLeft > left) {
fixedColsTemp.push(col);
leftShadowCol[level] = col;
}
left += getCalculatedColWidth(col);
if (isFixedRight) {
/** 是否需要固定。依赖列声明宽度累加,声明宽度与实际渲染宽度不一致时会失真 */
const needFix = scrollLeft + clientWidth - left < position;
if (i >= rightSuffixStart || needFix) {
fixedColsTemp.push(col);
}
// 右固定列阴影,只要第一列。阴影仍按滚动位置判断,避免未遮挡时也显示阴影
if (needFix && !rightShadowCol[level]) {
rightShadowCol[level] = col;
}
}
}
}
if (props.fixedColShadow) {
fixedShadowCols.value = leftShadowCol.concat(rightShadowCol).filter(Boolean) as StkTableColumn[];
}
// rightShadowCol 是每层最靠左的、已遮挡内容的右固定列,正是需要绘制左边框的列
fixedBorderLeftCols.value = rightShadowCol.filter(Boolean) as StkTableColumn[];
fixedCols.value = fixedColsTemp;
}
return [fixedCols, fixedColClassMap, updateFixedShadow] as const;
}