react-kanban-kit
Version:
A flexible and customizable Kanban board component for React applications, built with TypeScript and modern drag-and-drop functionality powered by Atlassian's pragmatic-drag-and-drop.
232 lines (231 loc) • 7.13 kB
JavaScript
import { useRef, useState, useCallback, useEffect } from "react";
import { withPrefix } from "../../utils/getPrefix.es.js";
import { dropTargetForElements, draggable } from "../../node_modules/@atlaskit/pragmatic-drag-and-drop/dist/esm/adapter/element-adapter.es.js";
import { attachClosestEdge, extractClosestEdge } from "../../node_modules/@atlaskit/pragmatic-drag-and-drop-hitbox/dist/esm/closest-edge.es.js";
import { setCustomNativeDragPreview } from "../../node_modules/@atlaskit/pragmatic-drag-and-drop/dist/esm/public-utils/element/custom-native-drag-preview/set-custom-native-drag-preview.es.js";
import { preserveOffsetOnSource } from "../../node_modules/@atlaskit/pragmatic-drag-and-drop/dist/esm/public-utils/element/custom-native-drag-preview/preserve-offset-on-source.es.js";
import { combine } from "../../node_modules/@atlaskit/pragmatic-drag-and-drop/dist/esm/public-utils/combine.es.js";
import { autoScrollForElements } from "../../node_modules/@atlaskit/pragmatic-drag-and-drop-auto-scroll/dist/esm/entry-point/element.es.js";
import { useKanbanContext } from "../../context/KanbanContext.es.js";
const isCardData = (data) => data.type === "card";
const isColumnData = (data) => data.type === "column";
const idle = {
type: "idle"
};
const useColumnDnd = (data, index, items, onColumnDndStateChange, allowColumnDrag, hasCustomPreview) => {
const {
viewOnly
} = useKanbanContext();
const isColumnDraggable = !viewOnly && allowColumnDrag !== false && data.isDraggable !== false;
const headerRef = useRef(null);
const outerFullHeightRef = useRef(null);
const innerRef = useRef(null);
const [state, setState] = useState(idle);
const cardOverShadowCount = state.type === "is-card-over" && !state.isOverChildCard ? 1 : 0;
const totalTasksCount = data.totalChildrenCount + cardOverShadowCount;
const setIsCardOver = useCallback(({
data: data2,
location
}) => {
const innerMost = location.current.dropTargets[0];
const isOverChildCard = Boolean(innerMost == null ? void 0 : innerMost.data["card-drop-target"]);
const proposed = {
type: "is-card-over",
dragging: data2.rect,
isOverChildCard
};
setState((current) => {
if (current.type === proposed.type && current.isOverChildCard === proposed.isOverChildCard && current.dragging === proposed.dragging) {
return current;
}
return proposed;
});
}, []);
useEffect(() => {
const outer = outerFullHeightRef.current;
const inner = innerRef.current;
const header = headerRef.current;
if (!outer || !inner || !header)
return;
const scroller = outer.querySelector(`.${withPrefix("column-content-list")}`);
const columnData = {
type: "column",
columnId: data.id,
column: data,
index
};
return combine(draggable({
element: header,
getInitialData: () => columnData,
canDrag: () => isColumnDraggable,
onGenerateDragPreview({
location,
nativeSetDragImage
}) {
const rect = outer.getBoundingClientRect();
setCustomNativeDragPreview({
nativeSetDragImage,
getOffset: preserveOffsetOnSource({
element: outer,
input: location.current.input
}),
render({
container
}) {
if (hasCustomPreview) {
setState({
type: "preview",
container,
dragging: rect
});
} else {
const clone = outer.cloneNode(true);
clone.style.width = `${rect.width}px`;
clone.style.height = `${rect.height}px`;
clone.style.transform = "rotate(4deg)";
clone.style.boxShadow = "0 8px 24px rgba(0,0,0,0.15)";
container.appendChild(clone);
}
}
});
},
onDragStart() {
setState({
type: "is-dragging"
});
},
onDrop() {
setState(idle);
}
}), dropTargetForElements({
element: outer,
getData({
input,
element
}) {
return attachClosestEdge(columnData, {
input,
element,
allowedEdges: ["left", "right"]
});
},
canDrop({
source
}) {
return isCardData(source.data) || isColumnData(source.data);
},
getIsSticky: () => true,
onDragStart({
source,
location
}) {
if (isCardData(source.data)) {
setIsCardOver({
data: source.data,
location
});
}
},
onDragEnter({
source,
location,
self
}) {
if (isCardData(source.data)) {
setIsCardOver({
data: source.data,
location
});
return;
}
if (isColumnData(source.data) && source.data.columnId !== data.id) {
const closestEdge = extractClosestEdge(self.data);
if (!closestEdge)
return;
const rect = outer.getBoundingClientRect();
setState({
type: "is-column-over",
closestEdge,
rect
});
}
},
onDrag({
source,
self
}) {
if (!isColumnData(source.data))
return;
if (source.data.columnId === data.id)
return;
const closestEdge = extractClosestEdge(self.data);
if (!closestEdge)
return;
setState((current) => {
if (current.type === "is-column-over" && current.closestEdge === closestEdge) {
return current;
}
const rect = outer.getBoundingClientRect();
return {
type: "is-column-over",
closestEdge,
rect
};
});
},
onDropTargetChange({
source,
location
}) {
if (isCardData(source.data)) {
setIsCardOver({
data: source.data,
location
});
}
},
onDragLeave({
source
}) {
if (isColumnData(source.data) && source.data.columnId === data.id) {
setState({
type: "is-dragging-and-left-self"
});
return;
}
setState(idle);
},
onDrop() {
setState(idle);
}
}), autoScrollForElements({
canScroll({
source
}) {
return isCardData(source.data);
},
getConfiguration: () => ({
maxScrollSpeed: "standard"
}),
element: scroller
}));
}, [data, index, items == null ? void 0 : items.length, isColumnDraggable, hasCustomPreview]);
useEffect(() => {
onColumnDndStateChange == null ? void 0 : onColumnDndStateChange({
state,
column: data
});
}, [state, onColumnDndStateChange]);
return {
headerRef,
outerFullHeightRef,
innerRef,
state,
cardOverShadowCount,
totalTasksCount
};
};
export {
useColumnDnd
};
//# sourceMappingURL=useColumnDnd.es.js.map