@absreim/react-bootstrap-data-grid-pro 2.6.1 → 2.7.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.
@@ -15,6 +15,9 @@ export interface InternalGridProps {
15
15
  bodyRows: ReactNode;
16
16
  prefixHeader?: ReactNode;
17
17
  };
18
+ classes?: {
19
+ headerRow?: string[];
20
+ };
18
21
  }
19
22
  declare const InternalGrid: FC<InternalGridProps>;
20
23
  export default InternalGrid;
@@ -10,7 +10,7 @@ import useInterfaces from "./toolbar/useInterfaces";
10
10
  import ToolbarContainer from "./toolbar/ToolbarContainer";
11
11
  import useExportFn from "./export/useExportFn";
12
12
  import getWidthStyle from "./util/getWidthStyle";
13
- const InternalGrid = ({ gridProps: { rows, cols, pagination, filterModel, selectModel, editModel, caption, styleModel, useToolbar, responsive, displayMode, }, hooks: { pipelineOutput, selectFns, unwrappedStyles }, slots: { colHeaderCells, bodyRows, prefixHeader }, }) => {
13
+ const InternalGrid = ({ gridProps: { rows, cols, pagination, filterModel, selectModel, editModel, caption, styleModel, useToolbar, responsive, displayMode, }, hooks: { pipelineOutput, selectFns, unwrappedStyles }, slots: { colHeaderCells, bodyRows, prefixHeader }, classes, }) => {
14
14
  var _a;
15
15
  const { normalizedTableFilterModel, filteredRows, filterState, sortedRowsOutput: { sortedRows }, currentPageRowsOutput: { paginatedRows, normalizedModel }, showSelectCol, } = pipelineOutput;
16
16
  const [filterOptionsVisible, setFilterOptionsVisible] = useState(false);
@@ -50,7 +50,7 @@ const InternalGrid = ({ gridProps: { rows, cols, pagination, filterModel, select
50
50
  const mainTable = (_jsxs("table", { className: classNames("table", {
51
51
  "table-hover": rowsAreSelectable,
52
52
  "d-block": displayMode === "block",
53
- }, 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: [prefixHeader, showSelectCol && (_jsx(SelectAllHeaderCell, { style: getWidthStyle(selectModel === null || selectModel === void 0 ? void 0 : selectModel.selectColWidth), selectionInfo: selectionInfo, onClick: selectAllOnClick, totalRows: rows.length, additionalClasses: unwrappedTableModel.rowSelectColTh, colIndexOffset: prefixHeader ? 1 : 0 })), colHeaderCells, editModel && (_jsx("th", { "aria-colindex": cols.length + 1 + (showSelectCol ? 1 : 0), className: classNames(unwrappedTableModel.editColTh), style: getWidthStyle(editModel === null || editModel === void 0 ? void 0 : editModel.editColWidth), children: "Edit Controls" }))] }) }), _jsx("tbody", { className: classNames(unwrappedTableModel.tbody), children: bodyRows })] }));
53
+ }, 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, (classes === null || classes === void 0 ? void 0 : classes.headerRow) || []), children: [prefixHeader, showSelectCol && (_jsx(SelectAllHeaderCell, { style: getWidthStyle(selectModel === null || selectModel === void 0 ? void 0 : selectModel.selectColWidth), selectionInfo: selectionInfo, onClick: selectAllOnClick, totalRows: rows.length, additionalClasses: unwrappedTableModel.rowSelectColTh, colIndexOffset: prefixHeader ? 1 : 0 })), colHeaderCells, editModel && (_jsx("th", { "aria-colindex": cols.length + 1 + (showSelectCol ? 1 : 0), className: classNames(unwrappedTableModel.editColTh), style: getWidthStyle(editModel === null || editModel === void 0 ? void 0 : editModel.editColWidth), children: "Edit Controls" }))] }) }), _jsx("tbody", { className: classNames(unwrappedTableModel.tbody), children: bodyRows })] }));
54
54
  return (_jsxs("div", { "data-testid": "rbdg-top-level-div", className: classNames(unwrappedAdditionalStyleModel.topLevelDiv), children: [normalizedTableFilterModel && !useToolbar && (_jsxs("div", { "data-testid": "rbdg-filter-inputs-div", className: classNames(unwrappedAdditionalStyleModel.filterInputsDiv), children: [_jsx(ToggleButton, { isActive: filterOptionsVisible, label: `${filterOptionsVisible ? "Hide" : "Show "} Filter Options`, onClick: handleToggleFilterOptions, classes: (_a = styleModel === null || styleModel === void 0 ? void 0 : styleModel.additionalComponentsStyleModel) === null || _a === void 0 ? void 0 : _a.filterUiToggleButton }), filterOptionsVisible && (_jsx(FilterOptionsTable, { caption: filterModel.filterTableCaption, filterState: filterState, setFilterState: normalizedTableFilterModel.setTableFilterState, styleModel: styleModel === null || styleModel === void 0 ? void 0 : styleModel.filterInputTableStyleModel }))] })), useToolbar && (_jsx(ToolbarContainer, { interfaces: toolbarInterfaces, styleModel: styleModel === null || styleModel === void 0 ? void 0 : styleModel.toolbarStyleModel })), _jsxs("div", { "data-testid": "rbdg-table-and-pagination-div", className: classNames(unwrappedAdditionalStyleModel.tableAndPaginationDiv), children: [responsive ? (_jsx("div", { "data-testid": "rbdg-table-div", className: "table-responsive", children: mainTable })) : (_jsx(_Fragment, { children: mainTable })), normalizedModel && (_jsx(Pagination, { normalizedModel: normalizedModel, prePagingNumRows: sortedRows.length, containerDivClasses: unwrappedAdditionalStyleModel.paginationUiDiv }))] })] }));
