import { useEffect, useState } from 'react'; import { useReactFlow } from '@xyflow/react'; import { type BranchStyle } from '@/types/map'; import { Button } from '@/components/Button'; import { IconButton } from '@/components/IconButton'; import { Menu, MenuItem, MenuLabel, MenuSeparator } from '@/components/Menu'; import { ArrowLeftIcon, ChevronDownIcon, FitIcon, GridIcon, LayoutIcon, MoonIcon, PaletteIcon, PlusIcon, RedoIcon, SearchIcon, ShareIcon, SlidersIcon, SunIcon, TrashIcon, UndoIcon, ZoomInIcon, ZoomOutIcon, } from '@/components/Icons'; import { ExportMenu } from '@/features/export/ExportMenu'; import { LAYOUT_OPTIONS, SPACING_LIMITS, SPACING_PRESETS, resolveSpacing, } from '@/features/editor/layout'; import { THEMES } from '@/features/editor/themes'; import { selectCanRedo, selectCanUndo, useEditorStore } from '@/stores/editorStore'; import { useUiStore } from '@/stores/uiStore'; import { navigate } from '@/hooks/useRoute'; const BRANCH_STYLES: Array<{ id: BranchStyle; label: string }> = [ { id: 'bezier', label: 'Bezier' }, { id: 'curved', label: 'Courbe' }, { id: 'straight', label: 'Droite' }, ]; const SAVE_LABELS = { idle: '', dirty: 'Modifications non enregistrees', saving: 'Sauvegarde...', saved: 'Sauvegarde', error: 'Sauvegarde impossible', } as const; export interface EditorToolbarProps { onOpenShare: () => void; onToggleSearch: () => void; } export function EditorToolbar({ onOpenShare, onToggleSearch }: EditorToolbarProps) { const map = useEditorStore((state) => state.map); const saveStatus = useEditorStore((state) => state.saveStatus); const canUndo = useEditorStore(selectCanUndo); const canRedo = useEditorStore(selectCanRedo); const selectedIds = useEditorStore((state) => state.selectedNodeIds); const undo = useEditorStore((state) => state.undo); const redo = useEditorStore((state) => state.redo); const addChild = useEditorStore((state) => state.addChild); const deleteNodes = useEditorStore((state) => state.deleteNodes); const applyAutoLayout = useEditorStore((state) => state.applyAutoLayout); const updateSettings = useEditorStore((state) => state.updateSettings); const renameMap = useEditorStore((state) => state.renameMap); const requestFit = useEditorStore((state) => state.requestFit); const colorMode = useUiStore((state) => state.colorMode); const toggleColorMode = useUiStore((state) => state.toggleColorMode); const propertiesPanelOpen = useUiStore((state) => state.propertiesPanelOpen); const setPropertiesPanelOpen = useUiStore((state) => state.setPropertiesPanelOpen); const { zoomIn, zoomOut } = useReactFlow(); const [name, setName] = useState(map?.name ?? ''); const spacing = resolveSpacing(map?.settings.layoutSpacing); useEffect(() => setName(map?.name ?? ''), [map?.id, map?.name]); if (!map) return null; const commitName = () => { const trimmed = name.trim(); if (!trimmed) { setName(map.name); return; } if (trimmed !== map.name) renameMap(trimmed); }; return (
} onClick={() => navigate('/')} /> setName(event.target.value)} onBlur={commitName} onKeyDown={(event) => { if (event.key === 'Enter') event.currentTarget.blur(); }} aria-label="Nom de la carte" className="min-w-0 max-w-[16rem] flex-1 truncate rounded-md border border-transparent bg-transparent px-2 py-1 text-sm font-semibold text-zinc-900 hover:border-zinc-200 focus:border-indigo-400 dark:text-zinc-50 dark:hover:border-zinc-700" /> {SAVE_LABELS[saveStatus]}
} disabled={!canUndo} onClick={undo} /> } disabled={!canRedo} onClick={redo} /> } onClick={() => addChild()} /> } tone="danger" disabled={selectedIds.length === 0} onClick={() => deleteNodes(selectedIds)} />
Organisation automatique } > {(close) => ( <> Disposition {LAYOUT_OPTIONS.map((option) => ( { applyAutoLayout(option.id); close(); }} /> ))} Espacement
{SPACING_PRESETS.map((preset) => ( ))}
updateSettings({ layoutSpacing: { ...spacing, parentGap: Number(event.target.value) }, }) } className="h-1.5 w-full accent-indigo-600" /> updateSettings({ layoutSpacing: { ...spacing, siblingGap: Number(event.target.value) }, }) } className="h-1.5 w-full accent-indigo-600" />

Changer l'espacement ne deplace rien : relancez l'organisation.

{ applyAutoLayout(); close(); }} /> )}
Theme } > {() => ( <> Theme de la carte
{THEMES.map((theme) => ( updateSettings({ theme: theme.id })} /> ))}
Style des branches {BRANCH_STYLES.map((branch) => ( updateSettings({ branchStyle: branch.id })} /> ))} } label={map.settings.showGrid ? 'Masquer la grille' : 'Afficher la grille'} onSelect={() => updateSettings({ showGrid: !map.settings.showGrid })} /> updateSettings({ snapToGrid: !map.settings.snapToGrid })} /> updateSettings({ alignmentGuides: map.settings.alignmentGuides === false }) } /> )}
} onClick={() => zoomIn()} /> } onClick={() => zoomOut()} /> } onClick={requestFit} /> } onClick={onToggleSearch} /> } active={propertiesPanelOpen} onClick={() => setPropertiesPanelOpen(!propertiesPanelOpen)} /> : } onClick={toggleColorMode} />
); }