리뷰어 b46f6f1 non-blocking 노트 반영. 기존 subtitle 은 shrink-0 이라
값이 길어지면 헤더 폭을 넘기며 title 을 두 줄로 밀어낼 수 있었음.
실제 트리거 예: PriceTargets 의 "목표 1,234,567 · 손절 987,654"
같은 큰 주가 범위.
변경:
- subtitle: shrink-0 제거 + min-w-0 truncate → 폭 부족 시 말줄임표.
- title flex span: min-w-0 추가 → flex shrink 계산이 양쪽 모두에서
정상 동작하도록 (min-w-0 없으면 자식 텍스트의 min-content 가 부모
shrink 를 막아 truncate 가 발동 안 됨).
title 텍스트 자체는 늘 짧음(호가/내 목표가/관련 종목/투자 메모/가격
알람) — title 쪽 truncate 는 불필요, min-w-0 만으로 충분.
검증:
- npx tsc --noEmit clean
- npx next lint "✔ No ESLint warnings or errors"
Three lower-sidebar panels are now wrapped in a shared Collapsible so
mobile readers can fold away sections they're not actively using.
New shared pieces:
- lib/collapsible.ts — readOpen/writeOpen helpers backed by
localStorage under 'panel-open:<name>'. Per-panel-name (not
per-code) because the preference is a panel-level habit; users
don't want different fold state for every symbol.
- components/Collapsible.tsx — button-as-header + chevron + body.
aria-expanded for AT. Body uses 'hidden' instead of conditional
unmount so textarea text, partial alert form input, and
RelatedStocks fetch results all survive collapse cycles.
SSR-safe: useState(defaultOpen) then useEffect-sync to avoid React
hydration mismatch when localStorage diverges from default.
Panel refactors:
- RelatedStocks: outer rounded div + header replaced with Collapsible;
loading/error/empty/data states all rendered inside body (header
stays visible so user can still toggle/recognize the section even
while loading).
- InvestmentNote: same swap. saved.updatedAt drives the subtitle.
- AlertsPanel: same swap. current price drives the subtitle.
Defaults are open=true everywhere; the surface area is the toggle
itself, not a forced collapse. User's first interaction sets the
sticky preference.