@211la/search-react 0.12.0 → 0.13.0

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 ADDED
@@ -0,0 +1,506 @@
1
+ import * as React from 'react';
2
+ import React__default, { useRef, useMemo, useState, useCallback, useEffect } from 'react';
3
+ import { connectRefinementList, connectHits } from 'react-instantsearch-dom';
4
+ import { createConnector } from 'react-instantsearch-core';
5
+ import { GoogleApiWrapper, Map as Map$1, Marker, InfoWindow } from 'google-maps-react';
6
+ import PlacesAutocomplete, { geocodeByAddress, getLatLng } from 'react-places-autocomplete';
7
+
8
+ var trim = function (prefix, suffix, text) {
9
+ var start = 0;
10
+ var end = 0;
11
+ if (text.startsWith(prefix)) {
12
+ start = prefix.length;
13
+ }
14
+ if (text.endsWith(suffix)) {
15
+ end = suffix.length;
16
+ }
17
+ return text.slice(start, text.length - end);
18
+ };
19
+
20
+ /**
21
+ * makeConnectFieldSearch is a factory for connectors that filter by concrete fields.
22
+ * @param exact should the component search for the exact value. Otherwise it keeps all fields that contain the search text as a prefix.
23
+ * In the exact search "homeless" matches only homeless, not "homeless migrants".
24
+ * When filtering by prefix value, "homeless" matches "homeless migrants" too.
25
+ * @param type type of the field data. Can be a number (for example, year or zip code) or a string (agency name).
26
+ */
27
+ var makeConnectFieldSearch = function (exact, type) {
28
+ return function (Component) {
29
+ var surround = function (value) { return value; };
30
+ var trimSurround = function (value) { return value; };
31
+ if (typeof exact !== 'undefined' && exact) {
32
+ if (typeof type !== 'undefined' && type === 'number') {
33
+ surround = function (value) { return value; };
34
+ trimSurround = function (value) { return value; };
35
+ }
36
+ else {
37
+ surround = function (value) { return "\"" + value + "\""; };
38
+ trimSurround = function (value) { return trim("\"", "\"", value); };
39
+ }
40
+ }
41
+ else {
42
+ surround = function (value) { return value + "*"; };
43
+ trimSurround = function (value) { return trim("", "*", value); };
44
+ }
45
+ return connectRefinementList(function (_a) {
46
+ _a.items; var refine = _a.refine, currentRefinement = _a.currentRefinement;
47
+ var onChange = function (value) {
48
+ if (value) {
49
+ refine([surround(value)]);
50
+ }
51
+ else {
52
+ refine([]);
53
+ }
54
+ };
55
+ var value = trimSurround(currentRefinement.length === 0 ? '' : currentRefinement[0]);
56
+ return (React.createElement(Component, { type: type || 'search', value: value, onChange: onChange }));
57
+ });
58
+ };
59
+ };
60
+ /**
61
+ * connectStringFieldSearch makes components to search a string field for all values that start with the given one.
62
+ */
63
+ var connectStringFieldSearch = makeConnectFieldSearch(false, 'string');
64
+ /**
65
+ * connectExactStringFieldSearch makes components to search a string field for the exact value.
66
+ */
67
+ var connectExactStringFieldSearch = makeConnectFieldSearch(true, 'string');
68
+ /**
69
+ * connectExactNumberFieldSearch makes components to search a number field for the exact value.
70
+ */
71
+ var connectExactNumberFieldSearch = makeConnectFieldSearch(true, 'number');
72
+
73
+ /******************************************************************************
74
+ Copyright (c) Microsoft Corporation.
75
+
76
+ Permission to use, copy, modify, and/or distribute this software for any
77
+ purpose with or without fee is hereby granted.
78
+
79
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
80
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
81
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
82
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
83
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
84
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
85
+ PERFORMANCE OF THIS SOFTWARE.
86
+ ***************************************************************************** */
87
+
88
+ var __assign = function() {
89
+ __assign = Object.assign || function __assign(t) {
90
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
91
+ s = arguments[i];
92
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
93
+ }
94
+ return t;
95
+ };
96
+ return __assign.apply(this, arguments);
97
+ };
98
+
99
+ function __rest(s, e) {
100
+ var t = {};
101
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
102
+ t[p] = s[p];
103
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
104
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
105
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
106
+ t[p[i]] = s[p[i]];
107
+ }
108
+ return t;
109
+ }
110
+
111
+ function __spreadArray(to, from, pack) {
112
+ if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
113
+ if (ar || !(i in from)) {
114
+ if (!ar) ar = Array.prototype.slice.call(from, 0, i);
115
+ ar[i] = from[i];
116
+ }
117
+ }
118
+ return to.concat(ar || Array.prototype.slice.call(from));
119
+ }
120
+
121
+ typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
122
+ var e = new Error(message);
123
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
124
+ };
125
+
126
+ /**
127
+ * connectGeolocation helps to make components that specify proximity to the given location.
128
+ * It deals with the refinement in format of {aroundLatLng:{lat:100.50, lng:100.20}, aroundRadius: 20}
129
+ * the aroundRadius is specified in miles.
130
+ * the aroundLatLng is an empty map if unspecified. It contains 'lat' and 'lng' number fields otherwise.
131
+ */
132
+ var connectGeolocation = createConnector({
133
+ displayName: 'ConnectGeolocation',
134
+ getProvidedProps: function (props, searchState) {
135
+ var currentRefinement = {
136
+ aroundLatLng: searchState.aroundLatLng || {},
137
+ aroundRadius: searchState.aroundRadius || 0,
138
+ };
139
+ return __assign({ currentRefinement: currentRefinement }, props);
140
+ },
141
+ refine: function (props, searchState, nextRefinement) {
142
+ var aroundLatLng = nextRefinement.aroundLatLng, aroundRadius = nextRefinement.aroundRadius;
143
+ var stringifyLatLng = function (_a) {
144
+ var lat = _a.lat, lng = _a.lng;
145
+ return (typeof lat !== 'number' ? '' : lat + ", " + lng);
146
+ };
147
+ return __assign(__assign({}, searchState), { aroundLatLng: aroundLatLng
148
+ ? stringifyLatLng(aroundLatLng)
149
+ : searchState.aroundLatLng, aroundRadius: typeof aroundRadius !== 'undefined'
150
+ ? aroundRadius
151
+ : searchState.aroundRadius });
152
+ },
153
+ getSearchParameters: function (searchParameters, props, searchState) {
154
+ return searchParameters.setQueryParameters({
155
+ aroundLatLng: searchState.aroundLatLng,
156
+ aroundRadius: searchState.aroundRadius,
157
+ });
158
+ },
159
+ cleanUp: function (props, searchState) {
160
+ searchState.aroundRadius; searchState.aroundLatLng; var nextSearchState = __rest(searchState, ["aroundRadius", "aroundLatLng"]);
161
+ return nextSearchState;
162
+ },
163
+ });
164
+
165
+ /**
166
+ * connectResources helps to make components to render results of the nexus211 resource search.
167
+ * It is just wrapper for the connectHits from Algolia.
168
+ * It's purpose is just to provide convenient typing for nexus resources, as it differs from the Algolia model.
169
+ * For the connectHits documentation please refer to https://community.algolia.com/react-instantsearch/connectors/connectHits.html
170
+ */
171
+ var connectResources = function (Resources) {
172
+ return connectHits(function (_a) {
173
+ var hits = _a.hits;
174
+ return React.createElement(Resources, { resources: hits });
175
+ });
176
+ };
177
+
178
+ /**
179
+ * connectSearchBoxFields allows you to manipulate keyword search fields based on user input.
180
+ */
181
+ var connectSearchBoxFields = createConnector({
182
+ displayName: 'ConnectWithSearchBoxFields',
183
+ getProvidedProps: function (props, searchState) {
184
+ var currentRefinements = searchState.searchFields || [];
185
+ var attributes = props.attributes;
186
+ return {
187
+ attributes: (attributes || []).map(function (attribute) { return (__assign(__assign({}, attribute), { isRefined: currentRefinements.includes(attribute.fieldName) })); }),
188
+ };
189
+ },
190
+ refine: function (props, searchState, nextRefinement) {
191
+ var searchBoxFields = searchState.searchFields || [];
192
+ return __assign(__assign({}, searchState), { searchFields: searchBoxFields.includes(nextRefinement)
193
+ ? searchBoxFields.filter(function (field) { return field !== nextRefinement; })
194
+ : __spreadArray(__spreadArray([], searchBoxFields), [nextRefinement]) });
195
+ },
196
+ getSearchParameters: function (searchParameters, props, searchState) {
197
+ if (!searchState.searchFields || !searchState.searchFields.length) {
198
+ return searchParameters.setQueryParameter('restrictSearchableAttributes');
199
+ }
200
+ return searchParameters.setQueryParameter('restrictSearchableAttributes', searchState.searchFields);
201
+ },
202
+ cleanUp: function (props, searchState) {
203
+ var newState = JSON.parse(JSON.stringify(searchState));
204
+ delete newState.searchFields;
205
+ return newState;
206
+ },
207
+ });
208
+
209
+ var ifStringToInt = function (v) {
210
+ switch (typeof v) {
211
+ case 'string':
212
+ return parseFloat(v);
213
+ case 'number':
214
+ return v;
215
+ default:
216
+ return null;
217
+ }
218
+ };
219
+ function transform(hits, transformer) {
220
+ return hits
221
+ .map(transformer)
222
+ .filter(function (entry) {
223
+ return entry !== null &&
224
+ entry !== undefined &&
225
+ entry.lat !== null &&
226
+ entry.lng !== null;
227
+ });
228
+ }
229
+ var defaultTransformer = function (hit) {
230
+ if (!hit.addresses || hit.addresses.length === 0) {
231
+ return null;
232
+ }
233
+ var address = hit.addresses[0];
234
+ return {
235
+ lat: ifStringToInt(address.latitude),
236
+ lng: ifStringToInt(address.longitude),
237
+ hit: hit,
238
+ };
239
+ };
240
+ var Map = function (props) {
241
+ var mapRef = useRef(null);
242
+ var markers = useMemo(function () { return transform(props.hits, props.transformMarkers || defaultTransformer); }, [props.hits, props.transformMarkers]);
243
+ var _a = useState(null), selectedMarker = _a[0], setSelectedMarker = _a[1];
244
+ var handleMarkerClick = useCallback(function (marker, hit) {
245
+ if (props.onMarkerClick) {
246
+ props.onMarkerClick(hit);
247
+ }
248
+ setSelectedMarker({
249
+ marker: marker,
250
+ hit: hit,
251
+ });
252
+ }, [props.onMarkerClick, setSelectedMarker]);
253
+ var UserInfoWindow = props.infoWindow;
254
+ useEffect(function () {
255
+ if (mapRef.current === null) {
256
+ return;
257
+ }
258
+ // Markers updated. Close the current window.
259
+ setSelectedMarker(null);
260
+ // Update bounds of the map
261
+ var bounds = markers.reduce(
262
+ //
263
+ function (bounds, marker) {
264
+ bounds.extend(new google.maps.LatLng(marker.lat, marker.lng));
265
+ return bounds;
266
+ }, new google.maps.LatLngBounds());
267
+ mapRef.current.map.fitBounds(bounds);
268
+ }, [mapRef, markers]);
269
+ return (React__default.createElement(Map$1, { ref: mapRef, google: props.google, disableDefaultUI: props.disableDefaultUI },
270
+ markers.map(function (data) { return (React__default.createElement(Marker, { key: data.hit.id, position: { lat: data.lat, lng: data.lng }, onClick: function (_, marker) {
271
+ if (marker) {
272
+ handleMarkerClick(marker, data.hit);
273
+ }
274
+ } })); }),
275
+ !!selectedMarker && !!UserInfoWindow && (React__default.createElement(InfoWindow, { map: mapRef.current.map, visible: true, google: props.google, marker: selectedMarker.marker },
276
+ React__default.createElement(UserInfoWindow, { hit: selectedMarker.hit })))));
277
+ };
278
+ /**
279
+ * Creates a map component to display search results on a map.
280
+ *
281
+ * @param apiKey Google Maps API key
282
+ * @param options Options to customize the map
283
+ */
284
+ var connectMap = function (apiKey) {
285
+ var Renderer = GoogleApiWrapper({
286
+ apiKey: apiKey,
287
+ })(function (props) {
288
+ 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 }));
289
+ });
290
+ return connectHits(Renderer);
291
+ };
292
+
293
+ /**
294
+ * LocationDistanceFilter allows to specify how close should the resource be located to be included in the search results.
295
+ * The location must be specified in other PlaceAutocomplete or a custom component with similar functionality.
296
+ */
297
+ var LocationDistanceFilter = connectGeolocation(function (_a) {
298
+ var refine = _a.refine, currentRefinement = _a.currentRefinement, miles = _a.miles;
299
+ var aroundRadius = currentRefinement.aroundRadius;
300
+ var onChange = function (aroundRadius) { return refine({ aroundRadius: aroundRadius }); };
301
+ var distances = miles.map(function (distance) { return (React.createElement("li", { className: "ais-RefinementList-item", key: distance },
302
+ React.createElement("label", { className: "ais-RefinementList-label", onClick: onChange.bind(null, distance) },
303
+ React.createElement("input", { className: "ais-RefinementList-checkbox", type: "radio", checked: aroundRadius === distance }),
304
+ React.createElement("span", { className: "ais-RefinementList-labelText" },
305
+ ' ',
306
+ distance,
307
+ " miles",
308
+ ' ')))); });
309
+ var mile = (React.createElement("li", { className: "ais-RefinementList-item" },
310
+ React.createElement("label", { className: "ais-RefinementList-label", onClick: onChange.bind(null, 0) },
311
+ React.createElement("input", { className: "ais-RefinementList-checkbox", type: "radio", checked: !aroundRadius }),
312
+ React.createElement("span", { className: "ais-RefinementList-labelText" }, " Everywhere "))));
313
+ return (React.createElement("div", { className: "ais-RefinementList" },
314
+ React.createElement("ul", { className: "ais-RefinementList-list" },
315
+ distances,
316
+ mile)));
317
+ });
318
+
319
+ /**
320
+ * PlaceAutocomplete provides a place autocomplete powered by google maps engine. It selects the location to be used
321
+ * to query resources located nearby.
322
+ * The proximity limit should be specified in LocationDistanceFilter or a custom component with similar functionality.
323
+ */
324
+ var PlaceAutocomplete = connectGeolocation(function (_a) {
325
+ var refine = _a.refine;
326
+ var _b = useState({ address: '' }), state = _b[0], setState = _b[1];
327
+ var handleChange = function (address) {
328
+ setState({ address: address });
329
+ };
330
+ var handleSelect = function (address) {
331
+ handleChange(address);
332
+ if (address === '') {
333
+ refine({ aroundLatLng: {} });
334
+ return;
335
+ }
336
+ geocodeByAddress(address)
337
+ .then(function (results) {
338
+ return getLatLng(results[0]).then(function (aroundLatLng) {
339
+ refine({ aroundLatLng: aroundLatLng });
340
+ });
341
+ })
342
+ .catch(function (error) { return console.error('Error', error); });
343
+ };
344
+ return (React.createElement(PlacesAutocomplete, { value: state.address, onChange: handleChange, onSelect: handleSelect }, function (_a) {
345
+ _a.getInputProps; var suggestions = _a.suggestions, getSuggestionItemProps = _a.getSuggestionItemProps, loading = _a.loading;
346
+ return (React.createElement("div", null,
347
+ React.createElement("div", { className: "autocomplete-dropdown-container" },
348
+ loading && React.createElement("div", null, "Loading..."),
349
+ suggestions.map(function (suggestion) {
350
+ var className = suggestion.active
351
+ ? 'suggestion-item--active'
352
+ : 'suggestion-item';
353
+ // inline style for demonstration purpose
354
+ var style = suggestion.active
355
+ ? { backgroundColor: '#fafafa', cursor: 'pointer' }
356
+ : { backgroundColor: '#ffffff', cursor: 'pointer' };
357
+ return (React.createElement("div", __assign({}, getSuggestionItemProps(suggestion, {
358
+ className: className,
359
+ style: style,
360
+ }), { key: suggestion.description }),
361
+ React.createElement("span", null, suggestion.description)));
362
+ }))));
363
+ }));
364
+ });
365
+
366
+ /**
367
+ * SearchBoxFields specifies a list of fields to be included in the search.
368
+ * @param attributes a list of fields in format of {title: string, fieldName: string}.
369
+ * Title specifies visible user-friendly for the field checkbox. fieldName is the field name in the resource object.
370
+ */
371
+ var SearchBoxFields = connectSearchBoxFields(function (_a) {
372
+ var attributes = _a.attributes, refine = _a.refine;
373
+ var rendered = attributes.map(function (attribute) {
374
+ var id = '211cs_search_field_' + attribute.fieldName.replaceAll('.', '__');
375
+ return (React.createElement("li", { className: "ais-RefinementList-item", key: attribute.title },
376
+ React.createElement("label", { className: "ais-RefinementList-label", htmlFor: id },
377
+ React.createElement("input", { id: id, className: "ais-RefinementList-checkbox", type: 'checkbox', checked: attribute.isRefined, onChange: function () { return refine(attribute.fieldName); } }),
378
+ React.createElement("span", { className: "ais-RefinementList-labelText" }, attribute.title))));
379
+ });
380
+ return (React.createElement("div", { className: "ais-RefinementList" },
381
+ React.createElement("ul", { className: "ais-RefinementList-list" }, rendered)));
382
+ });
383
+
384
+ /**
385
+ * StringFieldSearch provides an input to filter field value by the given string prefix.
386
+ * To configure the number of hits being shown, use the HitsPerPage widget, connectHitsPerPage connector or the Configure widget.
387
+ *
388
+ * https://community.algolia.com/react-instantsearch/widgets/Hits.html
389
+ */
390
+ var StringFieldSearch = connectStringFieldSearch(function (_a) {
391
+ var value = _a.value, onChange = _a.onChange;
392
+ return (React.createElement("div", { className: "ais-SearchBox" },
393
+ React.createElement("form", { noValidate: true, className: "ais-SearchBox-form", action: "", role: "search" },
394
+ React.createElement("input", { type: "search", placeholder: "Search here\u2026", autoComplete: "off", autoCorrect: "off", autoCapitalize: "off", spellCheck: "false", maxLength: 512, className: "ais-SearchBox-input", value: value, onChange: function (e) { return onChange(e.target.value); }, required: true }))));
395
+ });
396
+
397
+ /**
398
+ * ExactStringFieldSearch provides an input to filter by the exact string field value.
399
+ */
400
+ var ExactStringFieldSearch = connectExactStringFieldSearch(function (_a) {
401
+ var value = _a.value, onChange = _a.onChange;
402
+ return (React.createElement("div", { className: "ais-SearchBox" },
403
+ React.createElement("form", { noValidate: true, className: "ais-SearchBox-form", action: "", role: "search" },
404
+ React.createElement("input", { type: "search", placeholder: "Search here\u2026", autoComplete: "off", autoCorrect: "off", autoCapitalize: "off", spellCheck: "false", maxLength: 512, className: "ais-SearchBox-input", value: value, onChange: function (e) { return onChange(e.target.value); }, required: true }))));
405
+ });
406
+
407
+ /**
408
+ * ExactNumberFieldSearch provides an input to filter by the exact number field value
409
+ */
410
+ var ExactNumberFieldSearch = connectExactNumberFieldSearch(function (_a) {
411
+ var value = _a.value, onChange = _a.onChange;
412
+ return (React.createElement("div", { className: "ais-SearchBox" },
413
+ React.createElement("form", { noValidate: true, className: "ais-SearchBox-form", action: "", role: "search" },
414
+ React.createElement("input", { type: "number", placeholder: "Search here\u2026", autoComplete: "off", autoCorrect: "off", autoCapitalize: "off", spellCheck: "false", maxLength: 512, className: "ais-SearchBox-input", value: value, onChange: function (e) { return onChange(e.target.value); }, required: true }))));
415
+ });
416
+
417
+ var connectTaxonomyRefinementList = createConnector({
418
+ displayName: 'ConnectTaxonomyRefinementList',
419
+ getProvidedProps: function (props) {
420
+ var items = props.items;
421
+ return {
422
+ items: items,
423
+ };
424
+ },
425
+ refine: function (props, searchState, nextRefinement) {
426
+ var _a;
427
+ var _b;
428
+ var items = ((_b = searchState.taxonomyRefinementList) === null || _b === void 0 ? void 0 : _b[props.attribute]) || [];
429
+ var newItems = __spreadArray([], items);
430
+ nextRefinement.split('|').forEach(function (item) {
431
+ var index = newItems.findIndex(function (x) { return x === item; });
432
+ if (index >= 0) {
433
+ // If found, remove...
434
+ newItems.splice(index, 1);
435
+ }
436
+ else {
437
+ // Otherwise, add
438
+ newItems.push(item);
439
+ }
440
+ });
441
+ return __assign(__assign({}, searchState), { taxonomyRefinementList: __assign(__assign({}, searchState.taxonomyRefinementList), (_a = {}, _a[props.attribute] = newItems, _a)) });
442
+ },
443
+ getSearchParameters: function (searchParameters, props, searchState) {
444
+ var _a;
445
+ var nextParameters = searchParameters.addDisjunctiveFacet(props.attribute);
446
+ var items = ((_a = searchState.taxonomyRefinementList) === null || _a === void 0 ? void 0 : _a[props.attribute]) || [];
447
+ return items.reduce(function (state, item) {
448
+ return state.addDisjunctiveFacetRefinement(props.attribute, item);
449
+ }, nextParameters);
450
+ },
451
+ cleanUp: function (props, searchState) {
452
+ var newState = JSON.parse(JSON.stringify(searchState));
453
+ delete newState.taxonomyRefinementList;
454
+ return newState;
455
+ },
456
+ });
457
+
458
+ var TaxonomyRefinementList = connectTaxonomyRefinementList(function (_a) {
459
+ var items = _a.items, refine = _a.refine;
460
+ return (React__default.createElement("div", { className: "ais-RefinementList" },
461
+ React__default.createElement("ul", { className: "ais-RefinementList-list" }, items.map(function (item) { return (React__default.createElement("li", { key: item.value, className: "ais-RefinementList-item" },
462
+ React__default.createElement("label", { className: "ais-RefinementList-label" },
463
+ React__default.createElement("input", { className: "ais-RefinementList-checkbox", type: "checkbox", value: item.value, onChange: function () {
464
+ refine(item.value);
465
+ } })),
466
+ React__default.createElement("span", { className: "ais-RefinementList-labelText" }, item.label))); }))));
467
+ });
468
+
469
+ var connectOpenCloseHours = createConnector({
470
+ displayName: 'ConnectOpenCloseHours',
471
+ getProvidedProps: function (props, searchState) {
472
+ var currentRefinement = {
473
+ openNow: searchState.openNow || false,
474
+ openAtDay: searchState.openAtDay || undefined,
475
+ openAtMinute: searchState.openAtMinute || undefined,
476
+ };
477
+ return __assign({ currentRefinement: currentRefinement }, props);
478
+ },
479
+ refine: function (props, searchState, nextRefinement) {
480
+ var openNow = nextRefinement.openNow, openAtDay = nextRefinement.openAtDay, openAtMinute = nextRefinement.openAtMinute;
481
+ var now = new Date();
482
+ var day = now.getDay();
483
+ var hours = now.getHours();
484
+ var minutes = now.getMinutes();
485
+ var totalMinutes = (hours * 60) + minutes;
486
+ return __assign(__assign({}, searchState), { openNow: openNow, openAtDay: openNow ? (openAtDay || day) : undefined, openAtMinute: openNow ? (openAtMinute || totalMinutes) : undefined });
487
+ },
488
+ getSearchParameters: function (searchParameters, props, searchState) {
489
+ var params = searchParameters.setQueryParameters({
490
+ openNow: searchState.openNow,
491
+ });
492
+ if (searchState.openNow) {
493
+ return params.setQueryParameters({
494
+ openAtDay: searchState.openAtDay,
495
+ openAtMinute: searchState.openAtMinute
496
+ });
497
+ }
498
+ return params;
499
+ },
500
+ cleanUp: function (props, searchState) {
501
+ searchState.openNow; searchState.openAtDay; searchState.openAtMinute; var nextSearchState = __rest(searchState, ["openNow", "openAtDay", "openAtMinute"]);
502
+ return nextSearchState;
503
+ },
504
+ });
505
+
506
+ export { ExactNumberFieldSearch, ExactStringFieldSearch, LocationDistanceFilter, PlaceAutocomplete, SearchBoxFields, StringFieldSearch, TaxonomyRefinementList, connectExactNumberFieldSearch, connectExactStringFieldSearch, connectGeolocation, connectMap, connectOpenCloseHours, connectResources, connectSearchBoxFields, connectStringFieldSearch, connectTaxonomyRefinementList, makeConnectFieldSearch };
@@ -0,0 +1,5 @@
1
+ import * as React from 'react';
2
+ /**
3
+ * ExactNumberFieldSearch provides an input to filter by the exact number field value
4
+ */
5
+ export declare const ExactNumberFieldSearch: React.ComponentClass<import("react-instantsearch-core").RefinementListExposed, any>;
@@ -0,0 +1,5 @@
1
+ import * as React from 'react';
2
+ /**
3
+ * ExactStringFieldSearch provides an input to filter by the exact string field value.
4
+ */
5
+ export declare const ExactStringFieldSearch: React.ComponentClass<import("react-instantsearch-core").RefinementListExposed, any>;
@@ -0,0 +1,6 @@
1
+ import * as React from 'react';
2
+ /**
3
+ * LocationDistanceFilter allows to specify how close should the resource be located to be included in the search results.
4
+ * The location must be specified in other PlaceAutocomplete or a custom component with similar functionality.
5
+ */
6
+ export declare const LocationDistanceFilter: React.ComponentClass<any, any>;
@@ -0,0 +1,24 @@
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 = {
5
+ lat: number;
6
+ lng: number;
7
+ } & {
8
+ hit: Hit<IndexedResource>;
9
+ };
10
+ interface ConnectedMapProps extends HitsProvided<IndexedResource> {
11
+ google: any;
12
+ infoWindow?: any;
13
+ disableDefaultUI?: boolean;
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'>>;
24
+ export {};
@@ -0,0 +1,7 @@
1
+ import * as React from 'react';
2
+ /**
3
+ * PlaceAutocomplete provides a place autocomplete powered by google maps engine. It selects the location to be used
4
+ * to query resources located nearby.
5
+ * The proximity limit should be specified in LocationDistanceFilter or a custom component with similar functionality.
6
+ */
7
+ export declare const PlaceAutocomplete: React.ComponentClass<any, any>;
@@ -0,0 +1,7 @@
1
+ import * as React from 'react';
2
+ /**
3
+ * SearchBoxFields specifies a list of fields to be included in the search.
4
+ * @param attributes a list of fields in format of {title: string, fieldName: string}.
5
+ * Title specifies visible user-friendly for the field checkbox. fieldName is the field name in the resource object.
6
+ */
7
+ export declare const SearchBoxFields: React.ComponentClass<any, any>;
@@ -0,0 +1,8 @@
1
+ import * as React from 'react';
2
+ /**
3
+ * StringFieldSearch provides an input to filter field value by the given string prefix.
4
+ * To configure the number of hits being shown, use the HitsPerPage widget, connectHitsPerPage connector or the Configure widget.
5
+ *
6
+ * https://community.algolia.com/react-instantsearch/widgets/Hits.html
7
+ */
8
+ export declare const StringFieldSearch: React.ComponentClass<import("react-instantsearch-core").RefinementListExposed, any>;
@@ -0,0 +1 @@
1
+ export declare const trim: (prefix: string, suffix: string, text: string) => string;
@@ -0,0 +1,27 @@
1
+ import * as React from 'react';
2
+ declare type FieldSearchProps = {
3
+ value: string;
4
+ onChange: (name: string) => any;
5
+ type: string;
6
+ };
7
+ /**
8
+ * makeConnectFieldSearch is a factory for connectors that filter by concrete fields.
9
+ * @param exact should the component search for the exact value. Otherwise it keeps all fields that contain the search text as a prefix.
10
+ * In the exact search "homeless" matches only homeless, not "homeless migrants".
11
+ * When filtering by prefix value, "homeless" matches "homeless migrants" too.
12
+ * @param type type of the field data. Can be a number (for example, year or zip code) or a string (agency name).
13
+ */
14
+ export declare const makeConnectFieldSearch: (exact: boolean, type?: "string" | "number" | undefined) => (Component: React.FunctionComponent<FieldSearchProps>) => React.ComponentClass<import("react-instantsearch-core").RefinementListExposed, any>;
15
+ /**
16
+ * connectStringFieldSearch makes components to search a string field for all values that start with the given one.
17
+ */
18
+ export declare const connectStringFieldSearch: (Component: React.FunctionComponent<FieldSearchProps>) => React.ComponentClass<import("react-instantsearch-core").RefinementListExposed, any>;
19
+ /**
20
+ * connectExactStringFieldSearch makes components to search a string field for the exact value.
21
+ */
22
+ export declare const connectExactStringFieldSearch: (Component: React.FunctionComponent<FieldSearchProps>) => React.ComponentClass<import("react-instantsearch-core").RefinementListExposed, any>;
23
+ /**
24
+ * connectExactNumberFieldSearch makes components to search a number field for the exact value.
25
+ */
26
+ export declare const connectExactNumberFieldSearch: (Component: React.FunctionComponent<FieldSearchProps>) => React.ComponentClass<import("react-instantsearch-core").RefinementListExposed, any>;
27
+ export {};
@@ -0,0 +1,8 @@
1
+ /// <reference types="react" />
2
+ /**
3
+ * connectGeolocation helps to make components that specify proximity to the given location.
4
+ * It deals with the refinement in format of {aroundLatLng:{lat:100.50, lng:100.20}, aroundRadius: 20}
5
+ * the aroundRadius is specified in miles.
6
+ * the aroundLatLng is an empty map if unspecified. It contains 'lat' and 'lng' number fields otherwise.
7
+ */
8
+ export declare const connectGeolocation: ((stateless: import("react").FunctionComponent<any>) => import("react").ComponentClass<any, any>) & (<TProps extends Partial<any>>(Composed: import("react").ComponentType<TProps>) => import("react-instantsearch-core").ConnectedComponentClass<TProps, any, any>);
@@ -0,0 +1,2 @@
1
+ /// <reference types="react" />
2
+ export declare const connectOpenCloseHours: ((stateless: import("react").FunctionComponent<any>) => import("react").ComponentClass<any, any>) & (<TProps extends Partial<any>>(Composed: import("react").ComponentType<TProps>) => import("react-instantsearch-core").ConnectedComponentClass<TProps, any, any>);
@@ -0,0 +1,11 @@
1
+ import * as React from 'react';
2
+ import { IndexedResource } from '@211la/search-client';
3
+ /**
4
+ * connectResources helps to make components to render results of the nexus211 resource search.
5
+ * It is just wrapper for the connectHits from Algolia.
6
+ * It's purpose is just to provide convenient typing for nexus resources, as it differs from the Algolia model.
7
+ * For the connectHits documentation please refer to https://community.algolia.com/react-instantsearch/connectors/connectHits.html
8
+ */
9
+ export declare const connectResources: (Resources: React.FunctionComponent<{
10
+ resources: IndexedResource[];
11
+ }>) => React.ComponentClass<{}, any>;
@@ -0,0 +1,11 @@
1
+ /// <reference types="react" />
2
+ /**
3
+ * connectSearchBoxFields allows you to manipulate keyword search fields based on user input.
4
+ */
5
+ export declare const connectSearchBoxFields: ((stateless: import("react").FunctionComponent<import("react-instantsearch-core").ConnectorProvided<{
6
+ attributes: any;
7
+ }>>) => import("react").ComponentClass<any, any>) & (<TProps extends Partial<import("react-instantsearch-core").ConnectorProvided<{
8
+ attributes: any;
9
+ }>>>(Composed: import("react").ComponentType<TProps>) => import("react-instantsearch-core").ConnectedComponentClass<TProps, import("react-instantsearch-core").ConnectorProvided<{
10
+ attributes: any;
11
+ }>, any>);
@@ -0,0 +1,14 @@
1
+ export { makeConnectFieldSearch, connectExactStringFieldSearch, connectExactNumberFieldSearch, connectStringFieldSearch, } from './connectFieldSearch';
2
+ export { connectGeolocation } from './connectGeolocation';
3
+ export { connectResources } from './connectResources';
4
+ export { connectSearchBoxFields } from './connectSearchBoxFields';
5
+ export { connectMap, MarkerData } from './Map';
6
+ export { LocationDistanceFilter } from './LocationDistanceFilter';
7
+ export { PlaceAutocomplete } from './PlaceAutocomplete';
8
+ export { SearchBoxFields } from './SearchBoxFields';
9
+ export { StringFieldSearch } from './StringFieldSearch';
10
+ export { ExactStringFieldSearch } from './ExactStringFieldSearch';
11
+ export { ExactNumberFieldSearch } from './ExactNumberFieldSearch';
12
+ export { connectTaxonomyRefinementList } from './taxonomyRefinementList/connectTaxonomyRefinementList';
13
+ export { TaxonomyRefinementList } from './taxonomyRefinementList/TaxonomyRefinementList';
14
+ export { connectOpenCloseHours } from './connectOpenCloseHours';
@@ -0,0 +1,8 @@
1
+ import React from 'react';
2
+ export declare const TaxonomyRefinementList: React.ComponentClass<{
3
+ attribute: string;
4
+ items: {
5
+ label: string;
6
+ value: string;
7
+ }[];
8
+ }, any>;
@@ -0,0 +1,25 @@
1
+ /// <reference types="react" />
2
+ declare type TaxonomyRefinementListProps = {
3
+ attribute: string;
4
+ items: {
5
+ label: string;
6
+ value: string;
7
+ }[];
8
+ };
9
+ export declare const connectTaxonomyRefinementList: ((stateless: import("react").FunctionComponent<import("react-instantsearch-core").ConnectorProvided<{
10
+ items: {
11
+ label: string;
12
+ value: string;
13
+ }[];
14
+ }>>) => import("react").ComponentClass<TaxonomyRefinementListProps, any>) & (<TProps extends Partial<import("react-instantsearch-core").ConnectorProvided<{
15
+ items: {
16
+ label: string;
17
+ value: string;
18
+ }[];
19
+ }>>>(Composed: import("react").ComponentType<TProps>) => import("react-instantsearch-core").ConnectedComponentClass<TProps, import("react-instantsearch-core").ConnectorProvided<{
20
+ items: {
21
+ label: string;
22
+ value: string;
23
+ }[];
24
+ }>, TaxonomyRefinementListProps>);
25
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@211la/search-react",
3
- "version": "0.12.0",
3
+ "version": "0.13.0",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/types/index.d.ts",
6
6
  "files": [
@@ -27,13 +27,13 @@
27
27
  "rollup": "^2.48.0",
28
28
  "rollup-plugin-peer-deps-external": "^2.2.4",
29
29
  "rollup-plugin-terser": "^7.0.2",
30
- "rollup-plugin-typescript2": "^0.30.0",
30
+ "rollup-plugin-typescript2": "^0.32.1",
31
31
  "ts-loader": "^8.0.12",
32
32
  "tslib": "^2.3.0",
33
33
  "typescript": "^4.0.5"
34
34
  },
35
35
  "peerDependencies": {
36
- "@211la/search-client": "^0.12.0",
36
+ "@211la/search-client": "^0.13.0",
37
37
  "react": ">=16.8.0",
38
38
  "react-dom": ">=16.8.0",
39
39
  "react-instantsearch-core": ">=6.11.1",