Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Directions: Move from/to to array of points #837

Merged
merged 1 commit into from
Dec 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 21 additions & 23 deletions src/components/Directions/DirectionsAutocomplete.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
InputAdornment,
TextField,
Tooltip,
useTheme,
} from '@mui/material';
import { useMapCenter } from '../SearchBox/utils';
import { useUserThemeContext } from '../../helpers/theme';
Expand All @@ -30,8 +31,9 @@ import { AlphabeticalMarker } from './TextMarker';
import MyLocationIcon from '@mui/icons-material/MyLocation';
import { DotLoader, useIsClient } from '../helpers';
import { useSnackbar } from '../utils/SnackbarContext';
import { useGetOnSubmitFactory } from './useGetOnSubmit';
import { useGetOnSubmitFactory, useUpdatePoint } from './useGetOnSubmit';
import { useDirectionsContext } from './DirectionsContext';
import { removeElementOnIndex } from '../FeaturePanel/Climbing/utils/array';
const DotLoaderContainer = styled.div`
font-size: 16px;
right: 6px;
Expand Down Expand Up @@ -60,6 +62,7 @@ const DirectionsInput = ({
const { InputLabelProps, InputProps, ...restParams } = params;
const isClient = useIsClient();
const { showToast } = useSnackbar();
const theme = useTheme();

useEffect(() => {
// @ts-ignore
Expand Down Expand Up @@ -171,15 +174,16 @@ const Row = styled.div`
`;

const useInputMapClickOverride = (
setValue: (value: Option) => void,
pointIndex: number,
setInputValue: (value: string) => void,
selectedOptionInputValue: React.MutableRefObject<string | null>,
) => {
const { mapClickOverrideRef } = useMapStateContext();
const previousBehaviourRef = useRef<MapClickOverride>();
const updatePoint = useUpdatePoint();

const mapClickCallback = (coords: LonLat, label: string) => {
setValue(getCoordsOption(coords, label));
updatePoint(pointIndex, getCoordsOption(coords, label));
setInputValue(label);
selectedOptionInputValue.current = label;

Expand All @@ -203,23 +207,18 @@ const useInputMapClickOverride = (
type Props = {
label: string;
value: Option;
setValue: (value: Option) => void;
pointIndex: number;
};

export const DirectionsAutocomplete = ({
label,
value,
setValue,
pointIndex,
}: Props) => {
export const DirectionsAutocomplete = ({ label, value, pointIndex }: Props) => {
const autocompleteRef = useRef();
const { inputValue, setInputValue } = useInputValueState();
const selectedOptionInputValue = useRef<string | null>(null);
const mapCenter = useMapCenter();
const { currentTheme } = useUserThemeContext();
const { userSettings } = useUserSettingsContext();
const { isImperial } = userSettings;
const updatePoint = useUpdatePoint();

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

const markerRef = useRef<maplibregl.Marker>();
const { from, to, mode, setResult, setLoading } = useDirectionsContext();
const { points, mode, setResult, setLoading, setPoints } =
useDirectionsContext();
const submitFactory = useGetOnSubmitFactory(setResult, setLoading);

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

const handleUpdate = (coordsOption: Option) => {
if (pointIndex === 0) {
submitFactory(coordsOption, to, mode);
}
if (pointIndex === 1) {
submitFactory(from, coordsOption, mode);
}
const newPoints = updatePoint(pointIndex, coordsOption);
submitFactory(newPoints, mode);
};

const submitFactory = useGetOnSubmitFactory(setResult, setLoading);
const onDragEnd = () => {
const lngLat = markerRef.current?.getLngLat();
if (lngLat) {
const coordsOption = getCoordsOption([lngLat.lng, lngLat.lat]);
setValue(coordsOption);
updatePoint(pointIndex, coordsOption);
handleUpdate(coordsOption);
}
};
Expand All @@ -279,13 +275,14 @@ export const DirectionsAutocomplete = ({
const onChange = (_: unknown, option: Option) => {
console.log('selected', option); // eslint-disable-line no-console
setInputValue(getOptionLabel(option));
setValue(option);
updatePoint(pointIndex, option);

selectedOptionInputValue.current = getOptionLabel(option);
handleUpdate(option);
};

const { onInputFocus, onInputBlur } = useInputMapClickOverride(
setValue,
pointIndex,
setInputValue,
selectedOptionInputValue,
);
Expand All @@ -296,8 +293,8 @@ export const DirectionsAutocomplete = ({
if (selectedOptionInputValue.current !== inputValue) {
if (options.length > 0 && inputValue) {
onChange(null, options[0]);
} else {
setValue(null);
} else if (points) {
setPoints(removeElementOnIndex(points, pointIndex));
}
}
};
Expand All @@ -321,6 +318,7 @@ export const DirectionsAutocomplete = ({
getOptionKey={(option) => JSON.stringify(option)}
onChange={onChange}
autoComplete
noOptionsText=""
disableClearable
autoHighlight
clearOnEscape
Expand Down
3 changes: 2 additions & 1 deletion src/components/Directions/DirectionsBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ const Wrapper = styled(Stack)<{ $isMobileMode: boolean }>`
right: 8px;
z-index: 1001; // over the LayerSwitcherButton
max-height: calc(100vh - 16px);
box-shadow: 0 10px 20px 0 rgba(0, 0, 0, 0.12);

