feat(review): add focused thread highlight and headerSlot to ThreadList

- FOCUSED_BG and FOCUSED_BORDER tokens in feedbackStyles
- headerSlot prop for draft pin comment input above thread list
- focusedThreadId prop highlights active thread row with accent border
- ThreadListItem gains focused prop with left border + subtle background
This commit is contained in:
Hiren Kangad
2026-03-12 22:09:12 +05:30
parent 9bd2810f5a
commit a1efdedb2a
3 changed files with 20 additions and 3 deletions
@@ -29,6 +29,10 @@ interface ThreadListProps {
newCommentText: string; newCommentText: string;
onNewCommentChange: (text: string) => void; onNewCommentChange: (text: string) => void;
onCreateThread: () => void; onCreateThread: () => void;
/** Optional slot rendered above the thread list (e.g., draft pin input) */
headerSlot?: React.ReactNode;
/** Currently focused thread ID for highlighting */
focusedThreadId?: string | null;
} }
export default function ThreadList({ export default function ThreadList({
@@ -45,6 +49,8 @@ export default function ThreadList({
newCommentText, newCommentText,
onNewCommentChange, onNewCommentChange,
onCreateThread, onCreateThread,
headerSlot,
focusedThreadId,
}: ThreadListProps) { }: ThreadListProps) {
const [showOrphans, setShowOrphans] = React.useState(false); const [showOrphans, setShowOrphans] = React.useState(false);
@@ -154,6 +160,9 @@ export default function ThreadList({
</div> </div>
)} )}
{/* Draft pin comment input (injected from FeedbackPanel) */}
{headerSlot}
{/* Thread list */} {/* Thread list */}
<div style={{ flex: 1, overflowY: 'auto', scrollbarWidth: 'thin', scrollbarColor: '#2a2a2e transparent' }}> <div style={{ flex: 1, overflowY: 'auto', scrollbarWidth: 'thin', scrollbarColor: '#2a2a2e transparent' }}>
{threads.length === 0 && ( {threads.length === 0 && (
@@ -181,6 +190,7 @@ export default function ThreadList({
thread={t} thread={t}
pinNumber={store.getPinNumber(t.id)} pinNumber={store.getPinNumber(t.id)}
onClick={() => onSelectThread(t.id)} onClick={() => onSelectThread(t.id)}
focused={focusedThreadId === t.id}
/> />
))} ))}
@@ -10,15 +10,18 @@ import {
STATUS_RESOLVED, STATUS_RESOLVED,
BORDER, BORDER,
HOVER_BG, HOVER_BG,
FOCUSED_BG,
ACCENT,
} from './feedbackStyles'; } from './feedbackStyles';
interface ThreadListItemProps { interface ThreadListItemProps {
thread: Thread; thread: Thread;
pinNumber: number; pinNumber: number;
onClick: () => void; onClick: () => void;
focused?: boolean;
} }
export default function ThreadListItem({ thread, pinNumber, onClick }: ThreadListItemProps) { export default function ThreadListItem({ thread, pinNumber, onClick, focused }: ThreadListItemProps) {
const firstComment = thread.comments[0]; const firstComment = thread.comments[0];
const isResolved = thread.status === 'resolved'; const isResolved = thread.status === 'resolved';
@@ -30,9 +33,11 @@ export default function ThreadListItem({ thread, pinNumber, onClick }: ThreadLis
borderBottom: `1px solid ${BORDER}`, borderBottom: `1px solid ${BORDER}`,
cursor: 'pointer', cursor: 'pointer',
transition: 'background 0.1s ease', transition: 'background 0.1s ease',
background: focused ? FOCUSED_BG : 'transparent',
borderLeft: focused ? `2px solid ${ACCENT}` : '2px solid transparent',
}} }}
onMouseEnter={(e) => (e.currentTarget.style.background = HOVER_BG)} onMouseEnter={(e) => { if (!focused) e.currentTarget.style.background = HOVER_BG; }}
onMouseLeave={(e) => (e.currentTarget.style.background = 'transparent')} onMouseLeave={(e) => { if (!focused) e.currentTarget.style.background = 'transparent'; }}
> >
{/* Row 1: author circle + name + timestamp */} {/* Row 1: author circle + name + timestamp */}
<div style={{ display: 'flex', alignItems: 'center', gap: '8px', marginBottom: '6px' }}> <div style={{ display: 'flex', alignItems: 'center', gap: '8px', marginBottom: '6px' }}>
@@ -17,6 +17,8 @@ export const INPUT_BORDER = '#26262c';
export const INPUT_BORDER_FOCUS = '#4a9eff44'; export const INPUT_BORDER_FOCUS = '#4a9eff44';
export const HOVER_BG = '#16161a'; export const HOVER_BG = '#16161a';
export const FILTER_ACTIVE_BG = '#1e1e24'; export const FILTER_ACTIVE_BG = '#1e1e24';
export const FOCUSED_BG = 'rgba(74, 158, 255, 0.06)';
export const FOCUSED_BORDER = 'rgba(74, 158, 255, 0.2)';
// Shared panel container style // Shared panel container style
export const panelContainerStyle: React.CSSProperties = { export const panelContainerStyle: React.CSSProperties = {