Обновление всплывающей подсказки в режиме реального времени с помощью ответной листовки при смене языка с i18n - PullRequest
0 голосов
/ 30 апреля 2020

В настоящее время я показываю Карту, благодаря букве реагирования, с компонентом Geo JSON. Я также отображаю всплывающие подсказки для некоторых стран и городов (например, при наведении курсора на Францию ​​отображается всплывающая подсказка «Франция»). Я также использую i18n для интернационализации. Интернационализация отлично работает для всплывающих подсказок для страны, они обновляются в режиме реального времени.

У меня есть функция updateDisplay, которая переключается между компонентом Geo Json для стран, или список маркеров для города, при изменении масштаба.

Проблема в том, что когда я переключаю языки, он отлично работает для всей страницы, но не для городских подсказок. Они обновляются только когда я увеличиваю изображение (то есть когда вызывается updateDisplay).

У меня будет ожидаемое поведение: независимо от масштаба, я бы хотел, чтобы всплывающие подсказки города обновлялись в реальном времени, когда я переключал язык .

Надеюсь, я ясно дал понять

Вот мой код:

/**
 * Display a Leaflet Map, containing a GeoJson object, or a list of Markers, depending on the zoom
 */
export default function CustomMap(): ReactElement {
  const { t }: { t: TFunction } = useTranslation();

  const countryToString = (countries: string[]): string => countries.map(c => t(c)).join(", ");


  // Contains the json containing the polygons of the countries
  const data: geojson.FeatureCollection = geoJsonData as geojson.FeatureCollection;
  let geoJson: JSX.Element = <GeoJSON
    key='my-geojson'
    data={data}
    style={() => ({
      color: '#4a83ec',
      weight: 1,
      fillColor: "#1a1d62",
      fillOpacity: 0.25,
    })}
    onEachFeature={(feature: geojson.Feature<geojson.GeometryObject>, layer: Layer) => {
      layer.on({
        'mouseover': (e: LeafletMouseEvent) => {
          const country = countries[e.target.feature.properties.adm0_a3];
          layer.bindTooltip(countryToString(country.tooltip as string[]));
          layer.openTooltip(country.latlng);
        },
        'mouseout': () => {
          layer.unbindTooltip();
          layer.closeTooltip();
        },
      });
    }}
  />

  // Contains a list of marker for the cities
  const cityMarkers: JSX.Element[] = cities.map(
    (
      c: position,
      i: number
    ) => {
      return (
        // Here are the tooltips that doesn't update in real time, when we switch language
        // FIX ME
        <Marker key={c.latlng.lat + c.latlng.lng} position={c.latlng}>
          <Tooltip>{t(c.tooltip as string)}</Tooltip>
        </Marker>
      );
    }
  );

  const [state, setState] = useState<state>({
    zoom: 3,
    display: geoJson,
  });


  // Update on zoom change
  function onZoom(e: LeafletMouseEvent): void {
    const zoom = e.target._zoom;
    const newDisplay = updateDisplay(zoom);
    setState({
      ...state,
      zoom,
      display: newDisplay,
    });
  }

  // Called on every zoom change, in order to display either the GeoJson, or the cities Marker
  function updateDisplay(zoom: number): Marker[] | any {
    if (zoom >= 4) {
      return cityMarkers;
    } else {
      return geoJson;
    }
  }


  return (
    <Map
      style={{ height: "500px" }}
      center={[54.370138916189596, -29.918133437500003]}
      zoom={state.zoom}
      onZoomend={onZoom}
    >
      <TileLayer url="https://api.mapbox.com/styles/v1/mapbox/streets-v11/tiles/{z}/{x}/{y}?access_token=pk.eyJ1IjoibWFwYm94IiwiYSI6ImNpejY4NXVycTA2emYycXBndHRqcmZ3N3gifQ.rJcFIG214AriISLbB6B5aw" />
      {state.display}
    </Map>
  );
}

Вы также можете посмотреть его здесь: https://github.com/TheTisiboth/WebCV/blob/WIP/src/components/customMap.tsx
Это на ветке WIP

1 Ответ

1 голос
/ 01 мая 2020

Для решения этой проблемы вы можете сделать следующее:

  1. Создать логический флаг, который будет храниться в памяти, если маркеры были добавлены
  2. Добавить маркеры на отображать с использованием собственного кода листовки вместо оболочек реагирующих листовок.

    • Если маркеры добавлены и zoom> = 4, установите флаг в значение true
    • Если zoom <4, удалите маркеры, чтобы иметь возможность показывать страны, установите флаг в false </li>
  3. При изменении языка, если масштабирование больше, равно 4 и добавлены маркеры, удалите предыдущие, добавьте новые с новой подсказкой

Вы можете достичь всего этого, удерживая ссылку на экземпляр карты.

Вот весь код, который вам понадобится (части городов, маркеры удалены):

import React, { useState, ReactElement } from "react";
import { useTranslation } from "react-i18next";
import { Map, Marker, TileLayer, GeoJSON } from "react-leaflet";
import geoJsonData from "../assets/geoJsonData.json";
import { LatLngLiteral, Layer, LeafletMouseEvent } from "leaflet";
import geojson from "geojson";
import { TFunction } from "i18next";
import L from "leaflet";

interface position {
  latlng: LatLngLiteral;
  tooltip: string;
}

interface state {
  markers: position[];
  zoom: number;
  display: position[] | any;
  geoJson: JSX.Element;
  countries: { [key: string]: position };
}

/**
 * Display a Leaflet Map, containing a GeoJson object, or a list of Markers, depending on the zoom
 */
