Skip to content

Commit 3363dff

Browse files
committed
Directions: Move from/to to array of points
1 parent 2baa443 commit 3363dff

File tree

11 files changed

+175
-91
lines changed

11 files changed

+175
-91
lines changed

src/components/Directions/DirectionsAutocomplete.tsx

Lines changed: 21 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
InputAdornment,
1515
TextField,
1616
Tooltip,
17+
useTheme,
1718
} from '@mui/material';
1819
import { useMapCenter } from '../SearchBox/utils';
1920
import { useUserThemeContext } from '../../helpers/theme';
@@ -30,8 +31,9 @@ import { AlphabeticalMarker } from './TextMarker';
3031
import MyLocationIcon from '@mui/icons-material/MyLocation';
3132
import { DotLoader, useIsClient } from '../helpers';
3233
import { useSnackbar } from '../utils/SnackbarContext';
33-
import { useGetOnSubmitFactory } from './useGetOnSubmit';
34+
import { useGetOnSubmitFactory, useUpdatePoint } from './useGetOnSubmit';
3435
import { useDirectionsContext } from './DirectionsContext';
36+
import { removeElementOnIndex } from '../FeaturePanel/Climbing/utils/array';
3537
const DotLoaderContainer = styled.div`
3638
font-size: 16px;
3739
right: 6px;
@@ -60,6 +62,7 @@ const DirectionsInput = ({
6062
const { InputLabelProps, InputProps, ...restParams } = params;
6163
const isClient = useIsClient();
6264
const { showToast } = useSnackbar();
65+
const theme = useTheme();
6366

6467
useEffect(() => {
6568
// @ts-ignore
@@ -171,15 +174,16 @@ const Row = styled.div`
171174
`;
172175

173176
const useInputMapClickOverride = (
174-
setValue: (value: Option) => void,
177+
pointIndex: number,
175178
setInputValue: (value: string) => void,
176179
selectedOptionInputValue: React.MutableRefObject<string | null>,
177180
) => {
178181
const { mapClickOverrideRef } = useMapStateContext();
179182
const previousBehaviourRef = useRef<MapClickOverride>();
183+
const updatePoint = useUpdatePoint();
180184

181185
const mapClickCallback = (coords: LonLat, label: string) => {
182-
setValue(getCoordsOption(coords, label));
186+
updatePoint(pointIndex, getCoordsOption(coords, label));
183187
setInputValue(label);
184188
selectedOptionInputValue.current = label;
185189

@@ -203,23 +207,18 @@ const useInputMapClickOverride = (
203207
type Props = {
204208
label: string;
205209
value: Option;
206-
setValue: (value: Option) => void;
207210
pointIndex: number;
208211
};
209212

210-
export const DirectionsAutocomplete = ({
211-
label,
212-
value,
213-
setValue,
214-
pointIndex,
215-
}: Props) => {
213+
export const DirectionsAutocomplete = ({ label, value, pointIndex }: Props) => {
216214
const autocompleteRef = useRef();
217215
const { inputValue, setInputValue } = useInputValueState();
218216
const selectedOptionInputValue = useRef<string | null>(null);
219217
const mapCenter = useMapCenter();
220218
const { currentTheme } = useUserThemeContext();
221219
const { userSettings } = useUserSettingsContext();
222220
const { isImperial } = userSettings;
221+
const updatePoint = useUpdatePoint();
223222

224223
const ALPHABETICAL_MARKER = useMemo(() => {
225224
let svgElement;
@@ -239,7 +238,9 @@ export const DirectionsAutocomplete = ({
239238
}, [pointIndex]);
240239

241240
const markerRef = useRef<maplibregl.Marker>();
242-
const { from, to, mode, setResult, setLoading } = useDirectionsContext();
241+
const { points, mode, setResult, setLoading, setPoints } =
242+
useDirectionsContext();
243+
const submitFactory = useGetOnSubmitFactory(setResult, setLoading);
243244

244245
useEffect(() => {
245246
const map = getGlobalMap();
@@ -254,20 +255,15 @@ export const DirectionsAutocomplete = ({
254255
}, [ALPHABETICAL_MARKER, value]);
255256

256257
const handleUpdate = (coordsOption: Option) => {
257-
if (pointIndex === 0) {
258-
submitFactory(coordsOption, to, mode);
259-
}
260-
if (pointIndex === 1) {
261-
submitFactory(from, coordsOption, mode);
262-
}
258+
const newPoints = updatePoint(pointIndex, coordsOption);
259+
submitFactory(newPoints, mode);
263260
};
264261

265-
const submitFactory = useGetOnSubmitFactory(setResult, setLoading);
266262
const onDragEnd = () => {
267263
const lngLat = markerRef.current?.getLngLat();
268264
if (lngLat) {
269265
const coordsOption = getCoordsOption([lngLat.lng, lngLat.lat]);
270-
setValue(coordsOption);
266+
updatePoint(pointIndex, coordsOption);
271267
handleUpdate(coordsOption);
272268
}
273269
};
@@ -279,13 +275,14 @@ export const DirectionsAutocomplete = ({
279275
const onChange = (_: unknown, option: Option) => {
280276
console.log('selected', option); // eslint-disable-line no-console
281277
setInputValue(getOptionLabel(option));
282-
setValue(option);
278+
updatePoint(pointIndex, option);
279+
283280
selectedOptionInputValue.current = getOptionLabel(option);
284281
handleUpdate(option);
285282
};
286283

287284
const { onInputFocus, onInputBlur } = useInputMapClickOverride(
288-
setValue,
285+
pointIndex,
289286
setInputValue,
290287
selectedOptionInputValue,
291288
);
@@ -296,8 +293,8 @@ export const DirectionsAutocomplete = ({
296293
if (selectedOptionInputValue.current !== inputValue) {
297294
if (options.length > 0 && inputValue) {
298295
onChange(null, options[0]);
299-
} else {
300-
setValue(null);
296+
} else if (points) {
297+
setPoints(removeElementOnIndex(points, pointIndex));
301298
}
302299
}
303300
};
@@ -321,6 +318,7 @@ export const DirectionsAutocomplete = ({
321318
getOptionKey={(option) => JSON.stringify(option)}
322319
onChange={onChange}
323320
autoComplete
321+
noOptionsText=""
324322
disableClearable
325323
autoHighlight
326324
clearOnEscape

src/components/Directions/DirectionsBox.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,10 @@ const Wrapper = styled(Stack)<{ $isMobileMode: boolean }>`
1515
right: 8px;
1616
z-index: 1001; // over the LayerSwitcherButton
1717
max-height: calc(100vh - 16px);
18+
box-shadow: 0 10px 20px 0 rgba(0, 0, 0, 0.12);
1819
1920
@media ${isTabletResolution} {
20-
max-width: 340px;
21+
max-width: 394px;
2122
}
2223
`;
2324

src/components/Directions/DirectionsContext.tsx

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,15 @@ import React, { createContext, useContext, useState } from 'react';
22
import { Profile, RoutingResult } from './routing/types';
33
import { Option } from '../SearchBox/types';
44

5-
type CragViewLayout = 'vertical' | 'horizontal' | 'auto';
6-
75
type DirectionsContextType = {
86
loading: boolean;
97
setLoading: (loading: boolean) => void;
108
mode: Profile;
119
setMode: (mode: Profile) => void;
12-
from: Option;
13-
setFrom: (from: Option) => void;
14-
to: Option;
15-
setTo: (to: Option) => void;
1610
result: RoutingResult;
1711
setResult: (result: RoutingResult) => void;
12+
points: Array<Option>;
13+
setPoints: (points: Array<Option>) => void;
1814
};
1915

2016
export const DirectionsContext =
@@ -23,21 +19,18 @@ export const DirectionsContext =
2319
export const DirectionsProvider: React.FC = ({ children }) => {
2420
const [loading, setLoading] = useState(false);
2521
const [mode, setMode] = useState<Profile>('car');
26-
const [from, setFrom] = useState<Option>();
27-
const [to, setTo] = useState<Option>();
2822
const [result, setResult] = useState<RoutingResult>(null);
23+
const [points, setPoints] = useState<Array<Option>>([]);
2924

3025
const value: DirectionsContextType = {
3126
loading,
3227
setLoading,
3328
mode,
3429
setMode,
35-
from,
36-
setFrom,
37-
to,
38-
setTo,
3930
result,
4031
setResult,
32+
points,
33+
setPoints,
4134
};
4235
return (
4336
<DirectionsContext.Provider value={value}>

src/components/Directions/DirectionsForm.tsx

Lines changed: 101 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { Option } from '../SearchBox/types';
22
import { RoutingResult } from './routing/types';
33
import { CloseButton } from './helpers';
4-
import React, { useEffect } from 'react';
4+
import React, { useEffect, useMemo, useState } from 'react';
55
import { StyledPaper } from './Result';
6-
import { Stack } from '@mui/material';
6+
import { Box, Stack } from '@mui/material';
77
import { ModeToggler } from './ModeToggler';
88
import { DirectionsAutocomplete } from './DirectionsAutocomplete';
99
import { t } from '../../services/intl';
@@ -12,46 +12,105 @@ import SearchIcon from '@mui/icons-material/Search';
1212
import { getCoordsOption } from '../SearchBox/options/coords';
1313
import { useMapStateContext } from '../utils/MapStateContext';
1414
import { useDirectionsContext } from './DirectionsContext';
15-
import { useGetOnSubmitFactory, useReactToUrl } from './useGetOnSubmit';
15+
import {
16+
useGetOnSubmitFactory,
17+
useReactToUrl,
18+
useUpdatePoint,
19+
} from './useGetOnSubmit';
1620

1721
type Props = {
1822
setResult: (result: RoutingResult) => void;
1923
hideForm: boolean;
2024
};
2125

2226
const useGlobalMapClickOverride = (
23-
from: Option,
24-
setFrom: (value: Option) => void,
25-
setTo: (value: Option) => void,
27+
points: Array<Option>,
28+
setPoints: (points: Array<Option>) => void,
2629
) => {
27-
const { setResult, setLoading, to, mode } = useDirectionsContext();
30+
const { setResult, setLoading, mode } = useDirectionsContext();
2831
const submitFactory = useGetOnSubmitFactory(setResult, setLoading);
32+
const updatePoint = useUpdatePoint();
2933

3034
const { mapClickOverrideRef } = useMapStateContext();
3135
useEffect(() => {
3236
mapClickOverrideRef.current = (coords, label) => {
33-
const coordinates = getCoordsOption(coords, label);
34-
if (!from) {
35-
submitFactory(coordinates, to, mode);
36-
setFrom(coordinates);
37-
} else {
38-
submitFactory(from, coordinates, mode);
39-
setTo(coordinates);
37+
if (points) {
38+
const coordinates = getCoordsOption(coords, label);
39+
if (!points[0]) {
40+
const newPoints = updatePoint(0, coordinates);
41+
submitFactory(newPoints, mode);
42+
} else {
43+
const newPoints = updatePoint(1, coordinates);
44+
submitFactory(newPoints, mode);
45+
}
4046
}
4147
};
4248

4349
return () => {
4450
mapClickOverrideRef.current = undefined;
4551
};
46-
}, [from, mapClickOverrideRef, mode, setFrom, setTo, submitFactory, to]);
52+
}, [
53+
mapClickOverrideRef,
54+
mode,
55+
points,
56+
setPoints,
57+
submitFactory,
58+
updatePoint,
59+
]);
60+
};
61+
62+
type InputItem = {
63+
value: Option;
64+
pointIndex: number;
65+
label: string;
4766
};
4867

4968
export const DirectionsForm = ({ setResult, hideForm }: Props) => {
50-
const { loading, setLoading, mode, setMode, from, setFrom, to, setTo } =
69+
const defaultFrom = {
70+
value: null,
71+
pointIndex: 0,
72+
label: t('directions.form.start_or_click'),
73+
};
74+
75+
const defaultTo = useMemo(
76+
() => ({
77+
value: null,
78+
pointIndex: 1,
79+
label: t('directions.form.destination'),
80+
}),
81+
[],
82+
);
83+
84+
const { loading, setLoading, mode, setMode, points, setPoints } =
5185
useDirectionsContext();
5286

53-
useGlobalMapClickOverride(from, setFrom, setTo);
54-
useReactToUrl(setMode, setFrom, setTo, setResult);
87+
const [inputs, setInputs] = useState<Array<InputItem>>([
88+
defaultFrom,
89+
defaultTo,
90+
]);
91+
92+
useEffect(() => {
93+
const newPoints =
94+
points?.map((point, index) => ({
95+
value: point,
96+
pointIndex: index,
97+
label: t(
98+
index === 0
99+
? 'directions.form.start_or_click'
100+
: 'directions.form.destination',
101+
),
102+
})) ?? [];
103+
104+
if (points?.length === 1) {
105+
setInputs([...newPoints, defaultTo]);
106+
}
107+
if (points?.length >= 2) {
108+
setInputs(newPoints);
109+
}
110+
}, [defaultTo, points]);
111+
112+
useGlobalMapClickOverride(points, setPoints);
113+
useReactToUrl(setMode, setPoints, setResult);
55114

56115
const onSubmitFactory = useGetOnSubmitFactory(setResult, setLoading);
57116

@@ -61,31 +120,36 @@ export const DirectionsForm = ({ setResult, hideForm }: Props) => {
61120

62121
return (
63122
<StyledPaper elevation={3}>
64-
<Stack direction="row" spacing={1} mb={2} alignItems="center">
123+
<Stack
124+
direction="row"
125+
spacing={1}
126+
mb={2}
127+
alignItems="center"
128+
justifyContent="space-between"
129+
>
65130
<ModeToggler
66131
value={mode}
67132
setMode={setMode}
68-
onChange={(newMode) => onSubmitFactory(from, to, newMode)}
133+
onChange={(newMode) => onSubmitFactory(points, newMode)}
69134
/>
70-
<div style={{ flex: 1 }} />
71-
<div>
72-
<CloseButton />
73-
</div>
135+
<CloseButton />
74136
</Stack>
75137

76138
<Stack spacing={1} mb={3}>
77-
<DirectionsAutocomplete
78-
value={from}
79-
setValue={setFrom}
80-
label={t('directions.form.start_or_click')}
81-
pointIndex={0}
82-
/>
83-
<DirectionsAutocomplete
84-
value={to}
85-
setValue={setTo}
86-
label={t('directions.form.destination')}
87-
pointIndex={1}
88-
/>
139+
{inputs.map((item, index) => {
140+
const { value, label, pointIndex } = item;
141+
return (
142+
<Box sx={{ position: 'relative' }} key={`input-${pointIndex}`}>
143+
<Stack direction="row" alignItems="center">
144+
<DirectionsAutocomplete
145+
value={value}
146+
label={label}
147+
pointIndex={index}
148+
/>
149+
</Stack>
150+
</Box>
151+
);
152+
})}
89153
</Stack>
90154

91155
<LoadingButton
@@ -94,7 +158,7 @@ export const DirectionsForm = ({ setResult, hideForm }: Props) => {
94158
variant="contained"
95159
fullWidth
96160
startIcon={<SearchIcon />}
97-
onClick={() => onSubmitFactory(from, to, mode)}
161+
onClick={() => onSubmitFactory(points, mode)}
98162
>
99163
{t('directions.get_directions')}
100164
</LoadingButton>

0 commit comments

Comments
 (0)