@1771technologies/lytenyte-pro
Version:
Blazingly fast headless React data grid with 100s of features.
128 lines (127 loc) • 5.58 kB
JavaScript
import { jsx as _jsx } from "react/jsx-runtime";
import { forwardRef, memo, useEffect, useRef, useState } from "react";
import { usePillRoot } from "./root.context.js";
import { usePillRow } from "./pill-row.context.js";
import { dragX, dragY, getDragData, useCombinedRefs, useSelector, } from "@1771technologies/lytenyte-core/internal";
import { getFocusables } from "@1771technologies/lytenyte-shared";
function ContainerBase(props, forwarded) {
const { setCloned, orientation, rows, movedRef } = usePillRoot();
const { row } = usePillRow();
const [over, setOver] = useState(false);
const x = useSelector(dragX);
const y = useSelector(dragY);
const [container, setContainer] = useState(null);
const wasOver = useRef(false);
useEffect(() => {
if (!container)
return;
const ds = getDragData();
if (!ds?.pill?.data) {
setOver(false);
return;
}
const { item: dragged, id } = ds.pill.data;
const bb = container.getBoundingClientRect();
const isOver = bb.left < x && bb.right > x && bb.top < y && bb.bottom > y;
// The current drag is from the current row.
if (id === row.id) {
movedRef.current = !isOver ? { id: row.id, pillId: dragged.id } : null;
return;
}
const thisRow = rows.findIndex((x) => x.id === row.id);
const originalRow = rows[thisRow];
if (isOver) {
wasOver.current = true;
if (row.pills.find((x) => x.id === dragged.id))
return;
if (!row.accepts || !dragged.tags || dragged.tags.every((x) => !row.accepts?.includes(x)))
return;
setOver(true);
const thisRow = rows.findIndex((x) => x.id === row.id);
const originalRow = rows[thisRow];
const newPills = [...originalRow.pills, { ...dragged, __temp: true }];
setCloned((prev) => {
if (!prev)
throw new Error("Can't call drag function without cloning nodes.");
const newPill = { ...prev[thisRow] };
newPill.pills = newPills;
const newDef = [...prev];
newDef[thisRow] = newPill;
return newDef;
});
}
else {
if (wasOver.current) {
setCloned((prev) => {
if (!prev)
return prev;
const next = [...prev];
next[thisRow] = structuredClone(originalRow);
return next;
});
wasOver.current = false;
}
setOver(false);
// The original row has the dragged pill as an ID so we can safely skip it.
if (originalRow.pills.find((x) => x.id === dragged.id))
return;
if (!row.pills.find((x) => x.id === dragged.id))
return;
const newPills = row.pills.filter((x) => x.id !== dragged.id);
const newRow = { ...row, pills: newPills };
setCloned((prev) => {
if (!prev)
throw new Error("Can't call drag function without cloning nodes.");
const newDef = [...prev];
newDef[thisRow] = newRow;
return newDef;
});
}
}, [container, movedRef, over, row, rows, setCloned, x, y]);
const combined = useCombinedRefs(forwarded, setContainer);
const rtl = useRef(null);
return (_jsx("div", { ...props, ref: combined, "data-ln-over": over, "data-ln-orientation": orientation, "data-ln-pill-container": true, "data-ln-pill-type": row.type, tabIndex: 0, onKeyDown: (ev) => {
if (rtl.current == null) {
const dir = getComputedStyle(ev.currentTarget).direction;
rtl.current = dir === "rtl";
}
const start = rtl.current ? "ArrowRight" : "ArrowLeft";
const end = rtl.current ? "ArrowLeft" : "ArrowRight";
const next = orientation === "horizontal" ? end : "ArrowDown";
const prev = orientation === "horizontal" ? start : "ArrowUp";
if (ev.key === next) {
const focusables = getFocusables(ev.currentTarget);
if (!focusables.length)
return;
const active = focusables.indexOf(document.activeElement);
if (active === -1 || active === focusables.length - 1) {
focusables[0].focus();
}
else {
focusables[active + 1].focus();
}
ev.preventDefault();
}
else if (ev.key === prev) {
const focusables = getFocusables(ev.currentTarget);
if (!focusables.length)
return;
const active = focusables.indexOf(document.activeElement);
if (active === -1 || active === 0) {
focusables.at(-1)?.focus();
}
else {
focusables[active - 1].focus();
}
ev.preventDefault();
}
}, onDrop: (e) => {
e.preventDefault();
e.stopPropagation();
}, onDragOver: (e) => {
e.dataTransfer.dropEffect = "move";
e.preventDefault();
e.stopPropagation();
} }));
}
export const PillContainer = memo(forwardRef(ContainerBase));