@absreim/react-bootstrap-data-grid 1.4.1 → 2.0.0

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.js CHANGED
@@ -6,7 +6,6 @@ import useFilter from "./pipeline/useFilter";
6
6
  import ToggleButton from "./ToggleButton";
7
7
  import FilterOptionsTable from "./filtering/FilterOptionsTable";
8
8
  import useFilterStateFromEditable from "./pipeline/useFilterStateFromEditable";
9
- import useAugmentedRows from "./pipeline/useAugmentedRows";
10
9
  import useSortedRows from "./pipeline/useSortedRows";
11
10
  import useDisplayRows from "./pipeline/useDisplayRows";
12
11
  import SelectAllHeaderCell from "./selection/SelectAllHeaderCell";
@@ -14,16 +13,16 @@ import SelectionInput from "./selection/SelectionInput";
14
13
  import Pagination from "./pagination/Pagination";
15
14
  import classNames from "classnames";
16
15
  import EditableRow from "./editing/EditableRow";
17
- import inputStrsToRowDef from "./editing/inputStrsToRowDef";
16
+ import inputStrsToRowData from "./editing/inputStrsToRowData";
18
17
  import { unwrapAdditionalComponentsStyleModel, unwrapTableStyleModel, } from "./styling/styleModelUnwrappers";
19
18
  import useCurrentPageRows from "./pipeline/useCurrentPageRows";
19
+ import isSubset from "./util/isSubset";
20
20
  var Grid = function (_a) {
21
21
  var _b;
22
22
  var rows = _a.rows, cols = _a.cols, pagination = _a.pagination, sortModel = _a.sortModel, filterModel = _a.filterModel, selectModel = _a.selectModel, editModel = _a.editModel, caption = _a.caption, styleModel = _a.styleModel;
23
23
  var editableFilterState = (filterModel === null || filterModel === void 0 ? void 0 : filterModel.tableFilterState) || null;
24
24
  var filterState = useFilterStateFromEditable(cols, editableFilterState);
25
- var augmentedRows = useAugmentedRows(rows);
26
- var filteredRows = useFilter(augmentedRows, editableFilterState);
25
+ var filteredRows = useFilter(rows, editableFilterState);
27
26
  var sortedRows = useSortedRows(filteredRows, cols, sortModel);
28
27
  var currentPageRows = useCurrentPageRows(sortedRows, pagination);
29
28
  var showSelectCol = selectModel && selectModel.mode !== "row";
@@ -56,8 +55,8 @@ var Grid = function (_a) {
56
55
  return;
57
56
  }
58
57
  if (!selectionExists && selectModel.type === "multi") {
59
- var allFilteredRowIndices = filteredRows.map(function (def) { return def.meta.origIndex; });
60
- selectModel.setSelected(allFilteredRowIndices);
58
+ var allRowIndices = rows.map(function (_, index) { return index; });
59
+ selectModel.setSelected(allRowIndices);
61
60
  }
62
61
  // Button should be disabled in the case of selectionExists &&
63
62
  // selectModel.type === "single", so function execution should never get
@@ -80,7 +79,7 @@ var Grid = function (_a) {
80
79
  selectModel.setSelected(selectModel.selected.filter(function (num) { return num !== index; }));
81
80
  }; };
82
81
  // used to group radio buttons for selection
