Skip to content

Commit

Permalink
Fix input behavior in controls (#29)
Browse files Browse the repository at this point in the history
<!--

Thank you for submitting a pull request!

Here's a checklist you might find useful.
[ ] There is an associated issue that is labelled
  'Bug' or 'help wanted' or is in the Community milestone
[ ] Code is up-to-date with the `main` branch
[ ] You've successfully run `npm test` locally
[ ] There are new or updated tests validating the change

-->

Fixes #
1. [x] After clearing the route input, we continue to show the digest
for the previous request
2. [x] If the geosearch returned an error when selecting a route point
from the digest, then we do not build a route and do not display any
errors to the user at all
3. [x] Send a suggest request for each focus event
4. [x] Firefox does not work selecting a point by clicking on the map
5. [x] In Safari, clearing route inputs by clicking does not work
  • Loading branch information
matthew44-mappable authored Sep 6, 2024
1 parent cb2c322 commit 4be807b
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 33 deletions.
38 changes: 23 additions & 15 deletions src/controls/MMapRouteControl/MMapWaypointInput/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ const INDICATOR_COLORS = {
dark: {from: '#D6FD63', to: '#C8D2E6'}
};

const DELAY_BETWEEN_BLUR_AND_CLICK = 200;

export type SelectWaypointArgs = {
feature: SearchResponseFeature;
};
Expand All @@ -36,10 +38,14 @@ export type MMapWaypointInputProps = {
suggest?: (args: CustomSuggest) => Promise<SuggestResponse> | SuggestResponse;
onSelectWaypoint?: (args: SelectWaypointArgs | null) => void;
onMouseMoveOnMap?: (coordinates: LngLat, lastCall: boolean) => void;
onError?: () => void;
};

const defaultProps = Object.freeze({geolocationTextInput: 'My location'});

/**
* @internal
*/
export class MMapWaypointInput extends mappable.MMapComplexEntity<MMapWaypointInputProps, typeof defaultProps> {
static defaultProps = defaultProps;
private _detachDom?: DomDetach;
Expand All @@ -56,9 +62,7 @@ export class MMapWaypointInput extends mappable.MMapComplexEntity<MMapWaypointIn
private _isBottomPosition: boolean;
private _isHoverMode = false;

private get _isInputFocused(): boolean {
return document.activeElement === this._inputEl;
}
private _isInputFocused: boolean = false;

public triggerFocus(): void {
this._inputEl.focus();
Expand Down Expand Up @@ -113,11 +117,13 @@ export class MMapWaypointInput extends mappable.MMapComplexEntity<MMapWaypointIn
form.appendChild(fieldButton);

this._locationButton = document.createElement('button');
this._locationButton.addEventListener('mousedown', this._getGeolocation);
this._locationButton.classList.add('mappable--route-control_waypoint-input__field-buttons__location');
this._locationButton.insertAdjacentHTML('afterbegin', locationSVG);
fieldButton.appendChild(this._locationButton);

this._resetButton = document.createElement('button');
this._resetButton.addEventListener('mousedown', this._resetInput);
this._resetButton.classList.add('mappable--route-control_waypoint-input__field-buttons__reset');
this._resetButton.insertAdjacentHTML('afterbegin', resetSVG);
fieldButton.appendChild(this._resetButton);
Expand Down Expand Up @@ -192,18 +198,20 @@ export class MMapWaypointInput extends mappable.MMapComplexEntity<MMapWaypointIn
}
}

private _resetInput() {
private _resetInput = () => {
this._inputEl.value = '';
this._suggestComponent.update({searchInputValue: ''});
this._updateIndicatorStatus('empty');
this._props.onSelectWaypoint(null);
}
};

private _onUpdateWaypoint = debounce((e: Event) => {
const target = e.target as HTMLInputElement;
this._suggestComponent.update({searchInputValue: target.value});
}, 200);

private _onFocusInput = (_event: FocusEvent) => {
this._isInputFocused = true;
this._addDirectChild(this._suggestComponent);
this._updateIndicatorStatus('focus');
};
Expand All @@ -216,15 +224,12 @@ export class MMapWaypointInput extends mappable.MMapComplexEntity<MMapWaypointIn
if (event.relatedTarget !== this._suggestComponent.activeSuggest) {
this._removeDirectChild(this._suggestComponent);
}
if (event.relatedTarget === this._locationButton) {
this._getGeolocation();
return;
}
if (event.relatedTarget === this._resetButton) {
this._resetInput();
return;
}

this._updateIndicatorStatus('empty');
// HACK: to check that input had focus before the click
setTimeout(() => {
this._isInputFocused = false;
}, DELAY_BETWEEN_BLUR_AND_CLICK);
};

