Skip to content

Commit

Permalink
fix(table): 列宽调整需要考虑列的最小宽度 (#803)
Browse files Browse the repository at this point in the history
* fix(table): column resize need to consider the minimum width

* fix(table): eslint

* fix(table): add variable type

* refactor(table): add comment and optimize code style

* fix(table): update import
  • Loading branch information
ZTao-z authored Sep 4, 2022
1 parent 8c4538b commit c1fc0b8
Show file tree
Hide file tree
Showing 4 changed files with 191 additions and 55 deletions.
186 changes: 146 additions & 40 deletions js/table/recalculate-column-width.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,147 @@
import isNumber from 'lodash/isNumber';
import { BaseTableCol } from './types';
import { BaseTableCol, ThMap } from './types';
import { getColWidthAttr } from './utils';

export default function recalculateColumnWidth<T extends BaseTableCol<T>>(
/**
* 填充未设置width属性的列
* @param columns 当前表格所有列
* @param missingWidthCols 未设置width属性的列
* @param thWidthList 列宽记录字典
* @param tableLayout 表格布局
* @param actualWidth 各列实际总列宽
* @param tableWidth 表格总宽度
*/
const setMissingColumnWidth = <T extends BaseTableCol<T>>(
columns: T[],
missingWidthCols: T[],
thWidthList: ThMap,
tableLayout: string,
actualWidth: number,
tableWidth: number
) : void => {
const thMap = thWidthList;
// 当前列宽总宽度小于表宽,将剩余宽度平均分配给未指定宽度的列
if (actualWidth < tableWidth) {
let widthDiff = tableWidth - actualWidth;
const remainCols: T[] = [];
// 优先保证设置了minWidth的列满足最小宽度
missingWidthCols.forEach((col) => {
const minWidth = getColWidthAttr(col, 'minWidth');
if (minWidth) {
thMap[col.colKey] = minWidth;
widthDiff -= minWidth;
} else {
remainCols.push(col);
}
});

// 如果剩余宽度 > 0
if (widthDiff > 0) {
// 如果存在未设置minWidth的列,这些列均分剩余宽度
if (remainCols.length) {
const avgWidth = widthDiff / remainCols.length;
remainCols.forEach((col) => {
thMap[col.colKey] = avgWidth;
});
} else {
// 否则所有列均分剩余宽度
const avgWidth = widthDiff / missingWidthCols.length;
missingWidthCols.forEach((col) => {
thMap[col.colKey] += avgWidth;
});
}
} else {
// 剩余宽度 <= 0, 所有剩余列默认填充100px
remainCols.forEach((col) => {
thMap[col.colKey] = 100;
});
}
} else if (tableLayout === 'fixed') {
// 当前列表总宽度大于等于表宽,且当前排版模式为fixed,默认填充minWidth || 100px
missingWidthCols.forEach((col) => {
thMap[col.colKey] = getColWidthAttr(col, 'minWidth') || 100;
});
} else {
// 当前列表总宽度大于等于表宽,且当前排版模式为aut
// 默认填充minWidth || 100px,然后按比例重新分配各列宽度
let extraWidth = 0;
missingWidthCols.forEach((col) => {
extraWidth += getColWidthAttr(col, 'minWidth') || 100;
});
const totalWidth = extraWidth + actualWidth;
columns.forEach((col) => {
if (!thMap[col.colKey]) {
const colWidth = getColWidthAttr(col, 'minWidth') || 100;
thMap[col.colKey] = (colWidth / totalWidth) * tableWidth;
} else {
thMap[col.colKey] = (thMap[col.colKey] / totalWidth) * tableWidth;
}
});
}
};

/**
* 设置所有列的宽度
* @param columns 当前表格所有列
* @param thWidthList 列宽记录字典
* @param actualWidth 各列实际总列宽
* @param tableWidth 表格总宽度
* @param notCalculateWidthCols 不需要参与计算的列id
*/
const setNormalColumnWidth = <T extends BaseTableCol<T>>(
columns: T[],
thWidthList: ThMap,
actualWidth: number,
tableWidth: number,
notCalculateWidthCols: string[]
) : void => {
const thMap = thWidthList;
columns.forEach((col) => {
if (notCalculateWidthCols.includes(col.colKey)) return;
thMap[col.colKey] = (thMap[col.colKey] / actualWidth) * tableWidth;
});
};

/**
* 表格未初始化时默认填充各列宽度
* @param missingWidthCols 未设置width属性的列
* @param thWidthList 列宽记录字典
*/
const setInitialColumnWidth = <T extends BaseTableCol<T>>(
missingWidthCols: T[],
thWidthList: { [colKey: string]: number },
) : void => {
const thMap = thWidthList;
// 表格宽度未初始化,默认填充minWidth || 100px
missingWidthCols.forEach((col) => {
thMap[col.colKey] = getColWidthAttr(col, 'minWidth') || 100;
});
};

/**
* 重新按规则分配各列宽度
* @param columns 当前表格所有列
* @param thWidthList 列宽记录字典
* @param tableLayout 表格布局
* @param tableElmWidth 表格宽度
* @param notCalculateWidthCols 不需要参与计算的列
* @param callback 回调函数
*/
export default function recalculateColumnWidth<T extends BaseTableCol<T>>(
columns: T[],
thWidthList: ThMap,
tableLayout: string,
tableElmWidth: number,
notCalculateWidthCols: string[],
callback: (widthMap: { [colKey: string]: number }) => void
): void {
let actualWidth = 0;
const missingWidthCols: T[] = [];
const thMap: { [colKey: string]: number } = {};
const thMap: ThMap = {};

// 计算现有列的列宽总和
columns.forEach((col) => {
if (!thWidthList[col.colKey]) {
thMap[col.colKey] = isNumber(col.width) ? col.width : parseFloat(col.width);
thMap[col.colKey] = getColWidthAttr(col, 'width');
} else {
thMap[col.colKey] = thWidthList[col.colKey];
}
Expand All @@ -34,31 +159,14 @@ export default function recalculateColumnWidth<T extends BaseTableCol<T>>(
if (tableWidth > 0) {
// 存在没有指定列宽的列
if (missingWidthCols.length) {
// 当前列宽总宽度小于表宽,将剩余宽度平均分配给未指定宽度的列
if (actualWidth < tableWidth) {
const widthDiff = tableWidth - actualWidth;
const avgWidth = widthDiff / missingWidthCols.length;
missingWidthCols.forEach((col) => {
thMap[col.colKey] = avgWidth;
});
} else if (tableLayout === 'fixed') {
// 当前列表总宽度大于等于表宽,且当前排版模式为fixed,默认填充100px
missingWidthCols.forEach((col) => {
const originWidth = thMap[col.colKey] || 100;
thMap[col.colKey] = isNumber(originWidth) ? originWidth : parseFloat(originWidth);
});
} else {
// 当前列表总宽度大于等于表宽,且当前排版模式为auto,默认填充100px,然后按比例重新分配各列宽度
const extraWidth = missingWidthCols.length * 100;
const totalWidth = extraWidth + actualWidth;
columns.forEach((col) => {
if (!thMap[col.colKey]) {
thMap[col.colKey] = (100 / totalWidth) * tableWidth;
} else {
thMap[col.colKey] = (thMap[col.colKey] / totalWidth) * tableWidth;
}
});
}
setMissingColumnWidth(
columns,
missingWidthCols,
thMap,
tableLayout,
actualWidth,
tableWidth
);
needUpdate = true;
} else {
// 所有列都已经指定宽度
Expand All @@ -73,20 +181,18 @@ export default function recalculateColumnWidth<T extends BaseTableCol<T>>(
}
// 重新计算其他列的宽度,按表格剩余宽度进行按比例分配
if (actualWidth !== tableWidth || notCalculateWidthCols.length) {
columns.forEach((col) => {
if (notCalculateWidthCols.includes(col.colKey)) return;
thMap[col.colKey] = (thMap[col.colKey] / actualWidth) * tableWidth;
});
setNormalColumnWidth(
columns,
thMap,
actualWidth,
tableWidth,
notCalculateWidthCols
);
needUpdate = true;
}
}
} else {
// 表格宽度未初始化,默认填充100px
missingWidthCols.forEach((col) => {
const originWidth = thMap[col.colKey] || 100;
thMap[col.colKey] = isNumber(originWidth) ? originWidth : parseFloat(originWidth);
});

setInitialColumnWidth(missingWidthCols, thMap);
needUpdate = true;
}

Expand Down
45 changes: 31 additions & 14 deletions js/table/set-column-width-by-drag.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import isNumber from 'lodash/isNumber';
import { BaseTableCol } from './types';
import { BaseTableCol, ThMap } from './types';
import { getColWidthAttr } from './utils';

/**
* 获取某一列的所有子列
* @param col 表格某一列
* @returns 当前列的所有子列
*/
const findAllChildren = <T extends BaseTableCol<T>>(col: T): T[] => {
const loopQue: T[] = [];
const result: T[] = [];
Expand All @@ -18,15 +23,23 @@ const findAllChildren = <T extends BaseTableCol<T>>(col: T): T[] => {
return result;
};

/**
* 更新拖动后的列宽记录
* @param dragCol 被拖动的列
* @param dragWidth 拖动大小
* @param effectCol 受影响的列
* @param options 配置参数
* @param callback 回调函数
*/
export default function setThWidthListByColumnDrag<T extends BaseTableCol<T>>(
dragCol: T,
dragWidth: number,
effectCol: T,
options: {
getThWidthList: () => { [colKey: string]: number },
getThWidthList: () => ThMap,
DEFAULT_MIN_WIDTH: number
},
callback: (widthMap: { [colKey: string]: number }, colKeys: string[]) => void
callback: (widthMap: ThMap, colKeys: string[]) => void
): void {
const { getThWidthList, DEFAULT_MIN_WIDTH } = options;
const thWidthList = getThWidthList();
Expand Down Expand Up @@ -54,17 +67,18 @@ export default function setThWidthListByColumnDrag<T extends BaseTableCol<T>>(

// 根据多级表头的叶节点计算实际宽度(拖动列)
dragChildrenCols.forEach((child) => {
const defaultWidth = isNumber(child.width) ? child.width : parseFloat(child.width);
oldWidth += thWidthList[child.colKey] || defaultWidth;
oldWidth += thWidthList[child.colKey] || getColWidthAttr(child, 'width');
notCalculateCols.push(child.colKey);
});

// 根据多级表头的叶节点计算实际宽度(受影响的列)
effectChildrenCols.forEach((child) => {
const defaultWidth = isNumber(child.width) ? child.width : parseFloat(child.width);
oldEffectWidth += thWidthList[child.colKey] || defaultWidth;
oldEffectWidth += thWidthList[child.colKey] || getColWidthAttr(child, 'width');
notCalculateCols.push(child.colKey);
effectColsMinWidth += child.resize?.minWidth || DEFAULT_MIN_WIDTH;
effectColsMinWidth += Math.max(
child.resize?.minWidth || DEFAULT_MIN_WIDTH,
getColWidthAttr(child, 'minWidth') || DEFAULT_MIN_WIDTH
);
});

// 按比例划分新宽度(拖动列)
Expand All @@ -76,27 +90,30 @@ export default function setThWidthListByColumnDrag<T extends BaseTableCol<T>>(
const remainWidth = Math.max(
effectColsMinWidth,
oldWidth + oldEffectWidth - dragWidth,
effectCol.resize?.minWidth || DEFAULT_MIN_WIDTH,
Math.max(
getColWidthAttr(effectCol, 'minWidth') || DEFAULT_MIN_WIDTH,
effectCol.resize?.minWidth || DEFAULT_MIN_WIDTH
),
);
effectChildrenCols.forEach((child) => {
updateMap[child.colKey] = Math.max(
child.resize?.minWidth || DEFAULT_MIN_WIDTH,
getColWidthAttr(child, 'minWidth') || DEFAULT_MIN_WIDTH,
(thWidthList[child.colKey] / oldEffectWidth) * remainWidth,
);
});

// 更新各列宽度
callback(updateMap, notCalculateCols);
} else {
const colWidth = isNumber(dragCol.width) ? dragCol.width : parseFloat(dragCol.width);
const effectWidth = isNumber(effectCol.width) ? effectCol.width : parseFloat(effectCol.width);
const oldWidth = thWidthList[dragCol.colKey] || colWidth;
const oldEffectWidth = thWidthList[effectCol.colKey] || effectWidth;
const oldWidth = thWidthList[dragCol.colKey] || getColWidthAttr(dragCol, 'width');
const oldEffectWidth = thWidthList[effectCol.colKey] || getColWidthAttr(effectCol, 'width');

callback({
[dragCol.colKey]: dragWidth,
[effectCol.colKey]: Math.max(
effectCol.resize?.minWidth || DEFAULT_MIN_WIDTH,
getColWidthAttr(effectCol, 'minWidth') || DEFAULT_MIN_WIDTH,
oldWidth + oldEffectWidth - dragWidth,
),
}, [dragCol.colKey, effectCol.colKey]);
Expand Down
7 changes: 6 additions & 1 deletion js/table/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@ export interface BaseTableCol<T> {
children?: T[],
colKey?: string,
resize?: { [attr: string]: any },
width?: number | string
width?: number | string,
minWidth?: number | string,
}

export interface ThMap {
[colKey: string]: number
}

export interface PlainObject {
Expand Down
8 changes: 8 additions & 0 deletions js/table/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import isFunction from 'lodash/isFunction';
import isNumber from 'lodash/isNumber';
import { BaseTableCol } from './types';

export function isRowSelectedDisabled(
selectColumn: { [key: string]: any },
Expand All @@ -15,3 +17,9 @@ export function isRowSelectedDisabled(
}
return !!disabled;
}

// 获取列属性
export function getColWidthAttr<T extends BaseTableCol<T>>(col: T, attrKey: 'width' | 'minWidth') {
const attr = col[attrKey];
return isNumber(attr) ? attr : parseFloat(attr);
}

0 comments on commit c1fc0b8

Please sign in to comment.