@211la/search-react 0.3.8 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import * as React from 'react';
2
- import React__default, { useState, useRef, useEffect } from 'react';
2
+ import React__default, { useRef, useMemo, useState, useCallback, useEffect } from 'react';
3
3
  import { connectRefinementList, connectHits } from 'react-instantsearch-dom';
4
4
  import { createConnector } from 'react-instantsearch-core';
5
- import { GoogleApiWrapper, InfoWindow, Marker, Map as Map$1 } from 'google-maps-react';
5
+ import { GoogleApiWrapper, Map as Map$1, Marker, InfoWindow } from 'google-maps-react';
6
6
  import PlacesAutocomplete, { geocodeByAddress, getLatLng } from 'react-places-autocomplete';
7
7
 
8
8
  var trim = function (prefix, suffix, text) {
@@ -197,78 +197,6 @@ var connectSearchBoxFields = createConnector({
197
197
  },
198
198
  });
199
199
 
200
- var groupBy = function (list, fn) {
201
- // @ts-ignore
202
- var buckets = {};
203
- list.forEach(function (resource) {
204
- var key = fn(resource);
205
- if (!buckets[key]) {
206
- buckets[key] = [];
207
- }
208
- buckets[key].push(resource);
209
- });
210
- return Object.keys(buckets).map(function (key) { return buckets[key]; });
211
- };
212
- var Map = function (props) {
213
- var grouped = groupBy(props.hits, function (resource) { return resource.lat + "_" + resource.lng; }).map(function (resources) {
214
- var hits = resources.map(function (resource) { return resource.hit; });
215
- var _a = resources[0], lat = _a.lat, lng = _a.lng;
216
- return { hits: hits, lat: lat, lng: lng, key: lat + "_" + lng };
217
- });
218
- var _a = useState(null), infoWindowContent = _a[0], setInfoWindowContent = _a[1];
219
- // @ts-ignore
220
- var makeInfoWindowSetter = function (hitGroup) {
221
- return function (props, marker) {
222
- if (!marker) {
223
- return;
224
- }
225
- setInfoWindowContent({
226
- marker: marker,
227
- hitGroup: hitGroup,
228
- });
229
- };
230
- };
231
- var closeInfoWindow = function () { return setInfoWindowContent(null); };
232
- var UserInfoWindow = props.infoWindow;
233
- var infoWindow = infoWindowContent && UserInfoWindow && (
234
- // @ts-ignore
235
- React.createElement(InfoWindow, { visible: true, google: props.google,
236
- // @ts-ignore
237
- onClose: closeInfoWindow, marker: infoWindowContent.marker },
238
- React.createElement(UserInfoWindow, { hitGroup: infoWindowContent.hitGroup })));
239
- var markers = grouped.map(function (resources) {
240
- var key = resources.key, lat = resources.lat, lng = resources.lng;
241
- return (
242
- // @ts-ignore
243
- React.createElement(Marker, { key: key, position: { lat: lat, lng: lng }, onClick: makeInfoWindowSetter(resources) }, ' '));
244
- });
245
- var mapRef = useRef(null);
246
- var groupKey = grouped
247
- .map(function (item) { return item.key; })
248
- .sort(function (a, b) { return a.localeCompare(b); })
249
- .join(';');
250
- useEffect(function () {
251
- if (mapRef.current === null) {
252
- return;
253
- }
254
- // data has changed. closing the window.
255
- setInfoWindowContent(null);
256
- // @ts-ignore
257
- var bounds = new google.maps.LatLngBounds();
258
- grouped.forEach(function (group) {
259
- // @ts-ignore
260
- bounds.extend(new google.maps.LatLng(group.lat, group.lng));
261
- });
262
- // @ts-ignore
263
- mapRef.current.map.fitBounds(bounds);
264
- }, [mapRef, groupKey]);
265
- return (React.createElement(Map$1, { ref: mapRef, google: props.google, disableDefaultUI: props.disableDefaultUI },
266
- markers,
267
- infoWindow));
268
- };
269
- Map.defaultProps = {
270
- disableDefaultUI: false,
271
- };
272
200
  var ifStringToInt = function (v) {
273
201
  switch (typeof v) {
274
202
  case 'string':
@@ -279,34 +207,78 @@ var ifStringToInt = function (v) {
279
207
  return null;
280
208
  }
281
209
  };
282
- var transformHits = function (hits, mapFunc) {
210
+ function transform(hits, transformer) {
283
211
  return hits
284
- .flatMap(mapFunc)
285
- .map(function (entry) {
286
- return __assign(__assign({}, entry), { lat: ifStringToInt(entry.lat), lng: ifStringToInt(entry.lng) });
287
- })
288
- .filter(function (entry) { return entry.lat !== null && entry.lng !== null; });
289
- };
290
- var defaultMapFunc = function (hit) {
291
- return (hit.site.addresses || []).map(function (address) {
292
- return {
293
- lat: address.latitude,
294
- lng: address.longitude,
295
- hit: __assign(__assign({}, hit), { site: __assign(__assign({}, hit.site), { addresses: [address] }) }),
296
- };
212
+ .map(transformer)
213
+ .filter(function (entry) {
214
+ return entry !== null &&
215
+ entry !== undefined &&
216
+ entry.lat !== null &&
217
+ entry.lng !== null;
297
218
  });
219
+ }
220
+ var defaultTransformer = function (hit) {
221
+ if (!hit.addresses || hit.addresses.length === 0) {
222
+ return null;
223
+ }
224
+ var address = hit.addresses[0];
225
+ return {
226
+ lat: ifStringToInt(address.latitude),
227
+ lng: ifStringToInt(address.longitude),
228
+ hit: hit,
229
+ };
230
+ };
231
+ var Map = function (props) {
232
+ var mapRef = useRef(null);
233
+ var markers = useMemo(function () { return transform(props.hits, props.transformMarkers || defaultTransformer); }, [props.hits, props.transformMarkers]);
234
+ var _a = useState(null), selectedMarker = _a[0], setSelectedMarker = _a[1];
235
+ var handleMarkerClick = useCallback(function (marker, hit) {
236
+ if (props.onMarkerClick) {
237
+ props.onMarkerClick(hit);
238
+ }
239
+ setSelectedMarker({
240
+ marker: marker,
241
+ hit: hit,
242
+ });
243
+ }, [props.onMarkerClick, setSelectedMarker]);
244
+ var UserInfoWindow = props.infoWindow;
245
+ useEffect(function () {
246
+ if (mapRef.current === null) {
247
+ return;
248
+ }
249
+ // Markers updated. Close the current window.
250
+ setSelectedMarker(null);
251
+ // Update bounds of the map
252
+ var bounds = markers.reduce(
253
+ //
254
+ function (bounds, marker) {
255
+ bounds.extend(new google.maps.LatLng(marker.lat, marker.lng));
256
+ return bounds;
257
+ }, new google.maps.LatLngBounds());
258
+ mapRef.current.map.fitBounds(bounds);
259
+ }, [mapRef, markers]);
260
+ return (React__default.createElement(Map$1, { ref: mapRef, google: props.google, disableDefaultUI: props.disableDefaultUI },
261
+ markers.map(function (data) { return (React__default.createElement(Marker, { key: data.hit.id, position: { lat: data.lat, lng: data.lng }, onClick: function (_, marker) {
262
+ if (marker) {
263
+ handleMarkerClick(marker, data.hit);
264
+ }
265
+ } })); }),
266
+ !!selectedMarker && !!UserInfoWindow && (React__default.createElement(InfoWindow, { map: mapRef.current.map, visible: true, google: props.google, marker: selectedMarker.marker },
267
+ React__default.createElement(UserInfoWindow, { hit: selectedMarker.hit })))));
298
268
  };
299
- // makeMap creates a react component class for the google maps algolia widget.
300
- var makeMap = function (apiKey, options) {
301
- var _a = options || {}, mapFunc = _a.mapFunc, infoWindow = _a.infoWindow, disableDefaultUI = _a.disableDefaultUI;
302
- var Elem = GoogleApiWrapper({
269
+ /**
270
+ * Creates a map component to display search results on a map.
271
+ *
272
+ * @param apiKey Google Maps API key
273
+ * @param options Options to customize the map
274
+ */
275
+ var connectMap = function (apiKey) {
276
+ var Renderer = GoogleApiWrapper({
303
277
  apiKey: apiKey,
304
278
  })(function (props) {
305
- var hits = transformHits(props.hits, mapFunc || defaultMapFunc);
306
- return React.createElement(Map, { google: props.google, hits: hits, infoWindow: infoWindow, disableDefaultUI: disableDefaultUI || false });
279
+ return (React__default.createElement(Map, { google: props.google, hits: props.hits, infoWindow: props === null || props === void 0 ? void 0 : props.infoWindow, disableDefaultUI: (props === null || props === void 0 ? void 0 : props.disableDefaultUI) || true, transformMarkers: props === null || props === void 0 ? void 0 : props.transformMarkers, onMarkerClick: props === null || props === void 0 ? void 0 : props.onMarkerClick }));
307
280
  });
308
- // @ts-ignore
309
- return connectHits(Elem);
281
+ return connectHits(Renderer);
310
282
  };
311
283
 
312
284
  /**
@@ -477,7 +449,7 @@ var connectTaxonomyRefinementList = createConnector({
477
449
  var TaxonomyRefinementList = connectTaxonomyRefinementList(function (_a) {
478
450
  var items = _a.items, refine = _a.refine;
479
451
  return (React__default.createElement("div", { className: "ais-RefinementList" },
480
- React__default.createElement("ul", { className: "ais-RefinementList-list" }, items.map(function (item) { return (React__default.createElement("li", { className: "ais-RefinementList-item" },
452
+ React__default.createElement("ul", { className: "ais-RefinementList-list" }, items.map(function (item) { return (React__default.createElement("li", { key: item.value, className: "ais-RefinementList-item" },
481
453
  React__default.createElement("label", { className: "ais-RefinementList-label" },
482
454
  React__default.createElement("input", { className: "ais-RefinementList-checkbox", type: "checkbox", value: item.value, onChange: function () {
483
455
  refine(item.value);
@@ -485,4 +457,4 @@ var TaxonomyRefinementList = connectTaxonomyRefinementList(function (_a) {
485
457
  React__default.createElement("span", { className: "ais-RefinementList-labelText" }, item.label))); }))));
486
458
  });
487
459
 
488
- export { ExactNumberFieldSearch, ExactStringFieldSearch, LocationDistanceFilter, PlaceAutocomplete, SearchBoxFields, StringFieldSearch, TaxonomyRefinementList, connectExactNumberFieldSearch, connectExactStringFieldSearch, connectGeolocation, connectResources, connectSearchBoxFields, connectStringFieldSearch, connectTaxonomyRefinementList, makeConnectFieldSearch, makeMap };
460
+ export { ExactNumberFieldSearch, ExactStringFieldSearch, LocationDistanceFilter, PlaceAutocomplete, SearchBoxFields, StringFieldSearch, TaxonomyRefinementList, connectExactNumberFieldSearch, connectExactStringFieldSearch, connectGeolocation, connectMap, connectResources, connectSearchBoxFields, connectStringFieldSearch, connectTaxonomyRefinementList, makeConnectFieldSearch };
@@ -1,21 +1,24 @@
1
- import * as React from 'react';
2
- export declare type HitGroup = {
3
- key: string;
1
+ import React from 'react';
2
+ import { IndexedResource } from '@211la/search-client';
3
+ import type { Hit, HitsProvided } from 'react-instantsearch-core';
4
+ export declare type MarkerData = {
4
5
  lat: number;
5
6
  lng: number;
6
- hits: any[];
7
+ } & {
8
+ hit: Hit<IndexedResource>;
7
9
  };
8
- declare type InfoWindowProps = {
9
- hitGroup: HitGroup;
10
- };
11
- declare type LatLng = {
12
- lat: number;
13
- lng: number;
14
- };
15
- export declare type MakeMapOptions = {
16
- mapFunc?: <T>(hit: any) => T[];
17
- infoWindow?: React.ComponentType<InfoWindowProps>;
10
+ interface ConnectedMapProps extends HitsProvided<IndexedResource> {
11
+ google: any;
12
+ infoWindow?: any;
18
13
  disableDefaultUI?: boolean;
19
- };
20
- export declare const makeMap: <T extends LatLng>(apiKey: string, options?: MakeMapOptions | undefined) => React.ComponentClass<{}, any>;
14
+ transformMarkers?: any;
15
+ onMarkerClick?: any;
16
+ }
17
+ /**
18
+ * Creates a map component to display search results on a map.
19
+ *
20
+ * @param apiKey Google Maps API key
21
+ * @param options Options to customize the map
22
+ */
23
+ export declare const connectMap: (apiKey: string) => React.ComponentClass<Omit<ConnectedMapProps, 'google' | 'hits'>>;
21
24
  export {};
@@ -2,7 +2,7 @@ export { makeConnectFieldSearch, connectExactStringFieldSearch, connectExactNumb
2
2
  export { connectGeolocation } from './connectGeolocation';
3
3
  export { connectResources } from './connectResources';
4
4
  export { connectSearchBoxFields } from './connectSearchBoxFields';
5
- export { makeMap, HitGroup } from './Map';
5
+ export { connectMap, MarkerData } from './Map';
6
6
  export { LocationDistanceFilter } from './LocationDistanceFilter';
7
7
  export { PlaceAutocomplete } from './PlaceAutocomplete';
8
8
  export { SearchBoxFields } from './SearchBoxFields';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@211la/search-react",
3
- "version": "0.3.8",
3
+ "version": "0.4.1",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/types/index.d.ts",
6
6
  "files": [
@@ -33,7 +33,7 @@
33
33
  "typescript": "^4.0.5"
34
34
  },
35
35
  "peerDependencies": {
36
- "@211la/search-client": "^0.3.8",
36
+ "@211la/search-client": "^0.4.0",
37
37
  "react": ">=16.8.0",
38
38
  "react-dom": ">=16.8.0",
39
39
  "react-instantsearch-core": ">=6.11.1",
@@ -44,4 +44,4 @@
44
44
  "url": "git+https://github.com/211la/cs-search-widget.git",
45
45
  "directory": "react"
46
46
  }
47
- }
47
+ }