fix: ADIF export field picker All/None buttons dead — hoist GroupCard
GroupCard was defined inside ExportFieldsDialog, so it got a new component identity every render and React remounted the whole grid on each sel change, making the per-group All/None buttons (and checkboxes) feel unresponsive. Hoist GroupCard to module scope with sel + handlers passed as props. Changelog 0.21.1.
This commit is contained in:
@@ -12,6 +12,34 @@ import { adif } from '@/../wailsjs/go/models';
|
||||
type FieldDef = adif.FieldDef;
|
||||
const PREF_KEY = 'opslog.exportFields';
|
||||
|
||||
// One category card with its checkboxes + All/None. Defined at MODULE scope (not
|
||||
// inside ExportFieldsDialog) so its component identity is stable across renders —
|
||||
// an inner component is re-created every render, remounting the whole subtree and
|
||||
// making the All/None buttons and checkboxes feel dead.
|
||||
function GroupCard({ title, tags, warn, sel, allLabel, noneLabel, onAll, onNone, onToggle }: {
|
||||
title: string; tags: string[]; warn?: boolean; sel: Set<string>;
|
||||
allLabel: string; noneLabel: string;
|
||||
onAll: (tags: string[]) => void; onNone: (tags: string[]) => void; onToggle: (name: string, on: boolean) => void;
|
||||
}) {
|
||||
return (
|
||||
<div className={`rounded-md border p-2 ${warn ? 'border-warning-border/50 bg-warning-muted/20' : 'border-border/60'}`}>
|
||||
<div className="flex items-center justify-between mb-1 gap-2">
|
||||
<span className={`text-[11px] font-semibold uppercase tracking-wide ${warn ? 'text-warning-muted-foreground' : 'text-muted-foreground'}`}>{title}</span>
|
||||
<span className="flex gap-1.5 shrink-0">
|
||||
<button type="button" className="text-[10px] text-primary hover:underline" onClick={() => onAll(tags)}>{allLabel}</button>
|
||||
<button type="button" className="text-[10px] text-muted-foreground hover:underline" onClick={() => onNone(tags)}>{noneLabel}</button>
|
||||
</span>
|
||||
</div>
|
||||
{tags.map((name) => (
|
||||
<label key={name} className="flex items-center gap-1.5 text-[11px] cursor-pointer py-0.5">
|
||||
<Checkbox checked={sel.has(name)} onCheckedChange={(c) => onToggle(name, !!c)} />
|
||||
<span className="font-mono break-all">{name}</span>
|
||||
</label>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// ExportFieldsDialog lets the operator pick exactly which ADIF fields an export
|
||||
// writes. Two groups: the official ADIF 3.1.7 dictionary (grouped by category)
|
||||
// and the OpsLog / non-standard tags actually present in the log's extras. The
|
||||
@@ -69,23 +97,14 @@ export function ExportFieldsDialog({ open, count, onExport, onClose }: {
|
||||
onExport(fields);
|
||||
};
|
||||
|
||||
const GroupCard = ({ title, tags, warn }: { title: string; tags: string[]; warn?: boolean }) => (
|
||||
<div className={`rounded-md border p-2 ${warn ? 'border-warning-border/50 bg-warning-muted/20' : 'border-border/60'}`}>
|
||||
<div className="flex items-center justify-between mb-1 gap-2">
|
||||
<span className={`text-[11px] font-semibold uppercase tracking-wide ${warn ? 'text-warning-muted-foreground' : 'text-muted-foreground'}`}>{title}</span>
|
||||
<span className="flex gap-1.5 shrink-0">
|
||||
<button type="button" className="text-[10px] text-primary hover:underline" onClick={() => setMany(tags, true)}>{t('exf.all')}</button>
|
||||
<button type="button" className="text-[10px] text-muted-foreground hover:underline" onClick={() => setMany(tags, false)}>{t('exf.none')}</button>
|
||||
</span>
|
||||
</div>
|
||||
{tags.map((name) => (
|
||||
<label key={name} className="flex items-center gap-1.5 text-[11px] cursor-pointer py-0.5">
|
||||
<Checkbox checked={sel.has(name)} onCheckedChange={(c) => toggle(name, !!c)} />
|
||||
<span className="font-mono break-all">{name}</span>
|
||||
</label>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
const allLabel = t('exf.all');
|
||||
const noneLabel = t('exf.none');
|
||||
const cardProps = {
|
||||
sel, allLabel, noneLabel,
|
||||
onAll: (tags: string[]) => setMany(tags, true),
|
||||
onNone: (tags: string[]) => setMany(tags, false),
|
||||
onToggle: toggle,
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={(o) => { if (!o) onClose(); }}>
|
||||
@@ -104,9 +123,9 @@ export function ExportFieldsDialog({ open, count, onExport, onClose }: {
|
||||
|
||||
<div className="grid grid-cols-3 gap-3 max-h-[56vh] overflow-y-auto pr-1">
|
||||
{/* OpsLog / non-standard group first (most relevant to keep or drop). */}
|
||||
{extras.length > 0 && <GroupCard title={t('exf.opslogGroup')} tags={extras} warn />}
|
||||
{extras.length > 0 && <GroupCard title={t('exf.opslogGroup')} tags={extras} warn {...cardProps} />}
|
||||
{groups.map(([g, list]) => (
|
||||
<GroupCard key={g} title={g} tags={list.map((d) => d.name)} />
|
||||
<GroupCard key={g} title={g} tags={list.map((d) => d.name)} {...cardProps} />
|
||||
))}
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user