// Toolbar — top strip with title, undo/redo, background popover, add-node menu,
// add-note button, save/load, and screenshot.
const { useState: useStateTB, useRef: useRefTB, useEffect: useEffectTB } = React;
function Toolbar({
title, setTitle, subtitle, setSubtitle,
defaultCurrency, setDefaultCurrency,
showTitleOnCanvas, setShowTitleOnCanvas,
bgPattern, setBgPattern, bgColor, setBgColor,
showSeq, setShowSeq,
showNotes, setShowNotes,
showAnnotations, setShowAnnotations,
canUndo, canRedo, onUndo, onRedo,
onAddNode, onAddNote,
onShotMode, onSaveProject, onLoadProject, onLoadDemo, onClear,
onShowSchedule, onAutoGroupByDate, onAutoConnectByTime,
}){
const [bgOpen, setBgOpen] = useStateTB(false);
const [moreOpen, setMoreOpen] = useStateTB(false);
const [fileOpen, setFileOpen] = useStateTB(false);
const [addOpen, setAddOpen] = useStateTB(false);
const [curOpen, setCurOpen] = useStateTB(false);
const bgRef = useRefTB(null);
const moreRef = useRefTB(null);
const fileRef = useRefTB(null);
const addRef = useRefTB(null);
const curRef = useRefTB(null);
useEffectTB(()=>{
const onDoc = (e)=>{
if (bgRef.current && !bgRef.current.contains(e.target)) setBgOpen(false);
if (moreRef.current && !moreRef.current.contains(e.target)) setMoreOpen(false);
if (fileRef.current && !fileRef.current.contains(e.target)) setFileOpen(false);
if (addRef.current && !addRef.current.contains(e.target)) setAddOpen(false);
if (curRef.current && !curRef.current.contains(e.target)) setCurOpen(false);
};
document.addEventListener('mousedown', onDoc);
return ()=>document.removeEventListener('mousedown', onDoc);
},[]);
return (
setBgOpen(v=>!v)} title="Background settings">
Background
{bgOpen && (
)}
{/* (Sequence-swap toggle removed — numbering is auto-derived from
each node's arrival/departure times.) */}
{/* Currency picker — sets the symbol auto-prefixed onto bare numbers
in the cost field. Existing entries with explicit symbols are
preserved verbatim and surface as separate per-currency totals
in the SummaryBar. */}
setCurOpen(v=>!v)}
title="Currency (auto-prefix on bare numbers)">
{defaultCurrency || '$'}
{curOpen && (
Currency (auto-prefix)
Bare numbers get this symbol auto-attached on blur.
Costs you typed with an explicit symbol (e.g. ¥, €) are preserved as-is.
{(window.CURRENCIES || []).map(c=>(
{ setDefaultCurrency(c.symbol); setCurOpen(false); }}
style={{padding:'6px 8px', justifyContent:'flex-start'}}
title={c.label}>
{c.symbol}
{c.label}
))}
)}
{/* Schedule view */}
Schedule
{/* Add menu — picks a node type. The "+ node" main button defaults to a
generic pin; the chevron opens the type picker for stations / airports
/ hotels / etc. */}
{onAddNode({type:'pin'}); setAddOpen(false);}}
title="Add new place">
node
setAddOpen(v=>!v)}
title="Pick type to add" style={{marginLeft:2, padding:'7px 7px'}}>
{addOpen && (
Place type
{NODE_TYPES.map(t=>(
{onAddNode({type:t.id}); setAddOpen(false);}}
title={`Add ${t.label}`}>
{t.label}
))}
{onAddNote(); setAddOpen(false);}}>
NoteAdd
)}
setFileOpen(v=>!v)} title="Project file">
{fileOpen && (
{onSaveProject(); setFileOpen(false);}}>
Save project file
{onLoadProject(); setFileOpen(false);}}>
Open project file
)}
setMoreOpen(v=>!v)} title="Other">
{moreOpen && (
Auto by time
{onAutoConnectByTime && onAutoConnectByTime(); setMoreOpen(false);}}
title="Re-route arrows automatically by node times">
Auto-connect by time
{onAutoGroupByDate && onAutoGroupByDate(); setMoreOpen(false);}}
title="Generate Day 1 / Day 2 / … groups from each node's arrival / departure dates">
Auto-group by date
Load sample
{onLoadDemo('travel'); setMoreOpen(false);}}>
Travel (Hakone, 2 days / 1 night)
{onLoadDemo('business'); setMoreOpen(false);}}>
Business trip (Osaka, 2 days / 1 night)
{onClear(); setMoreOpen(false);}}>
Clear all
)}
Screenshot
);
}
window.Toolbar = Toolbar;