forked from MapComplete/MapComplete
		
	
		
			
				
	
	
		
			100 lines
		
	
	
	
		
			3.1 KiB
		
	
	
	
		
			Svelte
		
	
	
	
	
	
			
		
		
	
	
			100 lines
		
	
	
	
		
			3.1 KiB
		
	
	
	
		
			Svelte
		
	
	
	
	
	
<script lang="ts">
 | 
						|
 | 
						|
  import { UIEventSource } from "../../Logic/UIEventSource";
 | 
						|
  import type { Feature } from "geojson";
 | 
						|
  import LayerConfig from "../../Models/ThemeConfig/LayerConfig";
 | 
						|
  import ToSvelte from "../Base/ToSvelte.svelte";
 | 
						|
  import Svg from "../../Svg.js";
 | 
						|
  import Translations from "../i18n/Translations";
 | 
						|
  import Loading from "../Base/Loading.svelte";
 | 
						|
  import Hotkeys from "../Base/Hotkeys";
 | 
						|
  import { Geocoding } from "../../Logic/Osm/Geocoding";
 | 
						|
  import { BBox } from "../../Logic/BBox";
 | 
						|
  import { GeoIndexedStoreForLayer } from "../../Logic/FeatureSource/Actors/GeoIndexedStore";
 | 
						|
  import {createEventDispatcher} from "svelte";
 | 
						|
 | 
						|
  export let perLayer: ReadonlyMap<string, GeoIndexedStoreForLayer> | undefined = undefined;
 | 
						|
  export let bounds: UIEventSource<BBox>;
 | 
						|
  export let selectedElement: UIEventSource<Feature> | undefined = undefined;
 | 
						|
  export let selectedLayer: UIEventSource<LayerConfig> | undefined = undefined;
 | 
						|
 | 
						|
  let searchContents: string = undefined;
 | 
						|
 | 
						|
  let isRunning: boolean = false;
 | 
						|
 | 
						|
  let inputElement: HTMLInputElement;
 | 
						|
 | 
						|
  let feedback: string = undefined;
 | 
						|
 | 
						|
  Hotkeys.RegisterHotkey(
 | 
						|
    { ctrl: "F" },
 | 
						|
    Translations.t.hotkeyDocumentation.selectSearch,
 | 
						|
    () => {
 | 
						|
      inputElement?.focus();
 | 
						|
      inputElement?.select();
 | 
						|
    }
 | 
						|
  );
 | 
						|
 | 
						|
  const dispatch = createEventDispatcher<{searchCompleted}>()
 | 
						|
  
 | 
						|
  async function performSearch() {
 | 
						|
    try {
 | 
						|
      isRunning = true;
 | 
						|
      searchContents = searchContents?.trim() ?? "";
 | 
						|
      if (searchContents === "") {
 | 
						|
        return;
 | 
						|
      }
 | 
						|
      const result = await Geocoding.Search(searchContents, bounds.data);
 | 
						|
      if (result.length == 0) {
 | 
						|
        feedback = Translations.t.general.search.nothing.txt;
 | 
						|
        return;
 | 
						|
      }
 | 
						|
      const poi = result[0];
 | 
						|
      const [lat0, lat1, lon0, lon1] = poi.boundingbox;
 | 
						|
      bounds.set(new BBox([[lon0, lat0], [lon1, lat1]]).pad(0.01));
 | 
						|
      if (perLayer !== undefined) {
 | 
						|
        const id = poi.osm_type + "/" + poi.osm_id;
 | 
						|
        const layers = Array.from(perLayer?.values() ?? []);
 | 
						|
        for (const layer of layers) {
 | 
						|
          const found = layer.features.data.find(f => f.properties.id === id);
 | 
						|
          selectedElement?.setData(found);
 | 
						|
          selectedLayer?.setData(layer.layer.layerDef);
 | 
						|
 | 
						|
        }
 | 
						|
      }
 | 
						|
      dispatch("searchCompleted")
 | 
						|
    } catch (e) {
 | 
						|
      console.error(e);
 | 
						|
      feedback = Translations.t.general.search.error.txt;
 | 
						|
    } finally {
 | 
						|
      isRunning = false;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
</script>
 | 
						|
 | 
						|
<div class="flex normal-background rounded-full pl-2 justify-between">
 | 
						|
  <form class="w-full">
 | 
						|
 | 
						|
    {#if isRunning}
 | 
						|
      <Loading>{Translations.t.general.search.searching}</Loading>
 | 
						|
    {:else if feedback !== undefined}
 | 
						|
      <div class="alert" on:click={() => feedback = undefined}>
 | 
						|
        {feedback}
 | 
						|
      </div>
 | 
						|
    {:else }
 | 
						|
      <input
 | 
						|
        type="search"
 | 
						|
        class="w-full"
 | 
						|
        bind:this={inputElement}
 | 
						|
        on:keypress={keypr => keypr.key === "Enter" ? performSearch() : undefined}
 | 
						|
 | 
						|
        bind:value={searchContents}
 | 
						|
        placeholder={Translations.t.general.search.search}>
 | 
						|
    {/if}
 | 
						|
 | 
						|
  </form>
 | 
						|
  <div class="w-6 h-6 self-end" on:click={performSearch}>
 | 
						|
    <ToSvelte construct={Svg.search_svg}></ToSvelte>
 | 
						|
  </div>
 | 
						|
</div>
 |