55
55
  };
56
56
  export default InternalGrid;
@@ -1,4 +1,4 @@
1
- import { AugFormattedRow } from "../types";
1
+ import { AugFormattedRow, RowId } from "../types";
2
2
  import { FC, ReactNode } from "react";
3
3
  import { UseGridSelectionFnsHook } from "../pipeline/useGridSelectionFns";
4
4
  import { UseUnwrappedGridStylesHook } from "../pipeline/useUnwrappedGridStyles";
@@ -16,6 +16,7 @@ interface BodyRowsProps {
16
16
  getInputStrSubmitCallback: UseGetInputStrSubmitCallbackHook;
17
17
  additionalColIndexOffset?: number;
18
18
  renderPrefixCells?: (augFormattedRows: AugFormattedRow) => ReactNode;
19
+ additionalRowStyles?: (rowId: RowId, displayIndex: number) => string[];
19
20
  }
20
21
  declare const BodyRows: FC<BodyRowsProps>;
21
22
  export default BodyRows;
@@ -3,9 +3,9 @@ import EditableRow from "../editing/EditableRow";
3
3
  import classNames from "classnames";
4
4
  import getWidthStyle from "../util/getWidthStyle";
5
5
  import SelectionInput from "../selection/SelectionInput";
6
- const BodyRows = ({ augFormattedRows, gridSelectionFns: { selectedSet, getSelectInputModel, getSelectHandler, getRowClickHandler, getAriaSelectedValue, }, unwrappedStyles: { unwrappedTableModel }, combinedPipelineOutput: { showSelectCol, ariaColIndexOffset }, selectModel, editModel, getInputStrSubmitCallback, additionalColIndexOffset, renderPrefixCells, }) => augFormattedRows.map((row, index) => {
6
+ const BodyRows = ({ augFormattedRows, gridSelectionFns: { selectedSet, getSelectInputModel, getSelectHandler, getRowClickHandler, getAriaSelectedValue, }, unwrappedStyles: { unwrappedTableModel }, combinedPipelineOutput: { showSelectCol, ariaColIndexOffset }, selectModel, editModel, getInputStrSubmitCallback, additionalColIndexOffset, renderPrefixCells, additionalRowStyles, }) => augFormattedRows.map((row, index) => {
7
7
  return (_jsxs(EditableRow, { onClick: getRowClickHandler(row.id), className: classNames({
8
8
  "table-active": selectedSet.has(row.id),
9
- }, unwrappedTableModel.tbodyTr(row.id, index)), "aria-rowindex": row.prePaginationIndex + 2, dataRowId: row.id, "aria-selected": getAriaSelectedValue(row.id), ariaColIndexOffset: ariaColIndexOffset + (additionalColIndexOffset || 0), cellData: row.contents, updateCallback: getInputStrSubmitCallback && getInputStrSubmitCallback(row.id), deleteCallback: (editModel === null || editModel === void 0 ? void 0 : editModel.getDeleteCallback) && editModel.getDeleteCallback(row.id), dataCellClasses: (colIndex) => unwrappedTableModel.tbodyTd(row.id, index, colIndex), dataCellInputClasses: (colIndex) => unwrappedTableModel.tbodyTdInput(row.id, index, colIndex), 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: [renderPrefixCells && renderPrefixCells(row), showSelectCol && (_jsx("td", { className: classNames(unwrappedTableModel.rowSelectColTd(row.id, index)), "aria-colindex": 1 + (additionalColIndexOffset || 0), style: getWidthStyle(selectModel.selectColWidth), 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));
9
+ }, unwrappedTableModel.tbodyTr(row.id, index), additionalRowStyles ? additionalRowStyles(row.id, index) : []), "aria-rowindex": row.prePaginationIndex + 2, dataRowId: row.id, "aria-selected": getAriaSelectedValue(row.id), ariaColIndexOffset: ariaColIndexOffset + (additionalColIndexOffset || 0), cellData: row.contents, updateCallback: getInputStrSubmitCallback && getInputStrSubmitCallback(row.id), deleteCallback: (editModel === null || editModel === void 0 ? void 0 : editModel.getDeleteCallback) && editModel.getDeleteCallback(row.id), dataCellClasses: (colIndex) => unwrappedTableModel.tbodyTd(row.id, index, colIndex), dataCellInputClasses: (colIndex) => unwrappedTableModel.tbodyTdInput(row.id, index, colIndex), 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: [renderPrefixCells && renderPrefixCells(row), showSelectCol && (_jsx("td", { className: classNames(unwrappedTableModel.rowSelectColTd(row.id, index)), "aria-colindex": 1 + (additionalColIndexOffset || 0), style: getWidthStyle(selectModel.selectColWidth), children: _jsx(SelectionInput, { index: index, selected: selectedSet.has(row.id), selectionInputModel: getSelectInputModel(row.id, selectModel), selectCallback: getSelectHandler(row.id), additionalClasses: unwrappedTableModel.rowSelectInput(row.id, index) }) }))] }, row.id));
10
10
  });
11
11
  export default BodyRows;
@@ -49,6 +49,6 @@ const SelectAllHeaderCell = ({ onClick, selectionInfo, totalRows, additionalClas
49
49
  }, [indeterminate]);
50
50
  return (_jsx("th", { style: style, "aria-colindex": 1 + (colIndexOffset || 0), title: description, "aria-description": description, className: classNames({
51
51
  "btn-primary": !additionalClasses || additionalClasses.length === 0,
52
- }, additionalClasses || []), children: _jsx("input", { type: "checkbox", checked: checked, ref: ref, disabled: disabled, "aria-label": description, onChange: onClick }) }));
52
+ }, additionalClasses || []), children: _jsx("input", { tabIndex: 0, type: "checkbox", checked: checked, ref: ref, disabled: disabled, "aria-label": description, onChange: onClick }) }));
53
53
  };
