import * as React from "react"; import { FieldTitle, useResourceContext } from "ra-core"; import { GripVertical } from "lucide-react"; import { Switch } from "@/components/ui/switch"; import { cn } from "@/lib/utils"; /** * A toggleable field item with drag-and-drop reordering, used by ColumnsSelector * * @internal */ export const FieldToggle = (props: FieldToggleProps) => { const { selected, label, onToggle, onMove, source, index } = props; const resource = useResourceContext(); const dropIndex = React.useRef(null); const x = React.useRef(null); const y = React.useRef(null); const handleDocumentDragOver = React.useCallback((event: DragEvent) => { x.current = event.clientX; y.current = event.clientY; }, []); const handleDragStart = () => { document.addEventListener( "dragover", handleDocumentDragOver as EventListener, ); }; const handleDrag = (event: React.DragEvent) => { // imperative DOM manipulations using the native Drag API const selectedItem = event.target as HTMLElement; selectedItem.dataset.dragActive = "true"; const list = selectedItem.closest("ul"); if (x.current == null || y.current == null) { return; } const elementAtDragCoordinates = document.elementFromPoint( x.current, y.current, ); let dropItem = elementAtDragCoordinates === null ? selectedItem : elementAtDragCoordinates.closest("li"); if (!dropItem) { return; } if (dropItem.classList.contains("dragIcon")) { const parent = dropItem.parentNode; if (parent instanceof HTMLElement) { dropItem = parent; } } if (dropItem === selectedItem) { return; } const dropItemParent = dropItem.parentNode; if ( list && dropItemParent instanceof HTMLElement && list === dropItemParent.closest("ul") ) { const dataIndex = dropItem.dataset.index; if (dataIndex) { dropIndex.current = parseInt(dataIndex, 10); } if (dropItem === selectedItem.nextSibling) { dropItem = dropItem.nextSibling as HTMLElement; } list.insertBefore(selectedItem, dropItem); } }; const handleDragEnd = (event: React.DragEvent) => { const selectedItem = event.target as HTMLElement; const list = selectedItem.closest("ul"); const elementFromPoint = x.current != null && y.current != null ? document.elementFromPoint(x.current, y.current) : null; let dropItem = x.current == null || y.current == null || elementFromPoint === null ? selectedItem : elementFromPoint.closest("li"); if (y.current !== null && list && !dropItem) { const closestUL = selectedItem.closest("ul"); if (closestUL && y.current > closestUL.getBoundingClientRect().bottom) { dropItem = list.lastChild as HTMLElement; } else { dropItem = list.firstChild as HTMLElement; } } if (dropItem && list && dropItem.closest("ul") === list) { if (onMove) onMove(selectedItem.dataset.index!, dropIndex.current!); } else { event.preventDefault(); event.stopPropagation(); } selectedItem.dataset.dragActive = "false"; document.removeEventListener( "dragover", handleDocumentDragOver as EventListener, ); }; const handleDragOver = (event: React.DragEvent) => { event.preventDefault(); event.dataTransfer.dropEffect = "move"; }; return (
  • {onMove && ( )}
  • ); }; export interface FieldToggleProps { selected: boolean; label: React.ReactNode; onToggle?: (event: boolean) => void; onMove?: ( dragIndex: string | number, dropIndex: string | number | null, ) => void; source: string; index: number | string; }