Skip to content

Commit

Permalink
handle children gracefully while dragging
Browse files Browse the repository at this point in the history
  • Loading branch information
bsholmes committed Jan 8, 2024
1 parent 7a8fbb4 commit 5048376
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 27 deletions.
59 changes: 42 additions & 17 deletions src/components/Table/GridTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,13 @@ export function GridTable<R extends Kinded, X extends Only<GridTableXss, X> = an
[props.api],
);

const [draggedRow, _setDraggedRow] = useState<GridDataRow<R> | undefined>(undefined);
const draggedRowRef = useRef(draggedRow);
const setDraggedRow = (row: GridDataRow<R> | undefined) => {
draggedRowRef.current = row;
_setDraggedRow(row);
};

const style = resolveStyles(maybeStyle);
const { tableState } = api;

Expand Down Expand Up @@ -293,14 +300,24 @@ export function GridTable<R extends Kinded, X extends Only<GridTableXss, X> = an
return clientY > rect.top + pt + ((rect.height - pb) / 2);
}

// allows us to unset children and grandchildren, etc.
function recursiveSetDraggedOver(rows: GridDataRow<R>[], draggedOver: DraggedOver) {
rows.forEach((r) => {
tableState.maybeSetRowDraggedOver(r.id, draggedOver);
if(r.children) {
recursiveSetDraggedOver(r.children, draggedOver);
}
});
}

function onDragStart (row: GridDataRow<R>, evt: DragEventType) {
if(!row.draggable || !droppedCallback) {
return;
}

evt.dataTransfer.effectAllowed = "move";
evt.dataTransfer.dropEffect = "move";
evt.dataTransfer.setData("text/plain", JSON.stringify({ row }));
setDraggedRow(row);
};

const onDragEnd = (row: GridDataRow<R>, evt: DragEventType) => {
Expand All @@ -310,9 +327,7 @@ export function GridTable<R extends Kinded, X extends Only<GridTableXss, X> = an

evt.preventDefault();
evt.dataTransfer.clearData();
rows.forEach((r) => {
tableState.setRowDraggedOver(r.id, DraggedOver.None);
});
recursiveSetDraggedOver(rows, DraggedOver.None);
};

