@absreim/react-bootstrap-data-grid 1.1.4 → 1.2.1
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/ColHeaderCell.jsx +13 -48
- package/Grid.d.ts +2 -1
- package/Grid.jsx +100 -35
- package/hooks/useControlledHover.d.ts +9 -0
- package/hooks/useControlledHover.jsx +17 -0
- package/icons/arrowPlaceholder.d.ts +2 -0
- package/icons/arrowPlaceholder.jsx +10 -0
- package/icons/deselectAll.d.ts +2 -0
- package/icons/deselectAll.jsx +5 -0
- package/icons/downArrow.d.ts +2 -0
- package/icons/downArrow.jsx +9 -0
- package/icons/selectAll.d.ts +2 -0
- package/icons/selectAll.jsx +6 -0
- package/icons/upArrow.d.ts +2 -0
- package/icons/upArrow.jsx +21 -0
- package/index.d.ts +1 -1
- package/index.js +1 -1
- package/package.json +1 -1
- package/{Pagination.d.ts → pagination/PageSelector.d.ts} +3 -3
- package/{Pagination.jsx → pagination/PageSelector.jsx} +2 -2
- package/pagination/PageSizeSelector.d.ts +10 -0
- package/pagination/PageSizeSelector.jsx +20 -0
- package/pagination/Pagination.d.ts +14 -0
- package/pagination/Pagination.jsx +22 -0
- package/selection/SelectAllHeaderCell.d.ts +9 -0
- package/selection/SelectAllHeaderCell.jsx +42 -0
- package/selection/SelectionInput.d.ts +18 -0
- package/selection/SelectionInput.jsx +21 -0
- package/types.d.ts +18 -0
- package/models/descriptions.d.ts +0 -2
- package/models/descriptions.js +0 -6
package/ColHeaderCell.jsx
CHANGED
|
@@ -1,50 +1,12 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
6
|
-
ar[i] = from[i];
|
|
7
|
-
}
|
|
8
|
-
}
|
|
9
|
-
return to.concat(ar || Array.prototype.slice.call(from));
|
|
10
|
-
};
|
|
11
|
-
import { useState } from "react";
|
|
2
|
+
import downArrow from "./icons/downArrow";
|
|
3
|
+
import upArrow from "./icons/upArrow";
|
|
4
|
+
import arrowPlaceholder from "./icons/arrowPlaceholder";
|
|
12
5
|
import classNames from "classnames";
|
|
13
|
-
|
|
14
|
-
"bi",
|
|
15
|
-
"bi-arrow-up"
|
|
16
|
-
], (grayed ? ["text-body-secondary"] : []), true))} viewBox="0 0 16 16">
|
|
17
|
-
{!grayed && (<>
|
|
18
|
-
<title>(sorted ascending)</title>
|
|
19
|
-
<desc>
|
|
20
|
-
Up arrow indicating that the column is being sorted in an ascending
|
|
21
|
-
manner
|
|
22
|
-
</desc>
|
|
23
|
-
</>)}
|
|
24
|
-
<path fillRule="evenodd" d="M8 15a.5.5 0 0 0 .5-.5V2.707l3.146 3.147a.5.5 0 0 0 .708-.708l-4-4a.5.5 0 0 0-.708 0l-4 4a.5.5 0 1 0 .708.708L7.5 2.707V14.5a.5.5 0 0 0 .5.5"/>
|
|
25
|
-
</svg>); };
|
|
26
|
-
var downArrow = (<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" className="bi bi-arrow-down" viewBox="0 0 16 16">
|
|
27
|
-
<title>(sorted descending)</title>
|
|
28
|
-
<desc>
|
|
29
|
-
Down arrow indicating that the column is being sorted in an descending
|
|
30
|
-
manner
|
|
31
|
-
</desc>
|
|
32
|
-
<path fillRule="evenodd" d="M8 1a.5.5 0 0 1 .5.5v11.793l3.146-3.147a.5.5 0 0 1 .708.708l-4 4a.5.5 0 0 1-.708 0l-4-4a.5.5 0 0 1 .708-.708L7.5 13.293V1.5A.5.5 0 0 1 8 1"/>
|
|
33
|
-
</svg>);
|
|
34
|
-
// Temporary solution to prevent column widths from changing when hovering over
|
|
35
|
-
// columns with a mouse.
|
|
36
|
-
// More ideal permanent solution would be to fix column widths based on preset
|
|
37
|
-
// values.
|
|
38
|
-
var placeholder = (<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" className="bi bi-arrow-down" viewBox="0 0 16 16"></svg>);
|
|
6
|
+
import useControlledHover from "./hooks/useControlledHover";
|
|
39
7
|
var ColHeaderCell = function (_a) {
|
|
40
8
|
var label = _a.label, sortModel = _a.sortModel, ariaColIndex = _a.ariaColIndex;
|
|
41
|
-
var _b =
|
|
42
|
-
var handleMouseOver = function () {
|
|
43
|
-
return setIsHovering(true);
|
|
44
|
-
};
|
|
45
|
-
var handleMouseOut = function () {
|
|
46
|
-
return setIsHovering(false);
|
|
47
|
-
};
|
|
9
|
+
var _b = useControlledHover(), isHovering = _b.isHovering, handleMouseOver = _b.handleMouseOver, handleMouseOut = _b.handleMouseOut;
|
|
48
10
|
var handleClick = function () {
|
|
49
11
|
if (!sortModel) {
|
|
50
12
|
return;
|
|
@@ -70,21 +32,24 @@ var ColHeaderCell = function (_a) {
|
|
|
70
32
|
switch (sortModel.sortOrder) {
|
|
71
33
|
case null: {
|
|
72
34
|
if (isHovering) {
|
|
73
|
-
return
|
|
35
|
+
return upArrow(true);
|
|
74
36
|
}
|
|
75
|
-
return
|
|
37
|
+
return arrowPlaceholder;
|
|
76
38
|
}
|
|
77
39
|
case "asc": {
|
|
78
|
-
return
|
|
40
|
+
return upArrow(false);
|
|
79
41
|
}
|
|
80
42
|
case "desc": {
|
|
81
43
|
return downArrow;
|
|
82
44
|
}
|
|
83
45
|
}
|
|
84
46
|
};
|
|
85
|
-
return (<th
|
|
47
|
+
return (<th className={classNames({
|
|
48
|
+
"cursor-pointer": sortModel,
|
|
49
|
+
"table-active": sortModel === null || sortModel === void 0 ? void 0 : sortModel.sortOrder,
|
|
50
|
+
})} onClick={sortModel && handleClick} onMouseOver={handleMouseOver} onMouseOut={handleMouseOut} aria-description={sortModel
|
|
86
51
|
? "Column header"
|
|
87
|
-
: "Column header that can be clicked to change the sorting mode"}
|
|
52
|
+
: "Column header that can be clicked to change the sorting mode"} aria-colindex={ariaColIndex}>
|
|
88
53
|
{label}
|
|
89
54
|
{getSortSymbol()}
|
|
90
55
|
</th>);
|
package/Grid.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { FC } from "react";
|
|
2
|
-
import { ColDef, FilterModel, RowDef, Size, TableSortModel } from "./types";
|
|
2
|
+
import { ColDef, FilterModel, RowDef, SelectModel, Size, TableSortModel } from "./types";
|
|
3
3
|
export interface GridPaginationState {
|
|
4
4
|
pageSizeOptions: number[];
|
|
5
5
|
pageSizeIndex: number;
|
|
@@ -15,6 +15,7 @@ export interface GridProps {
|
|
|
15
15
|
pagination?: GridPaginationState;
|
|
16
16
|
sortModel?: TableSortModel;
|
|
17
17
|
filterModel?: FilterModel;
|
|
18
|
+
selectModel?: SelectModel;
|
|
18
19
|
}
|
|
19
20
|
declare const Grid: FC<GridProps>;
|
|
20
21
|
export default Grid;
|
package/Grid.jsx
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { useMemo, useState } from "react";
|
|
3
|
-
import Pagination from "./Pagination";
|
|
4
|
-
import classNames from "classnames";
|
|
5
3
|
import ColHeaderCell from "./ColHeaderCell";
|
|
6
4
|
import useFilter from "./hooks/useFilter";
|
|
7
5
|
import ToggleButton from "./ToggleButton";
|
|
@@ -10,8 +8,12 @@ import useFilterStateFromEditable from "./hooks/useFilterStateFromEditable";
|
|
|
10
8
|
import useAugmentedRows from "./hooks/useAugmentedRows";
|
|
11
9
|
import useSortedRows from "./hooks/useSortedRows";
|
|
12
10
|
import useDisplayRows from "./hooks/useDisplayRows";
|
|
11
|
+
import SelectAllHeaderCell from "./selection/SelectAllHeaderCell";
|
|
12
|
+
import SelectionInput from "./selection/SelectionInput";
|
|
13
|
+
import Pagination from "./pagination/Pagination";
|
|
14
|
+
import classNames from "classnames";
|
|
13
15
|
var Grid = function (_a) {
|
|
14
|
-
var rows = _a.rows, cols = _a.cols, pagination = _a.pagination, sortModel = _a.sortModel, filterModel = _a.filterModel;
|
|
16
|
+
var rows = _a.rows, cols = _a.cols, pagination = _a.pagination, sortModel = _a.sortModel, filterModel = _a.filterModel, selectModel = _a.selectModel;
|
|
15
17
|
var editableFilterState = (filterModel === null || filterModel === void 0 ? void 0 : filterModel.tableFilterState) || null;
|
|
16
18
|
var filterState = useFilterStateFromEditable(cols, editableFilterState);
|
|
17
19
|
var augmentedRows = useAugmentedRows(rows);
|
|
@@ -29,26 +31,95 @@ var Grid = function (_a) {
|
|
|
29
31
|
}, [sortedRows, pagination]);
|
|
30
32
|
var displayRows = useDisplayRows(currentPageRows, cols);
|
|
31
33
|
var _b = useState(false), filterOptionsVisible = _b[0], setFilterOptionsVisible = _b[1];
|
|
32
|
-
var
|
|
33
|
-
|
|
34
|
+
var handleToggleFilterOptions = function () {
|
|
35
|
+
setFilterOptionsVisible(!filterOptionsVisible);
|
|
36
|
+
};
|
|
37
|
+
var getSelectionExists = function () {
|
|
38
|
+
if (!selectModel) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
if (selectModel.type === "single") {
|
|
42
|
+
return selectModel.selected !== null;
|
|
43
|
+
}
|
|
44
|
+
return selectModel.selected.length > 0;
|
|
45
|
+
};
|
|
46
|
+
var selectionExists = getSelectionExists();
|
|
47
|
+
var selectAllOnClick = function () {
|
|
48
|
+
if (!selectModel) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
if (selectionExists && selectModel.type === "single") {
|
|
52
|
+
selectModel.setSelected(null);
|
|
34
53
|
return;
|
|
35
54
|
}
|
|
36
|
-
|
|
55
|
+
if (selectionExists && selectModel.type === "multi") {
|
|
56
|
+
selectModel.setSelected([]);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
if (!selectionExists && selectModel.type === "multi") {
|
|
60
|
+
var allFilteredRowIndices = filteredRows.map(function (def) { return def.meta.origIndex; });
|
|
61
|
+
selectModel.setSelected(allFilteredRowIndices);
|
|
62
|
+
}
|
|
63
|
+
// Button should be disabled in the case of selectionExists &&
|
|
64
|
+
// selectModel.type === "single", so function execution should never get
|
|
65
|
+
// to this point.
|
|
37
66
|
};
|
|
38
|
-
var
|
|
39
|
-
if (
|
|
67
|
+
var getSelectHandler = function (index) { return function () {
|
|
68
|
+
if (!selectModel) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
if (selectModel.type === "single") {
|
|
72
|
+
selectModel.setSelected(index);
|
|
40
73
|
return;
|
|
41
74
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
pagination.setCurrentPage(maxPages);
|
|
75
|
+
selectModel.setSelected(selectModel.selected.concat(index));
|
|
76
|
+
}; };
|
|
77
|
+
var getDeselectHandler = function (index) { return function () {
|
|
78
|
+
if (!selectModel || selectModel.type === "single") {
|
|
79
|
+
return;
|
|
48
80
|
}
|
|
81
|
+
selectModel.setSelected(selectModel.selected.filter(function (num) { return num !== index; }));
|
|
82
|
+
}; };
|
|
83
|
+
// used to group radio buttons for selection
|
|
84
|
+
var getSelectInputModel = function (index, selectModel) {
|
|
85
|
+
if (selectModel.type === "single") {
|
|
86
|
+
return {
|
|
87
|
+
type: "radio",
|
|
88
|
+
name: selectModel.groupName,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
return {
|
|
92
|
+
type: "checkbox",
|
|
93
|
+
deselectCallback: getDeselectHandler(index),
|
|
94
|
+
};
|
|
49
95
|
};
|
|
50
|
-
var
|
|
51
|
-
|
|
96
|
+
var showSelectCol = selectModel && selectModel.mode !== "row";
|
|
97
|
+
var selectedSet = new Set();
|
|
98
|
+
if (selectModel && selectModel.type === "multi") {
|
|
99
|
+
selectModel.selected.forEach(function (value) { return selectedSet.add(value); });
|
|
100
|
+
}
|
|
101
|
+
if (selectModel &&
|
|
102
|
+
selectModel.type === "single" &&
|
|
103
|
+
selectModel.selected !== null) {
|
|
104
|
+
selectedSet.add(selectModel.selected);
|
|
105
|
+
}
|
|
106
|
+
var rowsAreSelectable = !!(selectModel && selectModel.mode !== "column");
|
|
107
|
+
var getRowClickHandler = function (index) { return function (event) {
|
|
108
|
+
event.preventDefault();
|
|
109
|
+
if (!rowsAreSelectable) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
if (selectedSet.has(index)) {
|
|
113
|
+
getDeselectHandler(index)();
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
getSelectHandler(index)();
|
|
117
|
+
}; };
|
|
118
|
+
var getAriaSelectedValue = function (index) {
|
|
119
|
+
if (!selectModel) {
|
|
120
|
+
return undefined;
|
|
121
|
+
}
|
|
122
|
+
return String(selectedSet.has(index));
|
|
52
123
|
};
|
|
53
124
|
// Once this component implements selection state, and if such interactivity is enabled, (conditionally) change the
|
|
54
125
|
// aria role to "grid".
|
|
@@ -58,9 +129,12 @@ var Grid = function (_a) {
|
|
|
58
129
|
<ToggleButton isActive={filterOptionsVisible} label={"".concat(filterOptionsVisible ? "Hide" : "Show ", " Filter Options")} onClick={handleToggleFilterOptions}/>
|
|
59
130
|
{filterOptionsVisible && (<FilterOptionsTable filterState={filterState} setFilterState={filterModel.setTableFilterState}/>)}
|
|
60
131
|
</div>)}
|
|
61
|
-
<table className="table"
|
|
132
|
+
<table className={classNames("table", {
|
|
133
|
+
"table-hover": rowsAreSelectable,
|
|
134
|
+
})} aria-rowcount={filteredRows.length + 1}>
|
|
62
135
|
<thead>
|
|
63
136
|
<tr aria-rowindex={1}>
|
|
137
|
+
{showSelectCol && (<SelectAllHeaderCell selectType={selectModel.type} onClick={selectAllOnClick} selectionExists={selectionExists}/>)}
|
|
64
138
|
{cols.map(function (_a, index) {
|
|
65
139
|
var _b;
|
|
66
140
|
var name = _a.name, label = _a.label, sortable = _a.sortable;
|
|
@@ -74,33 +148,24 @@ var Grid = function (_a) {
|
|
|
74
148
|
},
|
|
75
149
|
}
|
|
76
150
|
: undefined;
|
|
77
|
-
return (<ColHeaderCell key={name} label={label} sortModel={colSortModel} ariaColIndex={index + 1}/>);
|
|
151
|
+
return (<ColHeaderCell key={name} label={label} sortModel={colSortModel} ariaColIndex={index + 1 + (showSelectCol ? 1 : 0)}/>);
|
|
78
152
|
})}
|
|
79
153
|
</tr>
|
|
80
154
|
</thead>
|
|
81
155
|
<tbody>
|
|
82
|
-
{displayRows.map(function (row, index) { return (<tr
|
|
83
|
-
|
|
156
|
+
{displayRows.map(function (row, index) { return (<tr onClick={getRowClickHandler(row.origIndex)} className={classNames({
|
|
157
|
+
"table-active": selectedSet.has(row.origIndex),
|
|
158
|
+
})} key={row.origIndex} aria-rowindex={index + 2} data-rowindex={row.origIndex} aria-selected={getAriaSelectedValue(row.origIndex)}>
|
|
159
|
+
{showSelectCol && (<td>
|
|
160
|
+
<SelectionInput selected={selectedSet.has(row.origIndex)} selectionInputModel={getSelectInputModel(row.origIndex, selectModel)} selectCallback={getSelectHandler(row.origIndex)}/>
|
|
161
|
+
</td>)}
|
|
162
|
+
{row.contents.map(function (value, index) { return (<td key={index} aria-colindex={index + 1 + (showSelectCol ? 1 : 0)}>
|
|
84
163
|
{value}
|
|
85
164
|
</td>); })}
|
|
86
165
|
</tr>); })}
|
|
87
166
|
</tbody>
|
|
88
167
|
</table>
|
|
89
|
-
{pagination && (<
|
|
90
|
-
<div>
|
|
91
|
-
<select className={classNames({
|
|
92
|
-
"form-select": true,
|
|
93
|
-
"form-select-lg": pagination.componentSize === "large",
|
|
94
|
-
"form-select-sm": pagination.componentSize === "small",
|
|
95
|
-
})} value={pagination.pageSizeIndex} aria-label="Number of Rows per Page" onChange={handleSetPageSize}>
|
|
96
|
-
{pagination.pageSizeOptions.map(function (numRows, index) { return (<option key={index} value={index}>
|
|
97
|
-
{numRows}
|
|
98
|
-
</option>); })}
|
|
99
|
-
</select>
|
|
100
|
-
</div>
|
|
101
|
-
<Pagination numPages={Math.ceil(rows.length /
|
|
102
|
-
pagination.pageSizeOptions[pagination.pageSizeIndex])} pageNum={pagination.currentPage} numButtons={pagination.maxPageButtons} setPageNum={handleSetPageNum} size={pagination.componentSize || "medium"}/>
|
|
103
|
-
</div>)}
|
|
168
|
+
{pagination && (<Pagination componentSize={pagination.componentSize || "medium"} pageSizeOptions={pagination.pageSizeOptions} pageSizeIndex={pagination.pageSizeIndex} handleSetPageSizeIndex={pagination.setPageSizeIndex} handleSetPageNum={pagination.setCurrentPage} prePagingNumRows={sortedRows.length} maxPageButtons={pagination.maxPageButtons} currentPage={pagination.currentPage}/>)}
|
|
104
169
|
</div>);
|
|
105
170
|
};
|
|
106
171
|
export default Grid;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { MouseEventHandler } from "react";
|
|
2
|
+
export interface UseControlledHoverHook<T> {
|
|
3
|
+
isHovering: boolean;
|
|
4
|
+
setIsHovering: (isHovering: boolean) => void;
|
|
5
|
+
handleMouseOver: MouseEventHandler<T>;
|
|
6
|
+
handleMouseOut: MouseEventHandler<T>;
|
|
7
|
+
}
|
|
8
|
+
declare const useControlledHover: <T>() => UseControlledHoverHook<T>;
|
|
9
|
+
export default useControlledHover;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { useState } from "react";
|
|
2
|
+
var useControlledHover = function () {
|
|
3
|
+
var _a = useState(false), isHovering = _a[0], setIsHovering = _a[1];
|
|
4
|
+
var handleMouseOver = function () {
|
|
5
|
+
return setIsHovering(true);
|
|
6
|
+
};
|
|
7
|
+
var handleMouseOut = function () {
|
|
8
|
+
return setIsHovering(false);
|
|
9
|
+
};
|
|
10
|
+
return {
|
|
11
|
+
isHovering: isHovering,
|
|
12
|
+
setIsHovering: setIsHovering,
|
|
13
|
+
handleMouseOver: handleMouseOver,
|
|
14
|
+
handleMouseOut: handleMouseOut
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
export default useControlledHover;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Temporary solution to prevent column widths from changing when hovering over
|
|
2
|
+
// columns with a mouse.
|
|
3
|
+
// More ideal permanent solution would be to fix column widths based on preset
|
|
4
|
+
// values.
|
|
5
|
+
var arrowPlaceholder = (<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
|
|
6
|
+
<desc>
|
|
7
|
+
Empty transparent square for styling purposes
|
|
8
|
+
</desc>
|
|
9
|
+
</svg>);
|
|
10
|
+
export default arrowPlaceholder;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
var deselectAll = (<svg className="svg-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16" fill="currentColor">
|
|
2
|
+
<desc>Minus sign inside a square</desc>
|
|
3
|
+
<rect className="svg-icon-foreground" x="3.5" y="7.5" width="9" height="1"/>
|
|
4
|
+
</svg>);
|
|
5
|
+
export default deselectAll;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
var downArrow = (<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
|
|
2
|
+
<title>(sorted descending)</title>
|
|
3
|
+
<desc>
|
|
4
|
+
Down arrow indicating that the column is being sorted in an descending
|
|
5
|
+
manner
|
|
6
|
+
</desc>
|
|
7
|
+
<path fillRule="evenodd" d="M8 1a.5.5 0 0 1 .5.5v11.793l3.146-3.147a.5.5 0 0 1 .708.708l-4 4a.5.5 0 0 1-.708 0l-4-4a.5.5 0 0 1 .708-.708L7.5 13.293V1.5A.5.5 0 0 1 8 1"/>
|
|
8
|
+
</svg>);
|
|
9
|
+
export default downArrow;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
var selectAll = (<svg className="svg-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16" fill="currentColor">
|
|
2
|
+
<desc>Plus sign inside a square</desc>
|
|
3
|
+
<rect className="svg-icon-foreground" x="3.5" y="7.5" width="9" height="1"/>
|
|
4
|
+
<rect className="svg-icon-foreground" x="3.5" y="7.5" width="9" height="1" transform="translate(0 16) rotate(-90)"/>
|
|
5
|
+
</svg>);
|
|
6
|
+
export default selectAll;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
2
|
+
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
3
|
+
if (ar || !(i in from)) {
|
|
4
|
+
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
5
|
+
ar[i] = from[i];
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
return to.concat(ar || Array.prototype.slice.call(from));
|
|
9
|
+
};
|
|
10
|
+
import classNames from "classnames";
|
|
11
|
+
var upArrow = function (grayed) { return (<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" className={classNames(__spreadArray([], (grayed ? ["text-body-secondary"] : []), true))} viewBox="0 0 16 16">
|
|
12
|
+
{!grayed && (<>
|
|
13
|
+
<title>(sorted ascending)</title>
|
|
14
|
+
<desc>
|
|
15
|
+
Up arrow indicating that the column is being sorted in an ascending
|
|
16
|
+
manner
|
|
17
|
+
</desc>
|
|
18
|
+
</>)}
|
|
19
|
+
<path fillRule="evenodd" d="M8 15a.5.5 0 0 0 .5-.5V2.707l3.146 3.147a.5.5 0 0 0 .708-.708l-4-4a.5.5 0 0 0-.708 0l-4 4a.5.5 0 1 0 .708.708L7.5 2.707V14.5a.5.5 0 0 0 .5.5"/>
|
|
20
|
+
</svg>); };
|
|
21
|
+
export default upArrow;
|
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": "1.1
|
|
4
|
+
"version": "1.2.1",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Brook Li",
|
|
7
7
|
"homepage": "https://react-bootstrap-data-grid.vercel.app/",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { JustifyContentSetting, Size } from "
|
|
1
|
+
import { JustifyContentSetting, Size } from "../types";
|
|
2
2
|
import { FC } from "react";
|
|
3
3
|
export interface PaginationProps {
|
|
4
4
|
numPages: number;
|
|
@@ -20,5 +20,5 @@ export interface PaginationProps {
|
|
|
20
20
|
* @param alignment - Flexbox justify-content setting on the <ul> element
|
|
21
21
|
* @param size - Size variant of the <ul> element
|
|
22
22
|
*/
|
|
23
|
-
declare const
|
|
24
|
-
export default
|
|
23
|
+
declare const PageSelector: FC<PaginationProps>;
|
|
24
|
+
export default PageSelector;
|
|
@@ -12,7 +12,7 @@ import classNames from "classnames";
|
|
|
12
12
|
* @param alignment - Flexbox justify-content setting on the <ul> element
|
|
13
13
|
* @param size - Size variant of the <ul> element
|
|
14
14
|
*/
|
|
15
|
-
var
|
|
15
|
+
var PageSelector = function (_a) {
|
|
16
16
|
var numPages = _a.numPages, numButtons = _a.numButtons, pageNum = _a.pageNum, setPageNum = _a.setPageNum, ariaLabel = _a.ariaLabel, alignment = _a.alignment, size = _a.size;
|
|
17
17
|
var ulClasses = ["pagination"];
|
|
18
18
|
if (size === "small") {
|
|
@@ -116,4 +116,4 @@ var Pagination = function (_a) {
|
|
|
116
116
|
</ul>
|
|
117
117
|
</nav>);
|
|
118
118
|
};
|
|
119
|
-
export default
|
|
119
|
+
export default PageSelector;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { FC } from "react";
|
|
2
|
+
import { Size } from "../types";
|
|
3
|
+
export interface PageSizeSelectorProps {
|
|
4
|
+
componentSize: Size;
|
|
5
|
+
pageSizeOptions: number[];
|
|
6
|
+
pageSizeIndex: number;
|
|
7
|
+
handleSetPageSize: (index: number) => void;
|
|
8
|
+
}
|
|
9
|
+
declare const PageSizeSelector: FC<PageSizeSelectorProps>;
|
|
10
|
+
export default PageSizeSelector;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import classNames from "classnames";
|
|
2
|
+
var PageSizeSelector = function (_a) {
|
|
3
|
+
var componentSize = _a.componentSize, pageSizeOptions = _a.pageSizeOptions, pageSizeIndex = _a.pageSizeIndex, handleSetPageSize = _a.handleSetPageSize;
|
|
4
|
+
var onChange = function (event) {
|
|
5
|
+
var pageSizeIndex = Number(event.target.value);
|
|
6
|
+
handleSetPageSize(pageSizeIndex);
|
|
7
|
+
};
|
|
8
|
+
return (<div>
|
|
9
|
+
<select className={classNames({
|
|
10
|
+
"form-select": true,
|
|
11
|
+
"form-select-lg": componentSize === "large",
|
|
12
|
+
"form-select-sm": componentSize === "small",
|
|
13
|
+
})} value={pageSizeIndex} aria-label="Number of Rows per Page" onChange={onChange}>
|
|
14
|
+
{pageSizeOptions.map(function (numRows, index) { return (<option key={index} value={index}>
|
|
15
|
+
{numRows}
|
|
16
|
+
</option>); })}
|
|
17
|
+
</select>
|
|
18
|
+
</div>);
|
|
19
|
+
};
|
|
20
|
+
export default PageSizeSelector;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { FC } from "react";
|
|
2
|
+
import { Size } from "../types";
|
|
3
|
+
export interface PaginationProps {
|
|
4
|
+
componentSize: Size;
|
|
5
|
+
pageSizeOptions: number[];
|
|
6
|
+
pageSizeIndex: number;
|
|
7
|
+
handleSetPageSizeIndex: (index: number) => void;
|
|
8
|
+
handleSetPageNum: (index: number) => void;
|
|
9
|
+
prePagingNumRows: number;
|
|
10
|
+
maxPageButtons: number;
|
|
11
|
+
currentPage: number;
|
|
12
|
+
}
|
|
13
|
+
declare const Pagination: FC<PaginationProps>;
|
|
14
|
+
export default Pagination;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import PageSizeSelector from "./PageSizeSelector";
|
|
2
|
+
import PageSelector from "./PageSelector";
|
|
3
|
+
var Pagination = function (_a) {
|
|
4
|
+
var componentSize = _a.componentSize, pageSizeOptions = _a.pageSizeOptions, pageSizeIndex = _a.pageSizeIndex, handleSetPageSizeIndex = _a.handleSetPageSizeIndex, handleSetPageNum = _a.handleSetPageNum, prePagingNumRows = _a.prePagingNumRows, maxPageButtons = _a.maxPageButtons, currentPage = _a.currentPage;
|
|
5
|
+
var numPages = Math.ceil(prePagingNumRows / pageSizeOptions[pageSizeIndex]);
|
|
6
|
+
var pageIndexAwarePageSizeSetter = function (newPageSizeIndex) {
|
|
7
|
+
var newPageSize = pageSizeOptions[newPageSizeIndex];
|
|
8
|
+
var maxPages = Math.ceil(prePagingNumRows / newPageSize);
|
|
9
|
+
handleSetPageSizeIndex(newPageSizeIndex);
|
|
10
|
+
// The new page size can cause the current page number to be out of bounds.
|
|
11
|
+
// In that case, set the page num to the maximum possible with new page
|
|
12
|
+
// size.
|
|
13
|
+
if (currentPage > maxPages) {
|
|
14
|
+
handleSetPageNum(maxPages);
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
return (<div className="d-flex justify-content-end gap-2">
|
|
18
|
+
<PageSizeSelector componentSize={componentSize} pageSizeOptions={pageSizeOptions} pageSizeIndex={pageSizeIndex} handleSetPageSize={pageIndexAwarePageSizeSetter}/>
|
|
19
|
+
<PageSelector numPages={numPages} pageNum={currentPage} numButtons={maxPageButtons} setPageNum={handleSetPageNum} size={componentSize}/>
|
|
20
|
+
</div>);
|
|
21
|
+
};
|
|
22
|
+
export default Pagination;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { SelectType } from "../types";
|
|
2
|
+
import { FC } from "react";
|
|
3
|
+
interface SelectAllHeaderCellProps {
|
|
4
|
+
onClick: () => void;
|
|
5
|
+
selectType: SelectType;
|
|
6
|
+
selectionExists: boolean;
|
|
7
|
+
}
|
|
8
|
+
declare const SelectAllHeaderCell: FC<SelectAllHeaderCellProps>;
|
|
9
|
+
export default SelectAllHeaderCell;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import deselectAll from "../icons/deselectAll";
|
|
2
|
+
import selectAll from "../icons/selectAll";
|
|
3
|
+
import arrowPlaceholder from "../icons/arrowPlaceholder";
|
|
4
|
+
import classNames from "classnames";
|
|
5
|
+
// It seems like React does not support setting indeterminate states on
|
|
6
|
+
// checkboxes in a controlled manner, which caused me to not want to refactor
|
|
7
|
+
// this feature to use a checkbox input instead of SVG icons. I am not sure how
|
|
8
|
+
// much of an issue using an uncontrolled input would be, but because at time of
|
|
9
|
+
// this writing I had already implemented a solution with SVG, on balance I felt
|
|
10
|
+
// like it was not worth going out of my way to change things up to use an
|
|
11
|
+
// uncontrolled checkbox input.
|
|
12
|
+
var getSelectIcon = function (selectMode, existingSelection) {
|
|
13
|
+
if (existingSelection) {
|
|
14
|
+
return deselectAll;
|
|
15
|
+
}
|
|
16
|
+
if (selectMode === "multi" && !existingSelection) {
|
|
17
|
+
return selectAll;
|
|
18
|
+
}
|
|
19
|
+
// Single select mode and none selected means that the button is disabled
|
|
20
|
+
return arrowPlaceholder;
|
|
21
|
+
};
|
|
22
|
+
var getCellAriaDescription = function (selectMode, existingSelection) {
|
|
23
|
+
if (existingSelection) {
|
|
24
|
+
return "Deselect all rows";
|
|
25
|
+
}
|
|
26
|
+
if (selectMode === "multi" && !existingSelection) {
|
|
27
|
+
return "Select all rows";
|
|
28
|
+
}
|
|
29
|
+
// Single select mode and none selected means that the button is disabled
|
|
30
|
+
return "Selection input header cell";
|
|
31
|
+
};
|
|
32
|
+
var SelectAllHeaderCell = function (_a) {
|
|
33
|
+
var onClick = _a.onClick, selectType = _a.selectType, selectionExists = _a.selectionExists;
|
|
34
|
+
var disabled = selectType === "single" && !selectionExists;
|
|
35
|
+
var description = getCellAriaDescription(selectType, selectionExists);
|
|
36
|
+
return (<th aria-colindex={1} title={description} aria-description={description} className={classNames("select-header-cell", "btn-primary", {
|
|
37
|
+
"cursor-pointer": !disabled,
|
|
38
|
+
})} onClick={onClick}>
|
|
39
|
+
{getSelectIcon(selectType, selectionExists)}
|
|
40
|
+
</th>);
|
|
41
|
+
};
|
|
42
|
+
export default SelectAllHeaderCell;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { FC } from "react";
|
|
2
|
+
export interface RadioSelectionInputModel {
|
|
3
|
+
type: "radio";
|
|
4
|
+
name: string;
|
|
5
|
+
}
|
|
6
|
+
export interface CheckboxSelectionInputModel {
|
|
7
|
+
type: "checkbox";
|
|
8
|
+
deselectCallback: () => void;
|
|
9
|
+
name?: string;
|
|
10
|
+
}
|
|
11
|
+
export type SelectionInputModel = RadioSelectionInputModel | CheckboxSelectionInputModel;
|
|
12
|
+
export interface SelectionInputProps {
|
|
13
|
+
selected: boolean;
|
|
14
|
+
selectionInputModel: SelectionInputModel;
|
|
15
|
+
selectCallback: () => void;
|
|
16
|
+
}
|
|
17
|
+
declare const SelectionInput: FC<SelectionInputProps>;
|
|
18
|
+
export default SelectionInput;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
var SelectionInput = function (_a) {
|
|
2
|
+
var selectionInputModel = _a.selectionInputModel, selected = _a.selected, selectCallback = _a.selectCallback;
|
|
3
|
+
var type = selectionInputModel.type;
|
|
4
|
+
var onChange = function (_a) {
|
|
5
|
+
var checked = _a.target.checked;
|
|
6
|
+
// theoretically, a radio button shouldn't become unchecked so the below
|
|
7
|
+
// check of the "type" variable is not needed except for narrowing the
|
|
8
|
+
// type of the "selectionInputModel"
|
|
9
|
+
if (!checked && type === "checkbox") {
|
|
10
|
+
selectionInputModel.deselectCallback();
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
if (checked) {
|
|
14
|
+
selectCallback();
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
return (<input aria-description="input to select the current row" onClick={function (event) {
|
|
18
|
+
event.stopPropagation();
|
|
19
|
+
}} type={type} checked={selected} onChange={onChange} name={selectionInputModel.name}/>);
|
|
20
|
+
};
|
|
21
|
+
export default SelectionInput;
|
package/types.d.ts
CHANGED
|
@@ -85,3 +85,21 @@ export interface FilterModel {
|
|
|
85
85
|
tableFilterState: EditableTableFilterState;
|
|
86
86
|
setTableFilterState: (state: EditableTableFilterState) => void;
|
|
87
87
|
}
|
|
88
|
+
export type SelectMode = "column" | "row" | "both";
|
|
89
|
+
export type SelectType = "single" | "multi";
|
|
90
|
+
export interface MultiSelectModel {
|
|
91
|
+
mode: SelectMode;
|
|
92
|
+
type: "multi";
|
|
93
|
+
selected: number[];
|
|
94
|
+
setSelected: (selected: number[]) => void;
|
|
95
|
+
}
|
|
96
|
+
export interface SingleSelectModel {
|
|
97
|
+
mode: SelectMode;
|
|
98
|
+
type: "single";
|
|
99
|
+
selected: number | null;
|
|
100
|
+
setSelected: (selected: number | null) => void;
|
|
101
|
+
groupName: string;
|
|
102
|
+
}
|
|
103
|
+
export type SelectModel = SingleSelectModel | MultiSelectModel;
|
|
104
|
+
export type MultiSelectModelInitialState = Omit<MultiSelectModel, "setSelected">;
|
|
105
|
+
export type SingleSelectModelInitialState = Omit<SingleSelectModel, "setSelected">;
|
package/models/descriptions.d.ts
DELETED