• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

terrestris / react-geo / 18274503963

06 Oct 2025 08:18AM UTC coverage: 63.354%. Remained the same
18274503963

push

github

web-flow
Merge pull request #4396 from terrestris/dependabot/npm_and_yarn/commitlint/cli-20.1.0

build(deps-dev): bump @commitlint/cli from 19.8.1 to 20.1.0

597 of 1040 branches covered (57.4%)

Branch coverage included in aggregate %.

1137 of 1697 relevant lines covered (67.0%)

11.77 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

41.51
/src/Field/SearchField/SearchField.tsx
1
import './SearchField.less';
2

3
import React, { ReactElement, useCallback, useEffect, useMemo, useState } from 'react';
4

5
import { AutoComplete, Spin } from 'antd';
6

7
import { AutoCompleteProps } from 'antd/lib/auto-complete';
8
import { Feature, FeatureCollection, GeoJsonProperties, Geometry } from 'geojson';
9
import { Extent } from 'ol/extent';
10
import OlFormatGeoJSON from 'ol/format/GeoJSON';
11
import { transformExtent } from 'ol/proj';
12

13
import { SearchFunction, SearchOptions, useSearch } from '@terrestris/react-util';
14
import useMap from '@terrestris/react-util/dist/Hooks/useMap/useMap';
15

16
import { CSS_PREFIX } from '../../constants';
17

18
export type SearchProps<
19
  G extends Geometry = Geometry,
20
  T extends NonNullable<GeoJsonProperties> = Record<string, any>,
21
  C extends FeatureCollection<G, T> = FeatureCollection<G, T>
22
> = {
23
  searchFunction: SearchFunction<G, T, C>;
24
  searchOptions?: SearchOptions<G, T, C>;
25
  getValue?: (feature: Feature<G, T>) => string;
26
  /**
27
   * An onSelect function which gets called with the selected item as it is
28
   * returned by the search function.
29
   */
30
  onSelect?: (feature: Feature<G, T>) => void;
31
  /**
32
   * A function which gets called with the search results if the search is completed.
33
   */
34
  onSearchCompleted?: (featureCollection: FeatureCollection<G, T> | undefined) => void;
35
  /**
36
   * An optional CSS class which should be added.
37
   */
38
  className?: string;
39
  /**
40
   * A function that gets called when the clear Button is pressed or the input
41
   * value is empty.
42
   */
43
  onClear?: () => void;
44
  zoomToFeature?: boolean;
45
  getExtent?: (feature: Feature<G, T>) => Extent;
46
  autoCompleteDisabled?: boolean;
47
} & Omit<AutoCompleteProps, 'onSelect'|'onSearch'|'onChange'|'onClear'|'notFoundContent'>;
48

49
/**
50
 * The SearchField.
51
 */
52
export function SearchField<
53
  G extends Geometry = Geometry,
54
  T extends NonNullable<GeoJsonProperties> = Record<string, any>,
55
  C extends FeatureCollection<G, T> = FeatureCollection<G, T>
56
>({
57
  className = `${CSS_PREFIX}search`,
1✔
58
  onSelect,
59
  onSearchCompleted,
60
  getValue = () => '',
✔
61
  searchFunction,
62
  searchOptions = {},
5✔
63
  zoomToFeature = true,
5✔
64
  autoCompleteDisabled = false,
5✔
65
  getExtent,
66
  ...passThroughProps
67
}: SearchProps<G, T, C>): ReactElement {
68

69
  const [searchTerm, setSearchTerm] = useState<string>('');
5✔
70
  const map = useMap();
5✔
71

72
  const {
73
    featureCollection,
74
    loading
75
  } = useSearch<G, T, C>(searchFunction, searchTerm, searchOptions);
5✔
76

77
  useEffect(() => {
5✔
78
    if (onSearchCompleted) {
3✔
79
      onSearchCompleted(featureCollection);
2✔
80
    }
81
  }, [featureCollection, onSearchCompleted]);
82

83
  const options = useMemo(
5✔
84
    () => featureCollection?.features.map(f => ({
3✔
85
      label: getValue(f),
86
      value: getValue(f)
87
    })),
88
    [featureCollection, getValue]
89
  );
90

91
  const onMenuItemSelected = useCallback((value: string) => {
5✔
92
    const selected = featureCollection?.features.find(f => getValue(f) === value);
×
93
    if (selected && onSelect) {
×
94
      onSelect(selected);
×
95
    }
96
    if (selected && zoomToFeature) {
×
97
      if (!map) {
×
98
        return;
×
99
      }
100
      let extent: Extent;
101
      if (getExtent) {
×
102
        extent = getExtent(selected);
×
103
      } else {
104
        const olFormat = new OlFormatGeoJSON();
×
105
        const geometry = olFormat.readGeometry(selected.geometry);
×
106
        extent = geometry.getExtent();
×
107
      }
108

109
      const olView = map?.getView();
×
110

111
      extent = transformExtent(extent, 'EPSG:4326', olView.getProjection());
×
112

113
      olView.fit(extent, {
×
114
        duration: 500
115
      });
116
    }
117
  }, [map, onSelect, getValue, getExtent, featureCollection?.features, zoomToFeature]);
118

119
  return (
5✔
120
    <AutoComplete
121
      className={className}
122
      allowClear
123
      classNames={{
124
        popup: {
125
          root: autoCompleteDisabled ? 'autocomplete-disabled' : undefined
5!
126
        }
127
      }}
128
      popupRender={autoCompleteDisabled ? () => <></> : undefined}
×
129
      onSearch={text =>
130
        setSearchTerm(text)
1✔
131
      }
132
      onClear={() =>
133
        setSearchTerm('')
×
134
      }
135
      onSelect={onMenuItemSelected}
136
      options={options}
137
      notFoundContent={loading ? <Spin size="small" /> : null}
5✔
138
      {...passThroughProps}
139
    />
140
  );
141
}
142

143
export default SearchField;
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc