fix(awards): stop the follow-list flicker/lost clicks (inline rows, no nested component)
The Row helper was defined inside AwardsSelectionPanel, so each render minted a new component type and React remounted the whole list on every click — flicker, and clicks lost between mousedown and click (hence needing many tries). Inline the row markup so the keyed buttons keep a stable identity.
This commit is contained in:
@@ -980,16 +980,6 @@ function AwardsSelectionPanel({ profile }: { profile?: { name?: string; callsign
|
||||
&& (needle === '' || `${a.code} ${a.name}`.toLowerCase().includes(needle)));
|
||||
const trackedItems = tracked.map((c) => byCode.get(c)).filter(Boolean) as { code: string; name: string }[];
|
||||
|
||||
const Row = ({ a, arrow, onClick }: { a: { code: string; name: string }; arrow: 'right' | 'left'; onClick: () => void }) => (
|
||||
<button type="button" onClick={onClick}
|
||||
className="group w-full flex items-center gap-2 rounded-md px-2 py-1 text-left hover:bg-primary/10">
|
||||
{arrow === 'left' && <span className="text-muted-foreground opacity-0 group-hover:opacity-100">←</span>}
|
||||
<span className="font-mono text-xs shrink-0">{a.code}</span>
|
||||
<span className="text-xs text-muted-foreground truncate flex-1">{a.name}</span>
|
||||
{arrow === 'right' && <span className="text-primary opacity-0 group-hover:opacity-100">→</span>}
|
||||
</button>
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<SectionHeader title={t('sec.awards')} hint={t('awards.followHint')} />
|
||||
@@ -1006,7 +996,14 @@ function AwardsSelectionPanel({ profile }: { profile?: { name?: string; callsign
|
||||
<Input value={q} onChange={(e) => setQ(e.target.value)} placeholder={t('awards.search')} className="h-8" />
|
||||
</div>
|
||||
<div className="max-h-[340px] overflow-y-auto p-1.5 space-y-0.5">
|
||||
{available.map((a) => <Row key={a.code} a={a} arrow="right" onClick={() => persist([...tracked, a.code])} />)}
|
||||
{available.map((a) => (
|
||||
<button key={a.code} type="button" onClick={() => persist([...tracked, a.code])}
|
||||
className="group w-full flex items-center gap-2 rounded-md px-2 py-1 text-left hover:bg-primary/10">
|
||||
<span className="font-mono text-xs shrink-0">{a.code}</span>
|
||||
<span className="text-xs text-muted-foreground truncate flex-1">{a.name}</span>
|
||||
<span className="text-primary opacity-0 group-hover:opacity-100">→</span>
|
||||
</button>
|
||||
))}
|
||||
{available.length === 0 && <div className="p-2 text-xs text-muted-foreground">{t('awards.allTracked')}</div>}
|
||||
</div>
|
||||
</div>
|
||||
@@ -1017,7 +1014,14 @@ function AwardsSelectionPanel({ profile }: { profile?: { name?: string; callsign
|
||||
disabled={tracked.length === 0} onClick={() => persist([])}>{t('awards.clear')}</button>
|
||||
</div>
|
||||
<div className="max-h-[392px] overflow-y-auto p-1.5 space-y-0.5">
|
||||
{trackedItems.map((a) => <Row key={a.code} a={a} arrow="left" onClick={() => persist(tracked.filter((c) => c !== a.code))} />)}
|
||||
{trackedItems.map((a) => (
|
||||
<button key={a.code} type="button" onClick={() => persist(tracked.filter((c) => c !== a.code))}
|
||||
className="group w-full flex items-center gap-2 rounded-md px-2 py-1 text-left hover:bg-primary/10">
|
||||
<span className="text-muted-foreground opacity-0 group-hover:opacity-100">←</span>
|
||||
<span className="font-mono text-xs shrink-0">{a.code}</span>
|
||||
<span className="text-xs text-muted-foreground truncate flex-1">{a.name}</span>
|
||||
</button>
|
||||
))}
|
||||
{tracked.length === 0 && <div className="p-2 text-xs text-muted-foreground">{t('awards.noneFollowed')}</div>}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user