54
54
  export default SelectAllHeaderCell;
@@ -14,6 +14,7 @@ export interface SelectionInputProps {
14
14
  selectionInputModel: SelectionInputModel;
15
15
  selectCallback: () => void;
16
16
  additionalClasses?: string[];
17
+ index: number;
17
18
  }
18
19
  declare const SelectionInput: FC<SelectionInputProps>;
19
20
  export default SelectionInput;
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import classNames from "classnames";
3
- const SelectionInput = ({ selectionInputModel, selected, selectCallback, additionalClasses, }) => {
3
+ const SelectionInput = ({ selectionInputModel, selected, selectCallback, additionalClasses, index, }) => {
4
4
  const type = selectionInputModel.type;
5
5
  const onChange = ({ target: { checked }, }) => {
6
6
  // theoretically, a radio button shouldn't become unchecked so the below
@@ -14,7 +14,7 @@ const SelectionInput = ({ selectionInputModel, selected, selectCallback, additio
14
14
  selectCallback();
15
15
  }
16
16
  };
17
- return (_jsx("input", { className: classNames(additionalClasses || []), "aria-label": "Input to select the current row", onClick: (event) => {
17
+ return (_jsx("input", { tabIndex: 0, className: classNames(additionalClasses || []), "aria-label": `Select row with index ${index}`, onClick: (event) => {
18
18
  event.stopPropagation();
19
19
  }, type: type, checked: selected, onChange: onChange, name: selectionInputModel.name }));
20
20
  };
@@ -0,0 +1,2 @@
1
+ declare const trueModulo: (num: number, divisor: number) => number;
2
+ export default trueModulo;
@@ -0,0 +1,2 @@
1
+ const trueModulo = (num, divisor) => ((num % divisor) + divisor) % divisor;
2
+ export default trueModulo;
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
- import { useCallback, useMemo } from "react";
3
+ import { useCallback, useEffect, useMemo } from "react";
4
4
  import useCombinedPipeline from "../grid/pipeline/useCombinedPipeline";
5
5
  import InternalGrid from "../grid/InternalGrid";
6
6
  import ColHeaderCellPro from "./ColHeaderCellPro";
@@ -12,6 +12,9 @@ import useResizeModel from "./resize/useResizeModel";
12
12
  import BodyRows from "../grid/main/BodyRows";
13
13
  import ReorderHeaderCell from "./reorder/ReorderHeaderCell";
14
14
  import ReorderHandleCell from "./reorder/ReorderHandleCell";
15
+ import useKeyboardReorder from "./reorder/useKeyboardReorder";
16
+ import useReorderStyles from "./reorder/useReorderStyles";
17
+ import useKeyboardReorderListener from "./reorder/useKeyboardReorderListener";
15
18
  const GridPro = (props) => {
16
19
  const { rows, cols, editModel, filterModel, sortModel, pagination, selectModel, styleModel, displayMode, reorder, } = props;
17
20
  const combinedPipelineOutput = useCombinedPipeline({
@@ -32,6 +35,57 @@ const GridPro = (props) => {
32
35
  return prev;
33
36
  }, {}), [resizeModel]);
34
37
  const augFormattedRows = useAugFormattedRows(colNameToWidth, displayRows);
38
+ const displayRowIds = useMemo(() => displayRows.map(({ id }) => id), [displayRows]);
39
+ const { drageeState, setDragee } = useKeyboardReorder(displayRowIds);
40
+ const reorderStyles = useReorderStyles(styleModel === null || styleModel === void 0 ? void 0 : styleModel.reorderModel);
41
+ const additionalBodyRowStyles = useCallback((id, displayIndex) => {
42
+ if (!drageeState) {
43
+ return [];
44
+ }
45
+ const { rowId, drageeIndex, destIndex } = drageeState;
46
+ if (id === rowId) {
47
+ return reorderStyles.draggedRowClasses;
48
+ }
49
+ const isDraggedRowPred = drageeIndex > 0 && displayIndex === drageeIndex - 1;
50
+ const isTargetUpperNeighbor = destIndex > 0 && displayIndex === destIndex - 1;
51
+ const isTargetLowerNeighbor = destIndex === displayIndex;
52
+ if (isDraggedRowPred && isTargetLowerNeighbor) {
53
+ return reorderStyles.draggedRowPredecessorClasses.concat(reorderStyles.bottomBorderRowClasses);
54
+ }
55
+ if (isDraggedRowPred && !isTargetLowerNeighbor) {
56
+ return reorderStyles.draggedRowPredecessorClasses;
57
+ }
58
+ if (!isDraggedRowPred && isTargetLowerNeighbor) {
59
+ return reorderStyles.bottomBorderRowClasses;
60
+ }
61
+ if (isTargetUpperNeighbor) {
62
+ return reorderStyles.topBorderRowClasses;
63
+ }
64
+ return [];
65
+ }, [reorderStyles, drageeState]);
66
+ const additionalHeaderRowStyles = useMemo(() => {
67
+ if (!drageeState) {
68
+ return [];
69
+ }
70
+ if (drageeState.drageeIndex === 0) {
71
+ return reorderStyles.draggedRowPredecessorClasses;
72
+ }
73
+ if (drageeState.destIndex === 0) {
74
+ return reorderStyles.topBorderRowClasses;
75
+ }
76
+ return [];
77
+ }, [
78
+ drageeState,
79
+ reorderStyles.draggedRowPredecessorClasses,
80
+ reorderStyles.topBorderRowClasses,
81
+ ]);
82
+ const tableOnKeydown = useKeyboardReorderListener(drageeState, reorder === null || reorder === void 0 ? void 0 : reorder.callback);
83
+ useEffect(() => {
84
+ document.addEventListener("keydown", tableOnKeydown);
85
+ return () => {
86
+ document.removeEventListener("keydown", tableOnKeydown);
87
+ };
88
+ }, [tableOnKeydown]);
35
89
  const colHeaderCells = cols.map(({ name, label, sortable, minResizeWidth, maxResizeWidth, keyboardResizeStep, }, index) => {
36
90
  var _a;
37
91
  const colSortModel = sortingEnabled && sortable
@@ -53,14 +107,24 @@ const GridPro = (props) => {
53
107
  return null;
54
108
  }
55
109
  const reorderCallback = (destIndex) => reorder.callback(augRow.id, destIndex);
56
- return (_jsx(ReorderHandleCell, { rowId: augRow.id, ariaRowIndex: augRow.prePaginationIndex + 2, disabled: filteringOccurring || sortingOccurring, reorderCallback: reorderCallback, styleModel: styleModel === null || styleModel === void 0 ? void 0 : styleModel.reorderModel }));
57
- }, [filteringOccurring, reorder, sortingOccurring, styleModel === null || styleModel === void 0 ? void 0 : styleModel.reorderModel]);
58
- const bodyRows = (_jsx(BodyRows, { augFormattedRows: augFormattedRows, gridSelectionFns: gridSelectionFns, selectModel: selectModel, unwrappedStyles: unwrappedStyles, combinedPipelineOutput: combinedPipelineOutput, editModel: editModel, getInputStrSubmitCallback: getInputStrSubmitCallback, renderPrefixCells: renderPrefixCells, additionalColIndexOffset: reorder ? 1 : 0 }));
110
+ return (_jsx(ReorderHandleCell, { rowId: augRow.id, ariaRowIndex: augRow.prePaginationIndex + 2, disabled: filteringOccurring || sortingOccurring || displayRows.length <= 1, reorderCallback: reorderCallback, styles: reorderStyles, keyboardStartCallback: () => setDragee(augRow.id), suppressKeyboardClick: drageeState !== null }));
111
+ }, [
112
+ displayRows.length,
113
+ drageeState,
114
+ filteringOccurring,
115
+ reorder,
116
+ reorderStyles,
117
+ setDragee,
118
+ sortingOccurring,
119
+ ]);
120
+ const bodyRows = (_jsx(BodyRows, { augFormattedRows: augFormattedRows, gridSelectionFns: gridSelectionFns, selectModel: selectModel, unwrappedStyles: unwrappedStyles, combinedPipelineOutput: combinedPipelineOutput, editModel: editModel, getInputStrSubmitCallback: getInputStrSubmitCallback, renderPrefixCells: renderPrefixCells, additionalColIndexOffset: reorder ? 1 : 0, additionalRowStyles: additionalBodyRowStyles }));
59
121
  const prefixHeader = reorder ? _jsx(ReorderHeaderCell, {}) : null;
60
122
  return (_jsx(InternalGrid, { gridProps: props, hooks: {
61
123
  pipelineOutput: combinedPipelineOutput,
62
124
  selectFns: gridSelectionFns,
63
125
  unwrappedStyles,
64
- }, slots: { colHeaderCells, bodyRows, prefixHeader } }));
126
+ }, slots: { colHeaderCells, bodyRows, prefixHeader }, classes: {
127
+ headerRow: additionalHeaderRowStyles,
128
+ } }));
65
129
  };
