Small changes and fix zooming
This commit is contained in:
parent
c69bb0d614
commit
329685a4ee
16 changed files with 823 additions and 202 deletions
|
|
@ -39,6 +39,7 @@ interface UseMapDataOptions {
|
|||
features: FeatureMeta[];
|
||||
viewFeature: string | null;
|
||||
activeFeature: string | null;
|
||||
pinnedFeature: string | null;
|
||||
travelTimeEntries: TravelTimeEntry[];
|
||||
/** Share-link code from the URL; appended to data fetches so the backend
|
||||
* grants bbox-scoped access for unlicensed recipients. */
|
||||
|
|
@ -50,6 +51,7 @@ export function useMapData({
|
|||
features,
|
||||
viewFeature,
|
||||
activeFeature,
|
||||
pinnedFeature,
|
||||
travelTimeEntries,
|
||||
shareCode,
|
||||
}: UseMapDataOptions) {
|
||||
|
|
@ -83,6 +85,10 @@ export function useMapData({
|
|||
() => (viewFeature ? (getSchoolBackendFeatureName(viewFeature) ?? viewFeature) : null),
|
||||
[viewFeature]
|
||||
);
|
||||
const pinnedDataViewFeature = useMemo(
|
||||
() => (pinnedFeature ? (getSchoolBackendFeatureName(pinnedFeature) ?? pinnedFeature) : null),
|
||||
[pinnedFeature]
|
||||
);
|
||||
|
||||
// Determine if the current viewFeature is an enum (for enum_dist param)
|
||||
const viewFeatureIsEnum = useMemo(
|
||||
|
|
@ -95,6 +101,7 @@ export function useMapData({
|
|||
(): string => buildFilterString(filters, features),
|
||||
[filters, features]
|
||||
);
|
||||
const filtersParam = useMemo(() => buildFilterParam(), [buildFilterParam]);
|
||||
|
||||
// Build the travel param string from entries with destinations.
|
||||
// Format: mode:slug[:best][:min:max] — server filters rows outside [min,max].
|
||||
|
|
@ -122,6 +129,37 @@ export function useMapData({
|
|||
);
|
||||
|
||||
const travelParam = useMemo(() => buildTravelParam(), [buildTravelParam]);
|
||||
const boundsParam = useMemo(
|
||||
() => (bounds ? `${bounds.south},${bounds.west},${bounds.north},${bounds.east}` : ''),
|
||||
[bounds]
|
||||
);
|
||||
const dataRequestKey = useMemo(
|
||||
() =>
|
||||
bounds
|
||||
? [
|
||||
usePostcodeView ? 'postcodes' : 'hexagons',
|
||||
resolution,
|
||||
boundsParam,
|
||||
filtersParam,
|
||||
dataViewFeature ?? '',
|
||||
viewFeatureIsEnum && dataViewFeature ? dataViewFeature : '',
|
||||
travelParam,
|
||||
shareCode ?? '',
|
||||
].join('|')
|
||||
: '',
|
||||
[
|
||||
bounds,
|
||||
boundsParam,
|
||||
dataViewFeature,
|
||||
filtersParam,
|
||||
resolution,
|
||||
shareCode,
|
||||
travelParam,
|
||||
usePostcodeView,
|
||||
viewFeatureIsEnum,
|
||||
]
|
||||
);
|
||||
const [loadedDataKey, setLoadedDataKey] = useState<string>('');
|
||||
|
||||
// Keep activeFeatureRef in sync
|
||||
useEffect(() => {
|
||||
|
|
@ -219,12 +257,11 @@ export function useMapData({
|
|||
|
||||
setLoading(true);
|
||||
try {
|
||||
const boundsStr = `${bounds.south},${bounds.west},${bounds.north},${bounds.east}`;
|
||||
const filtersStr = buildFilterParam();
|
||||
const requestKey = dataRequestKey;
|
||||
|
||||
if (usePostcodeView) {
|
||||
const params = new URLSearchParams({ bounds: boundsStr });
|
||||
if (filtersStr) params.set('filters', filtersStr);
|
||||
const params = new URLSearchParams({ bounds: boundsParam });
|
||||
if (filtersParam) params.set('filters', filtersParam);
|
||||
params.set(
|
||||
'fields',
|
||||
dataViewFeature && !dataViewFeature.startsWith('tt_') ? dataViewFeature : ''
|
||||
|
|
@ -254,12 +291,13 @@ export function useMapData({
|
|||
const json: { features: PostcodeFeature[] } = await res.json();
|
||||
setPostcodeData(json.features);
|
||||
setRawData([]);
|
||||
setLoadedDataKey(requestKey);
|
||||
} else {
|
||||
const params = new URLSearchParams({
|
||||
resolution: resolution.toString(),
|
||||
bounds: boundsStr,
|
||||
bounds: boundsParam,
|
||||
});
|
||||
if (filtersStr) params.set('filters', filtersStr);
|
||||
if (filtersParam) params.set('filters', filtersParam);
|
||||
params.set(
|
||||
'fields',
|
||||
dataViewFeature && !dataViewFeature.startsWith('tt_') ? dataViewFeature : ''
|
||||
|
|
@ -289,6 +327,7 @@ export function useMapData({
|
|||
const json: ApiResponse = await res.json();
|
||||
setRawData(json.features);
|
||||
setPostcodeData([]);
|
||||
setLoadedDataKey(requestKey);
|
||||
}
|
||||
|
||||
// Clear drag data when committed fetch completes and we're not mid-drag
|
||||
|
|
@ -315,7 +354,9 @@ export function useMapData({
|
|||
resolution,
|
||||
bounds,
|
||||
filters,
|
||||
buildFilterParam,
|
||||
filtersParam,
|
||||
boundsParam,
|
||||
dataRequestKey,
|
||||
dataViewFeature,
|
||||
viewFeatureIsEnum,
|
||||
usePostcodeView,
|
||||
|
|
@ -377,8 +418,8 @@ export function useMapData({
|
|||
];
|
||||
}, [dataViewFeature, rawData, postcodeData, usePostcodeView, features, bounds]);
|
||||
|
||||
// Color range for the legend and hex coloring
|
||||
const colorRange = useMemo((): [number, number] | null => {
|
||||
// Live color range for the legend and hex coloring.
|
||||
const liveColorRange = useMemo((): [number, number] | null => {
|
||||
if (!dataViewFeature) return null;
|
||||
|
||||
// Travel time keys: use dataRange directly (no FeatureMeta)
|
||||
|
|
@ -396,6 +437,61 @@ export function useMapData({
|
|||
return null;
|
||||
}, [dataViewFeature, features, dataRange]);
|
||||
|
||||
const isEyePreviewingPinnedFeature =
|
||||
!activeFeature && dataViewFeature != null && dataViewFeature === pinnedDataViewFeature;
|
||||
|
||||
const [frozenPreviewRange, setFrozenPreviewRange] = useState<{
|
||||
feature: string;
|
||||
range: [number, number];
|
||||
} | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
setFrozenPreviewRange((prev) => {
|
||||
if (!pinnedDataViewFeature) return prev ? null : prev;
|
||||
return prev?.feature === pinnedDataViewFeature ? prev : null;
|
||||
});
|
||||
}, [pinnedDataViewFeature]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isEyePreviewingPinnedFeature || !pinnedDataViewFeature) return;
|
||||
|
||||
const meta = pinnedDataViewFeature.startsWith('tt_')
|
||||
? null
|
||||
: features.find((f) => f.name === pinnedDataViewFeature);
|
||||
const rangeToFreeze =
|
||||
dataRange && loadedDataKey === dataRequestKey
|
||||
? dataRange
|
||||
: meta?.type === 'enum' && liveColorRange
|
||||
? liveColorRange
|
||||
: null;
|
||||
if (!rangeToFreeze) return;
|
||||
|
||||
setFrozenPreviewRange((prev) =>
|
||||
prev?.feature === pinnedDataViewFeature
|
||||
? prev
|
||||
: { feature: pinnedDataViewFeature, range: rangeToFreeze }
|
||||
);
|
||||
}, [
|
||||
dataRange,
|
||||
dataRequestKey,
|
||||
features,
|
||||
isEyePreviewingPinnedFeature,
|
||||
loadedDataKey,
|
||||
liveColorRange,
|
||||
pinnedDataViewFeature,
|
||||
]);
|
||||
|
||||
const colorRange = useMemo((): [number, number] | null => {
|
||||
if (
|
||||
isEyePreviewingPinnedFeature &&
|
||||
frozenPreviewRange &&
|
||||
frozenPreviewRange.feature === dataViewFeature
|
||||
) {
|
||||
return frozenPreviewRange.range;
|
||||
}
|
||||
return liveColorRange;
|
||||
}, [dataViewFeature, frozenPreviewRange, isEyePreviewingPinnedFeature, liveColorRange]);
|
||||
|
||||
const handleViewChange = useCallback(
|
||||
({
|
||||
resolution: newRes,
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
import { useState, useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import type { Step, CallBackProps } from 'react-joyride';
|
||||
import { ACTIONS, EVENTS, STATUS } from 'react-joyride';
|
||||
|
||||
const STORAGE_KEY = 'tutorial_completed';
|
||||
const JOYRIDE_ACTION_CLOSE = 'close';
|
||||
const JOYRIDE_EVENT_STEP_AFTER = 'step:after';
|
||||
const JOYRIDE_STATUS_FINISHED = 'finished';
|
||||
const JOYRIDE_STATUS_SKIPPED = 'skipped';
|
||||
|
||||
export function useTutorial(initialLoading: boolean, isMobile: boolean, blocked = false) {
|
||||
const { t } = useTranslation();
|
||||
|
|
@ -67,12 +70,12 @@ export function useTutorial(initialLoading: boolean, isMobile: boolean, blocked
|
|||
const handleCallback = useCallback((data: CallBackProps) => {
|
||||
const { status, action, type } = data;
|
||||
|
||||
if (status === STATUS.FINISHED || status === STATUS.SKIPPED) {
|
||||
if (status === JOYRIDE_STATUS_FINISHED || status === JOYRIDE_STATUS_SKIPPED) {
|
||||
localStorage.setItem(STORAGE_KEY, '1');
|
||||
setRun(false);
|
||||
}
|
||||
// Also stop if user closes a tooltip via the X button
|
||||
if (action === ACTIONS.CLOSE && type === EVENTS.STEP_AFTER) {
|
||||
if (action === JOYRIDE_ACTION_CLOSE && type === JOYRIDE_EVENT_STEP_AFTER) {
|
||||
localStorage.setItem(STORAGE_KEY, '1');
|
||||
setRun(false);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue