@absreim/react-bootstrap-data-grid 0.1.2 → 0.1.3
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/Grid.d.ts +18 -0
- package/Grid.jsx +113 -0
- package/Pagination.d.ts +24 -0
- package/Pagination.jsx +119 -0
- package/index.d.ts +3 -1
- package/index.js +3 -1
- package/package.json +4 -1
- package/types.d.ts +4 -3
package/Grid.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { FC } from "react";
|
|
2
|
+
import { ColDef, RowDef, Size } from "./types";
|
|
3
|
+
export interface GridPaginationState {
|
|
4
|
+
pageSizeOptions: number[];
|
|
5
|
+
pageSizeIndex: number;
|
|
6
|
+
setPageSizeIndex: (pageSizeIndex: number) => void;
|
|
7
|
+
currentPage: number;
|
|
8
|
+
setCurrentPage: (pageNum: number) => void;
|
|
9
|
+
maxPageButtons: number;
|
|
10
|
+
componentSize?: Size;
|
|
11
|
+
}
|
|
12
|
+
export interface GridProps {
|
|
13
|
+
rows: RowDef[];
|
|
14
|
+
cols: ColDef[];
|
|
15
|
+
pagination?: GridPaginationState;
|
|
16
|
+
}
|
|
17
|
+
declare const Grid: FC<GridProps>;
|
|
18
|
+
export default Grid;
|
package/Grid.jsx
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useMemo } from "react";
|
|
3
|
+
import Pagination from "./Pagination";
|
|
4
|
+
import classNames from "classnames";
|
|
5
|
+
var Grid = function (_a) {
|
|
6
|
+
var rows = _a.rows, cols = _a.cols, pagination = _a.pagination;
|
|
7
|
+
var currentPageRows = useMemo(function () {
|
|
8
|
+
if (pagination === undefined) {
|
|
9
|
+
return rows;
|
|
10
|
+
}
|
|
11
|
+
var pageSizeOptions = pagination.pageSizeOptions, pageSizeIndex = pagination.pageSizeIndex, currentPage = pagination.currentPage;
|
|
12
|
+
var pageSize = pageSizeOptions[pageSizeIndex];
|
|
13
|
+
var lowerIndex = pageSize * (currentPage - 1);
|
|
14
|
+
var upperIndex = lowerIndex + pageSize;
|
|
15
|
+
return rows.slice(lowerIndex, upperIndex);
|
|
16
|
+
}, [rows, pagination]);
|
|
17
|
+
var displayRows = useMemo(function () {
|
|
18
|
+
var nameToIndex = new Map();
|
|
19
|
+
cols.forEach(function (_a, index) {
|
|
20
|
+
var name = _a.name;
|
|
21
|
+
nameToIndex.set(name, index);
|
|
22
|
+
});
|
|
23
|
+
return currentPageRows.map(function (row, index) {
|
|
24
|
+
cols
|
|
25
|
+
.map(function (_a) {
|
|
26
|
+
var name = _a.name;
|
|
27
|
+
return name;
|
|
28
|
+
})
|
|
29
|
+
.forEach(function (name) {
|
|
30
|
+
if (!(name in row)) {
|
|
31
|
+
throw new Error("Column definition specifies a property named \"".concat(name, "\",\n but it was no found in the row data object at index ").concat(index));
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
var displayRow = [];
|
|
35
|
+
Object.keys(row).forEach(function (name) {
|
|
36
|
+
if (!nameToIndex.has(name)) {
|
|
37
|
+
throw new Error("Row data contains a property named \"".concat(name, "\",\n but it was not found among the column definitions"));
|
|
38
|
+
}
|
|
39
|
+
var index = nameToIndex.get(name);
|
|
40
|
+
var formatter = cols[index].formatter;
|
|
41
|
+
var typeString = cols[index].type;
|
|
42
|
+
var value = row[name];
|
|
43
|
+
if (formatter) {
|
|
44
|
+
displayRow[index] = formatter(value);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (typeString === "date") {
|
|
48
|
+
displayRow[index] = value.toDateString();
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
if (typeString === "datetime") {
|
|
52
|
+
displayRow[index] = value.toLocaleString();
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
if (typeString === "number") {
|
|
56
|
+
displayRow[index] = value.toLocaleString();
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
displayRow[index] = value;
|
|
60
|
+
});
|
|
61
|
+
return displayRow;
|
|
62
|
+
});
|
|
63
|
+
}, [currentPageRows, cols]);
|
|
64
|
+
var handleSetPageNum = function (pageNum) {
|
|
65
|
+
if (pagination === undefined) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
pagination.setCurrentPage(pageNum);
|
|
69
|
+
};
|
|
70
|
+
var handleSetPageSize = function (event) {
|
|
71
|
+
if (pagination === undefined) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
pagination.setPageSizeIndex(Number(event.target.value));
|
|
75
|
+
};
|
|
76
|
+
// Once this component implements selection state, and if such interactivity is enabled, (conditionally) change the
|
|
77
|
+
// aria role to "grid".
|
|
78
|
+
// Array index is okay for the key for rows until some type of feature involving changing the index of rows, such as
|
|
79
|
+
// sorting or pagination, is implemented.
|
|
80
|
+
return (<div>
|
|
81
|
+
<table className="table">
|
|
82
|
+
<thead>
|
|
83
|
+
<tr>
|
|
84
|
+
{cols.map(function (_a) {
|
|
85
|
+
var name = _a.name, label = _a.label;
|
|
86
|
+
return (<th key={name}>{label}</th>);
|
|
87
|
+
})}
|
|
88
|
+
</tr>
|
|
89
|
+
</thead>
|
|
90
|
+
<tbody>
|
|
91
|
+
{displayRows.map(function (row, index) { return (<tr key={index}>
|
|
92
|
+
{row.map(function (value, index) { return (<td key={index}>{value}</td>); })}
|
|
93
|
+
</tr>); })}
|
|
94
|
+
</tbody>
|
|
95
|
+
</table>
|
|
96
|
+
{pagination && (<div className="d-flex justify-content-end gap-2">
|
|
97
|
+
<div>
|
|
98
|
+
<select className={classNames({
|
|
99
|
+
"form-select": true,
|
|
100
|
+
"form-select-lg": pagination.componentSize === "large",
|
|
101
|
+
"form-select-sm": pagination.componentSize === "small",
|
|
102
|
+
})} value={pagination.pageSizeIndex} aria-label="Number of Rows per Page" onChange={handleSetPageSize}>
|
|
103
|
+
{pagination.pageSizeOptions.map(function (numRows, index) { return (<option key={index} value={index}>
|
|
104
|
+
{numRows}
|
|
105
|
+
</option>); })}
|
|
106
|
+
</select>
|
|
107
|
+
</div>
|
|
108
|
+
<Pagination numPages={Math.ceil(rows.length /
|
|
109
|
+
pagination.pageSizeOptions[pagination.pageSizeIndex])} pageNum={pagination.currentPage} numButtons={pagination.maxPageButtons} setPageNum={handleSetPageNum} size={pagination.componentSize || "medium"}/>
|
|
110
|
+
</div>)}
|
|
111
|
+
</div>);
|
|
112
|
+
};
|
|
113
|
+
export default Grid;
|
package/Pagination.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { JustifyContentSetting, Size } from "./types";
|
|
2
|
+
import { FC } from "react";
|
|
3
|
+
export interface PaginationProps {
|
|
4
|
+
numPages: number;
|
|
5
|
+
pageNum: number;
|
|
6
|
+
numButtons: number;
|
|
7
|
+
setPageNum: (pageNum: number) => void;
|
|
8
|
+
ariaLabel?: string;
|
|
9
|
+
alignment?: JustifyContentSetting;
|
|
10
|
+
size?: Size;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* An interactive pagination component that parameterizes Bootstrap styling
|
|
14
|
+
* options as props.
|
|
15
|
+
* @param numPages - Total number of pages
|
|
16
|
+
* @param numButtons - Number of buttons of numerical indices to show at once
|
|
17
|
+
* @param pageNum - The currently selected page
|
|
18
|
+
* @param setPageNum - Callback function to set the selected page
|
|
19
|
+
* @param ariaLabel - Aria label of the <nav> element
|
|
20
|
+
* @param alignment - Flexbox justify-content setting on the <ul> element
|
|
21
|
+
* @param size - Size variant of the <ul> element
|
|
22
|
+
*/
|
|
23
|
+
declare const Pagination: FC<PaginationProps>;
|
|
24
|
+
export default Pagination;
|
package/Pagination.jsx
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useMemo } from "react";
|
|
3
|
+
import classNames from "classnames";
|
|
4
|
+
/**
|
|
5
|
+
* An interactive pagination component that parameterizes Bootstrap styling
|
|
6
|
+
* options as props.
|
|
7
|
+
* @param numPages - Total number of pages
|
|
8
|
+
* @param numButtons - Number of buttons of numerical indices to show at once
|
|
9
|
+
* @param pageNum - The currently selected page
|
|
10
|
+
* @param setPageNum - Callback function to set the selected page
|
|
11
|
+
* @param ariaLabel - Aria label of the <nav> element
|
|
12
|
+
* @param alignment - Flexbox justify-content setting on the <ul> element
|
|
13
|
+
* @param size - Size variant of the <ul> element
|
|
14
|
+
*/
|
|
15
|
+
var Pagination = function (_a) {
|
|
16
|
+
var numPages = _a.numPages, numButtons = _a.numButtons, pageNum = _a.pageNum, setPageNum = _a.setPageNum, ariaLabel = _a.ariaLabel, alignment = _a.alignment, size = _a.size;
|
|
17
|
+
var ulClasses = ["pagination"];
|
|
18
|
+
if (size === "small") {
|
|
19
|
+
ulClasses.push("pagination-sm");
|
|
20
|
+
}
|
|
21
|
+
else if (size === "large") {
|
|
22
|
+
ulClasses.push("pagination-lg");
|
|
23
|
+
}
|
|
24
|
+
if (alignment) {
|
|
25
|
+
ulClasses.push("justify-content-".concat(alignment));
|
|
26
|
+
}
|
|
27
|
+
var lowerBound = pageNum - Math.floor((numButtons - 1) / 2);
|
|
28
|
+
var upperBound = pageNum + Math.ceil((numButtons - 1) / 2);
|
|
29
|
+
if (upperBound > numPages) {
|
|
30
|
+
var diff = upperBound - numPages;
|
|
31
|
+
lowerBound = Math.max(lowerBound - diff, 1);
|
|
32
|
+
upperBound -= diff;
|
|
33
|
+
}
|
|
34
|
+
else if (lowerBound < 1) {
|
|
35
|
+
var diff = 1 - lowerBound;
|
|
36
|
+
lowerBound = 1;
|
|
37
|
+
upperBound = Math.min(numPages, upperBound + diff);
|
|
38
|
+
}
|
|
39
|
+
var buttonIndices = useMemo(function () {
|
|
40
|
+
var indices = [];
|
|
41
|
+
for (var i = lowerBound; i <= upperBound; i++) {
|
|
42
|
+
indices.push(i);
|
|
43
|
+
}
|
|
44
|
+
return indices;
|
|
45
|
+
}, [lowerBound, upperBound]);
|
|
46
|
+
function getFirstArrowButton() {
|
|
47
|
+
if (lowerBound === 1) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
return (<li className="page-item">
|
|
51
|
+
<a className="page-link" href="#" aria-label="First" onClick={function (event) {
|
|
52
|
+
event.preventDefault();
|
|
53
|
+
setPageNum(1);
|
|
54
|
+
}}>
|
|
55
|
+
<span aria-hidden="true"><<</span>
|
|
56
|
+
</a>
|
|
57
|
+
</li>);
|
|
58
|
+
}
|
|
59
|
+
function getPrevArrowButton() {
|
|
60
|
+
if (pageNum === 1) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
return (<li className="page-item">
|
|
64
|
+
<a className="page-link" href="#" aria-label="Previous" onClick={function (event) {
|
|
65
|
+
event.preventDefault();
|
|
66
|
+
setPageNum(pageNum - 1);
|
|
67
|
+
}}>
|
|
68
|
+
<span aria-hidden="true"><</span>
|
|
69
|
+
</a>
|
|
70
|
+
</li>);
|
|
71
|
+
}
|
|
72
|
+
function getNextArrowButton() {
|
|
73
|
+
if (pageNum === numPages) {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
return (<li className="page-item">
|
|
77
|
+
<a className="page-link" href="#" aria-label="Next" onClick={function (event) {
|
|
78
|
+
event.preventDefault();
|
|
79
|
+
setPageNum(pageNum + 1);
|
|
80
|
+
}}>
|
|
81
|
+
<span aria-hidden="true">></span>
|
|
82
|
+
</a>
|
|
83
|
+
</li>);
|
|
84
|
+
}
|
|
85
|
+
function getLastArrowButton() {
|
|
86
|
+
if (upperBound === numPages) {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
return (<li className="page-item">
|
|
90
|
+
<a className="page-link" href="#" aria-label="Last" onClick={function (event) {
|
|
91
|
+
event.preventDefault();
|
|
92
|
+
setPageNum(numPages);
|
|
93
|
+
}}>
|
|
94
|
+
<span aria-hidden="true">>></span>
|
|
95
|
+
</a>
|
|
96
|
+
</li>);
|
|
97
|
+
}
|
|
98
|
+
var indexButtons = buttonIndices.map(function (buttonIndex) { return (<li key={buttonIndex} className={classNames({
|
|
99
|
+
"page-item": true,
|
|
100
|
+
active: pageNum === buttonIndex,
|
|
101
|
+
})} aria-current={pageNum === buttonIndex ? "page" : undefined}>
|
|
102
|
+
<a className="page-link" href="#" onClick={function (event) {
|
|
103
|
+
event.preventDefault();
|
|
104
|
+
setPageNum(buttonIndex);
|
|
105
|
+
}}>
|
|
106
|
+
{buttonIndex}
|
|
107
|
+
</a>
|
|
108
|
+
</li>); });
|
|
109
|
+
return (<nav aria-label={ariaLabel}>
|
|
110
|
+
<ul className={classNames(ulClasses)}>
|
|
111
|
+
{getFirstArrowButton()}
|
|
112
|
+
{getPrevArrowButton()}
|
|
113
|
+
{indexButtons}
|
|
114
|
+
{getNextArrowButton()}
|
|
115
|
+
{getLastArrowButton()}
|
|
116
|
+
</ul>
|
|
117
|
+
</nav>);
|
|
118
|
+
};
|
|
119
|
+
export default Pagination;
|
package/index.d.ts
CHANGED
package/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@absreim/react-bootstrap-data-grid",
|
|
3
3
|
"description": "Data grid UI component for use with web apps using React and Bootstrap",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.3",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Brook Li",
|
|
7
7
|
"homepage": "https://react-bootstrap-data-grid.vercel.app/",
|
|
@@ -13,6 +13,9 @@
|
|
|
13
13
|
"table",
|
|
14
14
|
"grid"
|
|
15
15
|
],
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"classnames": "^2.5.1"
|
|
18
|
+
},
|
|
16
19
|
"peerDependencies": {
|
|
17
20
|
"react": "^18",
|
|
18
21
|
"react-dom": "^18"
|
package/types.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
type ColDataType = string | number | Date;
|
|
2
|
-
type ColDataTypeStrings = "string" | "number" | "date" | "datetime";
|
|
1
|
+
export type ColDataType = string | number | Date;
|
|
2
|
+
export type ColDataTypeStrings = "string" | "number" | "date" | "datetime";
|
|
3
3
|
export interface ColDef {
|
|
4
4
|
type: ColDataTypeStrings;
|
|
5
5
|
name: string;
|
|
@@ -7,4 +7,5 @@ export interface ColDef {
|
|
|
7
7
|
formatter?: (value: any) => string;
|
|
8
8
|
}
|
|
9
9
|
export type RowDef = Record<string, ColDataType>;
|
|
10
|
-
export
|
|
10
|
+
export type JustifyContentSetting = "start" | "end" | "center" | "between" | "around" | "evenly";
|
|
11
|
+
export type Size = "small" | "medium" | "large";
|