UNPKG

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
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