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:
@@ -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 = {
|
||||||
|
|||||||
Reference in New Issue
Block a user