import { useRef } from 'react' import { createPortal } from 'react-dom' import Markdown from 'react-markdown' import remarkGfm from 'remark-gfm' import remarkBreaks from 'remark-breaks' import { NOTE_ICONS, getNoteIcon } from './DayPlanSidebar.constants' import NoteFormatToolbar from '../shared/NoteFormatToolbar' import NoteColorPicker from '../shared/NoteColorPicker' import { noteSurface } from './noteSurface' import { markdownLinkComponents } from '../shared/markdownLink' interface NoteModalUi { mode: 'add' | 'edit' icon: string text: string time: string color?: string | null } type NoteUiMap = Record interface DayPlanSidebarNoteModalProps { noteUi: NoteUiMap setNoteUi: (updater: (prev: NoteUiMap) => NoteUiMap) => void noteInputRef: React.RefObject cancelNote: (dayId: number) => void saveNote: (dayId: number) => void t: (key: string, params?: Record) => string } const BODY_MAX = 2000 /** * Add/edit dialog for a day note (#1629). * * Two columns on anything wider than a phone: how the note looks on the left * (icon, colour, and a preview of the card it will become), what it says on the * right. The preview is the reason the colour picker is worth having — a swatch * row tells you nothing about what a tinted card looks like next to a place. */ export function DayPlanSidebarNoteModal({ noteUi, setNoteUi, noteInputRef, cancelNote, saveNote, t }: DayPlanSidebarNoteModalProps) { return ( <> {Object.entries(noteUi).map(([dayId, ui]) => ui && createPortal( , document.body ))} ) } function NoteDialog({ dayId, ui, setNoteUi, noteInputRef, cancelNote, saveNote, t }: Omit & { dayId: string ui: NoteModalUi }) { const bodyRef = useRef(null) const patch = (fields: Partial) => setNoteUi(prev => ({ ...prev, [dayId]: { ...(prev[dayId] as NoteModalUi), ...fields } })) const color = ui.color ?? null const surface = noteSurface(color) const PreviewIcon = getNoteIcon(ui.icon) const canSave = Boolean(ui.text?.trim()) const bodyLen = ui.time?.length || 0 return (
cancelNote(Number(dayId))}>
e.stopPropagation()}>
{ui.mode === 'add' ? t('dayplan.noteAdd') : t('dayplan.noteEdit')}
{/* Left — how it looks. */}
{t('dayplan.noteIcon')}
{NOTE_ICONS.map(({ id, Icon }) => ( ))}
{t('notes.color.label')}
patch({ color: c })} />
{t('notes.preview')}
{/* The same shape the card takes in the day plan. */}
{ui.text?.trim() || t('dayplan.noteTitle')}
{/* Right — what it says. */}
patch({ text: e.target.value })} onKeyDown={e => { if (e.key === 'Enter') { e.preventDefault(); saveNote(Number(dayId)) } if (e.key === 'Escape') cancelNote(Number(dayId)) }} placeholder={t('dayplan.noteTitle')} required className="text-content bg-surface-input" style={{ fontSize: 'calc(13px * var(--fs-scale-body, 1))', fontWeight: 500, border: '1px solid var(--border-primary)', borderRadius: 8, padding: '9px 11px', fontFamily: 'inherit', outline: 'none', width: '100%', boxSizing: 'border-box' }} />
patch({ time: v })} compact />