UI changes
This commit is contained in:
parent
0aba73a2a3
commit
8616837c01
13 changed files with 126 additions and 100 deletions
|
|
@ -24,7 +24,7 @@ import {
|
|||
POI_CLUSTER_MAX_ZOOM,
|
||||
} from '../lib/consts';
|
||||
import { emojiToTwemojiUrl, getFeatureFillColor } from '../lib/map-utils';
|
||||
import { type TravelTimeEntry, travelFieldKey } from './useTravelTime';
|
||||
import type { TravelTimeEntry } from './useTravelTime';
|
||||
import { MarchingAntsExtension } from '../lib/MarchingAntsExtension';
|
||||
|
||||
interface UseDeckLayersProps {
|
||||
|
|
@ -120,9 +120,6 @@ export function useDeckLayers({
|
|||
const hoveredPostcodeRef = useRef(hoveredPostcode);
|
||||
hoveredPostcodeRef.current = hoveredPostcode;
|
||||
|
||||
const travelTimeEntriesRef = useRef(travelTimeEntries);
|
||||
travelTimeEntriesRef.current = travelTimeEntries;
|
||||
|
||||
const colorFeatureMeta = useMemo(
|
||||
() => (viewFeature ? features.find((f) => f.name === viewFeature) || null : null),
|
||||
[viewFeature, features]
|
||||
|
|
@ -302,28 +299,6 @@ export function useDeckLayers({
|
|||
getHexagon: (d) => d.h3,
|
||||
getFillColor: (d) => {
|
||||
const dark = isDarkRef.current;
|
||||
const entries = travelTimeEntriesRef.current;
|
||||
|
||||
// Dim-filter: all travel entries with timeRange dim hexagons outside range
|
||||
for (let i = 0; i < entries.length; i++) {
|
||||
const entry = entries[i];
|
||||
if (!entry.timeRange || !entry.slug) continue;
|
||||
const fk = travelFieldKey(entry);
|
||||
const modeVal = d[`avg_${fk}`];
|
||||
if (
|
||||
modeVal == null ||
|
||||
(modeVal as number) < entry.timeRange[0] ||
|
||||
(modeVal as number) > entry.timeRange[1]
|
||||
) {
|
||||
return (dark ? [60, 55, 50, 60] : [180, 180, 180, 60]) as [
|
||||
number,
|
||||
number,
|
||||
number,
|
||||
number,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
const vf = viewFeatureRef.current;
|
||||
const clr = colorRangeRef.current;
|
||||
const fr = filterRangeRef.current;
|
||||
|
|
@ -425,28 +400,6 @@ export function useDeckLayers({
|
|||
getFillColor: (f) => {
|
||||
const d = f.properties;
|
||||
const dark = isDarkRef.current;
|
||||
const entries = travelTimeEntriesRef.current;
|
||||
|
||||
// Dim-filter: all travel entries with timeRange dim postcodes outside range
|
||||
for (let i = 0; i < entries.length; i++) {
|
||||
const entry = entries[i];
|
||||
if (!entry.timeRange || !entry.slug) continue;
|
||||
const fk = travelFieldKey(entry);
|
||||
const modeVal = d[`avg_${fk}`];
|
||||
if (
|
||||
modeVal == null ||
|
||||
(modeVal as number) < entry.timeRange[0] ||
|
||||
(modeVal as number) > entry.timeRange[1]
|
||||
) {
|
||||
return (dark ? [60, 55, 50, 60] : [180, 180, 180, 60]) as [
|
||||
number,
|
||||
number,
|
||||
number,
|
||||
number,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
const vf = viewFeatureRef.current;
|
||||
const clr = colorRangeRef.current;
|
||||
const fr = filterRangeRef.current;
|
||||
|
|
|
|||
|
|
@ -120,6 +120,20 @@ export function useFilters({ initialFilters, features }: UseFiltersOptions) {
|
|||
dragValueRef.current = null;
|
||||
}, []);
|
||||
|
||||
/** End drag without committing to filters — caller handles the commit (e.g. travel time). */
|
||||
const handleDragEndNoCommit = useCallback((): [number, number] | null => {
|
||||
if (pendingDragRef.current) {
|
||||
pendingDragRef.current = null;
|
||||
return null;
|
||||
}
|
||||
const dv = dragValueRef.current;
|
||||
setActiveFeature(null);
|
||||
setDragValue(null);
|
||||
dragActiveRef.current = null;
|
||||
dragValueRef.current = null;
|
||||
return dv;
|
||||
}, []);
|
||||
|
||||
const handleSetFilters = useCallback((newFilters: FeatureFilters) => {
|
||||
setFilters(newFilters);
|
||||
setActiveFeature(null);
|
||||
|
|
@ -159,6 +173,7 @@ export function useFilters({ initialFilters, features }: UseFiltersOptions) {
|
|||
handleDragStart,
|
||||
handleDragChange,
|
||||
handleDragEnd,
|
||||
handleDragEndNoCommit,
|
||||
handleTogglePin,
|
||||
handleSetPin,
|
||||
handleCancelPin,
|
||||
|
|
|
|||
|
|
@ -81,25 +81,34 @@ export function useMapData({
|
|||
);
|
||||
|
||||
// Build the travel param string from entries with destinations.
|
||||
// timeRange is NOT included — range filtering is handled purely client-side
|
||||
// (dimming in useDeckLayers) so slider changes never trigger server refetches.
|
||||
const travelParam = useMemo((): string => {
|
||||
const segments: string[] = [];
|
||||
for (const entry of travelTimeEntries) {
|
||||
if (!entry.slug) continue;
|
||||
let seg = `${entry.mode}:${entry.slug}`;
|
||||
if (entry.useBest) seg += ':best';
|
||||
segments.push(seg);
|
||||
}
|
||||
return segments.join('|');
|
||||
}, [travelTimeEntries]);
|
||||
// Format: mode:slug[:best][:min:max] — server filters rows outside [min,max].
|
||||
// When excludeFieldKey is set, that entry's time range is omitted (for drag preview).
|
||||
const buildTravelParam = useCallback(
|
||||
(excludeFieldKey?: string): string => {
|
||||
const segments: string[] = [];
|
||||
for (const entry of travelTimeEntries) {
|
||||
if (!entry.slug) continue;
|
||||
let seg = `${entry.mode}:${entry.slug}`;
|
||||
if (entry.useBest) seg += ':best';
|
||||
const isExcluded = excludeFieldKey === `tt_${entry.mode}_${entry.slug}`;
|
||||
if (entry.timeRange && !isExcluded) seg += `:${entry.timeRange[0]}:${entry.timeRange[1]}`;
|
||||
segments.push(seg);
|
||||
}
|
||||
return segments.join('|');
|
||||
},
|
||||
[travelTimeEntries]
|
||||
);
|
||||
|
||||
const travelParam = useMemo(() => buildTravelParam(), [buildTravelParam]);
|
||||
|
||||
// Keep activeFeatureRef in sync
|
||||
useEffect(() => {
|
||||
activeFeatureRef.current = activeFeature;
|
||||
}, [activeFeature]);
|
||||
|
||||
// Drag prefetch: when activeFeature starts, fetch data excluding that filter
|
||||
// Drag prefetch: when activeFeature starts, fetch data excluding that filter.
|
||||
// For regular filters: excludes the filter from the filter string.
|
||||
// For travel time: excludes the time range from that entry's travel param segment.
|
||||
useEffect(() => {
|
||||
if (!activeFeature || !bounds) return;
|
||||
|
||||
|
|
@ -108,11 +117,14 @@ export function useMapData({
|
|||
|
||||
const filtersStr = buildFilterString(filters, features, activeFeature);
|
||||
const boundsStr = `${bounds.south},${bounds.west},${bounds.north},${bounds.east}`;
|
||||
const isTravelTimeDrag = activeFeature.startsWith('tt_');
|
||||
const dragTravelParam = isTravelTimeDrag ? buildTravelParam(activeFeature) : travelParam;
|
||||
|
||||
if (usePostcodeView) {
|
||||
const params = new URLSearchParams({ bounds: boundsStr });
|
||||
if (filtersStr) params.set('filters', filtersStr);
|
||||
params.set('fields', activeFeature);
|
||||
if (dragTravelParam) params.set('travel', dragTravelParam);
|
||||
|
||||
fetch(apiUrl('postcodes', params), authHeaders({ signal: dragAbortRef.current.signal }))
|
||||
.then((res) => res.json())
|
||||
|
|
@ -129,7 +141,7 @@ export function useMapData({
|
|||
});
|
||||
if (filtersStr) params.set('filters', filtersStr);
|
||||
params.set('fields', activeFeature);
|
||||
if (travelParam) params.set('travel', travelParam);
|
||||
if (dragTravelParam) params.set('travel', dragTravelParam);
|
||||
|
||||
fetch(apiUrl('hexagons', params), authHeaders({ signal: dragAbortRef.current.signal }))
|
||||
.then((res) => res.json())
|
||||
|
|
@ -147,7 +159,7 @@ export function useMapData({
|
|||
dragAbortRef.current = null;
|
||||
}
|
||||
};
|
||||
}, [activeFeature, bounds, resolution, filters, features, usePostcodeView, travelParam]);
|
||||
}, [activeFeature, bounds, resolution, filters, features, usePostcodeView, travelParam, buildTravelParam]);
|
||||
|
||||
// Fetch hexagons or postcodes when bounds/filters change
|
||||
useEffect(() => {
|
||||
|
|
|
|||
|
|
@ -24,10 +24,11 @@ export function usePaneResize(
|
|||
const handlePointerMove = useCallback(
|
||||
(e: React.PointerEvent) => {
|
||||
if (!draggingRef.current) return;
|
||||
const resolvedMax = maxWidth <= 1 ? window.innerWidth * maxWidth : maxWidth;
|
||||
const newWidth =
|
||||
side === 'left'
|
||||
? Math.min(maxWidth, Math.max(minWidth, e.clientX))
|
||||
: Math.min(maxWidth, Math.max(minWidth, window.innerWidth - e.clientX));
|
||||
? Math.min(resolvedMax, Math.max(minWidth, e.clientX))
|
||||
: Math.min(resolvedMax, Math.max(minWidth, window.innerWidth - e.clientX));
|
||||
setWidth(newWidth);
|
||||
},
|
||||
[side, minWidth, maxWidth]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue