@blocknote/react
Version:
A "Notion-style" block-based extensible text editor built on top of Prosemirror and Tiptap.
184 lines (170 loc) • 4.28 kB
text/typescript
import { offset, useFloating, useTransitionStyles } from "@floating-ui/react";
import { useEffect, useMemo } from "react";
function getBoundingClientRectRow(
referencePosCell: DOMRect | null,
referencePosTable: DOMRect | null,
draggingState?: {
draggedCellOrientation: "row" | "col";
mousePos: number;
},
) {
if (draggingState && draggingState.draggedCellOrientation === "row") {
return new DOMRect(
referencePosTable!.x,
draggingState.mousePos,
referencePosTable!.width,
0,
);
}
return new DOMRect(
referencePosTable!.x,
referencePosCell!.y,
referencePosTable!.width,
referencePosCell!.height,
);
}
function getBoundingClientRectCol(
referencePosCell: DOMRect | null,
referencePosTable: DOMRect | null,
draggingState?: {
draggedCellOrientation: "row" | "col";
mousePos: number;
},
) {
if (draggingState && draggingState.draggedCellOrientation === "col") {
return new DOMRect(
draggingState.mousePos,
referencePosTable!.y,
0,
referencePosTable!.height,
);
}
return new DOMRect(
referencePosCell!.x,
referencePosTable!.y,
referencePosCell!.width,
referencePosTable!.height,
);
}
function getBoundingClientRectCell(referencePosCell: DOMRect | null) {
return new DOMRect(
referencePosCell!.x,
referencePosCell!.y,
referencePosCell!.width,
0,
);
}
function useTableHandlePosition(
orientation: "row" | "col" | "cell",
show: boolean,
referencePosCell: DOMRect | null,
referencePosTable: DOMRect | null,
draggingState?: {
draggedCellOrientation: "row" | "col";
mousePos: number;
},
): {
isMounted: boolean;
ref: (node: HTMLElement | null) => void;
style: React.CSSProperties;
} {
const { refs, update, context, floatingStyles } = useFloating({
open: show,
placement:
orientation === "row"
? "left"
: orientation === "col"
? "top"
: "bottom-end",
middleware: [
offset(
orientation === "row"
? -10
: orientation === "col"
? -12
: { mainAxis: 1, crossAxis: -1 },
),
],
});
const { isMounted, styles } = useTransitionStyles(context);
useEffect(() => {
update();
}, [referencePosCell, referencePosTable, update]);
useEffect(() => {
// Will be null on initial render when used in UI component controllers.
if (
referencePosCell === null ||
referencePosTable === null ||
// Ignore cell handle when dragging
(draggingState && orientation === "cell")
) {
return;
}
refs.setReference({
getBoundingClientRect: () => {
const fn =
orientation === "row"
? getBoundingClientRectRow
: orientation === "col"
? getBoundingClientRectCol
: getBoundingClientRectCell;
return fn(referencePosCell, referencePosTable, draggingState);
},
});
}, [draggingState, orientation, referencePosCell, referencePosTable, refs]);
return useMemo(
() => ({
isMounted: isMounted,
ref: refs.setFloating,
style: {
display: "flex",
...styles,
...floatingStyles,
},
}),
[floatingStyles, isMounted, refs.setFloating, styles],
);
}
export function useTableHandlesPositioning(
show: boolean,
referencePosCell: DOMRect | null,
referencePosTable: DOMRect | null,
draggingState?: {
draggedCellOrientation: "row" | "col";
mousePos: number;
},
): {
rowHandle: ReturnType<typeof useTableHandlePosition>;
colHandle: ReturnType<typeof useTableHandlePosition>;
cellHandle: ReturnType<typeof useTableHandlePosition>;
} {
const rowHandle = useTableHandlePosition(
"row",
show,
referencePosCell,
referencePosTable,
draggingState,
);
const colHandle = useTableHandlePosition(
"col",
show,
referencePosCell,
referencePosTable,
draggingState,
);
const cellHandle = useTableHandlePosition(
"cell",
show,
referencePosCell,
referencePosTable,
draggingState,
);
return useMemo(
() => ({
rowHandle,
colHandle,
cellHandle,
}),
[colHandle, rowHandle, cellHandle],
);
}