Skip to content

Commit

Permalink
feat: Cross fixed column support (#1073)
Browse files Browse the repository at this point in the history
* chore: init

* chore: refactor of hooks

* chore: basic fixed offsets

* chore: support multiple fixed

* test: update test

* chore: style

* test: update test

* chore: ci

* chore: ci

* update deps
  • Loading branch information
zombieJ authored Jan 30, 2024
1 parent 4b67382 commit 5db3b86
Show file tree
Hide file tree
Showing 12 changed files with 3,283 additions and 126 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
key: lock-${{ github.sha }}

- name: create package-lock.json
run: npm i --package-lock-only --ignore-scripts
run: npm i --package-lock-only --ignore-scripts --legacy-peer-deps

- name: hack for singe file
run: |
Expand All @@ -41,7 +41,7 @@ jobs:

- name: install
if: steps.node_modules_cache_id.outputs.cache-hit != 'true'
run: npm ci
run: npm ci --legacy-peer-deps

lint:
runs-on: ubuntu-latest
Expand Down
7 changes: 7 additions & 0 deletions assets/index.less
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,13 @@
}

// ================== Cell ==================
&-fixed-column-gapped {
.@{tablePrefixCls}-cell-fix-left-last::after,
.@{tablePrefixCls}-cell-fix-right-first::after {
display: none !important;
}
}