private _submitWaypointInput = (event?: SubmitEvent) => {
Expand Down Expand Up @@ -252,7 +257,7 @@ export class MMapWaypointInput extends mappable.MMapComplexEntity<MMapWaypointIn
}
};

private async _getGeolocation() {
private _getGeolocation = async () => {
const text = this._props.geolocationTextInput;
this._inputEl.value = text;

Expand All @@ -263,7 +268,7 @@ export class MMapWaypointInput extends mappable.MMapComplexEntity<MMapWaypointIn
};
this._updateIndicatorStatus('setted');
this._props.onSelectWaypoint({feature});
}
};

private async _search(params: SearchParams, reverseGeocodingCoordinate?: LngLat) {
try {
Expand All @@ -285,6 +290,8 @@ export class MMapWaypointInput extends mappable.MMapComplexEntity<MMapWaypointIn
// eslint-disable-next-line no-console
console.error(error);
this._updateIndicatorStatus('empty');
this._inputEl.value = '';
this._props.onError?.();
}
}

Expand All @@ -305,6 +312,7 @@ export class MMapWaypointInput extends mappable.MMapComplexEntity<MMapWaypointIn
private _onMapFastClick = (object: DomEventHandlerObject, event: DomEvent): void => {
if (this._isInputFocused) {
this._isHoverMode = false;
this._isInputFocused = false;
this._props.onMouseMoveOnMap?.(event.coordinates, true);
this._inputEl.blur();
this._search({text: event.coordinates.toString()}, event.coordinates);
Expand Down
37 changes: 28 additions & 9 deletions src/controls/MMapRouteControl/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,14 @@ class MMapCommonRouteControl extends mappable.MMapComplexEntity<MMapRouteControl
}

protected _onUpdate(diffProps: Partial<MMapRouteControlProps>): void {
if (diffProps.search !== undefined) {
this._waypointInputFromElement.update({search: diffProps.search});
this._waypointInputToElement.update({search: diffProps.search});
}
if (diffProps.suggest !== undefined) {
this._waypointInputFromElement.update({suggest: diffProps.suggest});
this._waypointInputToElement.update({suggest: diffProps.suggest});
}
if (diffProps.waypoints !== undefined) {
this._waypointInputFromElement.update({waypoint: diffProps.waypoints[0]});
this._waypointInputToElement.update({waypoint: diffProps.waypoints[1]});
Expand Down Expand Up @@ -226,14 +234,14 @@ class MMapCommonRouteControl extends mappable.MMapComplexEntity<MMapRouteControl

private _createWaypointInput(type: MMapWaypointInputProps['type'], waypoint?: LngLat): MMapWaypointInput {
const waypointIndex = type === 'from' ? 0 : 1;
const {geolocationTextInput, onMouseMoveOnMap} = this._props;
const {geolocationTextInput, onMouseMoveOnMap, waypointsPlaceholders, search, suggest} = this._props;
return new MMapWaypointInput({
type,
inputPlaceholder: this._props.waypointsPlaceholders[waypointIndex],
inputPlaceholder: waypointsPlaceholders[waypointIndex],
waypoint,
geolocationTextInput: geolocationTextInput,
search: this._props.search,
suggest: this._props.suggest,
geolocationTextInput,
search,
suggest,
onSelectWaypoint: (result) => {
if (result === null) {
this._waypoints[waypointIndex] = null;
Expand All @@ -247,6 +255,11 @@ class MMapCommonRouteControl extends mappable.MMapComplexEntity<MMapRouteControl
},
onMouseMoveOnMap: (coordinates, lastCall) => {
onMouseMoveOnMap?.(coordinates, waypointIndex, lastCall);
},
onError: () => {
this._showServerError(() => {
this._rootElement.removeChild(this._routeInfoElement);
});
}
});
}
Expand Down Expand Up @@ -307,28 +320,34 @@ class MMapCommonRouteControl extends mappable.MMapComplexEntity<MMapRouteControl

this._routeInfoElement.classList.remove('mappable--route-control_info__error');
this._routeInfoElement.replaceChildren(createLoadingSpinner());
this._rootElement.appendChild(this._routeInfoElement);

try {
const response = (await this._props.route?.({params, map: this.root})) ?? (await mappable.route(params));
const route = response[0].toRoute();
if (route.geometry.coordinates.length !== 0) {
this._props.onRouteResult?.(response[0], this._routeMode);
this._rootElement.appendChild(this._routeInfoElement);
this._routeInfoElement.replaceChildren(...this._getRouteDetails(response[0]));
} else {
this._props.onBuildRouteError?.();
this._rootElement.appendChild(this._routeInfoElement);
this._routeInfoElement.classList.add('mappable--route-control_info__error');
this._routeInfoElement.replaceChildren(...createRouteNoBuildError());
}
} catch (error) {
// eslint-disable-next-line no-console
console.error(error);
this._props.onBuildRouteError?.();
this._routeInfoElement.classList.add('mappable--route-control_info__error');
this._routeInfoElement.replaceChildren(...createRouteServerError(() => this._route()));
this._showServerError(() => this._route());
}
}, 200);

private _showServerError(onButtonClick: () => void) {
this._props.onBuildRouteError?.();
this._rootElement.appendChild(this._routeInfoElement);
this._routeInfoElement.classList.add('mappable--route-control_info__error');
this._routeInfoElement.replaceChildren(...createRouteServerError(onButtonClick));
}

private _getRouteDetails(response: BaseRouteResponse): HTMLElement[] {
if (!response.toSteps) {
return [];
Expand Down
17 changes: 8 additions & 9 deletions src/controls/MMapSearchControl/MMapSuggest/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,19 @@ class MMapSuggest extends mappable.MMapComplexEntity<MMapSuggestProps> {
private _rootElement?: HTMLElement;
private _unwatchThemeContext?: () => void;
private _unwatchControlContext?: () => void;
private _searchInputValue?: string;

public get activeSuggest() {
return this._getSuggestElements().find((element) => element.classList.contains(ACTIVE_CLASS));
return this._getSuggestElements().find((element) => element?.classList.contains(ACTIVE_CLASS));
}

private _updateSuggest(props: Partial<MMapSuggestProps>) {
if (props.searchInputValue) {
if (props.searchInputValue !== undefined && props.searchInputValue !== this._searchInputValue) {
this._searchInputValue = props.searchInputValue;
this._updateSuggestList(props.searchInputValue);
}

if (props.suggestNavigationAction) {
if (props.suggestNavigationAction !== undefined) {
this._updateActiveSuggest(props.suggestNavigationAction);
}
}
Expand Down Expand Up @@ -147,7 +149,8 @@ class MMapSuggest extends mappable.MMapComplexEntity<MMapSuggestProps> {

protected override _onAttach(): void {
this._rootElement = document.createElement('mappable');
this._rootElement.classList.add(SUGGEST_CLASS, HIDE_CLASS);
this._rootElement.classList.add(SUGGEST_CLASS);
this._rootElement?.classList.toggle(HIDE_CLASS, !this.children.length);
this._rootElement.addEventListener('mouseover', this._onMouseOverHandler);
this._rootElement.addEventListener('mouseout', this._onMouseOutHandler);

Expand All @@ -158,9 +161,7 @@ class MMapSuggest extends mappable.MMapComplexEntity<MMapSuggestProps> {
this._unwatchThemeContext = this._watchContext(
mappable.ThemeContext,
() => this._updateTheme(this._rootElement),
{
immediate: true
}
{immediate: true}
);

this._unwatchControlContext = this._watchContext(
Expand All @@ -175,8 +176,6 @@ class MMapSuggest extends mappable.MMapComplexEntity<MMapSuggestProps> {
}

protected override _onDetach(): void {
this._removeSuggestItems();

this._detachDom?.();
this._detachDom = undefined;

Expand Down

0 comments on commit 4be807b

Please sign in to comment.