/** * FileBreadcrumb — ファイルブラウザのパンくず(ルート / seg / seg …)。 * スペースのファイルタブとタスクのファイルタブで共有する。 */ import { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { DND_FILES_MIME, readDragSources } from '../../lib/fileDnd'; interface FileBreadcrumbProps { /** 現在パスのセグメント配列(空配列 = ルート)。 */ pathSegments: string[]; /** セグメントをクリックしたときに移動するパス('' = ルート)。 */ onNavigate: (path: string) => void; /** ルートラベル左に出す任意のプレフィックス(例: '/files')。 */ testid?: string; /** * Phase 2: パンくずの祖先セグメント(やルート)へドラッグ&ドロップしたときの移動。 * 指定すると祖先がドロップ先になり、上の階層へファイルを移せる。 */ onMoveDrop?: (sourcePaths: string[], destDir: string) => void; } export function FileBreadcrumb({ pathSegments, onNavigate, testid, onMoveDrop }: FileBreadcrumbProps) { const { t } = useTranslation('files'); const [dropPath, setDropPath] = useState(null); // ドロップ先になれるのは「現在地ではない」祖先のみ(現在地=最後のセグメント)。 const dropProps = (path: string, isCurrent: boolean) => onMoveDrop && !isCurrent ? { onDragOver: (e: React.DragEvent) => { if (!e.dataTransfer.types.includes(DND_FILES_MIME)) return; e.preventDefault(); e.dataTransfer.dropEffect = 'move' as const; if (dropPath !== path) setDropPath(path); }, onDragLeave: () => setDropPath(p => (p === path ? null : p)), onDrop: (e: React.DragEvent) => { e.preventDefault(); setDropPath(null); const sources = readDragSources(e.dataTransfer); if (sources.length) onMoveDrop(sources, path); }, 'data-drop-target': 'true' as const, } : {}; const dropRing = (path: string) => dropPath === path ? ' ring-2 ring-[var(--brand-primary)] text-slate-900' : ''; return ( ); }