@media ${isTabletResolution} {
max-width: 340px;
max-width: 394px;
}
`;

Expand Down
17 changes: 5 additions & 12 deletions src/components/Directions/DirectionsContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,15 @@ import React, { createContext, useContext, useState } from 'react';
import { Profile, RoutingResult } from './routing/types';
import { Option } from '../SearchBox/types';

type CragViewLayout = 'vertical' | 'horizontal' | 'auto';

type DirectionsContextType = {
loading: boolean;
setLoading: (loading: boolean) => void;
mode: Profile;
setMode: (mode: Profile) => void;
from: Option;
setFrom: (from: Option) => void;
to: Option;
setTo: (to: Option) => void;
result: RoutingResult;
setResult: (result: RoutingResult) => void;
points: Array<Option>;
setPoints: (points: Array<Option>) => void;
};

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

const value: DirectionsContextType = {
loading,
setLoading,
mode,
setMode,
from,
setFrom,
to,
setTo,
result,
setResult,
points,
setPoints,
};
return (
<DirectionsContext.Provider value={value}>
Expand Down
138 changes: 101 additions & 37 deletions src/components/Directions/DirectionsForm.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Option } from '../SearchBox/types';
import { RoutingResult } from './routing/types';
import { CloseButton } from './helpers';
import React, { useEffect } from 'react';
import React, { useEffect, useMemo, useState } from 'react';
import { StyledPaper } from './Result';
import { Stack } from '@mui/material';
import { Box, Stack } from '@mui/material';
import { ModeToggler } from './ModeToggler';
import { DirectionsAutocomplete } from './DirectionsAutocomplete';
import { t } from '../../services/intl';
Expand All @@ -12,46 +12,105 @@ import SearchIcon from '@mui/icons-material/Search';
import { getCoordsOption } from '../SearchBox/options/coords';
import { useMapStateContext } from '../utils/MapStateContext';
import { useDirectionsContext } from './DirectionsContext';
import { useGetOnSubmitFactory, useReactToUrl } from './useGetOnSubmit';
import {
useGetOnSubmitFactory,
useReactToUrl,
useUpdatePoint,
} from './useGetOnSubmit';

type Props = {
setResult: (result: RoutingResult) => void;
hideForm: boolean;
};

const useGlobalMapClickOverride = (
from: Option,
setFrom: (value: Option) => void,
setTo: (value: Option) => void,
points: Array<Option>,
setPoints: (points: Array<Option>) => void,
) => {
const { setResult, setLoading, to, mode } = useDirectionsContext();
const { setResult, setLoading, mode } = useDirectionsContext();
const submitFactory = useGetOnSubmitFactory(setResult, setLoading);
const updatePoint = useUpdatePoint();

const { mapClickOverrideRef } = useMapStateContext();
useEffect(() => {
mapClickOverrideRef.current = (coords, label) => {
const coordinates = getCoordsOption(coords, label);
if (!from) {
submitFactory(coordinates, to, mode);
setFrom(coordinates);
} else {
submitFactory(from, coordinates, mode);
setTo(coordinates);
if (points) {
const coordinates = getCoordsOption(coords, label);
if (!points[0]) {
const newPoints = updatePoint(0, coordinates);
submitFactory(newPoints, mode);
} else {
const newPoints = updatePoint(1, coordinates);
submitFactory(newPoints, mode);
}
}
};

return () => {
mapClickOverrideRef.current = undefined;
};
}, [from, mapClickOverrideRef, mode, setFrom, setTo, submitFactory, to]);
}, [
mapClickOverrideRef,
mode,
points,
setPoints,
submitFactory,
updatePoint,
]);
};

type InputItem = {
value: Option;
pointIndex: number;
label: string;
};

export const DirectionsForm = ({ setResult, hideForm }: Props) => {
const { loading, setLoading, mode, setMode, from, setFrom, to, setTo } =
const defaultFrom = {
value: null,
pointIndex: 0,
label: t('directions.form.start_or_click'),
};

const defaultTo = useMemo(
() => ({
value: null,
pointIndex: 1,
label: t('directions.form.destination'),
}),
[],
);

const { loading, setLoading, mode, setMode, points, setPoints } =
useDirectionsContext();

useGlobalMapClickOverride(from, setFrom, setTo);
useReactToUrl(setMode, setFrom, setTo, setResult);
const [inputs, setInputs] = useState<Array<InputItem>>([
defaultFrom,
defaultTo,
]);

useEffect(() => {
const newPoints =
points?.map((point, index) => ({
value: point,
pointIndex: index,
label: t(
index === 0
? 'directions.form.start_or_click'
: 'directions.form.destination',
),
})) ?? [];

if (points?.length === 1) {
setInputs([...newPoints, defaultTo]);
}
if (points?.length >= 2) {
setInputs(newPoints);
}
}, [defaultTo, points]);

useGlobalMapClickOverride(points, setPoints);
useReactToUrl(setMode, setPoints, setResult);

const onSubmitFactory = useGetOnSubmitFactory(setResult, setLoading);

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

return (
<StyledPaper elevation={3}>
<Stack direction="row" spacing={1} mb={2} alignItems="center">
<Stack
direction="row"
spacing={1}
mb={2}
alignItems="center"
justifyContent="space-between"
>
<ModeToggler
value={mode}
setMode={setMode}
onChange={(newMode) => onSubmitFactory(from, to, newMode)}
onChange={(newMode) => onSubmitFactory(points, newMode)}
/>
<div style={{ flex: 1 }} />
<div>
<CloseButton />
</div>
<CloseButton />
</Stack>

<Stack spacing={1} mb={3}>
<DirectionsAutocomplete
value={from}
setValue={setFrom}
label={t('directions.form.start_or_click')}
pointIndex={0}
/>
<DirectionsAutocomplete
value={to}
setValue={setTo}
label={t('directions.form.destination')}
pointIndex={1}
/>
{inputs.map((item, index) => {
const { value, label, pointIndex } = item;
return (
<Box sx={{ position: 'relative' }} key={`input-${pointIndex}`}>
<Stack direction="row" alignItems="center">
<DirectionsAutocomplete
value={value}
label={label}
pointIndex={index}
/>
</Stack>
</Box>
);
})}
</Stack>

<LoadingButton
Expand All @@ -94,7 +158,7 @@ export const DirectionsForm = ({ setResult, hideForm }: Props) => {
variant="contained"
fullWidth
startIcon={<SearchIcon />}
onClick={() => onSubmitFactory(from, to, mode)}
onClick={() => onSubmitFactory(points, mode)}
>
{t('directions.get_directions')}
</LoadingButton>
Expand Down
Loading
Loading