import React, { useCallback, useEffect, useMemo, useState } from 'react' import { DndContext, DragOverlay, DragEndEvent, DragStartEvent, KeyboardSensor, PointerSensor, closestCenter, useSensor, useSensors, } from '@dnd-kit/core' import { SortableContext, sortableKeyboardCoordinates, useSortable, verticalListSortingStrategy, } from '@dnd-kit/sortable' import { CSS } from '@dnd-kit/utilities' import { MenuItemComponent, MenuItemComponentProps } from './MenuItemComponent' import { findMenuItem, indentMenuItem, moveMenuItem, outdentMenuItem } from './menuTree.utils' import { MenuItem } from '@/proxy/menus/menu' import { PermissionDefinitionRecord } from '@/proxy/admin/models' import { getPermissionsList } from '@/services/identity.service' import { SelectBoxOption } from '@/types/shared' interface SortableMenuTreeProps { /** Ekranda gösterilen (filtrelenmiş olabilir) ağaç */ items: MenuItem[] /** Taşıma işlemlerinin uygulanacağı tam ağaç */ sourceItems: MenuItem[] onItemsChange: (items: MenuItem[]) => void isDesignMode: boolean isFiltered?: boolean matchedIds?: Set collapsedIds: Set onToggleExpand: (id: string) => void refetch: () => void } type SortableRowProps = Omit & { disabled: boolean } const SortableMenuRow: React.FC = ({ disabled, ...props }) => { const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id: props.item.id ?? '', data: { type: 'menu-item' }, disabled, }) return ( ) } export const SortableMenuTree: React.FC = ({ items, sourceItems, onItemsChange, isDesignMode, isFiltered = false, matchedIds, collapsedIds, onToggleExpand, refetch, }) => { const [permissions, setPermissions] = useState([]) const [activeItem, setActiveItem] = useState(null) // Filtre açıkken ara düğümler gizli olduğu için sürükle-bırak kapatılır. const dragEnabled = isDesignMode && !isFiltered const sensors = useSensors( useSensor(PointerSensor, { activationConstraint: { distance: 8 } }), useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }), ) useEffect(() => { let cancelled = false getPermissionsList() .then((response) => { if (cancelled || !response.data) return const options = response.data .filter((permission: PermissionDefinitionRecord) => Boolean(permission.name)) .map((permission: PermissionDefinitionRecord) => ({ value: permission.name, label: permission.name, })) .sort((a, b) => (a.label ?? '').localeCompare(b.label ?? '')) setPermissions(options) }) .catch(() => { if (!cancelled) setPermissions([]) }) return () => { cancelled = true } }, []) const handleDragStart = useCallback( (event: DragStartEvent) => setActiveItem(findMenuItem(sourceItems, event.active.id as string)), [sourceItems], ) const handleDragEnd = useCallback( (event: DragEndEvent) => { setActiveItem(null) const { active, over } = event if (!over || !dragEnabled || active.id === over.id) return const next = moveMenuItem(sourceItems, active.id as string, over.id as string) if (next) onItemsChange(next) }, [dragEnabled, onItemsChange, sourceItems], ) const handleIndent = useCallback( (id: string) => { const next = indentMenuItem(sourceItems, id) if (next) onItemsChange(next) }, [onItemsChange, sourceItems], ) const handleOutdent = useCallback( (id: string) => { const next = outdentMenuItem(sourceItems, id) if (next) onItemsChange(next) }, [onItemsChange, sourceItems], ) const renderMenuItem = (item: MenuItem, depth: number, index: number) => { const id = item.id ?? '' const children = item.children ?? [] const isExpanded = isFiltered || !collapsedIds.has(id) return (
0} canOutdent={isDesignMode && !isFiltered && depth > 0} depth={depth} disabled={!dragEnabled} isDesignMode={isDesignMode} isExpanded={isExpanded} isHighlighted={Boolean(matchedIds?.has(id))} item={item} permissions={permissions} refetch={refetch} onIndent={handleIndent} onOutdent={handleOutdent} onToggleExpand={onToggleExpand} > {children.length > 0 && ( child.id ?? '').filter(Boolean)} strategy={verticalListSortingStrategy} >
{children.map((child, childIndex) => renderMenuItem(child, depth + 1, childIndex), )}
)}
) } const rootIds = useMemo( () => items.map((item) => item.id ?? '').filter(Boolean), [items], ) return ( setActiveItem(null)} >
{items.map((item, index) => renderMenuItem(item, 0, index))}
{activeItem && (
)}
) }