&-cell {
background: #f4f4f4;

Expand Down
3 changes: 2 additions & 1 deletion docs/examples/fixedColumns.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const columns: ColumnType<RecordType>[] = [
{ title: 'title8', dataIndex: 'b', key: 'h' },
{ title: 'title9', dataIndex: 'b', key: 'i' },
{ title: 'title10', dataIndex: 'b', key: 'j' },
{ title: 'title11', dataIndex: 'b', key: 'k', width: 50, fixed: 'right' },
{ title: 'title11', dataIndex: 'b', key: 'k', width: 50 },
{ title: 'title12', dataIndex: 'b', key: 'l', width: 100, fixed: 'right' },
];

Expand Down Expand Up @@ -65,6 +65,7 @@ const Demo = () => {
Scroll Y
</label>
<Table
// direction="rtl"
columns={columns}
expandedRowRender={({ b, c }) => b || c}
scroll={{ x: 1200, y: scrollY ? 200 : null }}
Expand Down
9 changes: 4 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,17 +62,16 @@
},
"devDependencies": {
"@rc-component/father-plugin": "^1.0.2",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/jest-dom": "^6.4.0",
"@testing-library/react": "^12.1.5",
"@types/enzyme": "^3.10.5",
"@types/react": "^18.0.28",
"@types/jest": "^29.5.0",
"@types/react": "^18.0.28",
"@types/react-dom": "^18.0.5",
"@types/responselike": "^1.0.0",
"@types/styled-components": "^5.1.32",
"@types/testing-library__jest-dom": "^6.0.0",
"@umijs/fabric": "^4.0.1",
"@vitest/coverage-c8": "^0.31.0",
"@vitest/coverage-v8": "^1.2.2",
"cross-env": "^7.0.0",
"dumi": "^2.1.3",
"enzyme": "^3.1.0",
Expand Down Expand Up @@ -105,7 +104,7 @@
"regenerator-runtime": "^0.14.0",
"styled-components": "^6.1.1",
"typescript": "~5.3.0",
"vitest": "^0.31.0"
"vitest": "^1.2.2"
},
"lint-staged": {
"**/*.{js,jsx,tsx,ts,md,json}": [
Expand Down
5 changes: 3 additions & 2 deletions src/Table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ function Table<RecordType extends DefaultRecordType>(
const scrollX = scroll?.x;
const [componentWidth, setComponentWidth] = React.useState(0);

const [columns, flattenColumns, flattenScrollX] = useColumns(
const [columns, flattenColumns, flattenScrollX, hasGapFixed] = useColumns(
{
...props,
...expandableConfig,
Expand Down Expand Up @@ -348,7 +348,7 @@ function Table<RecordType extends DefaultRecordType>(
const colsKeys = getColumnsKey(flattenColumns);
const pureColWidths = colsKeys.map(columnKey => colsWidths.get(columnKey));
const colWidths = React.useMemo(() => pureColWidths, [pureColWidths.join('_')]);
const stickyOffsets = useStickyOffsets(colWidths, flattenColumns.length, direction);
const stickyOffsets = useStickyOffsets(colWidths, flattenColumns, direction);
const fixHeader = scroll && validateValue(scroll.y);
const horizonScroll = (scroll && validateValue(mergedScrollX)) || Boolean(expandableConfig.fixed);
const fixColumn = horizonScroll && flattenColumns.some(({ fixed }) => fixed);
Expand Down Expand Up @@ -750,6 +750,7 @@ function Table<RecordType extends DefaultRecordType>(
[`${prefixCls}-fixed-header`]: fixHeader,
/** No used but for compatible */
[`${prefixCls}-fixed-column`]: fixColumn,
[`${prefixCls}-fixed-column-gapped`]: fixColumn && hasGapFixed,
[`${prefixCls}-scroll-horizontal`]: horizonScroll,
[`${prefixCls}-has-fix-left`]: flattenColumns[0] && flattenColumns[0].fixed,
[`${prefixCls}-has-fix-right`]:
Expand Down
65 changes: 36 additions & 29 deletions src/hooks/useColumns/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -88,30 +88,6 @@ function flatColumns<RecordType>(
}, []);
}

function warningFixed(flattenColumns: readonly { fixed?: FixedType }[]) {
let allFixLeft = true;
for (let i = 0; i < flattenColumns.length; i += 1) {
const col = flattenColumns[i];
if (allFixLeft && col.fixed !== 'left') {
allFixLeft = false;
} else if (!allFixLeft && col.fixed === 'left') {
warning(false, `Index ${i - 1} of \`columns\` missing \`fixed='left'\` prop.`);
break;
}
}

let allFixRight = true;
for (let i = flattenColumns.length - 1; i >= 0; i -= 1) {
const col = flattenColumns[i];
if (allFixRight && col.fixed !== 'right') {
allFixRight = false;
} else if (!allFixRight && col.fixed === 'right') {
warning(false, `Index ${i + 1} of \`columns\` missing \`fixed='right'\` prop.`);
break;
}
}
}

function revertForRtl<RecordType>(columns: ColumnsType<RecordType>): ColumnsType<RecordType> {
return columns.map(column => {
const { fixed, ...restProps } = column;
Expand Down Expand Up @@ -176,6 +152,7 @@ function useColumns<RecordType>(
columns: ColumnsType<RecordType>,
flattenColumns: readonly ColumnType<RecordType>[],
realScrollWidth: undefined | number,
hasGapFixed: boolean,
] {
const baseColumns = React.useMemo<ColumnsType<RecordType>>(() => {
const newColumns = columns || convertChildrenToColumns(children) || [];
Expand Down Expand Up @@ -294,10 +271,40 @@ function useColumns<RecordType>(
return flatColumns(mergedColumns);
}, [mergedColumns, direction, scrollWidth]);

// Only check out of production since it's waste for each render
if (process.env.NODE_ENV !== 'production') {
warningFixed(direction === 'rtl' ? flattenColumns.slice().reverse() : flattenColumns);
}
// ========================= Gap Fixed ========================
const hasGapFixed = React.useMemo(() => {
// Fixed: left, since old browser not support `findLastIndex`, we should use reverse loop
let lastLeftIndex = -1;
for (let i = flattenColumns.length - 1; i >= 0; i -= 1) {
const colFixed = flattenColumns[i].fixed;
if (colFixed === 'left' || colFixed === true) {
lastLeftIndex = i;
break;
}
}

if (lastLeftIndex >= 0) {
for (let i = 0; i <= lastLeftIndex; i += 1) {
const colFixed = flattenColumns[i].fixed;
if (colFixed !== 'left' && colFixed !== true) {
return true;
}
}
}

// Fixed: right
const firstRightIndex = flattenColumns.findIndex(({ fixed: colFixed }) => colFixed === 'right');
if (firstRightIndex >= 0) {
for (let i = firstRightIndex; i < flattenColumns.length; i += 1) {
const colFixed = flattenColumns[i].fixed;
if (colFixed !== 'right') {
return true;
}
}
}

return false;
}, [flattenColumns]);

// ========================= FillWidth ========================
const [filledColumns, realScrollWidth] = useWidthColumns(
Expand All @@ -306,7 +313,7 @@ function useColumns<RecordType>(
clientWidth,
);

return [mergedColumns, filledColumns, realScrollWidth];
return [mergedColumns, filledColumns, realScrollWidth, hasGapFixed];
}

export default useColumns;
64 changes: 33 additions & 31 deletions src/hooks/useStickyOffsets.ts
Original file line number Diff line number Diff line change
@@ -1,43 +1,45 @@
import { useMemo } from 'react';
import type { Direction, StickyOffsets } from '../interface';
import type { ColumnType, Direction, StickyOffsets } from '../interface';

/**
* Get sticky column offset width
*/
function useStickyOffsets(colWidths: number[], columnCount: number, direction: Direction) {
function useStickyOffsets<RecordType>(
colWidths: number[],
flattenColumns: readonly ColumnType<RecordType>[],
direction: Direction,
) {
const stickyOffsets: StickyOffsets = useMemo(() => {
const leftOffsets: number[] = [];
const rightOffsets: number[] = [];
let left = 0;
let right = 0;

for (let start = 0; start < columnCount; start += 1) {
if (direction === 'rtl') {
// Left offset
rightOffsets[start] = right;
right += colWidths[start] || 0;

// Right offset
const end = columnCount - start - 1;
leftOffsets[end] = left;
left += colWidths[end] || 0;
} else {
// Left offset
leftOffsets[start] = left;
left += colWidths[start] || 0;

// Right offset
const end = columnCount - start - 1;
rightOffsets[end] = right;
right += colWidths[end] || 0;
const columnCount = flattenColumns.length;

const getOffsets = (startIndex: number, endIndex: number, offset: number) => {
const offsets: number[] = [];
let total = 0;

for (let i = startIndex; i !== endIndex; i += offset) {
offsets.push(total);

if (flattenColumns[i].fixed) {
total += colWidths[i] || 0;
}
}
}

return {
left: leftOffsets,
right: rightOffsets,
return offsets;
};
}, [colWidths, columnCount, direction]);

const startOffsets = getOffsets(0, columnCount, 1);
const endOffsets = getOffsets(columnCount - 1, -1, -1).reverse();

return direction === 'rtl'
? {
left: endOffsets,
right: startOffsets,
}
: {
left: startOffsets,
right: endOffsets,
};
}, [colWidths, flattenColumns, direction]);

return stickyOffsets;
}
Expand Down
3 changes: 2 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { EXPAND_COLUMN, INTERNAL_HOOKS } from './constant';
import { FooterComponents as Summary } from './Footer';
import type { ColumnType, Reference } from './interface';
import type { ColumnType, ColumnsType, Reference } from './interface';
import Column from './sugar/Column';
import ColumnGroup from './sugar/ColumnGroup';
import type { TableProps } from './Table';
Expand All @@ -23,6 +23,7 @@ export {
type VirtualTableProps,
type Reference,
type ColumnType,
type ColumnsType,
};

export default Table;
Loading

0 comments on commit 5db3b86

Please sign in to comment.