simple-table-core 0.1.5 → 0.1.6
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/package.json +5 -1
- package/.storybook/main.ts +0 -18
- package/.storybook/preview.ts +0 -14
- package/assets/table-example.png +0 -0
- package/rollup.config.js +0 -27
- package/src/components/Animate.tsx +0 -69
- package/src/components/SimpleTable/SimpleTable.tsx +0 -165
- package/src/components/SimpleTable/TableBody.tsx +0 -79
- package/src/components/SimpleTable/TableCell.tsx +0 -51
- package/src/components/SimpleTable/TableFooter.tsx +0 -61
- package/src/components/SimpleTable/TableHeader.tsx +0 -60
- package/src/components/SimpleTable/TableHeaderCell.tsx +0 -129
- package/src/components/SimpleTable/TableLastColumnCell.tsx +0 -17
- package/src/components/SimpleTable/TableRowSeparator.tsx +0 -5
- package/src/consts/SampleData.ts +0 -101
- package/src/helpers/calculateBoundingBoxes.ts +0 -29
- package/src/helpers/shuffleArray.ts +0 -6
- package/src/hooks/usePrevious.ts +0 -15
- package/src/hooks/useSelection.ts +0 -106
- package/src/hooks/useTableHeaderCell.ts +0 -76
- package/src/icons/AngleLeftIcon.tsx +0 -15
- package/src/icons/AngleRightIcon.tsx +0 -15
- package/src/index.tsx +0 -3
- package/src/react-app-env.d.ts +0 -1
- package/src/stories/SimpleTable.stories.ts +0 -16
- package/src/stories/SimpleTableExample.tsx +0 -17
- package/src/stories/assets/accessibility.png +0 -0
- package/src/stories/assets/accessibility.svg +0 -1
- package/src/stories/assets/addon-library.png +0 -0
- package/src/stories/assets/assets.png +0 -0
- package/src/stories/assets/avif-test-image.avif +0 -0
- package/src/stories/assets/context.png +0 -0
- package/src/stories/assets/discord.svg +0 -1
- package/src/stories/assets/docs.png +0 -0
- package/src/stories/assets/figma-plugin.png +0 -0
- package/src/stories/assets/github.svg +0 -1
- package/src/stories/assets/share.png +0 -0
- package/src/stories/assets/styling.png +0 -0
- package/src/stories/assets/testing.png +0 -0
- package/src/stories/assets/theming.png +0 -0
- package/src/stories/assets/tutorials.svg +0 -1
- package/src/stories/assets/youtube.svg +0 -1
- package/src/styles/simple-table.css +0 -242
- package/src/types/HeaderObject.ts +0 -10
- package/src/utils/performanceUtils.ts +0 -17
- package/src/utils/sortUtils.ts +0 -30
- package/tsconfig.json +0 -22
package/package.json
CHANGED
package/.storybook/main.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import type { StorybookConfig } from "@storybook/react-webpack5";
|
|
2
|
-
|
|
3
|
-
const config: StorybookConfig = {
|
|
4
|
-
stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"],
|
|
5
|
-
addons: [
|
|
6
|
-
"@storybook/preset-create-react-app",
|
|
7
|
-
"@storybook/addon-onboarding",
|
|
8
|
-
"@storybook/addon-links",
|
|
9
|
-
"@storybook/addon-essentials",
|
|
10
|
-
"@chromatic-com/storybook",
|
|
11
|
-
"@storybook/addon-interactions",
|
|
12
|
-
],
|
|
13
|
-
framework: {
|
|
14
|
-
name: "@storybook/react-webpack5",
|
|
15
|
-
options: {},
|
|
16
|
-
},
|
|
17
|
-
};
|
|
18
|
-
export default config;
|
package/.storybook/preview.ts
DELETED
package/assets/table-example.png
DELETED
|
Binary file
|
package/rollup.config.js
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import babel from "@rollup/plugin-babel";
|
|
2
|
-
import resolve from "@rollup/plugin-node-resolve";
|
|
3
|
-
import postcss from "rollup-plugin-postcss";
|
|
4
|
-
import typescript from "@rollup/plugin-typescript";
|
|
5
|
-
import { terser } from "rollup-plugin-terser";
|
|
6
|
-
|
|
7
|
-
export default {
|
|
8
|
-
input: "src/index.tsx",
|
|
9
|
-
output: {
|
|
10
|
-
file: "dist/index.js",
|
|
11
|
-
format: "esm",
|
|
12
|
-
},
|
|
13
|
-
plugins: [
|
|
14
|
-
postcss({
|
|
15
|
-
plugins: [],
|
|
16
|
-
minimize: true,
|
|
17
|
-
}),
|
|
18
|
-
babel({
|
|
19
|
-
exclude: "node_modules/**",
|
|
20
|
-
presets: ["@babel/preset-react"],
|
|
21
|
-
}),
|
|
22
|
-
resolve(),
|
|
23
|
-
typescript(),
|
|
24
|
-
terser(),
|
|
25
|
-
],
|
|
26
|
-
external: ["react", "react/jsx-runtime"],
|
|
27
|
-
};
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import React, { useState, useLayoutEffect, useEffect } from "react";
|
|
2
|
-
import usePrevious from "../hooks/usePrevious";
|
|
3
|
-
import calculateBoundingBoxes from "../helpers/calculateBoundingBoxes";
|
|
4
|
-
|
|
5
|
-
interface AnimateProps {
|
|
6
|
-
allowHorizontalAnimate?: boolean;
|
|
7
|
-
children: any;
|
|
8
|
-
pause?: boolean;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
const Animate = ({
|
|
12
|
-
allowHorizontalAnimate = true,
|
|
13
|
-
children,
|
|
14
|
-
pause,
|
|
15
|
-
}: AnimateProps) => {
|
|
16
|
-
const [boundingBox, setBoundingBox] = useState<any>({});
|
|
17
|
-
const [prevBoundingBox, setPrevBoundingBox] = useState<any>({});
|
|
18
|
-
const prevChildren = usePrevious(children);
|
|
19
|
-
|
|
20
|
-
useLayoutEffect(() => {
|
|
21
|
-
const newBoundingBox = calculateBoundingBoxes(children);
|
|
22
|
-
setBoundingBox(newBoundingBox);
|
|
23
|
-
}, [children]);
|
|
24
|
-
|
|
25
|
-
useLayoutEffect(() => {
|
|
26
|
-
const prevBoundingBox = calculateBoundingBoxes(prevChildren);
|
|
27
|
-
setPrevBoundingBox(prevBoundingBox);
|
|
28
|
-
}, [prevChildren]);
|
|
29
|
-
|
|
30
|
-
useEffect(() => {
|
|
31
|
-
if (pause) return;
|
|
32
|
-
const hasPrevBoundingBox = Object.keys(prevBoundingBox).length;
|
|
33
|
-
|
|
34
|
-
if (hasPrevBoundingBox) {
|
|
35
|
-
React.Children.forEach(children, (child) => {
|
|
36
|
-
const domNode = child.ref.current;
|
|
37
|
-
const firstBox = prevBoundingBox[child.key];
|
|
38
|
-
const lastBox = boundingBox[child.key];
|
|
39
|
-
|
|
40
|
-
const changeInX = firstBox.left - lastBox.left;
|
|
41
|
-
const changeInY = !allowHorizontalAnimate
|
|
42
|
-
? firstBox.top - lastBox.top
|
|
43
|
-
: 0;
|
|
44
|
-
|
|
45
|
-
const absoluteChangeInX = Math.abs(changeInX);
|
|
46
|
-
const absoluteChangeInY = Math.abs(changeInY);
|
|
47
|
-
|
|
48
|
-
if (absoluteChangeInX > 10 || absoluteChangeInY > 10) {
|
|
49
|
-
requestAnimationFrame(() => {
|
|
50
|
-
// Before the DOM paints, invert child to old position
|
|
51
|
-
domNode.style.transform = `translate(${changeInX}px, ${changeInY}px)`;
|
|
52
|
-
domNode.style.transition = "transform 0s";
|
|
53
|
-
|
|
54
|
-
requestAnimationFrame(() => {
|
|
55
|
-
// After the previous frame, remove
|
|
56
|
-
// the transition to play the animation
|
|
57
|
-
domNode.style.transform = "";
|
|
58
|
-
domNode.style.transition = "transform 500ms";
|
|
59
|
-
});
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
}, [boundingBox, prevBoundingBox, children, pause, allowHorizontalAnimate]);
|
|
65
|
-
|
|
66
|
-
return children;
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
export default Animate;
|
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
useState,
|
|
3
|
-
useRef,
|
|
4
|
-
useEffect,
|
|
5
|
-
useReducer,
|
|
6
|
-
ReactNode,
|
|
7
|
-
useMemo,
|
|
8
|
-
} from "react";
|
|
9
|
-
import useSelection from "../../hooks/useSelection";
|
|
10
|
-
import TableHeader from "./TableHeader";
|
|
11
|
-
import { onSort } from "../../utils/sortUtils";
|
|
12
|
-
import TableBody from "./TableBody";
|
|
13
|
-
import HeaderObject from "../../types/HeaderObject";
|
|
14
|
-
import TableFooter from "./TableFooter";
|
|
15
|
-
import AngleLeftIcon from "../../icons/AngleLeftIcon";
|
|
16
|
-
import AngleRightIcon from "../../icons/AngleRightIcon";
|
|
17
|
-
import "../../styles/simple-table.css";
|
|
18
|
-
|
|
19
|
-
interface SpreadsheetProps {
|
|
20
|
-
defaultHeaders: HeaderObject[];
|
|
21
|
-
enableColumnResizing?: boolean;
|
|
22
|
-
height?: string;
|
|
23
|
-
hideFooter?: boolean;
|
|
24
|
-
nextIcon?: ReactNode;
|
|
25
|
-
prevIcon?: ReactNode;
|
|
26
|
-
rows: { [key: string]: string | number | boolean | undefined | null }[];
|
|
27
|
-
rowsPerPage?: number;
|
|
28
|
-
shouldPaginate?: boolean;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const SimpleTable = ({
|
|
32
|
-
defaultHeaders,
|
|
33
|
-
enableColumnResizing = true,
|
|
34
|
-
height,
|
|
35
|
-
hideFooter = false,
|
|
36
|
-
nextIcon = <AngleRightIcon />,
|
|
37
|
-
prevIcon = <AngleLeftIcon />,
|
|
38
|
-
rows,
|
|
39
|
-
rowsPerPage = 10,
|
|
40
|
-
shouldPaginate = true,
|
|
41
|
-
}: SpreadsheetProps) => {
|
|
42
|
-
const [isWidthDragging, setIsWidthDragging] = useState(false);
|
|
43
|
-
const headersRef = useRef(defaultHeaders);
|
|
44
|
-
const [sortedRows, setSortedRows] = useState(rows);
|
|
45
|
-
const [sortConfig, setSortConfig] = useState<{
|
|
46
|
-
key: HeaderObject;
|
|
47
|
-
direction: string;
|
|
48
|
-
} | null>(null);
|
|
49
|
-
const [, forceUpdate] = useReducer((x) => x + 1, 0);
|
|
50
|
-
|
|
51
|
-
const [currentPage, setCurrentPage] = useState(1);
|
|
52
|
-
|
|
53
|
-
const tableRef = useRef<HTMLDivElement>(null);
|
|
54
|
-
|
|
55
|
-
const shouldDisplayLastColumnCell = useMemo(() => {
|
|
56
|
-
if (!tableRef.current) return false;
|
|
57
|
-
const totalColumnWidth = headersRef.current.reduce(
|
|
58
|
-
(acc, header) => acc + header.width,
|
|
59
|
-
0
|
|
60
|
-
);
|
|
61
|
-
return totalColumnWidth < tableRef.current.clientWidth;
|
|
62
|
-
}, []);
|
|
63
|
-
|
|
64
|
-
const {
|
|
65
|
-
handleMouseDown,
|
|
66
|
-
handleMouseOver,
|
|
67
|
-
handleMouseUp,
|
|
68
|
-
isSelected,
|
|
69
|
-
getBorderClass,
|
|
70
|
-
isTopLeftCell,
|
|
71
|
-
setSelectedCells,
|
|
72
|
-
} = useSelection(sortedRows, headersRef.current);
|
|
73
|
-
|
|
74
|
-
const handleSort = (columnIndex: number) => {
|
|
75
|
-
const { sortedData, newSortConfig } = onSort(
|
|
76
|
-
headersRef.current,
|
|
77
|
-
sortedRows,
|
|
78
|
-
sortConfig,
|
|
79
|
-
columnIndex
|
|
80
|
-
);
|
|
81
|
-
setSortedRows(sortedData);
|
|
82
|
-
setSortConfig(newSortConfig);
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
const onTableHeaderDragEnd = (newHeaders: HeaderObject[]) => {
|
|
86
|
-
headersRef.current = newHeaders;
|
|
87
|
-
forceUpdate();
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
useEffect(() => {
|
|
91
|
-
const handleClickOutside = (event: MouseEvent) => {
|
|
92
|
-
const target = event.target as HTMLElement;
|
|
93
|
-
if (!target.closest(".st-table-cell")) {
|
|
94
|
-
setSelectedCells([]);
|
|
95
|
-
}
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
document.addEventListener("mousedown", handleClickOutside);
|
|
99
|
-
return () => {
|
|
100
|
-
document.removeEventListener("mousedown", handleClickOutside);
|
|
101
|
-
};
|
|
102
|
-
}, [setSelectedCells]);
|
|
103
|
-
|
|
104
|
-
const currentRows = shouldPaginate
|
|
105
|
-
? sortedRows.slice(
|
|
106
|
-
(currentPage - 1) * rowsPerPage,
|
|
107
|
-
currentPage * rowsPerPage
|
|
108
|
-
)
|
|
109
|
-
: sortedRows;
|
|
110
|
-
|
|
111
|
-
return (
|
|
112
|
-
<div
|
|
113
|
-
ref={tableRef}
|
|
114
|
-
className="st-table-wrapper"
|
|
115
|
-
style={height ? { height } : {}}
|
|
116
|
-
>
|
|
117
|
-
<div
|
|
118
|
-
className="st-table"
|
|
119
|
-
onMouseUp={handleMouseUp}
|
|
120
|
-
onMouseLeave={handleMouseUp}
|
|
121
|
-
style={{
|
|
122
|
-
gridTemplateColumns: `${headersRef.current
|
|
123
|
-
?.map((header) => `${header.width}px`)
|
|
124
|
-
.join(" ")} 1fr`,
|
|
125
|
-
}}
|
|
126
|
-
>
|
|
127
|
-
<TableHeader
|
|
128
|
-
enableColumnResizing={enableColumnResizing}
|
|
129
|
-
forceUpdate={forceUpdate}
|
|
130
|
-
headersRef={headersRef}
|
|
131
|
-
isWidthDragging={isWidthDragging}
|
|
132
|
-
onSort={handleSort}
|
|
133
|
-
onTableHeaderDragEnd={onTableHeaderDragEnd}
|
|
134
|
-
setIsWidthDragging={setIsWidthDragging}
|
|
135
|
-
shouldDisplayLastColumnCell={shouldDisplayLastColumnCell}
|
|
136
|
-
/>
|
|
137
|
-
<TableBody
|
|
138
|
-
getBorderClass={getBorderClass}
|
|
139
|
-
handleMouseDown={handleMouseDown}
|
|
140
|
-
handleMouseOver={handleMouseOver}
|
|
141
|
-
headers={headersRef.current}
|
|
142
|
-
isSelected={isSelected}
|
|
143
|
-
isTopLeftCell={isTopLeftCell}
|
|
144
|
-
isWidthDragging={isWidthDragging}
|
|
145
|
-
shouldDisplayLastColumnCell={shouldDisplayLastColumnCell}
|
|
146
|
-
shouldPaginate={shouldPaginate}
|
|
147
|
-
sortedRows={currentRows}
|
|
148
|
-
/>
|
|
149
|
-
</div>
|
|
150
|
-
{shouldPaginate && (
|
|
151
|
-
<TableFooter
|
|
152
|
-
currentPage={currentPage}
|
|
153
|
-
hideFooter={hideFooter}
|
|
154
|
-
onPageChange={setCurrentPage}
|
|
155
|
-
rowsPerPage={rowsPerPage}
|
|
156
|
-
totalRows={sortedRows.length}
|
|
157
|
-
nextIcon={nextIcon}
|
|
158
|
-
prevIcon={prevIcon}
|
|
159
|
-
/>
|
|
160
|
-
)}
|
|
161
|
-
</div>
|
|
162
|
-
);
|
|
163
|
-
};
|
|
164
|
-
|
|
165
|
-
export default SimpleTable;
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import { createRef, Fragment } from "react";
|
|
2
|
-
import TableCell from "./TableCell";
|
|
3
|
-
import Animate from "../Animate";
|
|
4
|
-
import HeaderObject from "../../types/HeaderObject";
|
|
5
|
-
import TableLastColumnCell from "./TableLastColumnCell";
|
|
6
|
-
import TableRowSeparator from "./TableRowSeparator";
|
|
7
|
-
|
|
8
|
-
interface TableBodyProps {
|
|
9
|
-
getBorderClass: (rowIndex: number, columnIndex: number) => string;
|
|
10
|
-
handleMouseDown: (rowIndex: number, columnIndex: number) => void;
|
|
11
|
-
handleMouseOver: (rowIndex: number, columnIndex: number) => void;
|
|
12
|
-
headers: HeaderObject[];
|
|
13
|
-
isSelected: (rowIndex: number, columnIndex: number) => boolean;
|
|
14
|
-
isTopLeftCell: (rowIndex: number, columnIndex: number) => boolean;
|
|
15
|
-
isWidthDragging: boolean;
|
|
16
|
-
shouldDisplayLastColumnCell: boolean;
|
|
17
|
-
shouldPaginate: boolean;
|
|
18
|
-
sortedRows: { [key: string]: any }[];
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const TableBody = ({
|
|
22
|
-
getBorderClass,
|
|
23
|
-
handleMouseDown,
|
|
24
|
-
handleMouseOver,
|
|
25
|
-
headers,
|
|
26
|
-
isSelected,
|
|
27
|
-
isTopLeftCell,
|
|
28
|
-
isWidthDragging,
|
|
29
|
-
shouldDisplayLastColumnCell,
|
|
30
|
-
shouldPaginate,
|
|
31
|
-
sortedRows,
|
|
32
|
-
}: TableBodyProps & { shouldDisplayLastColumnCell: boolean }) => {
|
|
33
|
-
return (
|
|
34
|
-
<>
|
|
35
|
-
{sortedRows.map((row, rowIndex) => {
|
|
36
|
-
return (
|
|
37
|
-
<Fragment key={row.id}>
|
|
38
|
-
<Animate
|
|
39
|
-
allowHorizontalAnimate={shouldPaginate}
|
|
40
|
-
pause={isWidthDragging}
|
|
41
|
-
>
|
|
42
|
-
{headers.map((header, columnIndex) => {
|
|
43
|
-
let content = row[header.accessor];
|
|
44
|
-
|
|
45
|
-
if (header.cellRenderer) {
|
|
46
|
-
content = header.cellRenderer(row);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
return (
|
|
50
|
-
<TableCell
|
|
51
|
-
borderClass={getBorderClass(rowIndex, columnIndex)}
|
|
52
|
-
colIndex={columnIndex}
|
|
53
|
-
content={content}
|
|
54
|
-
isSelected={isSelected(rowIndex, columnIndex)}
|
|
55
|
-
isTopLeftCell={isTopLeftCell(rowIndex, columnIndex)}
|
|
56
|
-
key={header.accessor}
|
|
57
|
-
onMouseDown={() => handleMouseDown(rowIndex, columnIndex)}
|
|
58
|
-
onMouseOver={() => handleMouseOver(rowIndex, columnIndex)}
|
|
59
|
-
ref={createRef()}
|
|
60
|
-
rowIndex={rowIndex}
|
|
61
|
-
isLastRow={rowIndex === sortedRows.length - 1}
|
|
62
|
-
/>
|
|
63
|
-
);
|
|
64
|
-
})}
|
|
65
|
-
<TableLastColumnCell
|
|
66
|
-
isLastRow={rowIndex === sortedRows.length - 1}
|
|
67
|
-
ref={createRef()}
|
|
68
|
-
visible={shouldDisplayLastColumnCell}
|
|
69
|
-
/>
|
|
70
|
-
</Animate>
|
|
71
|
-
{rowIndex !== sortedRows.length - 1 && <TableRowSeparator />}
|
|
72
|
-
</Fragment>
|
|
73
|
-
);
|
|
74
|
-
})}
|
|
75
|
-
</>
|
|
76
|
-
);
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
export default TableBody;
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import { forwardRef, LegacyRef } from "react";
|
|
2
|
-
|
|
3
|
-
interface TableCellProps {
|
|
4
|
-
rowIndex: number;
|
|
5
|
-
colIndex: number;
|
|
6
|
-
content: any;
|
|
7
|
-
isSelected: boolean;
|
|
8
|
-
isTopLeftCell: boolean;
|
|
9
|
-
borderClass: string;
|
|
10
|
-
onMouseDown: (rowIndex: number, colIndex: number) => void;
|
|
11
|
-
onMouseOver: (rowIndex: number, colIndex: number) => void;
|
|
12
|
-
isLastRow: boolean;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const TableCell = forwardRef(
|
|
16
|
-
(
|
|
17
|
-
{
|
|
18
|
-
rowIndex,
|
|
19
|
-
colIndex,
|
|
20
|
-
content,
|
|
21
|
-
isSelected,
|
|
22
|
-
isTopLeftCell,
|
|
23
|
-
borderClass,
|
|
24
|
-
onMouseDown,
|
|
25
|
-
onMouseOver,
|
|
26
|
-
isLastRow,
|
|
27
|
-
}: TableCellProps,
|
|
28
|
-
ref: LegacyRef<HTMLTableCellElement>
|
|
29
|
-
) => {
|
|
30
|
-
const isOddRow = rowIndex % 2 === 0;
|
|
31
|
-
return (
|
|
32
|
-
<div
|
|
33
|
-
onMouseDown={() => onMouseDown(rowIndex, colIndex)}
|
|
34
|
-
onMouseOver={() => onMouseOver(rowIndex, colIndex)}
|
|
35
|
-
ref={ref}
|
|
36
|
-
className={`st-table-cell ${
|
|
37
|
-
isSelected
|
|
38
|
-
? isTopLeftCell
|
|
39
|
-
? `st-table-cell-selected-first-cell ${borderClass}`
|
|
40
|
-
: `st-table-cell-selected ${borderClass}`
|
|
41
|
-
: ""
|
|
42
|
-
} ${isLastRow ? "st-table-cell-last-row" : ""}
|
|
43
|
-
${isOddRow ? "st-table-cell-odd-row" : ""}`}
|
|
44
|
-
>
|
|
45
|
-
{content}
|
|
46
|
-
</div>
|
|
47
|
-
);
|
|
48
|
-
}
|
|
49
|
-
);
|
|
50
|
-
|
|
51
|
-
export default TableCell;
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { ReactNode } from "react";
|
|
2
|
-
|
|
3
|
-
interface TableFooterProps {
|
|
4
|
-
currentPage: number;
|
|
5
|
-
hideFooter?: boolean;
|
|
6
|
-
nextIcon?: ReactNode;
|
|
7
|
-
onPageChange: (page: number) => void;
|
|
8
|
-
prevIcon?: ReactNode;
|
|
9
|
-
rowsPerPage: number;
|
|
10
|
-
totalRows: number;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
const TableFooter = ({
|
|
14
|
-
currentPage,
|
|
15
|
-
hideFooter,
|
|
16
|
-
nextIcon,
|
|
17
|
-
onPageChange,
|
|
18
|
-
prevIcon,
|
|
19
|
-
rowsPerPage,
|
|
20
|
-
totalRows,
|
|
21
|
-
}: TableFooterProps) => {
|
|
22
|
-
const totalPages = Math.ceil(totalRows / rowsPerPage);
|
|
23
|
-
|
|
24
|
-
const handlePageChange = (page: number) => {
|
|
25
|
-
if (page >= 1 && page <= totalPages) {
|
|
26
|
-
onPageChange(page);
|
|
27
|
-
}
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
if (hideFooter) return null;
|
|
31
|
-
|
|
32
|
-
return (
|
|
33
|
-
<div className="st-footer">
|
|
34
|
-
<button
|
|
35
|
-
className="st-next-prev-btn"
|
|
36
|
-
onClick={() => handlePageChange(currentPage - 1)}
|
|
37
|
-
disabled={currentPage === 1}
|
|
38
|
-
>
|
|
39
|
-
{prevIcon}
|
|
40
|
-
</button>
|
|
41
|
-
<button
|
|
42
|
-
className="st-next-prev-btn"
|
|
43
|
-
onClick={() => handlePageChange(currentPage + 1)}
|
|
44
|
-
disabled={currentPage === totalPages}
|
|
45
|
-
>
|
|
46
|
-
{nextIcon}
|
|
47
|
-
</button>
|
|
48
|
-
{Array.from({ length: totalPages }, (_, index) => (
|
|
49
|
-
<button
|
|
50
|
-
key={index}
|
|
51
|
-
onClick={() => handlePageChange(index + 1)}
|
|
52
|
-
className={`st-page-btn ${currentPage === index + 1 ? "active" : ""}`}
|
|
53
|
-
>
|
|
54
|
-
{index + 1}
|
|
55
|
-
</button>
|
|
56
|
-
))}
|
|
57
|
-
</div>
|
|
58
|
-
);
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
export default TableFooter;
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import { createRef, Dispatch, SetStateAction, useRef } from "react";
|
|
2
|
-
import Animate from "../Animate";
|
|
3
|
-
import TableHeaderCell from "./TableHeaderCell";
|
|
4
|
-
import HeaderObject from "../../types/HeaderObject";
|
|
5
|
-
import TableLastColumnCell from "./TableLastColumnCell";
|
|
6
|
-
import TableRowSeparator from "./TableRowSeparator";
|
|
7
|
-
|
|
8
|
-
interface TableHeaderProps {
|
|
9
|
-
enableColumnResizing: boolean;
|
|
10
|
-
forceUpdate: () => void;
|
|
11
|
-
headersRef: React.RefObject<HeaderObject[]>;
|
|
12
|
-
isWidthDragging: boolean;
|
|
13
|
-
onSort: (columnIndex: number) => void;
|
|
14
|
-
onTableHeaderDragEnd: (newHeaders: HeaderObject[]) => void;
|
|
15
|
-
setIsWidthDragging: Dispatch<SetStateAction<boolean>>;
|
|
16
|
-
shouldDisplayLastColumnCell: boolean;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const TableHeader = ({
|
|
20
|
-
enableColumnResizing,
|
|
21
|
-
forceUpdate,
|
|
22
|
-
headersRef,
|
|
23
|
-
isWidthDragging,
|
|
24
|
-
onSort,
|
|
25
|
-
onTableHeaderDragEnd,
|
|
26
|
-
setIsWidthDragging,
|
|
27
|
-
shouldDisplayLastColumnCell,
|
|
28
|
-
}: TableHeaderProps) => {
|
|
29
|
-
const draggedHeaderRef = useRef<HeaderObject | null>(null);
|
|
30
|
-
const hoveredHeaderRef = useRef<HeaderObject | null>(null);
|
|
31
|
-
|
|
32
|
-
return (
|
|
33
|
-
<>
|
|
34
|
-
<Animate pause={isWidthDragging}>
|
|
35
|
-
{headersRef.current?.map((header, index) => (
|
|
36
|
-
<TableHeaderCell
|
|
37
|
-
draggedHeaderRef={draggedHeaderRef}
|
|
38
|
-
enableColumnResizing={enableColumnResizing}
|
|
39
|
-
forceUpdate={forceUpdate}
|
|
40
|
-
headersRef={headersRef}
|
|
41
|
-
hoveredHeaderRef={hoveredHeaderRef}
|
|
42
|
-
index={index}
|
|
43
|
-
key={header.accessor}
|
|
44
|
-
onSort={onSort}
|
|
45
|
-
onTableHeaderDragEnd={onTableHeaderDragEnd}
|
|
46
|
-
ref={createRef()}
|
|
47
|
-
setIsWidthDragging={setIsWidthDragging}
|
|
48
|
-
/>
|
|
49
|
-
))}
|
|
50
|
-
<TableLastColumnCell
|
|
51
|
-
ref={createRef()}
|
|
52
|
-
visible={shouldDisplayLastColumnCell}
|
|
53
|
-
/>
|
|
54
|
-
</Animate>
|
|
55
|
-
<TableRowSeparator />
|
|
56
|
-
</>
|
|
57
|
-
);
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
export default TableHeader;
|
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
import { forwardRef, useRef, SetStateAction, Dispatch, useState } from "react";
|
|
2
|
-
import useTableHeaderCell from "../../hooks/useTableHeaderCell";
|
|
3
|
-
import { throttle } from "../../utils/performanceUtils";
|
|
4
|
-
import HeaderObject from "../../types/HeaderObject";
|
|
5
|
-
|
|
6
|
-
interface TableHeaderCellProps {
|
|
7
|
-
draggedHeaderRef: React.MutableRefObject<HeaderObject | null>;
|
|
8
|
-
enableColumnResizing: boolean;
|
|
9
|
-
forceUpdate: () => void;
|
|
10
|
-
headersRef: React.RefObject<HeaderObject[]>;
|
|
11
|
-
hoveredHeaderRef: React.MutableRefObject<HeaderObject | null>;
|
|
12
|
-
index: number;
|
|
13
|
-
onSort: (columnIndex: number) => void;
|
|
14
|
-
onTableHeaderDragEnd: (newHeaders: HeaderObject[]) => void;
|
|
15
|
-
setIsWidthDragging: Dispatch<SetStateAction<boolean>>;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const TableHeaderCell = forwardRef<HTMLDivElement, TableHeaderCellProps>(
|
|
19
|
-
(
|
|
20
|
-
{
|
|
21
|
-
draggedHeaderRef,
|
|
22
|
-
enableColumnResizing,
|
|
23
|
-
forceUpdate,
|
|
24
|
-
headersRef,
|
|
25
|
-
hoveredHeaderRef,
|
|
26
|
-
index,
|
|
27
|
-
onSort,
|
|
28
|
-
onTableHeaderDragEnd,
|
|
29
|
-
setIsWidthDragging,
|
|
30
|
-
},
|
|
31
|
-
ref
|
|
32
|
-
) => {
|
|
33
|
-
const prevDraggingPosition = useRef({ pageX: 0, pageY: 0 });
|
|
34
|
-
const [isDragging, setIsDragging] = useState(false);
|
|
35
|
-
|
|
36
|
-
const header = headersRef.current?.[index];
|
|
37
|
-
|
|
38
|
-
const { handleDragStart, handleDragOver, handleDragEnd } =
|
|
39
|
-
useTableHeaderCell({
|
|
40
|
-
draggedHeaderRef,
|
|
41
|
-
headersRef,
|
|
42
|
-
hoveredHeaderRef,
|
|
43
|
-
onTableHeaderDragEnd,
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
const handleDragStartWrapper = (header: HeaderObject) => {
|
|
47
|
-
setIsDragging(true);
|
|
48
|
-
handleDragStart(header);
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
const handleDragEndWrapper = () => {
|
|
52
|
-
setIsDragging(false);
|
|
53
|
-
handleDragEnd();
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
// Throttle the handleDragOver function
|
|
57
|
-
const throttledHandleDragOver = useRef(
|
|
58
|
-
throttle((header: HeaderObject) => {
|
|
59
|
-
handleDragOver(header);
|
|
60
|
-
}, 100)
|
|
61
|
-
).current;
|
|
62
|
-
|
|
63
|
-
const handleResizeStart = (e: React.MouseEvent) => {
|
|
64
|
-
setIsWidthDragging(true);
|
|
65
|
-
e.preventDefault();
|
|
66
|
-
const startX = e.clientX;
|
|
67
|
-
if (!header) return;
|
|
68
|
-
const startWidth = header.width;
|
|
69
|
-
|
|
70
|
-
const throttledMouseMove = throttle((e: MouseEvent) => {
|
|
71
|
-
const newWidth = Math.max(startWidth + (e.clientX - startX), 10); // Ensure a minimum width
|
|
72
|
-
if (!header) return;
|
|
73
|
-
headersRef.current[index].width = newWidth;
|
|
74
|
-
forceUpdate();
|
|
75
|
-
}, 10); // Adjust the throttle delay as needed
|
|
76
|
-
|
|
77
|
-
const handleMouseUp = () => {
|
|
78
|
-
document.removeEventListener("mousemove", throttledMouseMove);
|
|
79
|
-
document.removeEventListener("mouseup", handleMouseUp);
|
|
80
|
-
setIsWidthDragging(false);
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
document.addEventListener("mousemove", throttledMouseMove);
|
|
84
|
-
document.addEventListener("mouseup", handleMouseUp);
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
if (!header) return null;
|
|
88
|
-
|
|
89
|
-
return (
|
|
90
|
-
<div
|
|
91
|
-
className={`st-table-header-cell ${
|
|
92
|
-
header === hoveredHeaderRef.current ? "st-hovered" : ""
|
|
93
|
-
} ${isDragging ? "st-dragging" : ""}`}
|
|
94
|
-
ref={ref}
|
|
95
|
-
style={{ width: header.width }}
|
|
96
|
-
>
|
|
97
|
-
<div
|
|
98
|
-
className="st-table-header-label"
|
|
99
|
-
draggable
|
|
100
|
-
onClick={() => onSort(index)}
|
|
101
|
-
onDragStart={() => handleDragStartWrapper(header)}
|
|
102
|
-
onDragOver={(event) => {
|
|
103
|
-
const { pageX, pageY } = event;
|
|
104
|
-
if (
|
|
105
|
-
pageX === prevDraggingPosition.current.pageX &&
|
|
106
|
-
pageY === prevDraggingPosition.current.pageY
|
|
107
|
-
) {
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
prevDraggingPosition.current = { pageX, pageY };
|
|
111
|
-
event.preventDefault();
|
|
112
|
-
throttledHandleDragOver(header, event);
|
|
113
|
-
}}
|
|
114
|
-
onDragEnd={handleDragEndWrapper}
|
|
115
|
-
>
|
|
116
|
-
{header?.label}
|
|
117
|
-
</div>
|
|
118
|
-
{enableColumnResizing && (
|
|
119
|
-
<div
|
|
120
|
-
className="st-table-header-resize-handle"
|
|
121
|
-
onMouseDown={handleResizeStart}
|
|
122
|
-
/>
|
|
123
|
-
)}
|
|
124
|
-
</div>
|
|
125
|
-
);
|
|
126
|
-
}
|
|
127
|
-
);
|
|
128
|
-
|
|
129
|
-
export default TableHeaderCell;
|