feat(cluster): single click fills the call, double click works the spot

Reading the cluster means running down dozens of lines, and a single
click did the whole job — QSY, mode, callsign — so every line looked at
dragged the radio with it. One stray click while reading took the
operator off the station they were working.

Looking and going are two different intentions, so they are two gestures
now. The band map is unchanged: clicking a spot there is already a
deliberate act, not a way of reading a list.
This commit is contained in:
2026-08-24 19:08:33 +02:00
parent 0848c275d2
commit 4e9b1eebe9
3 changed files with 32 additions and 5 deletions
+13 -2
View File
@@ -1,7 +1,7 @@
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import {
AllCommunityModule, ModuleRegistry,
type ColDef, type ColumnState, type GridReadyEvent, type RowClickedEvent,
type ColDef, type ColumnState, type GridReadyEvent, type RowClickedEvent, type RowDoubleClickedEvent,
} from 'ag-grid-community';
import { hamlogGridTheme } from '@/lib/gridTheme';
import { AgGridReact } from 'ag-grid-react';
@@ -75,7 +75,13 @@ export type SpotStatusEntry = {
type Props = {
rows: ClusterSpot[];
spotStatus: Record<string, SpotStatusEntry>;
// A DOUBLE click works the spot: QSY, mode, callsign, the lot.
onSpotClick?: (s: ClusterSpot) => void;
// A SINGLE click only fills the callsign. Reading the cluster means passing
// over dozens of lines, and every pass used to drag the radio with it — one
// stray click while looking took the operator off the station they were
// working. Looking and going are now two different gestures.
onSpotSelect?: (s: ClusterSpot) => void;
};
const COL_STATE_KEY = 'hamlog.clusterColState.v1';
@@ -500,7 +506,7 @@ const GROUP_ORDER = ['Spot', 'Geo'];
const CLG_GRP_KEYS: Record<string, string> = { Spot: 'clg2.grpSpot', Geo: 'clg2.grpGeo' };
const groupLabel = (t: TFn, g: string): string => t(CLG_GRP_KEYS[g] ?? g);
export function ClusterGrid({ rows, spotStatus, onSpotClick }: Props) {
export function ClusterGrid({ rows, spotStatus, onSpotClick, onSpotSelect }: Props) {
const { t } = useI18n();
const gridRef = useRef<any>(null);
const [pickerOpen, setPickerOpen] = useState(false);
@@ -583,6 +589,10 @@ export function ClusterGrid({ rows, spotStatus, onSpotClick }: Props) {
}, []);
function handleRowClicked(e: RowClickedEvent<ClusterSpot>) {
if (e.data && onSpotSelect) onSpotSelect(e.data);
}
function handleRowDoubleClicked(e: RowDoubleClickedEvent<ClusterSpot>) {
if (e.data && onSpotClick) onSpotClick(e.data);
}
@@ -647,6 +657,7 @@ export function ClusterGrid({ rows, spotStatus, onSpotClick }: Props) {
onColumnVisible={saveColumnState}
onSortChanged={saveColumnState}
onRowClicked={handleRowClicked}
onRowDoubleClicked={handleRowDoubleClicked}
animateRows={false}
suppressCellFocus
getRowId={(p) => `${(p.data as any).received_at}-${(p.data as any).dx_call}-${(p.data as any).source_id}`}