83
- var getSelectInputModel = function (index, selectModel) {
82
+ var getSelectInputModel = function (id, selectModel) {
84
83
  if (selectModel.type === "single") {
85
84
  return {
86
85
  type: "radio",
@@ -89,7 +88,7 @@ var Grid = function (_a) {
89
88
  }
90
89
  return {
91
90
  type: "checkbox",
92
- deselectCallback: getDeselectHandler(index),
91
+ deselectCallback: getDeselectHandler(id),
93
92
  };
94
93
  };
95
94
  var selectedSet = new Set();
@@ -102,7 +101,35 @@ var Grid = function (_a) {
102
101
  selectedSet.add(selectModel.selected);
103
102
  }
104
103
  var rowsAreSelectable = !!(selectModel && selectModel.mode !== "column");
105
- var getRowClickHandler = function (index) { return function (event) {
104
+ var selectionInfo = useMemo(function () {
105
+ if (!selectModel) {
106
+ return null;
107
+ }
108
+ if (selectModel.type === "single") {
109
+ return {
110
+ selectType: "single",
111
+ existingSelection: selectionExists,
112
+ };
113
+ }
114
+ var getMultiExistingSelection = function (selectionExists, rows) {
115
+ var rowIndices = rows.map(function (_, index) { return index; });
116
+ var isFullSelection = isSubset(rowIndices, selectModel.selected);
117
+ // Note that isFullSelection is true if there are no rows at all. In that case, the existing selection value
118
+ // should be "none", not "full".
119
+ if (!selectionExists) {
120
+ return "none";
121
+ }
122
+ if (isFullSelection) {
123
+ return "full";
124
+ }
125
+ return "partial";
126
+ };
127
+ return {
128
+ selectType: "multi",
129
+ existingSelection: getMultiExistingSelection(selectionExists, rows),
130
+ };
131
+ }, [selectModel, selectionExists, rows]);
132
+ var getRowClickHandler = function (index) { return function () {
106
133
  if (!rowsAreSelectable) {
107
134
  return;
108
135
  }
@@ -112,18 +139,18 @@ var Grid = function (_a) {
112
139
  }
113
140
  getSelectHandler(index)();
114
141
  }; };
115
- var getAriaSelectedValue = function (index) {
142
+ var getAriaSelectedValue = function (id) {
116
143
  if (!selectModel) {
117
144
  return undefined;
118
145
  }
119
- return String(selectedSet.has(index));
146
+ return String(selectedSet.has(id));
120
147
  };
121
148
  var getInputStrSubmitCallback = editModel &&
122
- (function (origIndex) {
123
- var indexSpecificCallback = editModel.getUpdateCallback(origIndex);
149
+ (function (id) {
150
+ var idSpecificCallback = editModel.getUpdateCallback(id);
124
151
  return function (inputStrs) {
125
- var rowDef = inputStrsToRowDef(cols, inputStrs);
126
- indexSpecificCallback(rowDef);
152
+ var rowData = inputStrsToRowData(cols, inputStrs);
153
+ idSpecificCallback(rowData);
127
154
  };
128
155
  });
129
156
  var unwrappedTableModel = useMemo(function () { return unwrapTableStyleModel(styleModel === null || styleModel === void 0 ? void 0 : styleModel.mainTableStyleModel); }, [styleModel === null || styleModel === void 0 ? void 0 : styleModel.mainTableStyleModel]);
@@ -132,7 +159,7 @@ var Grid = function (_a) {
132
159
  }, [styleModel === null || styleModel === void 0 ? void 0 : styleModel.additionalComponentsStyleModel]);
133
160
  return (_jsxs("div", { "data-testid": "rbdg-top-level-div", className: classNames(unwrappedAdditionalStyleModel.topLevelDiv), children: [filterState && filterModel && (_jsxs("div", { "data-testid": "rbdg-filter-inputs-div", className: classNames(unwrappedAdditionalStyleModel.filterInputsDiv), children: [_jsx(ToggleButton, { isActive: filterOptionsVisible, label: "".concat(filterOptionsVisible ? "Hide" : "Show ", " Filter Options"), onClick: handleToggleFilterOptions, classes: (_b = styleModel === null || styleModel === void 0 ? void 0 : styleModel.additionalComponentsStyleModel) === null || _b === void 0 ? void 0 : _b.filterUiToggleButton }), filterOptionsVisible && (_jsx(FilterOptionsTable, { caption: filterModel.filterTableCaption, filterState: filterState, setFilterState: filterModel.setTableFilterState, styleModel: styleModel === null || styleModel === void 0 ? void 0 : styleModel.filterInputTableStyleModel }))] })), _jsxs("div", { "data-testid": "rbdg-table-and-pagination-div", className: classNames(unwrappedAdditionalStyleModel.tableAndPaginationDiv), children: [_jsxs("table", { className: classNames("table", {
134
161
  "table-hover": rowsAreSelectable,
135
- }, unwrappedTableModel.table), "aria-rowcount": filteredRows.length + 1, children: [caption !== undefined && (_jsx("caption", { className: classNames(unwrappedTableModel.caption), children: caption })), _jsx("thead", { className: classNames(unwrappedTableModel.thead), children: _jsxs("tr", { "aria-rowindex": 1, className: classNames(unwrappedTableModel.theadTr), children: [showSelectCol && (_jsx(SelectAllHeaderCell, { selectType: selectModel.type, onClick: selectAllOnClick, selectionExists: selectionExists, additionalClasses: unwrappedTableModel.rowSelectColTh })), cols.map(function (_a, index) {
162
+ }, unwrappedTableModel.table), "aria-rowcount": filteredRows.length + 1, children: [caption !== undefined && (_jsx("caption", { className: classNames(unwrappedTableModel.caption), children: caption })), _jsx("thead", { className: classNames(unwrappedTableModel.thead), children: _jsxs("tr", { "aria-rowindex": 1, className: classNames(unwrappedTableModel.theadTr), children: [showSelectCol && (_jsx(SelectAllHeaderCell, { selectionInfo: selectionInfo, onClick: selectAllOnClick, totalRows: rows.length, additionalClasses: unwrappedTableModel.rowSelectColTh })), cols.map(function (_a, index) {
136
163
  var _b;
137
164
  var name = _a.name, label = _a.label, sortable = _a.sortable;
138
165
  var colSortModel = sortModel && sortable
@@ -147,15 +174,15 @@ var Grid = function (_a) {
147
174
  : undefined;
148
175
  return (_jsx(ColHeaderCell, { label: label, sortModel: colSortModel, ariaColIndex: index + 1 + (showSelectCol ? 1 : 0), additionalClasses: unwrappedTableModel.theadTh(index) }, name));
149
176
  }), editModel && (_jsx("th", { "aria-colindex": cols.length + 1 + (showSelectCol ? 1 : 0), className: classNames(unwrappedTableModel.editColTh), children: "Edit Controls" }))] }) }), _jsx("tbody", { className: classNames(unwrappedTableModel.tbody), children: displayRows.map(function (row, index) {
150
- return (_jsx(EditableRow, { onClick: getRowClickHandler(row.origIndex), className: classNames({
151
- "table-active": selectedSet.has(row.origIndex),
152
- }, unwrappedTableModel.tbodyTr(row.origIndex, index)), "aria-rowindex": index + 2, dataRowIndex: row.origIndex, "aria-selected": getAriaSelectedValue(row.origIndex), ariaColIndexOffset: ariaColIndexOffset, cellData: row.contents, updateCallback: getInputStrSubmitCallback &&
153
- getInputStrSubmitCallback(row.origIndex), deleteCallback: (editModel === null || editModel === void 0 ? void 0 : editModel.getDeleteCallback) &&
154
- editModel.getDeleteCallback(row.origIndex), dataCellClasses: function (colIndex) {
155
- return unwrappedTableModel.tbodyTd(row.origIndex, index, colIndex);
177
+ return (_jsx(EditableRow, { onClick: getRowClickHandler(row.id), className: classNames({
178
+ "table-active": selectedSet.has(row.id),
179
+ }, unwrappedTableModel.tbodyTr(row.id, index)), "aria-rowindex": index + 2, dataRowId: row.id, "aria-selected": getAriaSelectedValue(row.id), ariaColIndexOffset: ariaColIndexOffset, cellData: row.contents, updateCallback: getInputStrSubmitCallback &&
180
+ getInputStrSubmitCallback(row.id), deleteCallback: (editModel === null || editModel === void 0 ? void 0 : editModel.getDeleteCallback) &&
181
+ editModel.getDeleteCallback(row.id), dataCellClasses: function (colIndex) {
182
+ return unwrappedTableModel.tbodyTd(row.id, index, colIndex);
156
183
  }, dataCellInputClasses: function (colIndex) {
157
- return unwrappedTableModel.tbodyTdInput(row.origIndex, index, colIndex);
158
- }, editCellClasses: unwrappedTableModel.editColTd(row.origIndex, index), saveButtonClasses: unwrappedTableModel.editSaveButton(row.origIndex, index), deleteButtonClasses: unwrappedTableModel.editDeleteButton(row.origIndex, index), startButtonClasses: unwrappedTableModel.editStartButton(row.origIndex, index), cancelButtonClasses: unwrappedTableModel.editCancelButton(row.origIndex, index), children: showSelectCol && (_jsx("td", { className: classNames(unwrappedTableModel.rowSelectColTd(row.origIndex, index)), "aria-colindex": 1, children: _jsx(SelectionInput, { selected: selectedSet.has(row.origIndex), selectionInputModel: getSelectInputModel(row.origIndex, selectModel), selectCallback: getSelectHandler(row.origIndex), additionalClasses: unwrappedTableModel.rowSelectInput(row.origIndex, index) }) })) }, row.origIndex));
184
+ return unwrappedTableModel.tbodyTdInput(row.id, index, colIndex);
185
+ }, editCellClasses: unwrappedTableModel.editColTd(row.id, index), saveButtonClasses: unwrappedTableModel.editSaveButton(row.id, index), deleteButtonClasses: unwrappedTableModel.editDeleteButton(row.id, index), startButtonClasses: unwrappedTableModel.editStartButton(row.id, index), cancelButtonClasses: unwrappedTableModel.editCancelButton(row.id, index), children: showSelectCol && (_jsx("td", { className: classNames(unwrappedTableModel.rowSelectColTd(row.id, index)), "aria-colindex": 1, children: _jsx(SelectionInput, { selected: selectedSet.has(row.id), selectionInputModel: getSelectInputModel(row.id, selectModel), selectCallback: getSelectHandler(row.id), additionalClasses: unwrappedTableModel.rowSelectInput(row.id, index) }) })) }, row.id));
159
186
  }) })] }), pagination && (_jsx(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, pageSelectorJustifyContent: pagination.pageSelectorJustifyContent, pageSelectorAriaLabel: pagination.pageSelectorAriaLabel }))] })] }));
160
187
  };
161
188
  export default Grid;
package/README.md CHANGED
@@ -2,4 +2,4 @@
2
2
 
3
3
  See the documentation site at https://react-bootstrap-data-grid.vercel.app/ for instructions on usage.
4
4
 
5
- See the source repository at https://github.com/absreim/react-bootstrap-data-grid to view the source code.
5
+ See the source repository at https://github.com/absreim/react-bootstrap-data-grid to view the source code.
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import classNames from "classnames";
3
3
  var stopPropagationWrapper = function (fn) { return function (event) {
4
4
  event.stopPropagation();
@@ -6,14 +6,14 @@ var stopPropagationWrapper = function (fn) { return function (event) {
6
6
  }; };
7
7
  var EditControlsCell = function (_a) {
8
8
  var ariaColIndex = _a.ariaColIndex, beginEditingCallback = _a.beginEditingCallback, cancelEditingCallback = _a.cancelEditingCallback, isEditing = _a.isEditing, saveCallback = _a.saveCallback, deleteCallback = _a.deleteCallback, editControlsCellClasses = _a.editControlsCellClasses, saveButtonClasses = _a.saveButtonClasses, deleteButtonClasses = _a.deleteButtonClasses, startButtonClasses = _a.startButtonClasses, cancelButtonClasses = _a.cancelButtonClasses;
9
- return (_jsx("td", { "aria-colindex": ariaColIndex, className: classNames(editControlsCellClasses), children: _jsx("div", { className: "hstack gap-2", children: isEditing ? (_jsxs(_Fragment, { children: [_jsx("button", { className: classNames("btn", cancelButtonClasses.length === 0
9
+ return (_jsx("td", { "aria-colindex": ariaColIndex, className: classNames(editControlsCellClasses), children: _jsx("div", { className: "hstack gap-2", children: isEditing ? (_jsxs(_Fragment, { children: [_jsx("button", { "aria-label": "Cancel", className: classNames("btn", cancelButtonClasses.length === 0
10
10
  ? ["btn-secondary"]
11
- : cancelButtonClasses), onClick: stopPropagationWrapper(cancelEditingCallback), children: "Cancel" }), _jsx("button", { className: classNames("btn", saveButtonClasses.length === 0
11
+ : cancelButtonClasses), onClick: stopPropagationWrapper(cancelEditingCallback), title: "Cancel", children: _jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", fill: "currentColor", viewBox: "0 0 16 16", children: _jsx("path", { d: "M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0M5.354 4.646a.5.5 0 1 0-.708.708L7.293 8l-2.647 2.646a.5.5 0 0 0 .708.708L8 8.707l2.646 2.647a.5.5 0 0 0 .708-.708L8.707 8l2.647-2.646a.5.5 0 0 0-.708-.708L8 7.293z" }) }) }), _jsx("button", { "aria-label": "Save", className: classNames("btn", saveButtonClasses.length === 0
12
12
  ? ["btn-primary"]
13
- : saveButtonClasses), onClick: stopPropagationWrapper(saveCallback), children: "Save" })] })) : (_jsxs(_Fragment, { children: [deleteCallback && (_jsx("button", { className: classNames("btn", deleteButtonClasses.length === 0
14
- ? ["btn-primary"]
15
- : deleteButtonClasses), onClick: stopPropagationWrapper(deleteCallback), children: "Delete" })), _jsx("button", { className: classNames("btn", startButtonClasses.length === 0
13
+ : saveButtonClasses), onClick: stopPropagationWrapper(saveCallback), title: "Save", children: _jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", fill: "currentColor", viewBox: "0 0 16 16", children: [_jsx("path", { d: "M12 2h-2v3h2z" }), _jsx("path", { d: "M1.5 0A1.5 1.5 0 0 0 0 1.5v13A1.5 1.5 0 0 0 1.5 16h13a1.5 1.5 0 0 0 1.5-1.5V2.914a1.5 1.5 0 0 0-.44-1.06L14.147.439A1.5 1.5 0 0 0 13.086 0zM4 6a1 1 0 0 1-1-1V1h10v4a1 1 0 0 1-1 1zM3 9h10a1 1 0 0 1 1 1v5H2v-5a1 1 0 0 1 1-1" })] }) })] })) : (_jsxs(_Fragment, { children: [deleteCallback && (_jsx("button", { "aria-label": "Delete", className: classNames("btn", deleteButtonClasses.length === 0
14
+ ? ["btn-secondary"]
15
+ : deleteButtonClasses), onClick: stopPropagationWrapper(deleteCallback), title: "Delete", children: _jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", fill: "currentColor", viewBox: "0 0 16 16", children: _jsx("path", { d: "M2.5 1a1 1 0 0 0-1 1v1a1 1 0 0 0 1 1H3v9a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V4h.5a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1H10a1 1 0 0 0-1-1H7a1 1 0 0 0-1 1zm3 4a.5.5 0 0 1 .5.5v7a.5.5 0 0 1-1 0v-7a.5.5 0 0 1 .5-.5M8 5a.5.5 0 0 1 .5.5v7a.5.5 0 0 1-1 0v-7A.5.5 0 0 1 8 5m3 .5v7a.5.5 0 0 1-1 0v-7a.5.5 0 0 1 1 0" }) }) })), _jsx("button", { "aria-label": "Edit", className: classNames("btn", startButtonClasses.length === 0
16
16
  ? ["btn-primary"]
17
- : startButtonClasses), onClick: stopPropagationWrapper(beginEditingCallback), children: "Edit" })] })) }) }));
17
+ : startButtonClasses), onClick: stopPropagationWrapper(beginEditingCallback), title: "Edit", children: _jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", fill: "currentColor", viewBox: "0 0 16 16", children: [_jsx("path", { d: "M15.502 1.94a.5.5 0 0 1 0 .706L14.459 3.69l-2-2L13.502.646a.5.5 0 0 1 .707 0l1.293 1.293zm-1.75 2.456-2-2L4.939 9.21a.5.5 0 0 0-.121.196l-.805 2.414a.25.25 0 0 0 .316.316l2.414-.805a.5.5 0 0 0 .196-.12l6.813-6.814z" }), _jsx("path", { fillRule: "evenodd", d: "M1 13.5A1.5 1.5 0 0 0 2.5 15h11a1.5 1.5 0 0 0 1.5-1.5v-6a.5.5 0 0 0-1 0v6a.5.5 0 0 1-.5.5h-11a.5.5 0 0 1-.5-.5v-11a.5.5 0 0 1 .5-.5H9a.5.5 0 0 0 0-1H2.5A1.5 1.5 0 0 0 1 2.5z" })] }) })] })) }) }));
18
18
  };
19
19
  export default EditControlsCell;
@@ -1,9 +1,10 @@
1
1
  import { FC, ReactNode } from "react";
2
+ import { RowId } from "../types";
2
3
  import React from "react";
3
4
  import { CellData } from "./types";
4
5
  export type EditableRowProps = Pick<React.ComponentProps<"tr">, "onClick" | "className" | "aria-rowindex" | "aria-selected"> & {
5
6
  ariaColIndexOffset: number;
6
- dataRowIndex: number;
7
+ dataRowId: RowId;
7
8
  children: ReactNode;
8
9
  cellData: CellData[];
9
10
  updateCallback?: (values: string[]) => void;
@@ -29,7 +29,7 @@ var colDataTypeToInputType = function (colDataType) {
29
29
  }
30
30
  };
31
31
  var EditableRow = function (_a) {
32
- var ariaColIndexOffset = _a.ariaColIndexOffset, cellData = _a.cellData, children = _a.children, updateCallback = _a.updateCallback, deleteCallback = _a.deleteCallback, onClick = _a.onClick, className = _a.className, ariaRowIndex = _a["aria-rowindex"], ariaSelected = _a["aria-selected"], dataRowIndex = _a.dataRowIndex, dataCellClasses = _a.dataCellClasses, dataCellInputClasses = _a.dataCellInputClasses, editCellClasses = _a.editCellClasses, saveButtonClasses = _a.saveButtonClasses, startButtonClasses = _a.startButtonClasses, cancelButtonClasses = _a.cancelButtonClasses, deleteButtonClasses = _a.deleteButtonClasses;
32
+ var ariaColIndexOffset = _a.ariaColIndexOffset, cellData = _a.cellData, children = _a.children, updateCallback = _a.updateCallback, deleteCallback = _a.deleteCallback, onClick = _a.onClick, className = _a.className, ariaRowIndex = _a["aria-rowindex"], ariaSelected = _a["aria-selected"], dataRowId = _a.dataRowId, dataCellClasses = _a.dataCellClasses, dataCellInputClasses = _a.dataCellInputClasses, editCellClasses = _a.editCellClasses, saveButtonClasses = _a.saveButtonClasses, startButtonClasses = _a.startButtonClasses, cancelButtonClasses = _a.cancelButtonClasses, deleteButtonClasses = _a.deleteButtonClasses;
33
33
  var trRef = useRef(null);
34
34
  var _b = useState(false), isEditing = _b[0], setIsEditing = _b[1];
35
35
  var handleSave = function () {
@@ -59,9 +59,9 @@ var EditableRow = function (_a) {
59
59
  setIsEditing(false);
60
60
  }
61
61
  };
62
- return (_jsxs("tr", { ref: trRef, onClick: onClick, className: className, "aria-rowindex": ariaRowIndex, "aria-selected": ariaSelected, "data-rowindex": dataRowIndex, children: [children, cellData.map(function (_a, index) {
62
+ return (_jsxs("tr", { ref: trRef, onClick: onClick, className: className, "aria-rowindex": ariaRowIndex, "aria-selected": ariaSelected, "data-rowid": dataRowId, children: [children, cellData.map(function (_a, index) {
63
63
  var type = _a.type, value = _a.value, formattedValue = _a.formattedValue, label = _a.label;
64
- return (_jsx("td", { "aria-colindex": index + ariaColIndexOffset + 1, className: classNames(dataCellClasses(index)), children: isEditing && !!updateCallback ? (_jsx("input", { "aria-label": label, name: "editable-cell-input-".concat(dataRowIndex, "-").concat(index), className: classNames("form-control", dataCellInputClasses(index)), type: colDataTypeToInputType(type), defaultValue: initValueToFormValue(value, type), required: type !== "string" })) : (formattedValue) }, index));
64
+ return (_jsx("td", { "aria-colindex": index + ariaColIndexOffset + 1, className: classNames(dataCellClasses(index)), children: isEditing && !!updateCallback ? (_jsx("input", { "aria-label": label, name: "editable-cell-input-".concat(dataRowId, "-").concat(index), className: classNames("form-control", dataCellInputClasses(index)), type: colDataTypeToInputType(type), defaultValue: initValueToFormValue(value, type), required: type !== "string" })) : (formattedValue) }, index));
65
65
  }), updateCallback && (_jsx(EditControlsCell, { ariaColIndex: ariaColIndexOffset + cellData.length + 1, beginEditingCallback: function () { return setIsEditing(true); }, cancelEditingCallback: function () { return setIsEditing(false); }, isEditing: isEditing, saveCallback: handleSave, deleteCallback: deleteCallback, editControlsCellClasses: editCellClasses, startButtonClasses: startButtonClasses, deleteButtonClasses: deleteButtonClasses, cancelButtonClasses: cancelButtonClasses, saveButtonClasses: saveButtonClasses }))] }));
66
66
  };
67
67
  export default EditableRow;
@@ -0,0 +1,3 @@
1
+ import { ColDef, RowData } from "../types";
2
+ declare const inputStrsToRowData: (cols: ColDef[], inputStrs: string[]) => RowData;
3
+ export default inputStrsToRowData;
@@ -1,4 +1,4 @@
1
- var inputStrsToRowDef = function (cols, inputStrs) {
1
+ var inputStrsToRowData = function (cols, inputStrs) {
2
2
  var newRow = {};
3
3
  inputStrs.forEach(function (value, index) {
4
4
  var col = cols[index];
@@ -15,4 +15,4 @@ var inputStrsToRowDef = function (cols, inputStrs) {
15
15
  });
16
16
  return newRow;
17
17
  };
18
- export default inputStrsToRowDef;
18
+ export default inputStrsToRowData;
@@ -1,4 +1,4 @@
1
- import { ColDataType, ColDataTypeStrings, RowDef } from "../types";
1
+ import { ColDataType, ColDataTypeStrings, RowData, RowId } from "../types";
2
2
  export interface CellData {
3
3
  fieldName: string;
4
4
  value: ColDataType;
@@ -7,8 +7,8 @@ export interface CellData {
7
7
  formattedValue: string;
8
8
  label: string;
9
9
  }
10
- export type UpdateCallbackGenerator = (origIndex: number) => (rowDef: RowDef) => void;
10
+ export type UpdateCallbackGenerator = (id: RowId) => (rowData: RowData) => void;
11
11
  export interface EditModel {
12
12
  getUpdateCallback: UpdateCallbackGenerator;
13
- getDeleteCallback?: (origIndex: number) => () => void;
13
+ getDeleteCallback?: (id: RowId) => () => void;
14
14
  }
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.4.1",
4
+ "version": "2.0.0",
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 { AugRowDef } from "../types";
1
+ import { RowDef } from "../types";
2
2
  import { GridPaginationState } from "../pagination/types";
3
- declare const useCurrentPageRows: (sortedRows: AugRowDef[], pagination: GridPaginationState | undefined) => AugRowDef[];
3
+ declare const useCurrentPageRows: (sortedRows: RowDef[], pagination: GridPaginationState | undefined) => RowDef[];
4
4
  export default useCurrentPageRows;
@@ -1,3 +1,3 @@
1
- import { AugRowDef, FormattedRow, ColDef } from "../types";
2
- declare const useDisplayRows: (currentPageRows: AugRowDef[], cols: ColDef[], ariaColIndexOffset: number) => FormattedRow[];
1
+ import { RowDef, FormattedRow, ColDef } from "../types";
2
+ declare const useDisplayRows: (currentPageRows: RowDef[], cols: ColDef[], ariaColIndexOffset: number) => FormattedRow[];
3
3
  export default useDisplayRows;
@@ -1,16 +1,17 @@
1
1
  import { useMemo } from "react";
2
+ import { dateToDatetimeInputStr, dateToInputStr } from "../util/datetime";
2
3
  var getFormattedValue = function (value, formatter, typeString) {
3
4
  if (formatter) {
4
5
  return formatter(value);
5
6
  }
6
7
  if (typeString === "date") {
7
- return value.toDateString();
8
+ return dateToInputStr(value);
8
9
  }
9
10
  if (typeString === "datetime") {
10
- return value.toLocaleString();
11
+ return dateToDatetimeInputStr(value);
11
12
  }
12
13
  if (typeString === "number") {
13
- return value.toLocaleString();
14
+ return value.toString();
14
15
  }
15
16
  return value;
16
17
  };
@@ -52,7 +53,7 @@ var useDisplayRows = function (currentPageRows, cols, ariaColIndexOffset) {
52
53
  label: cols[index].label,
53
54
  };
54
55
  });
55
- return { contents: displayRow, origIndex: row.meta.origIndex };
56
+ return { contents: displayRow, id: row.id };
56
57
  });
57
58
  }, [currentPageRows, cols, ariaColIndexOffset]);
58
59
  };
@@ -1,4 +1,4 @@
1
- import { AugRowDef } from "../types";
1
+ import { RowDef } from "../types";
2
2
  import { EditableTableFilterState } from "../filtering/types";
3
- declare const useFilter: (rows: AugRowDef[], filterState: EditableTableFilterState | null) => AugRowDef[];
3
+ declare const useFilter: (rows: RowDef[], filterState: EditableTableFilterState | null) => RowDef[];
4
4
  export default useFilter;
@@ -1,4 +1,4 @@
1
- import { AugRowDef, ColDef } from "../types";
1
+ import { ColDef, RowDef } from "../types";
2
2
  import { TableSortModel } from "../sorting/types";
3
- declare const useSortedRows: (rows: AugRowDef[], cols: ColDef[], sortModel: TableSortModel | undefined) => AugRowDef[];
3
+ declare const useSortedRows: (rows: RowDef[], cols: ColDef[], sortModel: TableSortModel | undefined) => RowDef[];
4
4
  export default useSortedRows;
@@ -1,9 +1,9 @@
1
1
  import { FC } from "react";
2
- import { SelectType } from "./types";
2
+ import { SelectionInfo } from "./types";
3
3
  interface SelectAllHeaderCellProps {
4
4
  onClick: () => void;
5
- selectType: SelectType;
6
- selectionExists: boolean;
5
+ selectionInfo: SelectionInfo;
6
+ totalRows: number;
7
7
  additionalClasses?: string[];
8
8
  }
9
9
  declare const SelectAllHeaderCell: FC<SelectAllHeaderCellProps>;
@@ -1,42 +1,55 @@
1
+ "use client";
1
2
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import deselectAll from "./deselectAll";
3
- import selectAll from "./selectAll";
4
- import arrowPlaceholder from "../sorting/arrowPlaceholder";
3
+ import { useEffect, useRef } from "react";
5
4
  import classNames from "classnames";
6
- // It seems like React does not support setting indeterminate states on
7
- // checkboxes in a controlled manner, which caused me to not want to refactor
8
- // this feature to use a checkbox input instead of SVG icons. I am not sure how
9
- // much of an issue using an uncontrolled input would be, but because at time of
10
- // this writing I had already implemented a solution with SVG, on balance I felt
11
- // like it was not worth going out of my way to change things up to use an
12
- // uncontrolled checkbox input.
13
- var getSelectIcon = function (selectMode, existingSelection) {
14
- if (existingSelection) {
15
- return deselectAll;
5
+ var getCheckboxState = function (selectionInfo, noRows) {
6
+ var disabledState = {
7
+ indeterminate: false,
8
+ checked: false,
9
+ disabled: true,
10
+ description: "Select all (disabled)",
11
+ };
12
+ if (noRows) {
13
+ return disabledState;
16
14
  }
17
- if (selectMode === "multi" && !existingSelection) {
18
- return selectAll;
15
+ var existingSelection = selectionInfo.existingSelection;
16
+ if (existingSelection === "full") {
17
+ return {
18
+ indeterminate: false,
19
+ checked: true,
20
+ disabled: false,
21
+ description: "Deselect all rows",
22
+ };
19
23
  }
20
- // Single select mode and none selected means that the button is disabled
21
- return arrowPlaceholder;
22
- };
23
- var getCellAriaDescription = function (selectMode, existingSelection) {
24
- if (existingSelection) {
25
- return "Deselect all rows";
24
+ if (existingSelection === true || existingSelection === "partial") {
25
+ return {
26
+ indeterminate: true,
27
+ checked: true,
28
+ disabled: false,
29
+ description: "Deselect all rows",
30
+ };
26
31
  }
27
- if (selectMode === "multi" && !existingSelection) {
28
- return "Select all rows";
32
+ if (existingSelection === "none") {
33
+ return {
34
+ indeterminate: false,
35
+ checked: false,
36
+ disabled: false,
37
+ description: "Select all rows",
38
+ };
29
39
  }
30
- // Single select mode and none selected means that the button is disabled
31
- return "Selection input header cell";
40
+ // single select mode and none selected
41
+ return disabledState;
32
42
  };
33
43
  var SelectAllHeaderCell = function (_a) {
34
- var onClick = _a.onClick, selectType = _a.selectType, selectionExists = _a.selectionExists, additionalClasses = _a.additionalClasses;
35
- var disabled = selectType === "single" && !selectionExists;
36
- var description = getCellAriaDescription(selectType, selectionExists);
37
- return (_jsx("th", { "aria-colindex": 1, title: description, "aria-description": description, className: classNames("rbdg-select-header-cell", {
44
+ var onClick = _a.onClick, selectionInfo = _a.selectionInfo, totalRows = _a.totalRows, additionalClasses = _a.additionalClasses;
45
+ var noRows = totalRows === 0;
46
+ var _b = getCheckboxState(selectionInfo, noRows), indeterminate = _b.indeterminate, checked = _b.checked, disabled = _b.disabled, description = _b.description;
47
+ var ref = useRef(null);
48
+ useEffect(function () {
49
+ ref.current.indeterminate = indeterminate;
50
+ }, [indeterminate]);
51
+ return (_jsx("th", { "aria-colindex": 1, title: description, "aria-description": description, className: classNames({
38
52
  "btn-primary": !additionalClasses || additionalClasses.length === 0,
39
- "rbdg-clickable-grid-header-cell": !disabled,
40
- }, additionalClasses || []), onClick: onClick, children: getSelectIcon(selectType, selectionExists) }));
53
+ }, additionalClasses || []), children: _jsx("input", { type: "checkbox", checked: checked, ref: ref, disabled: disabled, "aria-label": description, onChange: onClick }) }));
41
54
  };
42
55
  export default SelectAllHeaderCell;
@@ -1,16 +1,27 @@
1
+ import { RowId } from "../types";
1
2
  export type SelectMode = "column" | "row" | "both";
2
3
  export type SelectType = "single" | "multi";
3
4
  export interface MultiSelectModel {
4
5
  mode: SelectMode;
5
6
  type: "multi";
6
- selected: number[];
7
- setSelected: (selected: number[]) => void;
7
+ selected: RowId[];
8
+ setSelected: (selected: RowId[]) => void;
8
9
  }
9
10
  export interface SingleSelectModel {
10
11
  mode: SelectMode;
11
12
  type: "single";
12
- selected: number | null;
13
- setSelected: (selected: number | null) => void;
13
+ selected: RowId | null;
14
+ setSelected: (selected: RowId | null) => void;
14
15
  groupName: string;
15
16
  }
16
17
  export type SelectModel = SingleSelectModel | MultiSelectModel;
18
+ export type MultiExistingSelection = "full" | "partial" | "none";
19
+ export interface SingleSelectionInfo {
20
+ selectType: "single";
21
+ existingSelection: boolean;
22
+ }
23
+ export interface MultiSelectionInfo {
24
+ selectType: "multi";
25
+ existingSelection: "full" | "partial" | "none";
26
+ }
27
+ export type SelectionInfo = SingleSelectionInfo | MultiSelectionInfo;
@@ -1,4 +1,4 @@
1
- // Selected indices are based on the original index of the input rows. If
1
+ // Selected indices are based on the id of the input rows. If
2
2
  // filtered-out items are included in the 'selected' property, there will be a
3
3
  // visual indication to the user of that fact.
4
4
  export {};
package/style.css CHANGED
@@ -1 +1 @@
1
- .rbdg-clickable-grid-header-cell{cursor:pointer}.rbdg-select-header-cell .rdbg-svg-icon{background-color:var(--bs-btn-bg)}.rbdg-select-header-cell .rdbg-svg-icon-foreground{color:var(--bs-btn-color)}.rbdg-select-header-cell:hover .rdbg-svg-icon{background-color:var(--bs-btn-hover-bg)}.rbdg-select-header-cell:hover .rdbg-svg-icon-foreground{color:var(--bs-btn-hover-color)}.rbdg-grid{display:table}.rbdg-grid-head{display:table-header-group}.rbdg-grid-body{display:table-row-group}.rbdg-grid-row{display:table-row}.rbdg-grid-cell{display:table-cell}
1
+ .rbdg-clickable-grid-header-cell{cursor:pointer}
package/style.scss CHANGED
@@ -1,41 +1,3 @@
1
1
  .rbdg-clickable-grid-header-cell {
2
2
  cursor: pointer;
3
3
  }
4
-
5
- .rbdg-select-header-cell {
6
- .rdbg-svg-icon {
7
- background-color: var(--bs-btn-bg);
8
- }
9
- .rdbg-svg-icon-foreground {
10
- color: var(--bs-btn-color);
11
- }
12
-
13
- &:hover {
14
- .rdbg-svg-icon {
15
- background-color: var(--bs-btn-hover-bg);
16
- }
17
- .rdbg-svg-icon-foreground {
18
- color: var(--bs-btn-hover-color);
19
- }
20
- }
21
- }
22
-
23
- .rbdg-grid {
24
- display: table;
25
- }
26
-
27
- .rbdg-grid-head {
28
- display: table-header-group;
29
- }
30
-
31
- .rbdg-grid-body {
32
- display: table-row-group;
33
- }
34
-
35
- .rbdg-grid-row {
36
- display: table-row;
37
- }
38
-
39
- .rbdg-grid-cell {
40
- display: table-cell;
41
- }
@@ -1,3 +1,4 @@
1
+ import { RowId } from "../types";
1
2
  export interface SharedTableStyleModel {
2
3
  table?: string[];
3
4
  tbody?: string[];
@@ -7,18 +8,18 @@ export interface SharedTableStyleModel {
7
8
  caption?: string[];
8
9
  }
9
10
  export type TableStyleModel = SharedTableStyleModel & {
10
- tbodyTr?: (origIndex: number, displayIndex: number) => string[];
11
- tbodyTd?: (origRowIndex: number, displayRowIndex: number, colIndex: number) => string[];
12
- tbodyTdInput?: (origRowIndex: number, displayRowIndex: number, colIndex: number) => string[];
11
+ tbodyTr?: (rowId: RowId, displayIndex: number) => string[];
12
+ tbodyTd?: (rowId: RowId, displayRowIndex: number, colIndex: number) => string[];
13
+ tbodyTdInput?: (rowId: RowId, displayRowIndex: number, colIndex: number) => string[];
13
14
  editColTh?: string[];
14
- editColTd?: (origIndex: number, displayIndex: number) => string[];
15
- editStartButton?: (origIndex: number, displayIndex: number) => string[];
16
- editDeleteButton?: (origIndex: number, displayIndex: number) => string[];
17
- editSaveButton?: (origIndex: number, displayIndex: number) => string[];
18
- editCancelButton?: (origIndex: number, displayIndex: number) => string[];
15
+ editColTd?: (rowId: RowId, displayIndex: number) => string[];
16
+ editStartButton?: (rowId: RowId, displayIndex: number) => string[];
17
+ editDeleteButton?: (rowId: RowId, displayIndex: number) => string[];
18
+ editSaveButton?: (rowId: RowId, displayIndex: number) => string[];
19
+ editCancelButton?: (rowId: RowId, displayIndex: number) => string[];
19
20
  rowSelectColTh?: string[];
20
- rowSelectColTd?: (origIndex: number, displayIndex: number) => string[];
21
- rowSelectInput?: (origIndex: number, displayIndex: number) => string[];
21
+ rowSelectColTd?: (rowId: RowId, displayIndex: number) => string[];
22
+ rowSelectInput?: (rowId: RowId, displayIndex: number) => string[];
22
23
  };
23
24
  export type FilterInputTableStyleModel = SharedTableStyleModel & {
24
25
  tbodyTr?: (rowIndex: number) => string[];
package/types.d.ts CHANGED
@@ -1,24 +1,24 @@
1
1
  import { CellData } from "./editing/types";
2
2
  export type ColDataType = string | number | Date;
3
3
  export type ColDataTypeStrings = "string" | "number" | "date" | "datetime";
4
- export interface ColDef {
4
+ export interface ColDef<ValueType = any> {
5
5
  type: ColDataTypeStrings;
6
6
  name: string;
7
7
  label: string;
8
- formatter?: (value: any) => string;
8
+ formatter?: (value: ValueType) => string;
9
9
  sortable?: boolean;
10
10
  }
11
- export type RowDef = Record<string, ColDataType>;
12
- export interface RowMetadata {
13
- origIndex: number;
14
- }
15
- export interface AugRowDef {
16
- data: RowDef;
17
- meta: RowMetadata;
11
+ type ValidRowData = Record<string, any>;
12
+ export type RowData<Data extends ValidRowData = ValidRowData> = Data;
13
+ export type RowId = string | number;
14
+ export interface RowDef<Data extends ValidRowData = ValidRowData> {
15
+ id: RowId;
16
+ data: RowData<Data>;
18
17
  }
19
18
  export interface FormattedRow {
20
19
  contents: CellData[];
21
- origIndex: number;
20
+ id: RowId;
22
21
  }
23
22
  export type JustifyContentSetting = "start" | "end" | "center" | "between" | "around" | "evenly";
24
23
  export type Size = "small" | "medium" | "large";
24
+ export {};
@@ -0,0 +1,2 @@
1
+ declare const isSubset: <T>(subset: T[], superSet: T[]) => boolean;
2
+ export default isSubset;
@@ -0,0 +1,11 @@
1
+ var isSubset = function (subset, superSet) {
2
+ var supersetSet = new Set(superSet);
3
+ for (var _i = 0, subset_1 = subset; _i < subset_1.length; _i++) {
4
+ var member = subset_1[_i];
5
+ if (!supersetSet.has(member)) {
6
+ return false;
7
+ }
8
+ }
9
+ return true;
10
+ };
11
+ export default isSubset;
@@ -1,3 +0,0 @@
1
- import { ColDef, RowDef } from "../types";
2
- declare const inputStrsToRowDef: (cols: ColDef[], inputStrs: string[]) => RowDef;
3
- export default inputStrsToRowDef;
@@ -1,3 +0,0 @@
1
- import { AugRowDef, RowDef } from "../types";
2
- declare const useAugmentedRows: (rows: RowDef[]) => AugRowDef[];
3
- export default useAugmentedRows;
@@ -1,12 +0,0 @@
1
- import { useMemo } from "react";
2
- var useAugmentedRows = function (rows) {
3
- return useMemo(function () {
4
- return rows.map(function (row, index) { return ({
5
- data: row,
6
- meta: {
7
- origIndex: index,
8
- },
9
- }); });
10
- }, [rows]);
11
- };
12
- export default useAugmentedRows;
@@ -1,2 +0,0 @@
1
- declare const deselectAll: import("react/jsx-runtime").JSX.Element;
2
- export default deselectAll;
@@ -1,3 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- var deselectAll = (_jsxs("svg", { className: "rdbg-svg-icon", xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 16 16", width: "16", height: "16", fill: "currentColor", children: [_jsx("desc", { children: "Minus sign inside a square" }), _jsx("rect", { className: "rdbg-svg-icon-foreground", x: "3.5", y: "7.5", width: "9", height: "1" })] }));
3
- export default deselectAll;
@@ -1,2 +0,0 @@
1
- declare const selectAll: import("react/jsx-runtime").JSX.Element;
2
- export default selectAll;
@@ -1,3 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- var selectAll = (_jsxs("svg", { className: "rdbg-svg-icon", xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 16 16", width: "16", height: "16", fill: "currentColor", children: [_jsx("desc", { children: "Plus sign inside a square" }), _jsx("rect", { className: "rdbg-svg-icon-foreground", x: "3.5", y: "7.5", width: "9", height: "1" }), _jsx("rect", { className: "rdbg-svg-icon-foreground", x: "3.5", y: "7.5", width: "9", height: "1", transform: "translate(0 16) rotate(-90)" })] }));
3
- export default selectAll;