import { useMemo, useState } from 'react'; import { Button } from '@/components/Button'; import { Modal } from '@/components/Modal'; import { SearchIcon } from '@/components/Icons'; import { useEditorStore } from '@/stores/editorStore'; import { useUiStore } from '@/stores/uiStore'; import { computeDepths, getDescendantIds } from '@/utils/tree'; import { cn } from '@/utils/cn'; export interface MoveToDialogProps { open: boolean; nodeId: string | null; onClose: () => void; } function normalize(value: string): string { return value .normalize('NFD') .replace(/[\u0300-\u036f]/g, '') .toLowerCase(); } /** * Choix d'un nouveau parent par recherche. * * Les cibles impossibles (le noeud lui-meme et ses descendants) sont exclues de * la liste : la detection de cycle de `reparentNode` reste la regle unique, ce * filtre ne fait qu'eviter de proposer un choix qui serait refuse. */ export function MoveToDialog({ open, nodeId, onClose }: MoveToDialogProps) { const map = useEditorStore((state) => state.map); const moveNodeToParent = useEditorStore((state) => state.moveNodeToParent); const pushToast = useUiStore((state) => state.pushToast); const [query, setQuery] = useState(''); const node = map?.nodes.find((candidate) => candidate.id === nodeId); const candidates = useMemo(() => { if (!map || !node) return []; const excluded = new Set([node.id, ...getDescendantIds(map.nodes, node.id)]); const depths = computeDepths(map.nodes); const needle = normalize(query.trim()); return map.nodes .filter((candidate) => !excluded.has(candidate.id) && candidate.id !== node.parentId) .filter((candidate) => !needle || normalize(candidate.text).includes(needle)) .map((candidate) => ({ node: candidate, depth: depths.get(candidate.id) ?? 0 })) .slice(0, 60); }, [map, node, query]); const move = (parentId: string | null) => { if (!node) return; if (moveNodeToParent(node.id, parentId)) { pushToast( parentId ? 'Branche deplacee.' : 'Noeud detache : il devient une racine.', 'success', ); onClose(); return; } pushToast('Deplacement impossible : cela creerait une boucle.', 'error'); }; return ( Annuler} >
setQuery(event.target.value)} placeholder="Rechercher un noeud parent" aria-label="Rechercher un noeud parent" className="w-full bg-transparent text-sm outline-none placeholder:text-zinc-400" />
{candidates.length === 0 ? (

Aucun noeud ne correspond.

) : ( )}
); }