style(details): size the Info tab fields to their contents
Six equal columns gave a US county name the same width as a two-digit CQ zone: the county truncated while the zones, the prefix and the DXCC number sat mostly empty. The row is twelve columns now, spans set per field. The bearing and the two distances are read-only and never exceed "12345 km", so they move to a flex row at a fixed width and the address — the one field here that was never wide enough — takes the remainder.
This commit is contained in:
@@ -122,9 +122,17 @@ function numOrUndef(v: string): number | undefined {
|
||||
}
|
||||
|
||||
// Compact field helper to keep the JSX dense.
|
||||
function Field({ label, span = 1, children }: { label: string; span?: 1 | 2 | 3 | 4 | 6; children: React.ReactNode }) {
|
||||
// Written out rather than built as `col-span-${n}`: Tailwind scans for literal
|
||||
// class names, so an interpolated one is never emitted.
|
||||
const SPAN_CLASS: Record<number, string> = {
|
||||
1: '', 2: 'col-span-2', 3: 'col-span-3', 4: 'col-span-4', 5: 'col-span-5',
|
||||
6: 'col-span-6', 7: 'col-span-7', 8: 'col-span-8', 9: 'col-span-9',
|
||||
10: 'col-span-10', 11: 'col-span-11', 12: 'col-span-12',
|
||||
};
|
||||
|
||||
function Field({ label, span = 1, className, children }: { label: string; span?: number; className?: string; children: React.ReactNode }) {
|
||||
return (
|
||||
<div className={cn('flex flex-col min-w-0', span === 2 && 'col-span-2', span === 3 && 'col-span-3', span === 4 && 'col-span-4', span === 6 && 'col-span-6')}>
|
||||
<div className={cn('flex flex-col min-w-0', SPAN_CLASS[span], className)}>
|
||||
<Label className="mb-1">{label}</Label>
|
||||
{children}
|
||||
</div>
|
||||
@@ -243,14 +251,18 @@ export function DetailsPanel({ callsign, prefix, operatorGrid, remoteGrid, qth,
|
||||
)}
|
||||
|
||||
{open === 'info' && (
|
||||
<div className="grid grid-cols-6 gap-2 px-3 py-2.5">
|
||||
<Field label={t('detp.statePref')}>
|
||||
<div className="grid grid-cols-12 gap-2 px-3 py-2.5">
|
||||
{/* Twelve columns, not six: the fields hold very different things. A US
|
||||
county name is long, a CQ zone is two digits — on an even six-column
|
||||
grid they were the same width, so the county truncated while the
|
||||
zones sat mostly empty. */}
|
||||
<Field label={t('detp.statePref')} span={2}>
|
||||
<Input value={details.state} onChange={(e) => onChange({ state: e.target.value })} />
|
||||
</Field>
|
||||
<Field label={t('detp.county')}>
|
||||
<Field label={t('detp.county')} span={4}>
|
||||
<Input value={details.cnty} onChange={(e) => onChange({ cnty: e.target.value })} />
|
||||
</Field>
|
||||
<Field label={t('detp.prefix')}>
|
||||
<Field label={t('detp.prefix')} span={2}>
|
||||
<Input className="font-mono uppercase" value={prefix} readOnly tabIndex={-1} />
|
||||
</Field>
|
||||
<Field label={t('detp.cqZone')}>
|
||||
@@ -264,7 +276,7 @@ export function DetailsPanel({ callsign, prefix, operatorGrid, remoteGrid, qth,
|
||||
{/* DXCC # closes the top row (next to the zones); Continent and
|
||||
Azimuth SP live in the main entry strip / bandeau. The long-path
|
||||
bearing and distances move to the row below. */}
|
||||
<Field label={t('detp.dxcc')}>
|
||||
<Field label={t('detp.dxcc')} span={2}>
|
||||
<Input
|
||||
readOnly
|
||||
tabIndex={-1}
|
||||
@@ -273,40 +285,45 @@ export function DetailsPanel({ callsign, prefix, operatorGrid, remoteGrid, qth,
|
||||
placeholder="—"
|
||||
/>
|
||||
</Field>
|
||||
<Field label={t('detp.azimuthLp')}>
|
||||
<Input
|
||||
readOnly
|
||||
tabIndex={-1}
|
||||
className="font-mono bg-muted/40 cursor-default"
|
||||
value={path ? fmtDeg(path.bearingLong) : ''}
|
||||
placeholder="—"
|
||||
/>
|
||||
</Field>
|
||||
<Field label={t('detp.distanceSp')}>
|
||||
<Input
|
||||
readOnly
|
||||
tabIndex={-1}
|
||||
className="font-mono bg-muted/40 cursor-default"
|
||||
value={path ? fmtKm(path.distanceShort) : ''}
|
||||
placeholder="—"
|
||||
/>
|
||||
</Field>
|
||||
<Field label={t('detp.distanceLp')}>
|
||||
<Input
|
||||
readOnly
|
||||
tabIndex={-1}
|
||||
className="font-mono bg-muted/40 cursor-default"
|
||||
value={path ? fmtKm(path.distanceLong) : ''}
|
||||
placeholder="—"
|
||||
/>
|
||||
</Field>
|
||||
<Field label={t('detp.address')} span={3}>
|
||||
<Input value={details.address} onChange={(e) => onChange({ address: e.target.value })} />
|
||||
</Field>
|
||||
<Field label={t('detp.qslMessage')} span={3}>
|
||||
{/* The bearing and the two distances are read-only and hold at most
|
||||
"12345 km", so they take a fixed width and the address — the one
|
||||
field here that is never wide enough — gets everything left over. */}
|
||||
<div className="col-span-12 flex gap-2">
|
||||
<Field label={t('detp.azimuthLp')} className="w-20 shrink-0">
|
||||
<Input
|
||||
readOnly
|
||||
tabIndex={-1}
|
||||
className="font-mono bg-muted/40 cursor-default"
|
||||
value={path ? fmtDeg(path.bearingLong) : ''}
|
||||
placeholder="—"
|
||||
/>
|
||||
</Field>
|
||||
<Field label={t('detp.distanceSp')} className="w-24 shrink-0">
|
||||
<Input
|
||||
readOnly
|
||||
tabIndex={-1}
|
||||
className="font-mono bg-muted/40 cursor-default"
|
||||
value={path ? fmtKm(path.distanceShort) : ''}
|
||||
placeholder="—"
|
||||
/>
|
||||
</Field>
|
||||
<Field label={t('detp.distanceLp')} className="w-24 shrink-0">
|
||||
<Input
|
||||
readOnly
|
||||
tabIndex={-1}
|
||||
className="font-mono bg-muted/40 cursor-default"
|
||||
value={path ? fmtKm(path.distanceLong) : ''}
|
||||
placeholder="—"
|
||||
/>
|
||||
</Field>
|
||||
<Field label={t('detp.address')} className="flex-1 min-w-0">
|
||||
<Input value={details.address} onChange={(e) => onChange({ address: e.target.value })} />
|
||||
</Field>
|
||||
</div>
|
||||
<Field label={t('detp.qslMessage')} span={7}>
|
||||
<Input value={details.qsl_msg} onChange={(e) => onChange({ qsl_msg: e.target.value })} />
|
||||
</Field>
|
||||
<Field label={t('detp.qslVia')} span={2}>
|
||||
<Field label={t('detp.qslVia')} span={5}>
|
||||
<Input value={details.qsl_via} onChange={(e) => onChange({ qsl_via: e.target.value })} />
|
||||
</Field>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user