simple-table-core 0.1.1 → 0.1.2
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/notes.txt +3 -0
- package/package.json +1 -1
- package/src/App.tsx +8 -7
- package/src/components/Animate.tsx +9 -10
- package/src/components/SimpleTable/SimpleTable.tsx +32 -32
- package/src/components/SimpleTable/{TableRow.tsx → TableBody.tsx} +23 -26
- package/src/components/SimpleTable/TableCell.tsx +5 -5
- package/src/components/SimpleTable/TableHeader.tsx +30 -22
- package/src/components/SimpleTable/TableHeaderCell.tsx +73 -29
- package/src/hooks/useTableHeaderCell.ts +9 -9
- package/src/styles/index.css +8 -1
- package/src/styles/table.css +76 -35
- package/src/types/HeaderObject.ts +1 -0
package/notes.txt
ADDED
package/package.json
CHANGED
package/src/App.tsx
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
import SimpleTable from "./components/SimpleTable/SimpleTable";
|
|
2
2
|
import { sampleData } from "./consts/SampleData";
|
|
3
|
+
import HeaderObject from "./types/HeaderObject";
|
|
3
4
|
|
|
4
|
-
const HEADERS = [
|
|
5
|
-
{ label: "id", accessor: "id" },
|
|
6
|
-
{ label: "name", accessor: "name" },
|
|
7
|
-
{ label: "age", accessor: "age" },
|
|
8
|
-
{ label: "email", accessor: "email" },
|
|
9
|
-
{ label: "address", accessor: "address" },
|
|
5
|
+
const HEADERS: HeaderObject[] = [
|
|
6
|
+
{ label: "id", accessor: "id", width: 100 },
|
|
7
|
+
{ label: "name", accessor: "name", width: 100 },
|
|
8
|
+
{ label: "age", accessor: "age", width: 100 },
|
|
9
|
+
{ label: "email", accessor: "email", width: 100 },
|
|
10
|
+
{ label: "address", accessor: "address", width: 100 },
|
|
10
11
|
];
|
|
11
12
|
|
|
12
13
|
const App = () => {
|
|
13
14
|
return (
|
|
14
15
|
<div className="app" style={{ padding: "2rem" }}>
|
|
15
|
-
<SimpleTable
|
|
16
|
+
<SimpleTable defaultHeaders={HEADERS} rows={sampleData} />
|
|
16
17
|
</div>
|
|
17
18
|
);
|
|
18
19
|
};
|
|
@@ -4,10 +4,10 @@ import calculateBoundingBoxes from "../helpers/calculateBoundingBoxes";
|
|
|
4
4
|
|
|
5
5
|
interface AnimateProps {
|
|
6
6
|
children: any;
|
|
7
|
-
|
|
7
|
+
pause?: boolean;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
const Animate = ({ children,
|
|
10
|
+
const Animate = ({ children, pause }: AnimateProps) => {
|
|
11
11
|
const [boundingBox, setBoundingBox] = useState<any>({});
|
|
12
12
|
const [prevBoundingBox, setPrevBoundingBox] = useState<any>({});
|
|
13
13
|
const prevChildren = usePrevious(children);
|
|
@@ -23,6 +23,7 @@ const Animate = ({ children, animateRow }: AnimateProps) => {
|
|
|
23
23
|
}, [prevChildren]);
|
|
24
24
|
|
|
25
25
|
useEffect(() => {
|
|
26
|
+
if (pause) return;
|
|
26
27
|
const hasPrevBoundingBox = Object.keys(prevBoundingBox).length;
|
|
27
28
|
|
|
28
29
|
if (hasPrevBoundingBox) {
|
|
@@ -30,16 +31,14 @@ const Animate = ({ children, animateRow }: AnimateProps) => {
|
|
|
30
31
|
const domNode = child.ref.current;
|
|
31
32
|
const firstBox = prevBoundingBox[child.key];
|
|
32
33
|
const lastBox = boundingBox[child.key];
|
|
33
|
-
const changeInPosition = animateRow
|
|
34
|
-
? firstBox.top - lastBox.top
|
|
35
|
-
: firstBox.left - lastBox.left;
|
|
36
34
|
|
|
37
|
-
|
|
35
|
+
const changeInX = firstBox.left - lastBox.left;
|
|
36
|
+
const changeInY = firstBox.top - lastBox.top;
|
|
37
|
+
|
|
38
|
+
if (changeInX || changeInY) {
|
|
38
39
|
requestAnimationFrame(() => {
|
|
39
40
|
// Before the DOM paints, invert child to old position
|
|
40
|
-
domNode.style.transform =
|
|
41
|
-
? `translateY(${changeInPosition}px)`
|
|
42
|
-
: `translateX(${changeInPosition}px)`;
|
|
41
|
+
domNode.style.transform = `translate(${changeInX}px, ${changeInY}px)`;
|
|
43
42
|
domNode.style.transition = "transform 0s";
|
|
44
43
|
|
|
45
44
|
requestAnimationFrame(() => {
|
|
@@ -52,7 +51,7 @@ const Animate = ({ children, animateRow }: AnimateProps) => {
|
|
|
52
51
|
}
|
|
53
52
|
});
|
|
54
53
|
}
|
|
55
|
-
}, [boundingBox, prevBoundingBox, children,
|
|
54
|
+
}, [boundingBox, prevBoundingBox, children, pause]);
|
|
56
55
|
|
|
57
56
|
return children;
|
|
58
57
|
};
|
|
@@ -3,17 +3,18 @@ import useSelection from "../../hooks/useSelection";
|
|
|
3
3
|
import TableHeader from "./TableHeader";
|
|
4
4
|
import { onSort } from "../../utils/sortUtils";
|
|
5
5
|
import Animate from "../Animate";
|
|
6
|
-
import TableRow from "./
|
|
6
|
+
import TableRow from "./TableBody";
|
|
7
7
|
import HeaderObject from "../../types/HeaderObject";
|
|
8
|
+
import TableBody from "./TableBody";
|
|
8
9
|
|
|
9
10
|
interface SpreadsheetProps {
|
|
10
|
-
|
|
11
|
+
defaultHeaders: HeaderObject[];
|
|
11
12
|
rows: { [key: string]: any }[];
|
|
12
13
|
}
|
|
13
14
|
|
|
14
|
-
const SimpleTable = ({
|
|
15
|
-
const
|
|
16
|
-
const [,
|
|
15
|
+
const SimpleTable = ({ defaultHeaders, rows }: SpreadsheetProps) => {
|
|
16
|
+
const [isWidthDragging, setIsWidthDragging] = useState(false);
|
|
17
|
+
const [headers, setHeaders] = useState(defaultHeaders);
|
|
17
18
|
const [sortedRows, setSortedRows] = useState(rows);
|
|
18
19
|
const [sortConfig, setSortConfig] = useState<{
|
|
19
20
|
key: HeaderObject;
|
|
@@ -39,42 +40,41 @@ const SimpleTable = ({ headers, rows }: SpreadsheetProps) => {
|
|
|
39
40
|
setSortedRows(sortedData);
|
|
40
41
|
setSortConfig(newSortConfig);
|
|
41
42
|
};
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
forceUpdate();
|
|
43
|
+
const onTableHeaderDragEnd = (newHeaders: HeaderObject[]) => {
|
|
44
|
+
setHeaders(newHeaders);
|
|
45
45
|
};
|
|
46
46
|
|
|
47
47
|
return (
|
|
48
|
-
<div className="table-wrapper">
|
|
49
|
-
<
|
|
50
|
-
className="
|
|
48
|
+
<div className="st-table-wrapper">
|
|
49
|
+
<div
|
|
50
|
+
className="st-table"
|
|
51
51
|
onMouseUp={handleMouseUp}
|
|
52
52
|
onMouseLeave={handleMouseUp}
|
|
53
|
+
style={{
|
|
54
|
+
gridTemplateColumns: headers
|
|
55
|
+
?.map((header) => `${header.width}px`)
|
|
56
|
+
.join(" "),
|
|
57
|
+
}}
|
|
53
58
|
>
|
|
54
59
|
<TableHeader
|
|
55
|
-
|
|
60
|
+
headers={headers}
|
|
56
61
|
onSort={handleSort}
|
|
57
|
-
|
|
62
|
+
onTableHeaderDragEnd={onTableHeaderDragEnd}
|
|
63
|
+
setHeaders={setHeaders}
|
|
64
|
+
setIsWidthDragging={setIsWidthDragging}
|
|
65
|
+
isWidthDragging={isWidthDragging}
|
|
58
66
|
/>
|
|
59
|
-
<
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
ref={createRef()}
|
|
71
|
-
row={row}
|
|
72
|
-
rowIndex={rowIndex}
|
|
73
|
-
/>
|
|
74
|
-
))}
|
|
75
|
-
</Animate>
|
|
76
|
-
</tbody>
|
|
77
|
-
</table>
|
|
67
|
+
<TableBody
|
|
68
|
+
getBorderClass={getBorderClass}
|
|
69
|
+
handleMouseDown={handleMouseDown}
|
|
70
|
+
handleMouseOver={handleMouseOver}
|
|
71
|
+
headers={headers}
|
|
72
|
+
isSelected={isSelected}
|
|
73
|
+
isTopLeftCell={isTopLeftCell}
|
|
74
|
+
sortedRows={sortedRows}
|
|
75
|
+
isWidthDragging={isWidthDragging}
|
|
76
|
+
/>
|
|
77
|
+
</div>
|
|
78
78
|
</div>
|
|
79
79
|
);
|
|
80
80
|
};
|
|
@@ -4,51 +4,48 @@ import Animate from "../Animate";
|
|
|
4
4
|
import HeaderObject from "../../types/HeaderObject";
|
|
5
5
|
|
|
6
6
|
interface TableRowProps {
|
|
7
|
-
rowIndex: number;
|
|
8
|
-
row: { [key: string]: any };
|
|
9
7
|
headers: HeaderObject[];
|
|
10
8
|
isSelected: (rowIndex: number, columnIndex: number) => boolean;
|
|
11
9
|
isTopLeftCell: (rowIndex: number, columnIndex: number) => boolean;
|
|
12
10
|
getBorderClass: (rowIndex: number, columnIndex: number) => string;
|
|
13
11
|
handleMouseDown: (rowIndex: number, columnIndex: number) => void;
|
|
14
12
|
handleMouseOver: (rowIndex: number, columnIndex: number) => void;
|
|
13
|
+
sortedRows: { [key: string]: any }[];
|
|
14
|
+
isWidthDragging: boolean;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
const TableRow =
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
return (
|
|
32
|
-
<tr key={row.id} ref={ref}>
|
|
33
|
-
<Animate>
|
|
17
|
+
const TableRow = ({
|
|
18
|
+
headers,
|
|
19
|
+
isSelected,
|
|
20
|
+
isTopLeftCell,
|
|
21
|
+
getBorderClass,
|
|
22
|
+
handleMouseDown,
|
|
23
|
+
handleMouseOver,
|
|
24
|
+
sortedRows,
|
|
25
|
+
isWidthDragging,
|
|
26
|
+
}: TableRowProps) => {
|
|
27
|
+
return (
|
|
28
|
+
<>
|
|
29
|
+
{sortedRows.map((row, rowIndex) => (
|
|
30
|
+
<Animate key={row.id} pause={isWidthDragging}>
|
|
34
31
|
{headers.map((header, columnIndex) => (
|
|
35
32
|
<TableCell
|
|
36
|
-
|
|
37
|
-
rowIndex={rowIndex}
|
|
33
|
+
borderClass={getBorderClass(rowIndex, columnIndex)}
|
|
38
34
|
colIndex={columnIndex}
|
|
39
35
|
content={row[header.accessor]}
|
|
40
36
|
isSelected={isSelected(rowIndex, columnIndex)}
|
|
41
37
|
isTopLeftCell={isTopLeftCell(rowIndex, columnIndex)}
|
|
42
|
-
|
|
38
|
+
key={header.accessor}
|
|
43
39
|
onMouseDown={() => handleMouseDown(rowIndex, columnIndex)}
|
|
44
40
|
onMouseOver={() => handleMouseOver(rowIndex, columnIndex)}
|
|
45
41
|
ref={createRef()}
|
|
42
|
+
rowIndex={rowIndex}
|
|
46
43
|
/>
|
|
47
44
|
))}
|
|
48
45
|
</Animate>
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
46
|
+
))}
|
|
47
|
+
</>
|
|
48
|
+
);
|
|
49
|
+
};
|
|
53
50
|
|
|
54
51
|
export default TableRow;
|
|
@@ -26,23 +26,23 @@ const TableCell = forwardRef(
|
|
|
26
26
|
ref: LegacyRef<HTMLTableCellElement>
|
|
27
27
|
) => {
|
|
28
28
|
return (
|
|
29
|
-
<
|
|
29
|
+
<div
|
|
30
30
|
onMouseDown={() => onMouseDown(rowIndex, colIndex)}
|
|
31
31
|
onMouseOver={() => onMouseOver(rowIndex, colIndex)}
|
|
32
32
|
ref={ref}
|
|
33
33
|
>
|
|
34
34
|
<div
|
|
35
|
-
className={`table-cell ${
|
|
35
|
+
className={`st-table-cell ${
|
|
36
36
|
isSelected
|
|
37
37
|
? isTopLeftCell
|
|
38
|
-
? `selected-first-cell ${borderClass}`
|
|
39
|
-
: `selected ${borderClass}`
|
|
38
|
+
? `st-table-cell-selected-first-cell ${borderClass}`
|
|
39
|
+
: `st-table-cell-selected ${borderClass}`
|
|
40
40
|
: ""
|
|
41
41
|
}`}
|
|
42
42
|
>
|
|
43
43
|
{content}
|
|
44
44
|
</div>
|
|
45
|
-
</
|
|
45
|
+
</div>
|
|
46
46
|
);
|
|
47
47
|
}
|
|
48
48
|
);
|
|
@@ -1,37 +1,45 @@
|
|
|
1
|
-
import { createRef, useRef } from "react";
|
|
1
|
+
import { createRef, Dispatch, SetStateAction, useRef } from "react";
|
|
2
2
|
import Animate from "../Animate";
|
|
3
3
|
import TableHeaderCell from "./TableHeaderCell";
|
|
4
4
|
import HeaderObject from "../../types/HeaderObject";
|
|
5
5
|
|
|
6
6
|
interface TableHeaderProps {
|
|
7
|
-
|
|
7
|
+
headers: HeaderObject[];
|
|
8
|
+
isWidthDragging: boolean;
|
|
8
9
|
onSort: (columnIndex: number) => void;
|
|
9
|
-
|
|
10
|
+
onTableHeaderDragEnd: (newHeaders: HeaderObject[]) => void;
|
|
11
|
+
setHeaders: Dispatch<SetStateAction<HeaderObject[]>>;
|
|
12
|
+
setIsWidthDragging: Dispatch<SetStateAction<boolean>>;
|
|
10
13
|
}
|
|
11
14
|
|
|
12
|
-
const TableHeader
|
|
15
|
+
const TableHeader: React.FC<TableHeaderProps> = ({
|
|
16
|
+
headers,
|
|
17
|
+
isWidthDragging,
|
|
18
|
+
onSort,
|
|
19
|
+
onTableHeaderDragEnd,
|
|
20
|
+
setHeaders,
|
|
21
|
+
setIsWidthDragging,
|
|
22
|
+
}) => {
|
|
13
23
|
const draggedHeaderRef = useRef<HeaderObject | null>(null);
|
|
14
24
|
const hoveredHeaderRef = useRef<HeaderObject | null>(null);
|
|
15
25
|
|
|
16
26
|
return (
|
|
17
|
-
<
|
|
18
|
-
|
|
19
|
-
<
|
|
20
|
-
{
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
</tr>
|
|
34
|
-
</thead>
|
|
27
|
+
<Animate pause={isWidthDragging}>
|
|
28
|
+
{headers?.map((header, index) => (
|
|
29
|
+
<TableHeaderCell
|
|
30
|
+
draggedHeaderRef={draggedHeaderRef}
|
|
31
|
+
headers={headers}
|
|
32
|
+
hoveredHeaderRef={hoveredHeaderRef}
|
|
33
|
+
index={index}
|
|
34
|
+
key={header.accessor}
|
|
35
|
+
onSort={onSort}
|
|
36
|
+
onTableHeaderDragEnd={onTableHeaderDragEnd}
|
|
37
|
+
ref={createRef()}
|
|
38
|
+
setHeaders={setHeaders}
|
|
39
|
+
setIsWidthDragging={setIsWidthDragging}
|
|
40
|
+
/>
|
|
41
|
+
))}
|
|
42
|
+
</Animate>
|
|
35
43
|
);
|
|
36
44
|
};
|
|
37
45
|
|
|
@@ -1,46 +1,56 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
forwardRef,
|
|
3
|
+
LegacyRef,
|
|
4
|
+
useState,
|
|
5
|
+
useRef,
|
|
6
|
+
useEffect,
|
|
7
|
+
SetStateAction,
|
|
8
|
+
Dispatch,
|
|
9
|
+
} from "react";
|
|
2
10
|
import useTableHeaderCell from "../../hooks/useTableHeaderCell";
|
|
3
11
|
import { throttle } from "../../utils/performanceUtils";
|
|
4
12
|
import HeaderObject from "../../types/HeaderObject";
|
|
5
13
|
|
|
6
14
|
interface TableHeaderCellProps {
|
|
7
15
|
draggedHeaderRef: React.MutableRefObject<HeaderObject | null>;
|
|
8
|
-
|
|
16
|
+
headers: HeaderObject[];
|
|
9
17
|
hoveredHeaderRef: React.MutableRefObject<HeaderObject | null>;
|
|
10
18
|
index: number;
|
|
11
|
-
onDragEnd: (newHeaders: HeaderObject[]) => void;
|
|
12
19
|
onSort: (columnIndex: number) => void;
|
|
20
|
+
onTableHeaderDragEnd: (newHeaders: HeaderObject[]) => void;
|
|
21
|
+
setHeaders: Dispatch<SetStateAction<HeaderObject[]>>;
|
|
22
|
+
setIsWidthDragging: Dispatch<SetStateAction<boolean>>;
|
|
13
23
|
}
|
|
14
24
|
|
|
15
|
-
const TableHeaderCell = forwardRef(
|
|
25
|
+
const TableHeaderCell = forwardRef<HTMLDivElement, TableHeaderCellProps>(
|
|
16
26
|
(
|
|
17
27
|
{
|
|
18
28
|
draggedHeaderRef,
|
|
19
|
-
|
|
29
|
+
headers,
|
|
20
30
|
hoveredHeaderRef,
|
|
21
31
|
index,
|
|
22
|
-
onDragEnd,
|
|
23
32
|
onSort,
|
|
24
|
-
|
|
25
|
-
|
|
33
|
+
onTableHeaderDragEnd,
|
|
34
|
+
setHeaders,
|
|
35
|
+
setIsWidthDragging,
|
|
36
|
+
},
|
|
37
|
+
ref
|
|
26
38
|
) => {
|
|
27
|
-
const
|
|
28
|
-
|
|
39
|
+
const header = headers[index];
|
|
40
|
+
|
|
29
41
|
const { handleDragStart, handleDragOver, handleDragEnd } =
|
|
30
42
|
useTableHeaderCell({
|
|
31
43
|
draggedHeaderRef,
|
|
32
|
-
|
|
44
|
+
headers,
|
|
33
45
|
hoveredHeaderRef,
|
|
34
|
-
|
|
46
|
+
onTableHeaderDragEnd,
|
|
35
47
|
});
|
|
36
48
|
|
|
37
49
|
const handleDragStartWrapper = (header: HeaderObject) => {
|
|
38
|
-
setIsDragging(true);
|
|
39
50
|
handleDragStart(header);
|
|
40
51
|
};
|
|
41
52
|
|
|
42
53
|
const handleDragEndWrapper = () => {
|
|
43
|
-
setIsDragging(false);
|
|
44
54
|
handleDragEnd();
|
|
45
55
|
};
|
|
46
56
|
|
|
@@ -50,26 +60,60 @@ const TableHeaderCell = forwardRef(
|
|
|
50
60
|
handleDragOver(header);
|
|
51
61
|
}, 50) // Adjust the delay as needed
|
|
52
62
|
).current;
|
|
63
|
+
|
|
64
|
+
const handleResizeStart = (e: React.MouseEvent) => {
|
|
65
|
+
setIsWidthDragging(true);
|
|
66
|
+
e.preventDefault();
|
|
67
|
+
const startX = e.clientX;
|
|
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
|
+
setHeaders((prevHeaders) => {
|
|
73
|
+
const updatedHeaders = [...prevHeaders];
|
|
74
|
+
updatedHeaders[index] = { ...updatedHeaders[index], width: newWidth };
|
|
75
|
+
return updatedHeaders;
|
|
76
|
+
});
|
|
77
|
+
}, 10); // Adjust the throttle delay as needed
|
|
78
|
+
|
|
79
|
+
const handleMouseUp = () => {
|
|
80
|
+
document.removeEventListener("mousemove", throttledMouseMove);
|
|
81
|
+
document.removeEventListener("mouseup", handleMouseUp);
|
|
82
|
+
setIsWidthDragging(false);
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
document.addEventListener("mousemove", throttledMouseMove);
|
|
86
|
+
document.addEventListener("mouseup", handleMouseUp);
|
|
87
|
+
};
|
|
88
|
+
|
|
53
89
|
if (!header) return null;
|
|
54
90
|
|
|
55
91
|
return (
|
|
56
|
-
<
|
|
57
|
-
className={`
|
|
58
|
-
header === hoveredHeaderRef.current ? "hovered" : ""
|
|
59
|
-
}
|
|
60
|
-
key={header?.accessor}
|
|
61
|
-
draggable
|
|
62
|
-
onDragStart={() => handleDragStartWrapper(header)}
|
|
63
|
-
onDragOver={(event) => {
|
|
64
|
-
event.preventDefault();
|
|
65
|
-
throttledHandleDragOver(header, event);
|
|
66
|
-
}}
|
|
67
|
-
onDragEnd={handleDragEndWrapper}
|
|
68
|
-
onClick={() => onSort(index)}
|
|
92
|
+
<div
|
|
93
|
+
className={`st-th ${
|
|
94
|
+
header === hoveredHeaderRef.current ? "st-hovered" : ""
|
|
95
|
+
}`}
|
|
69
96
|
ref={ref}
|
|
97
|
+
style={{ width: header.width }}
|
|
70
98
|
>
|
|
71
|
-
|
|
72
|
-
|
|
99
|
+
<div
|
|
100
|
+
className="st-table-header-label"
|
|
101
|
+
draggable
|
|
102
|
+
onClick={() => onSort(index)}
|
|
103
|
+
onDragStart={() => handleDragStartWrapper(header)}
|
|
104
|
+
onDragOver={(event) => {
|
|
105
|
+
event.preventDefault();
|
|
106
|
+
throttledHandleDragOver(header, event);
|
|
107
|
+
}}
|
|
108
|
+
onDragEnd={handleDragEndWrapper}
|
|
109
|
+
>
|
|
110
|
+
{header?.label}
|
|
111
|
+
</div>
|
|
112
|
+
<div
|
|
113
|
+
className="st-table-header-resize-handle"
|
|
114
|
+
onMouseDown={handleResizeStart}
|
|
115
|
+
/>
|
|
116
|
+
</div>
|
|
73
117
|
);
|
|
74
118
|
}
|
|
75
119
|
);
|
|
@@ -2,17 +2,17 @@ import HeaderObject from "../types/HeaderObject";
|
|
|
2
2
|
|
|
3
3
|
interface UseTableHeaderCellProps {
|
|
4
4
|
draggedHeaderRef: React.MutableRefObject<HeaderObject | null>;
|
|
5
|
-
|
|
5
|
+
headers: HeaderObject[];
|
|
6
6
|
hoveredHeaderRef: React.MutableRefObject<HeaderObject | null>;
|
|
7
|
-
|
|
7
|
+
onTableHeaderDragEnd: (newHeaders: HeaderObject[]) => void;
|
|
8
8
|
}
|
|
9
9
|
var isUpdating = false;
|
|
10
10
|
|
|
11
11
|
const useTableHeaderCell = ({
|
|
12
12
|
draggedHeaderRef,
|
|
13
|
-
|
|
13
|
+
headers,
|
|
14
14
|
hoveredHeaderRef,
|
|
15
|
-
|
|
15
|
+
onTableHeaderDragEnd,
|
|
16
16
|
}: UseTableHeaderCellProps) => {
|
|
17
17
|
const handleDragStart = (header: HeaderObject) => {
|
|
18
18
|
draggedHeaderRef.current = header;
|
|
@@ -28,11 +28,11 @@ const useTableHeaderCell = ({
|
|
|
28
28
|
!isUpdating
|
|
29
29
|
) {
|
|
30
30
|
isUpdating = true;
|
|
31
|
-
const newHeaders = [...
|
|
32
|
-
const draggedHeaderIndex =
|
|
31
|
+
const newHeaders = [...headers];
|
|
32
|
+
const draggedHeaderIndex = headers.findIndex(
|
|
33
33
|
(header) => header.accessor === draggedHeaderRef.current?.accessor
|
|
34
34
|
);
|
|
35
|
-
const hoveredHeaderIndex =
|
|
35
|
+
const hoveredHeaderIndex = headers.findIndex(
|
|
36
36
|
(header) => header.accessor === hoveredHeader.accessor
|
|
37
37
|
);
|
|
38
38
|
if (draggedHeaderIndex === undefined || hoveredHeaderIndex === undefined)
|
|
@@ -42,9 +42,9 @@ const useTableHeaderCell = ({
|
|
|
42
42
|
newHeaders.splice(hoveredHeaderIndex, 0, draggedHeader);
|
|
43
43
|
|
|
44
44
|
// Check if the newHeaders array is different from the original headers array
|
|
45
|
-
if (JSON.stringify(newHeaders) !== JSON.stringify(
|
|
45
|
+
if (JSON.stringify(newHeaders) !== JSON.stringify(headers))
|
|
46
46
|
setTimeout(() => {
|
|
47
|
-
|
|
47
|
+
onTableHeaderDragEnd(newHeaders);
|
|
48
48
|
|
|
49
49
|
setTimeout(() => {
|
|
50
50
|
isUpdating = false;
|
package/src/styles/index.css
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
/* Import Nunito font from Google Fonts */
|
|
2
2
|
@import url("https://fonts.googleapis.com/css2?family=Nunito:wght@400;700&display=swap");
|
|
3
3
|
|
|
4
|
+
/* Global styles */
|
|
5
|
+
* {
|
|
6
|
+
box-sizing: border-box;
|
|
7
|
+
margin: 0;
|
|
8
|
+
padding: 0;
|
|
9
|
+
}
|
|
10
|
+
|
|
4
11
|
/* Apply Nunito as the default font */
|
|
5
12
|
body {
|
|
6
|
-
font-family: "Nunito"
|
|
13
|
+
font-family: "Nunito";
|
|
7
14
|
}
|
package/src/styles/table.css
CHANGED
|
@@ -9,85 +9,126 @@
|
|
|
9
9
|
--blue-700: #2c5282;
|
|
10
10
|
--blue-800: #2a4365;
|
|
11
11
|
--blue-900: #1a365d;
|
|
12
|
+
|
|
12
13
|
--selected-first-cell: white;
|
|
13
|
-
--light-grey: #d3d3d3;
|
|
14
14
|
--border-radius: 4px;
|
|
15
15
|
--border-width: 1px;
|
|
16
|
-
}
|
|
17
16
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
17
|
+
/* Gray colors */
|
|
18
|
+
--gray-50: #f9fafb;
|
|
19
|
+
--gray-100: #f3f4f6;
|
|
20
|
+
--gray-200: #e5e7eb;
|
|
21
|
+
--gray-300: #d1d5db;
|
|
22
|
+
--gray-400: #9ca3af;
|
|
23
|
+
--gray-500: #6b7280;
|
|
24
|
+
--gray-600: #4b5563;
|
|
25
|
+
--gray-700: #374151;
|
|
26
|
+
--gray-800: #1f2937;
|
|
27
|
+
--gray-900: #111827;
|
|
28
|
+
|
|
29
|
+
/* Slate colors */
|
|
30
|
+
--slate-50: #f8fafc;
|
|
31
|
+
--slate-100: #f1f5f9;
|
|
32
|
+
--slate-200: #e2e8f0;
|
|
33
|
+
--slate-300: #cbd5e1;
|
|
34
|
+
--slate-400: #94a3b8;
|
|
35
|
+
--slate-500: #64748b;
|
|
36
|
+
--slate-600: #475569;
|
|
37
|
+
--slate-700: #334155;
|
|
38
|
+
--slate-800: #1e293b;
|
|
39
|
+
--slate-900: #0f172a;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/* Wrapper for the table */
|
|
43
|
+
.st-table-wrapper {
|
|
44
|
+
border: 1px solid var(--slate-300);
|
|
27
45
|
border-radius: var(--border-radius);
|
|
28
|
-
overflow:
|
|
46
|
+
overflow: auto;
|
|
29
47
|
}
|
|
30
48
|
|
|
31
|
-
table
|
|
49
|
+
/* Main table styling */
|
|
50
|
+
.st-table {
|
|
51
|
+
display: grid;
|
|
32
52
|
border-collapse: collapse;
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
thead tr,
|
|
38
|
-
tr:not(:last-child) {
|
|
39
|
-
border-bottom: 1px solid var(--light-grey);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
tr {
|
|
43
|
-
transition: transform 0.4s, top 0.4s, opacity 0.2s;
|
|
44
|
-
transform: translateY(0);
|
|
45
|
-
}
|
|
53
|
+
table-layout: auto;
|
|
54
|
+
white-space: nowrap;
|
|
46
55
|
|
|
47
|
-
|
|
48
|
-
background-color: var(--blue-100);
|
|
56
|
+
width: 100%;
|
|
49
57
|
}
|
|
50
58
|
|
|
51
|
-
|
|
52
|
-
|
|
59
|
+
/* Styles for table header cells */
|
|
60
|
+
.st-th {
|
|
61
|
+
width: 100px;
|
|
62
|
+
position: relative;
|
|
53
63
|
}
|
|
54
64
|
|
|
55
|
-
|
|
56
|
-
.
|
|
65
|
+
/* Common styles for table header and cells */
|
|
66
|
+
.st-th,
|
|
67
|
+
.st-table-cell {
|
|
57
68
|
user-select: none;
|
|
58
69
|
cursor: pointer;
|
|
59
70
|
padding: 8px;
|
|
60
71
|
text-align: left;
|
|
72
|
+
|
|
73
|
+
white-space: nowrap;
|
|
74
|
+
text-overflow: ellipsis;
|
|
61
75
|
}
|
|
62
76
|
|
|
63
|
-
|
|
77
|
+
/* Style for a cell when it is being dragged */
|
|
78
|
+
.st-dragging {
|
|
79
|
+
background-color: var(--blue-100);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/* Basic styling for table cells */
|
|
83
|
+
.st-table-cell {
|
|
64
84
|
border: var(--border-width) solid transparent;
|
|
65
85
|
}
|
|
66
86
|
|
|
67
|
-
|
|
87
|
+
/* Style for selected table cells */
|
|
88
|
+
.st-table-cell-selected {
|
|
68
89
|
background-color: var(--blue-200);
|
|
69
90
|
}
|
|
70
91
|
|
|
71
|
-
|
|
92
|
+
/* Style for the first selected table cell */
|
|
93
|
+
.st-table-cell-selected-first {
|
|
72
94
|
background-color: var(--selected-first-cell);
|
|
73
95
|
}
|
|
74
96
|
|
|
97
|
+
/* Blue top border for cells */
|
|
75
98
|
.border-top-blue {
|
|
76
99
|
border-top: var(--border-width) solid var(--blue-500);
|
|
77
100
|
}
|
|
78
101
|
|
|
102
|
+
/* Blue bottom border for cells */
|
|
79
103
|
.border-bottom-blue {
|
|
80
104
|
border-bottom: var(--border-width) solid var(--blue-500);
|
|
81
105
|
}
|
|
82
106
|
|
|
107
|
+
/* Blue left border for cells */
|
|
83
108
|
.border-left-blue {
|
|
84
109
|
border-left: var(--border-width) solid var(--blue-500);
|
|
85
110
|
}
|
|
86
111
|
|
|
112
|
+
/* Blue right border for cells */
|
|
87
113
|
.border-right-blue {
|
|
88
114
|
border-right: var(--border-width) solid var(--blue-500);
|
|
89
115
|
}
|
|
90
116
|
|
|
117
|
+
/* White top border for cells */
|
|
91
118
|
.border-top-white {
|
|
92
119
|
border-top: var(--border-width) solid white;
|
|
93
120
|
}
|
|
121
|
+
|
|
122
|
+
/* Resize handle for table headers */
|
|
123
|
+
.st-table-header-resize-handle {
|
|
124
|
+
position: absolute;
|
|
125
|
+
right: 0;
|
|
126
|
+
top: 0;
|
|
127
|
+
bottom: 0;
|
|
128
|
+
width: 5px;
|
|
129
|
+
cursor: col-resize;
|
|
130
|
+
background-color: var(--slate-300);
|
|
131
|
+
margin-top: 0.25rem;
|
|
132
|
+
margin-bottom: 0.25rem;
|
|
133
|
+
border-radius: 0.25rem;
|
|
134
|
+
}
|