@ltht-react/table 2.0.2 → 2.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -15
- package/package.json +10 -9
- package/src/atoms/questionnaire-withdrawn-table-cell.tsx +15 -0
- package/src/index.tsx +7 -0
- package/src/molecules/table-cell.tsx +93 -0
- package/src/molecules/table-component.tsx +146 -0
- package/src/molecules/table-methods.tsx +234 -0
- package/src/molecules/table-styled-components.tsx +233 -0
- package/src/molecules/table.tsx +161 -0
- package/src/molecules/useDimensionRef.tsx +37 -0
- package/src/molecules/useScrollRef.tsx +36 -0
- package/src/organisms/generic-table.tsx +33 -0
- package/src/organisms/questionnaire-table-methods.tsx +334 -0
- package/src/organisms/questionnaire-table.tsx +55 -0
package/README.md
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
# Select
|
|
2
|
-
|
|
3
|
-
<!-- STORY -->
|
|
4
|
-
|
|
5
|
-
### Import
|
|
6
|
-
|
|
7
|
-
```js
|
|
8
|
-
import Select from '@ltht-react/table'
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
### Usage
|
|
12
|
-
|
|
13
|
-
```jsx
|
|
14
|
-
<Table />
|
|
15
|
-
```
|
|
1
|
+
# Select
|
|
2
|
+
|
|
3
|
+
<!-- STORY -->
|
|
4
|
+
|
|
5
|
+
### Import
|
|
6
|
+
|
|
7
|
+
```js
|
|
8
|
+
import Select from '@ltht-react/table'
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
### Usage
|
|
12
|
+
|
|
13
|
+
```jsx
|
|
14
|
+
<Table />
|
|
15
|
+
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ltht-react/table",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.4",
|
|
4
4
|
"description": "ltht-react Table component.",
|
|
5
5
|
"author": "LTHT",
|
|
6
6
|
"homepage": "",
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
"main": "lib/index.js",
|
|
9
9
|
"typings": "lib/index.d.ts",
|
|
10
10
|
"files": [
|
|
11
|
-
"lib"
|
|
11
|
+
"lib",
|
|
12
|
+
"src"
|
|
12
13
|
],
|
|
13
14
|
"directories": {
|
|
14
15
|
"lib": "lib"
|
|
@@ -27,15 +28,15 @@
|
|
|
27
28
|
"dependencies": {
|
|
28
29
|
"@emotion/react": "^11.0.0",
|
|
29
30
|
"@emotion/styled": "^11.0.0",
|
|
30
|
-
"@ltht-react/button": "^2.0.
|
|
31
|
-
"@ltht-react/icon": "^2.0.
|
|
32
|
-
"@ltht-react/menu": "^2.0.
|
|
33
|
-
"@ltht-react/styles": "^2.0.
|
|
34
|
-
"@ltht-react/types": "^2.0.
|
|
35
|
-
"@ltht-react/utils": "^2.0.
|
|
31
|
+
"@ltht-react/button": "^2.0.4",
|
|
32
|
+
"@ltht-react/icon": "^2.0.4",
|
|
33
|
+
"@ltht-react/menu": "^2.0.4",
|
|
34
|
+
"@ltht-react/styles": "^2.0.4",
|
|
35
|
+
"@ltht-react/types": "^2.0.4",
|
|
36
|
+
"@ltht-react/utils": "^2.0.4",
|
|
36
37
|
"@tanstack/react-table": "^8.10.7",
|
|
37
38
|
"react": "^18.2.0",
|
|
38
39
|
"react-uuid": "^2.0.0"
|
|
39
40
|
},
|
|
40
|
-
"gitHead": "
|
|
41
|
+
"gitHead": "b8fe243323a0f1b9ab345475db38c73fd3b35312"
|
|
41
42
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { FC } from 'react'
|
|
2
|
+
import styled from '@emotion/styled'
|
|
3
|
+
|
|
4
|
+
const StyledText = styled.div`
|
|
5
|
+
text-decoration: line-through;
|
|
6
|
+
color: gray;
|
|
7
|
+
`
|
|
8
|
+
|
|
9
|
+
const QuestionnaireWithdrawnTableCell: FC<WithdrawnCellProps> = ({ text }) => <StyledText>{text ?? ''}</StyledText>
|
|
10
|
+
|
|
11
|
+
export interface WithdrawnCellProps {
|
|
12
|
+
text?: string
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export default QuestionnaireWithdrawnTableCell
|
package/src/index.tsx
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import Table, { Header, DataEntity, TableData, CellProps } from './molecules/table'
|
|
2
|
+
import GenericTable from './organisms/generic-table'
|
|
3
|
+
import QuestionnaireTable from './organisms/questionnaire-table'
|
|
4
|
+
import { AdminActionsForQuestionnaire } from './organisms/questionnaire-table-methods'
|
|
5
|
+
|
|
6
|
+
export default Table
|
|
7
|
+
export { Header, TableData, DataEntity, CellProps, GenericTable, QuestionnaireTable, AdminActionsForQuestionnaire }
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { FC } from 'react'
|
|
2
|
+
import { Button } from '@ltht-react/button'
|
|
3
|
+
import styled from '@emotion/styled'
|
|
4
|
+
import Icon, { IconButton, IconProps } from '@ltht-react/icon'
|
|
5
|
+
import ActionMenu, { ActionMenuOption } from '@ltht-react/menu'
|
|
6
|
+
import { PopUp, getZIndex, BTN_COLOURS } from '@ltht-react/styles'
|
|
7
|
+
import { Axis } from '@ltht-react/types'
|
|
8
|
+
|
|
9
|
+
const StyledIconText = styled.span`
|
|
10
|
+
margin-left: 0.4rem;
|
|
11
|
+
`
|
|
12
|
+
|
|
13
|
+
// TODO: This component is still a WIP and will be re-factored soon!
|
|
14
|
+
// May be best to split it out into different components, the important part is unifying Type used by React-Table so the mapping can be simplified
|
|
15
|
+
// It will need to facilitate the Actions list capability Jonny Dyson has requested
|
|
16
|
+
// Betters ways of handling the customComponentOverride will be considered too
|
|
17
|
+
const TableCell: FC<CellProps> = ({
|
|
18
|
+
adminActions,
|
|
19
|
+
isButton = false,
|
|
20
|
+
text,
|
|
21
|
+
iconProps,
|
|
22
|
+
headerAxis = 'x',
|
|
23
|
+
clickHandler,
|
|
24
|
+
customComponentOverride,
|
|
25
|
+
}) => {
|
|
26
|
+
if (customComponentOverride) {
|
|
27
|
+
return customComponentOverride
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (adminActions) {
|
|
31
|
+
if (adminActions.length === 0) {
|
|
32
|
+
return <></>
|
|
33
|
+
}
|
|
34
|
+
return (
|
|
35
|
+
<ActionMenu
|
|
36
|
+
actions={adminActions}
|
|
37
|
+
menuButtonOptions={{
|
|
38
|
+
type: 'button',
|
|
39
|
+
text: '',
|
|
40
|
+
buttonProps: {
|
|
41
|
+
styling: {
|
|
42
|
+
buttonStyle: 'standard',
|
|
43
|
+
padding: headerAxis === 'x' ? '0.3rem 0.5rem' : '0.15rem 0.3rem',
|
|
44
|
+
},
|
|
45
|
+
icon: (
|
|
46
|
+
<Icon {...{ type: headerAxis === 'x' ? 'ellipsis-vertical' : 'ellipsis-horizontal', size: 'medium' }} />
|
|
47
|
+
),
|
|
48
|
+
iconPlacement: 'center',
|
|
49
|
+
color: `${BTN_COLOURS.DANGER.VALUE}`,
|
|
50
|
+
},
|
|
51
|
+
}}
|
|
52
|
+
popupStyle={{ zIndex: getZIndex(PopUp) }}
|
|
53
|
+
popupPlacement={headerAxis === 'x' ? 'bottom-start' : 'right-start'}
|
|
54
|
+
/>
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (isButton) {
|
|
59
|
+
return <Button value={text} icon={iconProps && <Icon {...iconProps} />} onClick={clickHandler} />
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (clickHandler && iconProps) {
|
|
63
|
+
return <IconButton iconProps={iconProps} text={text} onClick={clickHandler} />
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (iconProps) {
|
|
67
|
+
return (
|
|
68
|
+
<>
|
|
69
|
+
<Icon {...iconProps} />
|
|
70
|
+
{text && <StyledIconText>{text}</StyledIconText>}
|
|
71
|
+
</>
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (text) {
|
|
76
|
+
return <div>{text ?? ''}</div>
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return <></>
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface CellProps {
|
|
83
|
+
adminActions?: ActionMenuOption[]
|
|
84
|
+
isButton?: boolean
|
|
85
|
+
text?: string
|
|
86
|
+
iconProps?: IconProps
|
|
87
|
+
clickHandler?: React.MouseEventHandler<HTMLButtonElement>
|
|
88
|
+
customComponentOverride?: JSX.Element
|
|
89
|
+
parentStyle?: React.CSSProperties
|
|
90
|
+
headerAxis?: Axis
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export default TableCell
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import Icon from '@ltht-react/icon'
|
|
2
|
+
import { flexRender, Header as ReactTableHeader, Table } from '@tanstack/react-table'
|
|
3
|
+
import React, { useMemo, useRef } from 'react'
|
|
4
|
+
import { calculateStaticColumnOffset } from './table-methods'
|
|
5
|
+
import {
|
|
6
|
+
StyledNextPageButtonContainer,
|
|
7
|
+
StyledSpinnerContainer,
|
|
8
|
+
StyledTable,
|
|
9
|
+
StyledTableRow,
|
|
10
|
+
StyledTableData,
|
|
11
|
+
StyledTableHeader,
|
|
12
|
+
StyledTHead,
|
|
13
|
+
} from './table-styled-components'
|
|
14
|
+
import useDimensionsRef from './useDimensionRef'
|
|
15
|
+
import { CellProps } from './table-cell'
|
|
16
|
+
import { ITableConfig } from './table'
|
|
17
|
+
|
|
18
|
+
const TableComponent = <T,>({ table, staticColumns = 0, headerAxis }: ITableHeadProps<T>): JSX.Element => {
|
|
19
|
+
const firstColumn = useRef(null)
|
|
20
|
+
const secondColumn = useRef(null)
|
|
21
|
+
const tableElement = useRef(null)
|
|
22
|
+
const { width: firstColumnWidth } = useDimensionsRef(firstColumn, tableElement)
|
|
23
|
+
const { width: secondColumnWidth } = useDimensionsRef(secondColumn, tableElement)
|
|
24
|
+
|
|
25
|
+
const usingExpanderColumn = table.getHeaderGroups().some((x) => x.headers.some((h) => h.column.id === 'expander'))
|
|
26
|
+
const totalStaticColumns = useMemo(
|
|
27
|
+
() => (usingExpanderColumn ? staticColumns + 1 : staticColumns),
|
|
28
|
+
[usingExpanderColumn, staticColumns]
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
const getHeaderColumn = <TData, TValue>(header: ReactTableHeader<TData, TValue>, headerIndex: number) => {
|
|
32
|
+
switch (headerIndex) {
|
|
33
|
+
case 0:
|
|
34
|
+
return getHeaderElement(
|
|
35
|
+
header,
|
|
36
|
+
calculateStaticColumnOffset(headerIndex, totalStaticColumns, firstColumnWidth, secondColumnWidth),
|
|
37
|
+
firstColumn
|
|
38
|
+
)
|
|
39
|
+
case 1:
|
|
40
|
+
return getHeaderElement(
|
|
41
|
+
header,
|
|
42
|
+
calculateStaticColumnOffset(headerIndex, totalStaticColumns, firstColumnWidth, secondColumnWidth),
|
|
43
|
+
secondColumn
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
default:
|
|
47
|
+
return getHeaderElement(
|
|
48
|
+
header,
|
|
49
|
+
calculateStaticColumnOffset(headerIndex, totalStaticColumns, firstColumnWidth, secondColumnWidth)
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const getHeaderElement = <TData, TValue>(
|
|
55
|
+
header: ReactTableHeader<TData, TValue>,
|
|
56
|
+
stickyWidth?: number,
|
|
57
|
+
elementRef?: React.MutableRefObject<null>
|
|
58
|
+
) => (
|
|
59
|
+
<StyledTableHeader
|
|
60
|
+
stickyWidth={stickyWidth}
|
|
61
|
+
key={header.id}
|
|
62
|
+
colSpan={header.colSpan}
|
|
63
|
+
ref={elementRef}
|
|
64
|
+
role="columnheader"
|
|
65
|
+
{...(header.column.id !== 'expander'
|
|
66
|
+
? {
|
|
67
|
+
style: {
|
|
68
|
+
cursor: header.column.getCanSort() ? 'pointer' : '',
|
|
69
|
+
},
|
|
70
|
+
onClick: header.column.getToggleSortingHandler(),
|
|
71
|
+
}
|
|
72
|
+
: {})}
|
|
73
|
+
>
|
|
74
|
+
{header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())}
|
|
75
|
+
</StyledTableHeader>
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
return (
|
|
79
|
+
<StyledTable ref={tableElement}>
|
|
80
|
+
<StyledTHead>
|
|
81
|
+
{table.getHeaderGroups().map((headerGroup) => (
|
|
82
|
+
<tr key={headerGroup.id} role="row">
|
|
83
|
+
{headerGroup.headers.map((header, headerIndex) => getHeaderColumn(header, headerIndex))}
|
|
84
|
+
</tr>
|
|
85
|
+
))}
|
|
86
|
+
</StyledTHead>
|
|
87
|
+
<tbody>
|
|
88
|
+
{table.getRowModel().rows.map((row) => (
|
|
89
|
+
<StyledTableRow tableHeaderAxis={headerAxis} key={row.id} role="row">
|
|
90
|
+
{row.getVisibleCells().map((cell, cellIdx) => (
|
|
91
|
+
<StyledTableData
|
|
92
|
+
tableHeaderAxis={headerAxis}
|
|
93
|
+
stickyWidth={calculateStaticColumnOffset(
|
|
94
|
+
cellIdx,
|
|
95
|
+
totalStaticColumns,
|
|
96
|
+
firstColumnWidth,
|
|
97
|
+
secondColumnWidth
|
|
98
|
+
)}
|
|
99
|
+
key={cell.id}
|
|
100
|
+
role="cell"
|
|
101
|
+
style={(cell.getValue() as CellProps)?.parentStyle}
|
|
102
|
+
>
|
|
103
|
+
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
|
104
|
+
</StyledTableData>
|
|
105
|
+
))}
|
|
106
|
+
</StyledTableRow>
|
|
107
|
+
))}
|
|
108
|
+
</tbody>
|
|
109
|
+
</StyledTable>
|
|
110
|
+
)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const TableNavigationButton = ({ position, hidden, clickHandler }: ITableNavButtonProps) => (
|
|
114
|
+
<StyledNextPageButtonContainer
|
|
115
|
+
role="button"
|
|
116
|
+
elementPosition={position}
|
|
117
|
+
onClick={() => clickHandler()}
|
|
118
|
+
hidden={hidden}
|
|
119
|
+
>
|
|
120
|
+
<Icon type="chevron" direction={position === 'bottom' ? 'down' : 'right'} size="medium" />
|
|
121
|
+
</StyledNextPageButtonContainer>
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
const TableSpinner = ({ position, hidden }: ITableSpinnerProps) => (
|
|
125
|
+
<StyledSpinnerContainer elementPosition={position} hidden={hidden}>
|
|
126
|
+
<Icon type="spinner" size="medium" title="Loading..." />
|
|
127
|
+
</StyledSpinnerContainer>
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
interface ITableNavButtonProps {
|
|
131
|
+
position: 'bottom' | 'right'
|
|
132
|
+
hidden: boolean
|
|
133
|
+
clickHandler: () => void
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
interface ITableSpinnerProps {
|
|
137
|
+
position: 'bottom' | 'right'
|
|
138
|
+
hidden: boolean
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
interface ITableHeadProps<T> extends ITableConfig {
|
|
142
|
+
table: Table<T>
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export default TableComponent
|
|
146
|
+
export { TableNavigationButton, TableSpinner }
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import { ColumnDef, ColumnHelper, createColumnHelper, HeaderContext, Table } from '@tanstack/react-table'
|
|
2
|
+
import { IconProps } from '@ltht-react/icon'
|
|
3
|
+
import { Axis } from '@ltht-react/types'
|
|
4
|
+
import React from 'react'
|
|
5
|
+
import { Header, TableData, DataEntity } from './table'
|
|
6
|
+
import TableCell, { CellProps } from './table-cell'
|
|
7
|
+
import { ScrollState } from './useScrollRef'
|
|
8
|
+
|
|
9
|
+
const createColumns = (tableData: TableData, headerAxis: Axis): ColumnDef<DataEntity>[] => {
|
|
10
|
+
const columnHelper = createColumnHelper<DataEntity>()
|
|
11
|
+
|
|
12
|
+
let columns = createColumnsRecursively(tableData.headers, columnHelper, headerAxis)
|
|
13
|
+
|
|
14
|
+
if (tableData.rows.some((row) => row.subRows)) {
|
|
15
|
+
columns = prependColumnWithExpansionControls(columns, columnHelper, headerAxis)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return columns
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const createColumnsRecursively = (
|
|
22
|
+
headers: Header[],
|
|
23
|
+
columnHelper: ColumnHelper<DataEntity>,
|
|
24
|
+
headerAxis: Axis
|
|
25
|
+
): ColumnDef<DataEntity>[] => {
|
|
26
|
+
const result: ColumnDef<DataEntity>[] = headers.map((header) => {
|
|
27
|
+
if (header.type === 'display') {
|
|
28
|
+
return columnHelper.display({
|
|
29
|
+
id: header.id,
|
|
30
|
+
header: () => {
|
|
31
|
+
const headerCellProps: CellProps = { ...header.cellProps, headerAxis }
|
|
32
|
+
return <TableCell {...headerCellProps} />
|
|
33
|
+
},
|
|
34
|
+
cell: (props) => {
|
|
35
|
+
const cellProps: CellProps = { ...(props.getValue() as CellProps), headerAxis }
|
|
36
|
+
return <TableCell {...cellProps} />
|
|
37
|
+
},
|
|
38
|
+
})
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (header.type === 'accessor') {
|
|
42
|
+
return columnHelper.accessor(header.id, {
|
|
43
|
+
header: (props) => {
|
|
44
|
+
const cellProps: CellProps = { ...header.cellProps, iconProps: deriveHeaderIconProps(props), headerAxis }
|
|
45
|
+
return <TableCell {...cellProps} />
|
|
46
|
+
},
|
|
47
|
+
cell: (props) => {
|
|
48
|
+
const cellProps: CellProps = { ...(props.getValue() as CellProps), headerAxis }
|
|
49
|
+
return <TableCell {...cellProps} />
|
|
50
|
+
},
|
|
51
|
+
}) as ColumnDef<DataEntity, unknown>
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return columnHelper.group({
|
|
55
|
+
header: header.cellProps.text ?? '',
|
|
56
|
+
columns: createColumnsRecursively(header.subHeaders ?? [], columnHelper, headerAxis),
|
|
57
|
+
})
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
return result
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const deriveHeaderIconProps = (props: HeaderContext<DataEntity, CellProps | DataEntity[]>): IconProps | undefined => {
|
|
64
|
+
if (props.column.getIsSorted() === 'asc') {
|
|
65
|
+
return {
|
|
66
|
+
type: 'chevron',
|
|
67
|
+
direction: 'up',
|
|
68
|
+
size: 'medium',
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (props.column.getIsSorted() === 'desc') {
|
|
72
|
+
return {
|
|
73
|
+
type: 'chevron',
|
|
74
|
+
direction: 'down',
|
|
75
|
+
size: 'medium',
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return undefined
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const prependColumnWithExpansionControls = (
|
|
82
|
+
columns: ColumnDef<DataEntity, unknown>[],
|
|
83
|
+
columnHelper: ColumnHelper<DataEntity>,
|
|
84
|
+
headerAxis: Axis
|
|
85
|
+
) => {
|
|
86
|
+
const expanderColumn = columnHelper.display({
|
|
87
|
+
id: 'expander',
|
|
88
|
+
header: ({ table }) => {
|
|
89
|
+
const headerCellProps: CellProps = {
|
|
90
|
+
iconProps: {
|
|
91
|
+
type: 'chevron',
|
|
92
|
+
direction: table.getIsAllRowsExpanded() ? 'down' : 'right',
|
|
93
|
+
size: 'medium',
|
|
94
|
+
},
|
|
95
|
+
headerAxis,
|
|
96
|
+
clickHandler: table.getToggleAllRowsExpandedHandler(),
|
|
97
|
+
}
|
|
98
|
+
return <TableCell {...headerCellProps} />
|
|
99
|
+
},
|
|
100
|
+
cell: (props) => {
|
|
101
|
+
const cellProps: CellProps = props.row.getCanExpand()
|
|
102
|
+
? {
|
|
103
|
+
iconProps: {
|
|
104
|
+
type: 'chevron',
|
|
105
|
+
direction: props.row.getIsExpanded() ? 'down' : 'right',
|
|
106
|
+
size: 'medium',
|
|
107
|
+
},
|
|
108
|
+
headerAxis,
|
|
109
|
+
clickHandler: props.row.getToggleExpandedHandler(),
|
|
110
|
+
}
|
|
111
|
+
: { headerAxis }
|
|
112
|
+
return <TableCell {...cellProps} />
|
|
113
|
+
},
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
return [expanderColumn].concat(columns)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const calculateStaticColumnOffset = (
|
|
120
|
+
cellIdx: number,
|
|
121
|
+
staticColumns: number,
|
|
122
|
+
firstColumnWidth: number,
|
|
123
|
+
secondColumnWidth: number
|
|
124
|
+
) => {
|
|
125
|
+
if (staticColumns === 0) {
|
|
126
|
+
return undefined
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
switch (cellIdx) {
|
|
130
|
+
case 0:
|
|
131
|
+
return 0
|
|
132
|
+
case 1:
|
|
133
|
+
return cellIdx < staticColumns ? firstColumnWidth : undefined
|
|
134
|
+
case 2:
|
|
135
|
+
return cellIdx < staticColumns ? firstColumnWidth + secondColumnWidth : undefined
|
|
136
|
+
default:
|
|
137
|
+
return undefined
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const handleScrollEvent = (table: Table<DataEntity>, headerAxis: Axis, scrollState: ScrollState) => {
|
|
142
|
+
const { scrollWidth, scrollHeight, currentXScroll, currentYScroll } = scrollState
|
|
143
|
+
if (
|
|
144
|
+
headerAxis === 'x' &&
|
|
145
|
+
currentYScroll >= scrollHeight - getPercentageOfMax(5, scrollHeight) &&
|
|
146
|
+
currentYScroll <= scrollHeight &&
|
|
147
|
+
table.getCanNextPage()
|
|
148
|
+
) {
|
|
149
|
+
table.nextPage()
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (
|
|
153
|
+
headerAxis === 'y' &&
|
|
154
|
+
currentXScroll >= scrollWidth - getPercentageOfMax(5, scrollWidth) &&
|
|
155
|
+
currentXScroll <= scrollWidth &&
|
|
156
|
+
table.getCanNextPage()
|
|
157
|
+
) {
|
|
158
|
+
table.nextPage()
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const getPercentageOfMax = (percentage: number, max: number) => max * (percentage / 100)
|
|
163
|
+
|
|
164
|
+
const handleDataUpdate = (
|
|
165
|
+
tableData: TableData,
|
|
166
|
+
pageIndex: number,
|
|
167
|
+
pageSize: number,
|
|
168
|
+
headerAxis: Axis,
|
|
169
|
+
setColumns: (value: React.SetStateAction<ColumnDef<DataEntity>[]>) => void,
|
|
170
|
+
setData: (value: React.SetStateAction<DataEntity[]>) => void,
|
|
171
|
+
setPageCount: (value: React.SetStateAction<number>) => void
|
|
172
|
+
) => {
|
|
173
|
+
if (tableData && tableData.headers.length > 0) {
|
|
174
|
+
if (headerAxis === 'x') {
|
|
175
|
+
setColumns(createColumns(tableData, headerAxis))
|
|
176
|
+
setData(tableData.rows.slice(0, (pageIndex + 1) * pageSize))
|
|
177
|
+
setPageCount(Math.ceil(tableData.rows.length / pageSize))
|
|
178
|
+
} else {
|
|
179
|
+
const head = tableData.headers[0]
|
|
180
|
+
const tail = tableData.headers.slice(1, tableData.headers.length)
|
|
181
|
+
setColumns(
|
|
182
|
+
createColumns(
|
|
183
|
+
{ headers: [head, ...tail.slice(0, (pageIndex + 1) * pageSize)], rows: tableData.rows },
|
|
184
|
+
headerAxis
|
|
185
|
+
)
|
|
186
|
+
)
|
|
187
|
+
setData(tableData.rows)
|
|
188
|
+
setPageCount(Math.ceil(tail.length / pageSize))
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const calculateSliceStartPoint = (oldLength: number, usingExpanderColumn: boolean) => {
|
|
194
|
+
if (oldLength > 0) {
|
|
195
|
+
return usingExpanderColumn ? 2 : 1
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return 0
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const handleDataUpdateForManualPagination = (
|
|
202
|
+
tableData: TableData,
|
|
203
|
+
headerAxis: Axis,
|
|
204
|
+
keepPreviousData: boolean,
|
|
205
|
+
setColumns: (value: React.SetStateAction<ColumnDef<DataEntity>[]>) => void,
|
|
206
|
+
setData: (value: React.SetStateAction<DataEntity[]>) => void
|
|
207
|
+
) => {
|
|
208
|
+
if (headerAxis === 'x') {
|
|
209
|
+
setData((old) => {
|
|
210
|
+
const newData = keepPreviousData ? [...old, ...tableData.rows] : tableData.rows
|
|
211
|
+
setColumns(createColumns({ headers: tableData.headers, rows: newData }, headerAxis))
|
|
212
|
+
return newData
|
|
213
|
+
})
|
|
214
|
+
} else if (tableData.headers.length > 0) {
|
|
215
|
+
setColumns((old) => {
|
|
216
|
+
const newColumns = createColumns(tableData, headerAxis)
|
|
217
|
+
const sliceStartPoint = calculateSliceStartPoint(
|
|
218
|
+
old.length,
|
|
219
|
+
tableData.rows.some((x) => x.subRows)
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
return keepPreviousData ? [...old, ...newColumns.slice(sliceStartPoint, newColumns.length)] : newColumns
|
|
223
|
+
})
|
|
224
|
+
setData(tableData.rows)
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export {
|
|
229
|
+
createColumns,
|
|
230
|
+
calculateStaticColumnOffset,
|
|
231
|
+
handleScrollEvent,
|
|
232
|
+
handleDataUpdate,
|
|
233
|
+
handleDataUpdateForManualPagination,
|
|
234
|
+
}
|