Skip to content

Commit 15e7d60

Browse files
committed
fix(ColumnGrouping): enabled, but no resize yet
1 parent eaba1d0 commit 15e7d60

File tree

8 files changed

+205
-113
lines changed

8 files changed

+205
-113
lines changed

.storybook/stories/Features/column-group.story.js

Lines changed: 70 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -29,75 +29,86 @@ storiesOf('Features/Column Grouping', module)
2929
.add('base', () => {
3030
const data = { nodes };
3131

32-
const theme = useTheme({
33-
HeaderRow: `
34-
& th.foo {
35-
color: white;
36-
background-color: blue;
37-
grid-column: 1 / span 2;
38-
}
39-
40-
& th.bar {
41-
color: white;
42-
background-color: orange;
43-
grid-column: 3 / span 3;
44-
}
45-
46-
& th.dissolve {
47-
display: none;
48-
}
49-
`,
50-
Row: `
51-
&:nth-of-type(odd) {
52-
.td:nth-of-type(2) {
53-
color: white;
54-
background-color: red;
55-
grid-column: 2 / span 2;
56-
}
57-
.td:nth-of-type(3) {
58-
display: none;
59-
}
60-
}
61-
`,
62-
});
63-
6432
return (
65-
<Table data={data} theme={theme}>
33+
<Table data={data}>
6634
{(tableList) => (
6735
<>
6836
<Header>
6937
<HeaderRow>
70-
<HeaderCell className="foo">Foo</HeaderCell>
71-
<HeaderCell className="dissolve" />
72-
<HeaderCell className="bar">Bar</HeaderCell>
73-
<HeaderCell className="dissolve" />
74-
<HeaderCell className="dissolve" />
75-
</HeaderRow>
76-
<HeaderRow>
77-
<HeaderCell>Task</HeaderCell>
78-
<HeaderCell>Deadline</HeaderCell>
79-
<HeaderCell>Type</HeaderCell>
80-
<HeaderCell>Complete</HeaderCell>
38+
<HeaderCell colSpan={2} style={{ backgroundColor: 'blue', color: 'white' }}>
39+
Task
40+
</HeaderCell>
41+
<HeaderCell colSpan={2} style={{ backgroundColor: 'green', color: 'white' }}>
42+
Type
43+
</HeaderCell>
8144
<HeaderCell>Tasks</HeaderCell>
8245
</HeaderRow>
8346
</Header>
8447

8548
<Body>
86-
{tableList.map((item) => (
87-
<Row key={item.id} item={item}>
88-
<Cell>{item.name}</Cell>
89-
<Cell>
90-
{item.deadline.toLocaleDateString('en-US', {
91-
year: 'numeric',
92-
month: '2-digit',
93-
day: '2-digit',
94-
})}
95-
</Cell>
96-
<Cell>{item.type}</Cell>
97-
<Cell>{item.isComplete.toString()}</Cell>
98-
<Cell>{item.nodes?.length}</Cell>
99-
</Row>
100-
))}
49+
{tableList.map((item, index) =>
50+
index === 1 ? (
51+
<Row key={item.id} item={item}>
52+
<Cell colSpan={3} style={{ backgroundColor: 'red', color: 'white' }}>
53+
{item.name}
54+
</Cell>
55+
<Cell>{item.isComplete.toString()}</Cell>
56+
<Cell>{item.nodes?.length}</Cell>
57+
</Row>
58+
) : index === 2 ? (
59+
<Row key={item.id} item={item}>
60+
<Cell>{item.name}</Cell>
61+
<Cell>
62+
{item.deadline.toLocaleDateString('en-US', {
63+
year: 'numeric',
64+
month: '2-digit',
65+
day: '2-digit',
66+
})}
67+
</Cell>
68+
<Cell>{item.type}</Cell>
69+
<Cell colSpan={2} style={{ backgroundColor: 'blue', color: 'white' }}>
70+
{item.isComplete.toString()}
71+
</Cell>
72+
</Row>
73+
) : index === 3 ? (
74+
<Row key={item.id} item={item}>
75+
<Cell>{item.name}</Cell>
76+
<Cell colSpan={2} style={{ backgroundColor: 'orange', color: 'white' }}>
77+
{item.deadline.toLocaleDateString('en-US', {
78+
year: 'numeric',
79+
month: '2-digit',
80+
day: '2-digit',
81+
})}
82+
</Cell>
83+
<Cell>{item.isComplete.toString()}</Cell>
84+
<Cell>{item.nodes?.length}</Cell>
85+
</Row>
86+
) : index === 4 ? (
87+
<Row key={item.id} item={item}>
88+
<Cell colSpan={2} style={{ backgroundColor: 'green', color: 'white' }}>
89+
{item.name}
90+
</Cell>
91+
<Cell>{item.type}</Cell>
92+
<Cell colSpan={2} style={{ backgroundColor: 'green', color: 'white' }}>
93+
{item.isComplete.toString()}
94+
</Cell>
95+
</Row>
96+
) : (
97+
<Row key={item.id} item={item}>
98+
<Cell>{item.name}</Cell>
99+
<Cell>
100+
{item.deadline.toLocaleDateString('en-US', {
101+
year: 'numeric',
102+
month: '2-digit',
103+
day: '2-digit',
104+
})}
105+
</Cell>
106+
<Cell>{item.type}</Cell>
107+
<Cell>{item.isComplete.toString()}</Cell>
108+
<Cell>{item.nodes?.length}</Cell>
109+
</Row>
110+
),
111+
)}
101112
</Body>
102113
</>
103114
)}

src/common/util/columns.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ export type DataColumn = {
3333
width: number;
3434
isStiff: boolean;
3535
isHide?: boolean;
36+
isColSpan?: boolean;
3637
};
3738

3839
export const toDataColumn = (column: HTMLElement, index: number) => ({
@@ -41,4 +42,5 @@ export const toDataColumn = (column: HTMLElement, index: number) => ({
4142
width: column.getBoundingClientRect().width,
4243
isStiff: column.classList.contains('stiff'),
4344
isHide: column.getAttribute('data-hide') === 'true',
45+
isColSpan: column.classList.contains('colspan'),
4446
});
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import * as React from 'react';
2+
3+
export const getPreviousColSpans = (children: React.ReactNode, index: number) => {
4+
return React.Children.toArray(children).reduce((acc, value, key) => {
5+
if (!React.isValidElement(value)) return acc;
6+
if (key >= index) return acc;
7+
if (!value.props.colSpan) return acc;
8+
9+
return acc + value.props.colSpan - 1;
10+
}, 0);
11+
};

src/common/util/isFragment.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import * as React from 'react';
2+
3+
export const isReactFragment = (variableToInspect: any) => {
4+
if (variableToInspect.type) {
5+
return variableToInspect.type === React.Fragment;
6+
}
7+
return variableToInspect === React.Fragment;
8+
};

src/table/Cell/Cell.tsx

Lines changed: 38 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,35 +10,55 @@ import { ThemeContext } from '@table-library/react-table-library/common/context/
1010
import { CellProps } from '@table-library/react-table-library/types/table';
1111

1212
export const Cell: React.FC<CellProps> = ({
13+
index,
1314
className,
1415
hide,
1516
pinLeft,
1617
pinRight,
1718
stiff,
19+
colSpan = 0,
20+
previousColSpans = 0,
1821
onClick,
1922
children,
23+
style,
2024
...rest
2125
}: CellProps) => {
2226
const theme = React.useContext(ThemeContext);
2327

28+
let colSpanStyle = {};
29+
if (colSpan) {
30+
colSpanStyle = {
31+
...colSpanStyle,
32+
'grid-column': `span ${colSpan} / ${index + colSpan + previousColSpans + 1}`,
33+
};
34+
}
35+
2436
return (
25-
<CellContainer
26-
role="gridcell"
27-
data-table-library_td=""
28-
css={css`
29-
${theme?.BaseCell}
30-
${theme?.Cell}
31-
`}
32-
className={cs('td', className, {
33-
stiff,
34-
hide,
35-
'pin-left': pinLeft,
36-
'pin-right': pinRight,
37-
})}
38-
onClick={onClick}
39-
{...rest}
40-
>
41-
<div>{children}</div>
42-
</CellContainer>
37+
<>
38+
<CellContainer
39+
role="gridcell"
40+
data-table-library_td=""
41+
style={{ ...colSpanStyle, ...style }}
42+
css={css`
43+
${theme?.BaseCell}
44+
${theme?.Cell}
45+
`}
46+
className={cs('td', className, {
47+
stiff,
48+
hide,
49+
'pin-left': pinLeft,
50+
'pin-right': pinRight,
51+
})}
52+
onClick={onClick}
53+
{...rest}
54+
>
55+
<div>{children}</div>
56+
</CellContainer>
57+
58+
{/* column grouping */}
59+
{Array.from({ length: colSpan - 1 }, () => (
60+
<CellContainer className={cs('td', 'hide', 'colspan')} />
61+
))}
62+
</>
4363
);
4464
};

src/table/Cell/HeaderCell.tsx

Lines changed: 47 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,12 @@ export const HeaderCell: React.FC<HeaderCellProps> = ({
7575
pinLeft,
7676
pinRight,
7777
stiff,
78+
colSpan = 0,
79+
previousColSpans = 0,
7880
resize,
7981
role = 'columnheader',
8082
children,
83+
style,
8184
...rest
8285
}: HeaderCellProps) => {
8386
const theme = React.useContext(ThemeContext);
@@ -86,34 +89,50 @@ export const HeaderCell: React.FC<HeaderCellProps> = ({
8689

8790
const { cellRef, resizeRef } = useResize(index!, hide);
8891

92+
let colSpanStyle = {};
93+
if (colSpan) {
94+
colSpanStyle = {
95+
...colSpanStyle,
96+
'grid-column': `span ${colSpan} / ${index + colSpan + previousColSpans + 1}`,
97+
};
98+
}
99+
89100
return (
90-
<HeaderCellContainer
91-
role={role}
92-
data-table-library_th=""
93-
data-hide={!!hide}
94-
data-resize-min-width={
95-
typeof resize === 'boolean' || resize?.minWidth == null ? 75 : resize.minWidth
96-
}
97-
css={css`
98-
${theme?.BaseCell}
99-
${theme?.HeaderCell}
100-
`}
101-
className={cs('th', className, {
102-
stiff,
103-
hide,
104-
resize,
105-
'pin-left': pinLeft,
106-
'pin-right': pinRight,
107-
})}
108-
ref={cellRef}
109-
{...rest}
110-
>
111-
<div>{children}</div>
112-
{resize && !hide && (
113-
<div className="resizer-area" ref={resizeRef} css={resizerStyle(resize).area}>
114-
<span className="resizer-handle" css={resizerStyle(resize).handle} />
115-
</div>
116-
)}
117-
</HeaderCellContainer>
101+
<>
102+
<HeaderCellContainer
103+
role={role}
104+
data-table-library_th=""
105+
data-hide={!!hide}
106+
data-resize-min-width={
107+
typeof resize === 'boolean' || resize?.minWidth == null ? 75 : resize.minWidth
108+
}
109+
style={{ ...colSpanStyle, ...style }}
110+
css={css`
111+
${theme?.BaseCell}
112+
${theme?.HeaderCell}
113+
`}
114+
className={cs('th', className, {
115+
stiff,
116+
hide,
117+
resize,
118+
'pin-left': pinLeft,
119+
'pin-right': pinRight,
120+
})}
121+
ref={cellRef}
122+
{...rest}
123+
>
124+
<div>{children}</div>
125+
{resize && !hide && (
126+
<div className="resizer-area" ref={resizeRef} css={resizerStyle(resize).area}>
127+
<span className="resizer-handle" css={resizerStyle(resize).handle} />
128+
</div>
129+
)}
130+
</HeaderCellContainer>
131+
132+
{/* column grouping */}
133+
{Array.from({ length: colSpan - 1 }, () => (
134+
<HeaderCellContainer className={cs('th', 'hide', 'colspan')} />
135+
))}
136+
</>
118137
);
119138
};

src/table/Row/HeaderRow.tsx

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,11 @@ import {
1515
toDataColumn,
1616
getHeaderColumns,
1717
} from '@table-library/react-table-library/common/util/columns';
18+
import { isReactFragment } from '@table-library/react-table-library/common/util/isFragment';
19+
import { getPreviousColSpans } from '@table-library/react-table-library/common/util/getPreviousColSpans';
1820

1921
import { HeaderRowProps } from '@table-library/react-table-library/types/table';
2022

21-
const isReactFragment = (variableToInspect: any) => {
22-
if (variableToInspect.type) {
23-
return variableToInspect.type === React.Fragment;
24-
}
25-
return variableToInspect === React.Fragment;
26-
};
27-
2823
const useInitialLayout = () => {
2924
const context = React.useContext(LayoutContext);
3025

@@ -93,10 +88,13 @@ export const HeaderRow: React.FC<HeaderRowProps> = ({
9388
// edge case: CompactTable renders checkbox (select feature) + cell in one fragment
9489
// this would break the resize feature
9590
// hence we need to pass the index from the outside then (see CompactTable)
91+
92+
// also column grouping
9693
if (!isReactFragment(child)) {
9794
extraProps = {
9895
...extraProps,
9996
index,
97+
previousColSpans: getPreviousColSpans(children, index),
10098
};
10199
}
102100

0 commit comments

Comments
 (0)