const onDrop = (row: GridDataRow<R>, evt: DragEventType) => {
Expand All @@ -323,9 +338,8 @@ export function GridTable<R extends Kinded, X extends Only<GridTableXss, X> = an
evt.preventDefault();
evt.dataTransfer.clearData();
if (droppedCallback) {
rows.forEach((r) => {r
tableState.setRowDraggedOver(r.id, DraggedOver.None);
});
recursiveSetDraggedOver(rows, DraggedOver.None);

try {
const draggedRowData = JSON.parse(evt.dataTransfer.getData("text/plain")).row;

Expand All @@ -349,15 +363,21 @@ export function GridTable<R extends Kinded, X extends Only<GridTableXss, X> = an

evt.preventDefault();

evt.cancelable

// set flags for css spacer
rows.forEach((r) => {r
tableState.setRowDraggedOver(r.id, DraggedOver.None);
});
recursiveSetDraggedOver(rows, DraggedOver.None);

// determine above or below
const isBelow = isCursorBelowMidpoint(evt.currentTarget, evt.clientY);
if(draggedRowRef.current) {

if(draggedRowRef.current.id === row.id) {
return;
}

tableState.setRowDraggedOver(row.id, isBelow ? DraggedOver.Below : DraggedOver.Above);
// determine above or below
const isBelow = isCursorBelowMidpoint(evt.currentTarget, evt.clientY);
tableState.maybeSetRowDraggedOver(row.id, isBelow ? DraggedOver.Below : DraggedOver.Above, draggedRowRef.current);
}
};

const onDragOver = (row: GridDataRow<R>, evt: DragEventType) => {
Expand All @@ -367,9 +387,15 @@ export function GridTable<R extends Kinded, X extends Only<GridTableXss, X> = an

evt.preventDefault();

const isBelow = isCursorBelowMidpoint(evt.currentTarget, evt.clientY);

tableState.setRowDraggedOver(row.id, isBelow ? DraggedOver.Below : DraggedOver.Above);
if(draggedRowRef.current) {
if(draggedRowRef.current.id === row.id) {
return;
}

// continuously determine above or below
const isBelow = isCursorBelowMidpoint(evt.currentTarget, evt.clientY);
tableState.maybeSetRowDraggedOver(row.id, isBelow ? DraggedOver.Below : DraggedOver.Above, draggedRowRef.current);
}
};

// Flatten, hide-if-filtered, hide-if-collapsed, and component-ize the sorted rows.
Expand All @@ -391,7 +417,6 @@ export function GridTable<R extends Kinded, X extends Only<GridTableXss, X> = an

// Get the flat list or rows from the header down...
visibleRows.forEach((rs) => {

const row = (
<Row
key={rs.key}
Expand Down
6 changes: 2 additions & 4 deletions src/components/Table/components/Row.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ import {
zIndices,
} from "src/components/Table/utils/utils";
import { Css, Palette } from "src/Css";
import { useBreakpoint } from "src/hooks";
import { AnyObject } from "src/types";
import { isFunction } from "src/utils";
import { Icon } from "src";
Expand Down Expand Up @@ -74,7 +73,6 @@ function RowImpl<R extends Kinded, S>(props: RowProps<R>): ReactElement {
} = props;

const { tableState } = useContext(TableStateContext);
const { sm } = useBreakpoint();
// We're wrapped in observer, so can access these without useComputeds
const { api, visibleColumns: columns } = tableState;
const { row, api: rowApi, isActive, isKept: isKeptRow, isLastKeptRow, level } = rs;
Expand Down Expand Up @@ -120,8 +118,8 @@ function RowImpl<R extends Kinded, S>(props: RowProps<R>): ReactElement {
[`:hover > .${revealOnRowHoverClass} > *`]: Css.visible.$,
},
...(isLastKeptRow && Css.addIn("&>*", style.keptLastRowCss).$),
...(rs.isDraggedOver === DraggedOver.Above && Css.add("paddingTop", "50px").$),
...(rs.isDraggedOver === DraggedOver.Below && Css.add("paddingBottom", "50px").$),
...(rs.isDraggedOver === DraggedOver.Above && Css.add("paddingTop", "35px").$),
...(rs.isDraggedOver === DraggedOver.Below && Css.add("paddingBottom", "35px").$),
};

let currentColspan = 1;
Expand Down
20 changes: 19 additions & 1 deletion src/components/Table/utils/RowStates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,26 @@ export class RowStates<R extends Kinded> {
return rs;
}

setRowDraggedOver(id: string, draggedOver: DraggedOver): void {
maybeSetRowDraggedOver(id: string, draggedOver: DraggedOver, requireSameParentRow: GridDataRow<R> | undefined = undefined): void {
const rs = this.get(id);

if(requireSameParentRow) {
const requireParentRowState = this.get(requireSameParentRow.id);
if(requireParentRowState.parent?.row?.id !== rs.parent?.row?.id) return;
}

// if this is an expanded parent and draggedOver is Below then we want to set this on this rows bottom-most child
if(!rs.collapsed && rs.children && rs.children?.length > 0 && draggedOver === DraggedOver.Below) {
let rowState = rs;
// recursively find the bottom-most child
while(rowState.children && rowState.children?.length > 0) {
rowState = rowState.children[rowState.children.length - 1];
}

rowState.isDraggedOver = draggedOver;
return;
}

// this allows a single-row re-render
rs.isDraggedOver = draggedOver;
}
Expand Down
7 changes: 2 additions & 5 deletions src/components/Table/utils/TableState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,11 +253,8 @@ export class TableState<R extends Kinded> {
this.rowStates.delete(ids);
}

setRowDraggedOver(id: string, draggedOver: DraggedOver): void {
// if we do rowStates.setRows here all of the rows will re-render

// this.rows[index] can be undefined here?
this.rowStates.setRowDraggedOver(id, draggedOver);
maybeSetRowDraggedOver(id: string, draggedOver: DraggedOver, requireSameParentRow: GridDataRow<R> | undefined = undefined): void {
this.rowStates.maybeSetRowDraggedOver(id, draggedOver, requireSameParentRow);
}
}

Expand Down

0 comments on commit 5048376

Please sign in to comment.