fix(mobile): prevent layout breaking from long unbroken text in notes

This commit is contained in:
Diogo Mendes
2026-10-02 10:18:21 -03:00
committed by Maurice
parent a856c68f87
commit a9c78f680e
9 changed files with 19 additions and 17 deletions
@@ -28,7 +28,7 @@ export default function MarkdownText({ children, className = '', clamp = false }
}) {
return (
<div
className={`collab-note-md ${clamp ? 'truncate ' : ''}${className}`}
className={`collab-note-md break-words [overflow-wrap:anywhere] [word-break:break-word] ${clamp ? 'truncate ' : ''}${className}`}
style={clamp ? { maxHeight: '1.2em', lineHeight: 1.2 } : undefined}
>
<Markdown
+3 -1
View File
@@ -1535,16 +1535,18 @@ img[alt="TREK"] {
/* Markdown in Collab Notes */
.collab-note-md strong, .collab-note-md-full strong { font-weight: 700 !important; }
.collab-note-md, .collab-note-md-full { overflow-wrap: anywhere; word-break: break-word; }
.collab-note-md em, .collab-note-md-full em { font-style: italic !important; }
.collab-note-md h1, .collab-note-md h2, .collab-note-md h3 { font-weight: 700 !important; margin: 0; }
.collab-note-md-full h1 { font-size: 1.3em !important; font-weight: 700 !important; margin: 0.8em 0 0.3em; }
.collab-note-md-full h2 { font-size: 1.15em !important; font-weight: 700 !important; margin: 0.8em 0 0.3em; }
.collab-note-md-full h3 { font-size: 1em !important; font-weight: 700 !important; margin: 0.8em 0 0.3em; }
.collab-note-md p, .collab-note-md-full p { margin: 0 0 0.3em; }
.collab-note-md p:last-child, .collab-note-md-full p:last-child { margin-bottom: 0; }
.collab-note-md ul, .collab-note-md-full ul { list-style-type: disc !important; padding-left: 1.4em !important; margin: 0.2em 0; }
.collab-note-md ol, .collab-note-md-full ol { list-style-type: decimal !important; padding-left: 1.4em !important; margin: 0.2em 0; }
.collab-note-md li, .collab-note-md-full li { display: list-item !important; margin: 0.1em 0; }
.collab-note-md code, .collab-note-md-full code { font-size: 0.9em; padding: 1px 5px; border-radius: 4px; background: var(--bg-secondary); }
.collab-note-md code, .collab-note-md-full code { font-size: 0.9em; padding: 1px 5px; border-radius: 4px; background: var(--bg-secondary); word-break: break-all; }
.collab-note-md-full pre { padding: 10px 12px; border-radius: 8px; background: var(--bg-secondary); overflow-x: auto; margin: 0.5em 0; }
.collab-note-md-full pre code { padding: 0; background: none; }
.collab-note-md a, .collab-note-md-full a { color: #3b82f6; text-decoration: underline; word-break: normal; overflow-wrap: break-word; }
@@ -152,7 +152,7 @@ export default function MPlacesBrowser({ planner, shell }: MPlacesBrowserProps)
return (
<div className="flex h-full flex-col">
<div className="min-h-0 flex-1 overflow-y-auto px-4 pb-[calc(var(--bottom-nav-h,84px)+22px)] pt-[calc(var(--m-safe-top,12px)+58px)]">
<div className="min-h-0 flex-1 overflow-x-hidden overflow-y-auto px-4 pb-[calc(var(--bottom-nav-h,84px)+22px)] pt-[calc(var(--m-safe-top,12px)+58px)]">
{/* ── Filter chips + Import (Add sits in the search row below to free space) ── */}
<div className="flex items-center gap-[6px]">
<FilterChip
@@ -160,7 +160,7 @@ export default function MPlanTimeline({ planner, shell }: MPlanTimelineProps) {
<div
ref={cardRef}
data-touch-drag={editing ? '' : undefined}
className="absolute left-4 right-4 overflow-y-auto overscroll-contain rounded-[22px] border border-[color:var(--m-cbr)] bg-[color:var(--m-card)] px-3.5 pb-2 pt-1 backdrop-blur-[24px] backdrop-saturate-[1.6] bottom-[calc(env(safe-area-inset-bottom,0px)+90px)]"
className="absolute left-4 right-4 overflow-x-hidden overflow-y-auto overscroll-contain rounded-[22px] border border-[color:var(--m-cbr)] bg-[color:var(--m-card)] px-3.5 pb-2 pt-1 backdrop-blur-[24px] backdrop-saturate-[1.6] bottom-[calc(env(safe-area-inset-bottom,0px)+90px)]"
style={{ top: `calc(var(--m-safe-top, 12px) + ${editing ? 140 : tl.upNext ? 216 : 102}px)` }}
>
{day && (
@@ -551,7 +551,7 @@ export function NoteRow({ note, chrome, reorder, drag, onEdit }: {
const { time: noteTime, detail } = splitNoteTime(note.time)
const time = noteTime ? fmtTime(noteTime, chrome) : ''
const [title, ...rest] = note.text.split('\n')
const titleExtra = rest.join(' ').trim()
const titleExtra = rest.join('\n').trim()
return (
<div
@@ -561,10 +561,10 @@ export function NoteRow({ note, chrome, reorder, drag, onEdit }: {
// A link in the rendered body keeps its own tap; the rest of the row edits.
onClick={chrome.editing ? (e => { if (!(e.target as HTMLElement).closest('a')) onEdit() }) : undefined}
onKeyDown={chrome.editing ? (e => { if (e.target === e.currentTarget && (e.key === 'Enter' || e.key === ' ')) { e.preventDefault(); onEdit() } }) : undefined}
className={`my-[2px] flex items-center gap-2.5 ${chrome.editing ? 'cursor-pointer' : ''} ${dragClass(drag)}`}
className={`my-[2px] flex items-start gap-2.5 ${chrome.editing ? 'cursor-pointer' : ''} ${dragClass(drag)}`}
>
{!chrome.editing && (
<AvatarRing style={note.color ? { background: skin.iconBackground, borderColor: skin.border } : undefined}>
<AvatarRing className="mt-[2px]" style={note.color ? { background: skin.iconBackground, borderColor: skin.border } : undefined}>
<Icon size={14} strokeWidth={2} style={{ color: skin.iconColor }} />
</AvatarRing>
)}
@@ -572,20 +572,20 @@ export function NoteRow({ note, chrome, reorder, drag, onEdit }: {
className="min-w-0 flex-1 rounded-[13px] border px-[11px] py-[7px]"
style={{ borderColor: skin.border, background: note.color ? skin.background : 'var(--m-ic)' }}
>
<div className="flex items-center gap-1.5">
<div className="flex min-w-0 items-start gap-1.5">
{time && <span className={TIME_CHIP}>{time}</span>}
<span className="min-w-0 text-[0.875rem] font-semibold">{title}</span>
<span className="min-w-0 break-words [overflow-wrap:anywhere] [word-break:break-word] text-[0.875rem] font-semibold">{title}</span>
</div>
{titleExtra && (
<div className="mt-px font-geist text-[0.71875rem] leading-[1.4] text-m-muted">{titleExtra}</div>
<MarkdownText className="mt-px font-geist text-[0.71875rem] leading-[1.4] text-m-muted break-words [overflow-wrap:anywhere] [word-break:break-word]">{titleExtra}</MarkdownText>
)}
{detail && (
// Rendered, not raw: a note written with the formatting bar would
// otherwise read as `**asterisks**` on the phone.
<MarkdownText className="mt-px font-geist text-[0.71875rem] leading-[1.45] text-m-muted [overflow-wrap:anywhere]">{detail}</MarkdownText>
<MarkdownText className="mt-px font-geist text-[0.71875rem] leading-[1.45] text-m-muted break-words [overflow-wrap:anywhere] [word-break:break-word]">{detail}</MarkdownText>
)}
</div>
{chrome.editing && <span className="flex flex-none items-center gap-1.5">{reorder}</span>}
{chrome.editing && <span className="mt-[2px] flex flex-none items-center gap-1.5">{reorder}</span>}
</div>
)
}
@@ -180,7 +180,7 @@ export default function MRoadtripTab({ planner, shell }: MTripTabPanelProps) {
{searchBar}
<div
ref={cardRef}
className={`absolute inset-0 overflow-y-auto overscroll-contain px-4 pb-[calc(var(--bottom-nav-h,84px)+22px)] ${
className={`absolute inset-0 overflow-x-hidden overflow-y-auto overscroll-contain px-4 pb-[calc(var(--bottom-nav-h,84px)+22px)] ${
searchBar ? 'pt-[calc(var(--m-safe-top,12px)+150px)]' : 'pt-[calc(var(--m-safe-top,12px)+102px)]'
}`}
>
@@ -474,7 +474,7 @@ export default function MPlaceSheet({ planner, shell }: MTripSheetsProps) {
{day.title || t('planner.dayN', { n: (day.day_number ?? planner.days.indexOf(day) + 1) || '?' })}
</div>
)}
<div className="whitespace-pre-wrap font-geist text-[0.75rem] leading-[1.5] text-m-muted">{assignment.notes}</div>
<div className="whitespace-pre-wrap break-words [overflow-wrap:anywhere] [word-break:break-word] font-geist text-[0.75rem] leading-[1.5] text-m-muted">{assignment.notes}</div>
</div>
))}
</div>
@@ -360,7 +360,7 @@ function NoteCardRow({ note, color, canEdit, onTap, onTogglePin, onDelete, t }:
<button type="button" onClick={onTap} className="block w-full px-3 pb-3 pt-[9px] text-left">
{note.content && (
<div className="line-clamp-3 font-geist text-[0.75rem] leading-[1.5] text-m-muted [&_a]:underline [&_ol]:list-decimal [&_ol]:pl-4 [&_p]:mb-1 [&_strong]:font-bold [&_ul]:list-disc [&_ul]:pl-4">
<div className="line-clamp-3 break-words [overflow-wrap:anywhere] [word-break:break-word] font-geist text-[0.75rem] leading-[1.5] text-m-muted [&_a]:underline [&_ol]:list-decimal [&_ol]:pl-4 [&_p]:mb-1 [&_strong]:font-bold [&_ul]:list-disc [&_ul]:pl-4">
<Markdown remarkPlugins={[remarkGfm, remarkBreaks]} rehypePlugins={sanitizedMarkdownPlugins} components={sanitizedMarkdownComponents}>{note.content}</Markdown>
</div>
)}
@@ -640,7 +640,7 @@ function NoteViewSheet({ open, note, onClose, t }: {
/>
<div className="min-h-0 flex-1 overflow-y-auto px-[18px] pb-4 pt-1">
{snapshot?.content && (
<div className="font-geist text-[0.8125rem] leading-[1.6] text-m-ink [&_a]:underline [&_ol]:list-decimal [&_ol]:pl-5 [&_p]:mb-2 [&_strong]:font-bold [&_ul]:list-disc [&_ul]:pl-5">
<div className="break-words [overflow-wrap:anywhere] [word-break:break-word] font-geist text-[0.8125rem] leading-[1.6] text-m-ink [&_a]:underline [&_ol]:list-decimal [&_ol]:pl-5 [&_p]:mb-2 [&_strong]:font-bold [&_ul]:list-disc [&_ul]:pl-5">
<Markdown remarkPlugins={[remarkGfm, remarkBreaks]} rehypePlugins={sanitizedMarkdownPlugins} components={sanitizedMarkdownComponents}>{snapshot.content}</Markdown>
</div>
)}
@@ -17,7 +17,7 @@ import { usePluginStore } from '../../../../store/pluginStore'
export function TabScroller({ children }: { children: ReactNode }) {
return (
<div className="flex h-full flex-col">
<div className="min-h-0 flex-1 overflow-y-auto px-4 pb-[calc(var(--bottom-nav-h,84px)+22px)] pt-[calc(var(--m-safe-top,12px)+58px)]">
<div className="min-h-0 flex-1 overflow-x-hidden overflow-y-auto px-4 pb-[calc(var(--bottom-nav-h,84px)+22px)] pt-[calc(var(--m-safe-top,12px)+58px)]">
{children}
</div>
</div>