export default function CustomMap(): ReactElement {
  const mapRef: any = React.useRef();
  const { t, i18n }: { t: TFunction; i18n: any } = useTranslation();
  const [markersAdded, setMarkersAdded] = useState(false);

  i18n.on("languageChanged", (lng: any) => {
    if (lng) {
      const map = mapRef.current;
      if (map && map.leafletElement.getZoom() >= 4 && markersAdded) {
        map.leafletElement.eachLayer(function (layer: L.Layer) {
          if (layer instanceof L.Marker) map.leafletElement.removeLayer(layer);
        });
        state.markers.map((c: position, i: number) => {
          L.marker(c.latlng)
            .addTo(map.leafletElement)
            .bindTooltip(t(c.tooltip));
        });
      }
    }
  });

  // const countryToString = (countries: string[]): string => countries.join(", ");

  // List of position and label of tooltip for the GeoJson object, for each country
  const countries: { [key: string]: position } = {
    DEU: {
      latlng: {
        lat: 51.0834196,
        lng: 10.4234469,
      },
      tooltip: "travel.germany",
    },
    CZE: {
      latlng: {
        lat: 49.667628,
        lng: 15.326962,
      },
      tooltip: "travel.tchequie",
    },
    BEL: {
      latlng: {
        lat: 50.6402809,
        lng: 4.6667145,
      },
      tooltip: "travel.belgium",
    },
  };

  // List of position and tooltip for the cities Markers
  const cities: position[] = [
    {
      latlng: {
        lat: 48.13825988769531,
        lng: 11.584508895874023,
      },
      tooltip: "travel.munich",
    },
    {
      latlng: {
        lat: 52.51763153076172,
        lng: 13.40965747833252,
      },
      tooltip: "travel.berlin",
    },
    {
      // greece
      latlng: {
        lat: 37.99076843261719,
        lng: 23.74122428894043,
      },
      tooltip: "travel.athens",
    },
    {
      // greece
      latlng: {
        lat: 37.938621520996094,
        lng: 22.92695426940918,
      },
      tooltip: "travel.corinth",
    },
  ];

  // Contains the json containing the polygons of the countries
  const data: geojson.FeatureCollection = geoJsonData as geojson.FeatureCollection;
  let geoJson: JSX.Element = (
    <GeoJSON
      key='my-geojson'
      data={data}
      style={() => ({
        color: "#4a83ec",
        weight: 1,
        fillColor: "#1a1d62",
        fillOpacity: 0.25,
      })}
      // PROBLEM : does not update the tooltips when we switch languages
      // FIX ME
      onEachFeature={(
        feature: geojson.Feature<geojson.GeometryObject>,
        layer: Layer
      ) => {
        layer.on({
          mouseover: (e: LeafletMouseEvent) => {
            const country =
              state.countries[e.target.feature.properties.adm0_a3];
            layer.bindTooltip(t(country?.tooltip));
            layer.openTooltip(country?.latlng);
          },
          mouseout: () => {
            layer.unbindTooltip();
            layer.closeTooltip();
          },
        });
      }}
    />
  );

  const [state, setState] = useState<state>({
    markers: cities,
    zoom: 3,
    geoJson: geoJson,
    display: geoJson,
    countries: countries,
  });

  // Update on zoom change
  function onZoom(e: LeafletMouseEvent): void {
    const zoom = e.target._zoom;
    const newDisplay = updateDisplay(zoom);
    setState({
      ...state,
      zoom,
      display: newDisplay,
    });
  }

  // Called on every zoom change, in order to display either the GeoJson, or the cities Marker
  function updateDisplay(zoom: number): Marker[] | any {
    const map = mapRef.current;
    if (zoom >= 4) {
      return state.markers.map((c: position, i: number) => {
        console.log(t(c.tooltip));
        if (map && !markersAdded) {
          console.log(map.leafletElement);
          L.marker(c.latlng)
            .addTo(map.leafletElement)
            .bindTooltip(t(c.tooltip));
          setMarkersAdded(true);
        }
      });
    } else {
      map.leafletElement.eachLayer(function (layer: L.Layer) {
        if (layer instanceof L.Marker) map.leafletElement.removeLayer(layer);
      });
      setMarkersAdded(false);
      return state.geoJson;
    }
  }

  return (
    <Map
      ref={mapRef}
      style={{ height: "500px" }}
      center={[54.370138916189596, -29.918133437500003]}
      zoom={state.zoom}
      onZoomend={onZoom}
    >
      <TileLayer url='https://api.mapbox.com/styles/v1/mapbox/streets-v11/tiles/{z}/{x}/{y}?access_token=pk.eyJ1IjoibWFwYm94IiwiYSI6ImNpejY4NXVycTA2emYycXBndHRqcmZ3N3gifQ.rJcFIG214AriISLbB6B5aw' />
      {state.display}
    </Map>
  );
}

Eng:

"travel": {
    "germany": "Munich, Berlin, Hambourg, Münster, Allemagne",
    "munich": "Munchen",
    "berlin": "Berlin",
    "tchequie": "Tchéquie, Prague",
    "belgium": "Belgique",
    "athens": "Athènes",
    "corinth": "Corinthe",
    ...
 }

Fr:

"travel": {
    "germany": "Munich, Berlin, Hamburg, Münster, Germany",
    "munich": "Munich",
    "berlin": "Berlin",
    "tchequie": "Czech Republic, Prague",
    "belgium": "Belgium",
    "athens": "Athens",
    "corinth": "Corinth",
     ...
 }

Вы можете сделать его более чистым, повторно используя блок кода удаления маркеров и блок кода добавления маркеров соответственно.

...