/** * 시간의 골목 — 가로 연표. * * 연도가 큰 활자로 서고 사건이 그 아래 붙는다. 레일 위의 **붉은 점은 도시의 성격이 바뀐 해**다 — * 점의 색이 장식이 아니라 정보라서, 범례를 한 줄 달아 그 약속을 밝힌다. * ★ 연도가 없는 항목은 지어내 끼우지 않고 레일 끝으로 민다. 연표에서 틀린 순서는 바로 들킨다. */ import {useMemo} from 'react'; import {Rail, SectionBody, SectionFrame} from '../../primitives'; import type {SectionRenderProps} from '../../types'; import {parseSectionData, type ChronicleItem} from '@o2o/shared'; import { ITEM_ACCENT, ITEM_BODY, ITEM_BORDER, ITEM_CARD, ITEM_HEADING, ITEM_INK, ParseError, PasteHint, SourceLine, } from '../items/common'; import '../items/items.css'; function Milestone({item, isLast}: {item: ChronicleItem; isLast: boolean}) { const turning = item.turning === true; return (

{item.year ?? '연도 미상'}

{/* 레일 — 점이 선 위에 놓여야 '흐르는 시간 위의 한 해'로 읽힌다 */}

{item.title}

{item.summary && (

{item.summary}

)} {item.place &&

지금 이 자리 · {item.place}

}
); } export function ChronicleRail(props: SectionRenderProps) { const {section, isSelected, onSelect} = props; const parsed = parseSectionData(section.type, section.data); // 연도가 있는 것부터 오름차순, 없는 것은 뒤로. 붙여넣은 순서를 믿지 않는다. const items = useMemo( () => [...parsed.items].sort((a, b) => { if (a.year == null) return b.year == null ? 0 : 1; if (b.year == null) return -1; return a.year - b.year; }), [parsed.items], ); const turningCount = items.filter((item) => item.turning === true).length; return (

{parsed.title || section.name}

{(parsed.subtitle || section.description) && (

{parsed.subtitle || section.description}

)}
{parsed.error ? ( ) : items.length === 0 ? ( ) : (

도시의 성격이 바뀐 해 {turningCount}개 · 전체 {items.length}개

{items.map((item, index) => ( ))}
)}
); }