66
130
  export default GridPro;
@@ -1,12 +1,13 @@
1
1
  import { FC } from "react";
2
- import { RowId } from "../";
3
- import { ReorderStyleModel } from "./types";
2
+ import { ReorderStyles, RowId } from "../";
4
3
  export type ReorderHandleCellProps = {
5
4
  rowId: RowId;
6
5
  ariaRowIndex: number;
7
6
  reorderCallback: (destIndex: number) => void;
7
+ keyboardStartCallback: () => void;
8
8
  disabled: boolean;
9
- styleModel?: ReorderStyleModel;
9
+ suppressKeyboardClick: boolean;
10
+ styles: ReorderStyles;
10
11
  };
11
12
  declare const ReorderHandleCell: FC<ReorderHandleCellProps>;
12
13
  export default ReorderHandleCell;
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { useCallback, useMemo } from "react";
2
+ import { useCallback, } from "react";
3
3
  import HorizontalGrip from "../assets/HorizontalGrip";
4
4
  import regDragCleanup from "../util/regDragCleanup";
5
5
  import classNames from "classnames";
@@ -12,16 +12,7 @@ function dropTargetRefValuesEqual(first, second) {
12
12
  }
13
13
  return first.index === second.index && first.upper === second.upper;
14
14
  }
15
- const ReorderHandleCell = ({ rowId, ariaRowIndex, disabled, reorderCallback, styleModel, }) => {
16
- const intDraggedRowClasses = useMemo(() => (styleModel === null || styleModel === void 0 ? void 0 : styleModel.draggedRowClasses) || ["rbdg-reorder-dragged-row"], [styleModel === null || styleModel === void 0 ? void 0 : styleModel.draggedRowClasses]);
17
- const intDraggedRowPredecessorClasses = useMemo(() => (styleModel === null || styleModel === void 0 ? void 0 : styleModel.draggedRowPredecessorClasses) || [
18
- "rbdg-reorder-dragged-row-pred",
19
- ], [styleModel === null || styleModel === void 0 ? void 0 : styleModel.draggedRowPredecessorClasses]);
20
- const intTopBorderRowClasses = useMemo(() => (styleModel === null || styleModel === void 0 ? void 0 : styleModel.topBorderRowClasses) || ["rbdg-reorder-above-drag-target-row"], [styleModel === null || styleModel === void 0 ? void 0 : styleModel.topBorderRowClasses]);
21
- const intBottomBorderRowClasses = useMemo(() => (styleModel === null || styleModel === void 0 ? void 0 : styleModel.bottomBorderRowClasses) || [
22
- "rbdg-reorder-below-drag-target-row",
23
- ], [styleModel === null || styleModel === void 0 ? void 0 : styleModel.bottomBorderRowClasses]);
24
- const intGhostTableClasses = useMemo(() => (styleModel === null || styleModel === void 0 ? void 0 : styleModel.ghostDivClasses) || ["border", "rbdg-drag-ghost"], [styleModel === null || styleModel === void 0 ? void 0 : styleModel.ghostDivClasses]);
15
+ const ReorderHandleCell = ({ rowId, ariaRowIndex, disabled, reorderCallback, styles: { draggedRowClasses, draggedRowPredecessorClasses, topBorderRowClasses, bottomBorderRowClasses, ghostDivClasses, }, keyboardStartCallback, suppressKeyboardClick, }) => {
25
16
  const onPointerDown = useCallback((event) => {
26
17
  const target = event.target;
27
18
  const { pointerId, clientX, clientY, button } = event;
@@ -30,7 +21,7 @@ const ReorderHandleCell = ({ rowId, ariaRowIndex, disabled, reorderCallback, sty
30
21
  }
31
22
  function createDragGhost() {
32
23
  const ghostDiv = document.createElement("div");
33
- ghostDiv.className = intGhostTableClasses.join(" ");
24
+ ghostDiv.className = ghostDivClasses.join(" ");
34
25
  ghostDiv.style.left = `${clientX}px`;
35
26
  ghostDiv.style.top = `${clientY}px`;
36
27
  ghostDiv.textContent = `ID: ${rowId}`;
@@ -56,17 +47,17 @@ const ReorderHandleCell = ({ rowId, ariaRowIndex, disabled, reorderCallback, sty
56
47
  .trim();
57
48
  }
58
49
  function applyDrageeStyles() {
59
- applyRowClassesFromOrig(drageeTrIndex, intDraggedRowClasses);
50
+ applyRowClassesFromOrig(drageeTrIndex, draggedRowClasses);
60
51
  if (drageeTrIndex - 1 === -1) {
61
52
  headTr.className = [
62
53
  origHeadTrClasses,
63
- intDraggedRowPredecessorClasses.join(" "),
54
+ draggedRowPredecessorClasses.join(" "),
64
55
  ]
65
56
  .join(" ")
66
57
  .trim();
67
58
  return;
68
59
  }
69
- applyRowClassesFromOrig(drageeTrIndex - 1, intDraggedRowPredecessorClasses);
60
+ applyRowClassesFromOrig(drageeTrIndex - 1, draggedRowPredecessorClasses);
70
61
  }
71
62
  applyDrageeStyles();
72
63
  const baseClassesByIndex = trs.map((tr) => tr.className);
@@ -129,24 +120,21 @@ const ReorderHandleCell = ({ rowId, ariaRowIndex, disabled, reorderCallback, sty
129
120
  }
130
121
  function applyDragStyles(trIndex, upper) {
131
122
  if (upper) {
132
- applyRowClassesFromBase(trIndex, intBottomBorderRowClasses);
123
+ applyRowClassesFromBase(trIndex, bottomBorderRowClasses);
133
124
  }
134
125
  if (upper && trIndex - 1 >= 0) {
135
- applyRowClassesFromBase(trIndex - 1, intTopBorderRowClasses);
126
+ applyRowClassesFromBase(trIndex - 1, topBorderRowClasses);
136
127
  }
137
128
  if (upper && trIndex - 1 === -1) {
138
- headTr.className = [
139
- baseHeadTrClasses,
140
- intTopBorderRowClasses.join(" "),
141
- ]
129
+ headTr.className = [baseHeadTrClasses, topBorderRowClasses.join(" ")]
142
130
  .join(" ")
143
131
  .trim();
144
132
  }
145
133
  if (!upper) {
146
- applyRowClassesFromBase(trIndex, intTopBorderRowClasses);
134
+ applyRowClassesFromBase(trIndex, topBorderRowClasses);
147
135
  }
148
136
  if (!upper && trIndex + 1 < trs.length) {
149
- applyRowClassesFromBase(trIndex + 1, intBottomBorderRowClasses);
137
+ applyRowClassesFromBase(trIndex + 1, bottomBorderRowClasses);
150
138
  }
151
139
  }
152
140
  function updateRowStyles(currentDropTargetVal) {
@@ -204,16 +192,27 @@ const ReorderHandleCell = ({ rowId, ariaRowIndex, disabled, reorderCallback, sty
204
192
  onContextMenu,
205
193
  });
206
194
  }, [
207
- intBottomBorderRowClasses,
208
- intDraggedRowClasses,
209
- intDraggedRowPredecessorClasses,
210
- intGhostTableClasses,
211
- intTopBorderRowClasses,
195
+ bottomBorderRowClasses,
196
+ draggedRowClasses,
197
+ draggedRowPredecessorClasses,
198
+ ghostDivClasses,
199
+ topBorderRowClasses,
212
200
  reorderCallback,
213
201
  rowId,
214
202
  ]);
215
203
  const label = `Reorder Row ${ariaRowIndex}`;
216
- return (_jsx("th", { "aria-colindex": 1, children: _jsx("button", { onClick: (event) => event.stopPropagation(), "aria-label": label, title: label, onPointerDown: onPointerDown, className: classNames("rbdg-draggable-container", "rbdg-reorder-container", "rbdg-plain-icon-button", {
204
+ const onClick = (event) => {
205
+ event.stopPropagation();
206
+ if (event.detail === 0) {
207
+ keyboardStartCallback();
208
+ }
209
+ };
210
+ const onKeydown = (event) => {
211
+ if (suppressKeyboardClick) {
212
+ event.preventDefault();
213
+ }
214
+ };
215
+ return (_jsx("th", { "aria-colindex": 1, children: _jsx("button", { tabIndex: 0, onClick: onClick, "aria-label": label, title: label, onPointerDown: onPointerDown, onKeyDown: onKeydown, className: classNames("rbdg-draggable-container", "rbdg-reorder-container", "rbdg-plain-icon-button", {
217
216
  "bs-btn-disabled": disabled,
218
217
  }), disabled: disabled, children: _jsx(HorizontalGrip, { className: "rbdg-draggable-icon" }) }) }));
219
218
  };
@@ -10,3 +10,18 @@ export interface ReorderStyleModel {
10
10
  bottomBorderRowClasses?: string[];
11
11
  ghostDivClasses?: string[];
12
12
  }
13
+ export type ReorderStyles = Required<ReorderStyleModel>;
14
+ export interface KeyboardReorderState {
15
+ rowId: RowId;
16
+ destIndex: number;
17
+ }
18
+ export type ActiveKeyboardReorderState = KeyboardReorderState & {
19
+ moveToPrevTarget: () => void;
20
+ moveToNextTarget: () => void;
21
+ clearState: () => void;
22
+ drageeIndex: number;
23
+ };
24
+ export interface UseKeyboardReorderOutput {
25
+ drageeState: ActiveKeyboardReorderState | null;
26
+ setDragee: (rowId: RowId) => void;
27
+ }
@@ -0,0 +1,4 @@
1
+ import { RowId } from "../";
2
+ import { UseKeyboardReorderOutput } from "./types";
3
+ declare const useKeyboardReorder: (displayRowIds: RowId[]) => UseKeyboardReorderOutput;
4
+ export default useKeyboardReorder;
@@ -0,0 +1,84 @@
1
+ import { useCallback, useMemo, useState } from "react";
2
+ import trueModulo from "../../grid/util/trueModulo";
3
+ const useKeyboardReorder = (displayRowIds) => {
4
+ const [state, setState] = useState(null);
5
+ // A change the displayed rows can suddenly make the existing state invalid.
6
+ // To cope with this possibility, the hook checks the validity of the state on
7
+ // each render and defaults to null when the state is invalid.
8
+ // Consumers of the hook can then call setState (generally via user
9
+ // interaction) to set the state back to a valid value.
10
+ const effectiveState = useMemo(() => {
11
+ if (displayRowIds.length <= 1 || state === null) {
12
+ return null;
13
+ }
14
+ const drageeIndex = displayRowIds.findIndex((id) => state.rowId === id);
15
+ if (drageeIndex < 0) {
16
+ return null;
17
+ }
18
+ if (drageeIndex === state.destIndex ||
19
+ drageeIndex + 1 === state.destIndex) {
20
+ return null;
21
+ }
22
+ return state;
23
+ }, [displayRowIds, state]);
24
+ const setDragee = useCallback((rowId) => {
25
+ const foundIndex = displayRowIds.findIndex((id) => rowId === id);
26
+ if (foundIndex < 0) {
27
+ return;
28
+ }
29
+ // Shouldn't happen under normal circumstances dye to the button being
30
+ // disabled in the UI. Not sure if some sort of
31
+ // race condition is possible with React to get this situation to occur.
32
+ if (displayRowIds.length < 2) {
33
+ return;
34
+ }
35
+ if (foundIndex + 2 > displayRowIds.length) {
36
+ setState({
37
+ rowId,
38
+ destIndex: foundIndex - 1,
39
+ });
40
+ return;
41
+ }
42
+ setState({
43
+ rowId,
44
+ destIndex: foundIndex + 2,
45
+ });
46
+ }, [displayRowIds]);
47
+ const output = useMemo(() => {
48
+ if (effectiveState === null) {
49
+ return {
50
+ drageeState: null,
51
+ setDragee,
52
+ };
53
+ }
54
+ const moveToPrevTarget = () => {
55
+ let newIndex = trueModulo(effectiveState.destIndex - 1, displayRowIds.length + 1);
56
+ const foundIndex = displayRowIds.findIndex((id) => effectiveState.rowId === id);
57
+ if (newIndex === foundIndex + 1) {
58
+ newIndex = trueModulo(newIndex - 2, displayRowIds.length + 1);
59
+ }
60
+ setState({
61
+ rowId: effectiveState.rowId,
62
+ destIndex: newIndex,
63
+ });
64
+ };
65
+ const moveToNextTarget = () => {
66
+ let newIndex = trueModulo(effectiveState.destIndex + 1, displayRowIds.length + 1);
67
+ const foundIndex = displayRowIds.findIndex((id) => effectiveState.rowId === id);
68
+ if (newIndex === foundIndex) {
69
+ newIndex = trueModulo(newIndex + 2, displayRowIds.length + 1);
70
+ }
71
+ setState({
72
+ rowId: effectiveState.rowId,
73
+ destIndex: newIndex,
74
+ });
75
+ };
76
+ return {
77
+ drageeState: Object.assign(Object.assign({}, effectiveState), { moveToPrevTarget,
78
+ moveToNextTarget, clearState: () => setState(null), drageeIndex: displayRowIds.findIndex((id) => effectiveState.rowId === id) }),
79
+ setDragee,
80
+ };
81
+ }, [displayRowIds, effectiveState, setDragee]);
82
+ return output;
83
+ };
84
+ export default useKeyboardReorder;
@@ -0,0 +1,3 @@
1
+ import { ActiveKeyboardReorderState, ReorderCallback } from "./types";
2
+ declare const useKeyboardReorderListener: (state: ActiveKeyboardReorderState | null, reorderCallback: ReorderCallback | undefined) => (event: KeyboardEvent) => void;
3
+ export default useKeyboardReorderListener;
@@ -0,0 +1,26 @@
1
+ import { useCallback } from "react";
2
+ const useKeyboardReorderListener = (state, reorderCallback) => {
3
+ return useCallback((event) => {
4
+ if (state === null || reorderCallback === undefined) {
5
+ return;
6
+ }
7
+ const { moveToPrevTarget, moveToNextTarget, clearState, rowId, destIndex, } = state;
8
+ if (event.key === "ArrowUp") {
9
+ moveToPrevTarget();
10
+ return;
11
+ }
12
+ if (event.key === "ArrowDown") {
13
+ moveToNextTarget();
14
+ return;
15
+ }
16
+ if (event.key === " " || event.key === "Enter") {
17
+ reorderCallback(rowId, destIndex);
18
+ clearState();
19
+ return;
20
+ }
21
+ if (event.key === "Escape") {
22
+ clearState();
23
+ }
24
+ }, [reorderCallback, state]);
25
+ };
26
+ export default useKeyboardReorderListener;
@@ -0,0 +1,3 @@
1
+ import { ReorderStyleModel, ReorderStyles } from "./types";
2
+ declare const useReorderStyles: (styleModel: ReorderStyleModel | undefined) => ReorderStyles;
3
+ export default useReorderStyles;
@@ -0,0 +1,20 @@
1
+ import { useMemo } from "react";
2
+ const useReorderStyles = (styleModel) => {
3
+ const draggedRowClasses = useMemo(() => (styleModel === null || styleModel === void 0 ? void 0 : styleModel.draggedRowClasses) || ["rbdg-reorder-dragged-row"], [styleModel === null || styleModel === void 0 ? void 0 : styleModel.draggedRowClasses]);
4
+ const draggedRowPredecessorClasses = useMemo(() => (styleModel === null || styleModel === void 0 ? void 0 : styleModel.draggedRowPredecessorClasses) || [
5
+ "rbdg-reorder-dragged-row-pred",
6
+ ], [styleModel === null || styleModel === void 0 ? void 0 : styleModel.draggedRowPredecessorClasses]);
7
+ const topBorderRowClasses = useMemo(() => (styleModel === null || styleModel === void 0 ? void 0 : styleModel.topBorderRowClasses) || ["rbdg-reorder-above-drag-target-row"], [styleModel === null || styleModel === void 0 ? void 0 : styleModel.topBorderRowClasses]);
8
+ const bottomBorderRowClasses = useMemo(() => (styleModel === null || styleModel === void 0 ? void 0 : styleModel.bottomBorderRowClasses) || [
9
+ "rbdg-reorder-below-drag-target-row",
10
+ ], [styleModel === null || styleModel === void 0 ? void 0 : styleModel.bottomBorderRowClasses]);
11
+ const ghostDivClasses = useMemo(() => (styleModel === null || styleModel === void 0 ? void 0 : styleModel.ghostDivClasses) || ["border", "rbdg-drag-ghost"], [styleModel === null || styleModel === void 0 ? void 0 : styleModel.ghostDivClasses]);
12
+ return {
13
+ draggedRowClasses,
14
+ draggedRowPredecessorClasses,
15
+ topBorderRowClasses,
16
+ bottomBorderRowClasses,
17
+ ghostDivClasses,
18
+ };
19
+ };
20
+ export default useReorderStyles;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@absreim/react-bootstrap-data-grid-pro",
3
3
  "description": "Pro Version of data grid UI component for use with web apps using React and Bootstrap",
4
- "version": "2.6.1",
4
+ "version": "2.7.0",
5
5
  "license": "Commercial. See LICENSE file.",
6
6
  "author": "Brook Li",
7
7
  "homepage": "https://react-bootstrap-data-grid.vercel.app/",