@algolia/autocomplete-core 1.3.0 → 1.5.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.
Files changed (48) hide show
  1. package/dist/esm/createAutocomplete.d.ts +8 -2
  2. package/dist/esm/createAutocomplete.js +9 -1
  3. package/dist/esm/createStore.js +3 -2
  4. package/dist/esm/getAutocompleteSetters.js +1 -1
  5. package/dist/esm/getDefaultProps.js +2 -2
  6. package/dist/esm/getPropGetters.js +41 -13
  7. package/dist/esm/index.d.ts +0 -1
  8. package/dist/esm/index.js +1 -2
  9. package/dist/esm/metadata.d.ts +33 -0
  10. package/dist/esm/metadata.js +44 -0
  11. package/dist/esm/onInput.d.ts +3 -0
  12. package/dist/esm/onInput.js +69 -40
  13. package/dist/esm/onKeyDown.js +12 -3
  14. package/dist/esm/reshape.js +1 -1
  15. package/dist/esm/resolve.js +2 -2
  16. package/dist/esm/stateReducer.js +1 -1
  17. package/dist/esm/types/AutocompleteApi.js +1 -0
  18. package/dist/esm/types/AutocompleteCollection.js +1 -0
  19. package/dist/esm/types/AutocompleteContext.js +1 -0
  20. package/dist/esm/types/AutocompleteEnvironment.d.ts +1 -0
  21. package/dist/esm/types/AutocompleteEnvironment.js +1 -0
  22. package/dist/esm/types/AutocompleteNavigator.js +1 -0
  23. package/dist/esm/types/AutocompleteOptions.js +1 -0
  24. package/dist/esm/types/AutocompletePlugin.d.ts +10 -0
  25. package/dist/esm/types/AutocompletePlugin.js +1 -0
  26. package/dist/esm/types/AutocompletePropGetters.js +1 -0
  27. package/dist/esm/types/AutocompleteReshape.js +1 -0
  28. package/dist/esm/types/AutocompleteSetters.js +1 -0
  29. package/dist/esm/types/AutocompleteSource.js +1 -0
  30. package/dist/esm/types/AutocompleteState.js +1 -0
  31. package/dist/esm/types/AutocompleteStore.d.ts +1 -0
  32. package/dist/esm/types/AutocompleteStore.js +1 -0
  33. package/dist/esm/types/AutocompleteSubscribers.js +1 -0
  34. package/dist/esm/utils/createConcurrentSafePromise.d.ts +4 -1
  35. package/dist/esm/utils/createConcurrentSafePromise.js +12 -1
  36. package/dist/esm/utils/getNormalizedSources.js +2 -3
  37. package/dist/esm/utils/index.d.ts +0 -1
  38. package/dist/esm/utils/index.js +1 -2
  39. package/dist/esm/utils/mapToAlgoliaResponse.js +1 -1
  40. package/dist/umd/index.development.js +289 -120
  41. package/dist/umd/index.development.js.map +1 -1
  42. package/dist/umd/index.production.js +2 -2
  43. package/dist/umd/index.production.js.map +1 -1
  44. package/package.json +3 -3
  45. package/dist/esm/utils/noop.d.ts +0 -1
  46. package/dist/esm/utils/noop.js +0 -1
  47. package/dist/esm/version.d.ts +0 -1
  48. package/dist/esm/version.js +0 -1
@@ -1,49 +1,22 @@
1
- /*! @algolia/autocomplete-core 1.3.0 | MIT License | © Algolia, Inc. and contributors | https://github.com/algolia/autocomplete */
1
+ /*! @algolia/autocomplete-core 1.5.1 | MIT License | © Algolia, Inc. and contributors | https://github.com/algolia/autocomplete */
2
2
  (function (global, factory) {
3
3
  typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
4
4
  typeof define === 'function' && define.amd ? define(['exports'], factory) :
5
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global['@algolia/autocomplete-core'] = {}));
6
- }(this, (function (exports) { 'use strict';
7
-
8
- function _typeof(obj) {
9
- "@babel/helpers - typeof";
10
-
11
- if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
12
- _typeof = function (obj) {
13
- return typeof obj;
14
- };
15
- } else {
16
- _typeof = function (obj) {
17
- return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
18
- };
19
- }
20
-
21
- return _typeof(obj);
22
- }
23
-
24
- function _defineProperty(obj, key, value) {
25
- if (key in obj) {
26
- Object.defineProperty(obj, key, {
27
- value: value,
28
- enumerable: true,
29
- configurable: true,
30
- writable: true
31
- });
32
- } else {
33
- obj[key] = value;
34
- }
35
-
36
- return obj;
37
- }
5
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global["@algolia/autocomplete-core"] = {}));
6
+ })(this, (function (exports) { 'use strict';
38
7
 
39
8
  function ownKeys(object, enumerableOnly) {
40
9
  var keys = Object.keys(object);
41
10
 
42
11
  if (Object.getOwnPropertySymbols) {
43
12
  var symbols = Object.getOwnPropertySymbols(object);
44
- if (enumerableOnly) symbols = symbols.filter(function (sym) {
45
- return Object.getOwnPropertyDescriptor(object, sym).enumerable;
46
- });
13
+
14
+ if (enumerableOnly) {
15
+ symbols = symbols.filter(function (sym) {
16
+ return Object.getOwnPropertyDescriptor(object, sym).enumerable;
17
+ });
18
+ }
19
+
47
20
  keys.push.apply(keys, symbols);
48
21
  }
49
22
 
@@ -70,6 +43,37 @@
70
43
  return target;
71
44
  }
72
45
 
46
+ function _typeof$1(obj) {
47
+ "@babel/helpers - typeof";
48
+
49
+ if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
50
+ _typeof$1 = function (obj) {
51
+ return typeof obj;
52
+ };
53
+ } else {
54
+ _typeof$1 = function (obj) {
55
+ return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
56
+ };
57
+ }
58
+
59
+ return _typeof$1(obj);
60
+ }
61
+
62
+ function _defineProperty(obj, key, value) {
63
+ if (key in obj) {
64
+ Object.defineProperty(obj, key, {
65
+ value: value,
66
+ enumerable: true,
67
+ configurable: true,
68
+ writable: true
69
+ });
70
+ } else {
71
+ obj[key] = value;
72
+ }
73
+
74
+ return obj;
75
+ }
76
+
73
77
  function _objectWithoutPropertiesLoose(source, excluded) {
74
78
  if (source == null) return {};
75
79
  var target = {};
@@ -107,27 +111,27 @@
107
111
  }
108
112
 
109
113
  function _toConsumableArray(arr) {
110
- return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread();
114
+ return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray$1(arr) || _nonIterableSpread();
111
115
  }
112
116
 
113
117
  function _arrayWithoutHoles(arr) {
114
- if (Array.isArray(arr)) return _arrayLikeToArray(arr);
118
+ if (Array.isArray(arr)) return _arrayLikeToArray$1(arr);
115
119
  }
116
120
 
117
121
  function _iterableToArray(iter) {
118
- if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter);
122
+ if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
119
123
  }
120
124
 
121
- function _unsupportedIterableToArray(o, minLen) {
125
+ function _unsupportedIterableToArray$1(o, minLen) {
122
126
  if (!o) return;
123
- if (typeof o === "string") return _arrayLikeToArray(o, minLen);
127
+ if (typeof o === "string") return _arrayLikeToArray$1(o, minLen);
124
128
  var n = Object.prototype.toString.call(o).slice(8, -1);
125
129
  if (n === "Object" && o.constructor) n = o.constructor.name;
126
130
  if (n === "Map" || n === "Set") return Array.from(o);
127
- if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
131
+ if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray$1(o, minLen);
128
132
  }
129
133
 
130
- function _arrayLikeToArray(arr, len) {
134
+ function _arrayLikeToArray$1(arr, len) {
131
135
  if (len == null || len > arr.length) len = arr.length;
132
136
 
133
137
  for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
@@ -140,23 +144,23 @@
140
144
  }
141
145
 
142
146
  function _slicedToArray(arr, i) {
143
- return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray$1(arr, i) || _nonIterableRest();
147
+ return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest();
144
148
  }
145
149
 
146
150
  function _nonIterableRest() {
147
151
  throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
148
152
  }
149
153
 
150
- function _unsupportedIterableToArray$1(o, minLen) {
154
+ function _unsupportedIterableToArray(o, minLen) {
151
155
  if (!o) return;
152
- if (typeof o === "string") return _arrayLikeToArray$1(o, minLen);
156
+ if (typeof o === "string") return _arrayLikeToArray(o, minLen);
153
157
  var n = Object.prototype.toString.call(o).slice(8, -1);
154
158
  if (n === "Object" && o.constructor) n = o.constructor.name;
155
159
  if (n === "Map" || n === "Set") return Array.from(o);
156
- if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray$1(o, minLen);
160
+ if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
157
161
  }
158
162
 
159
- function _arrayLikeToArray$1(arr, len) {
163
+ function _arrayLikeToArray(arr, len) {
160
164
  if (len == null || len > arr.length) len = arr.length;
161
165
 
162
166
  for (var i = 0, arr2 = new Array(len); i < len; i++) {
@@ -167,14 +171,17 @@
167
171
  }
168
172
 
169
173
  function _iterableToArrayLimit(arr, i) {
170
- if (typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) return;
174
+ var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
175
+
176
+ if (_i == null) return;
171
177
  var _arr = [];
172
178
  var _n = true;
173
179
  var _d = false;
174
- var _e = undefined;
180
+
181
+ var _s, _e;
175
182
 
176
183
  try {
177
- for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
184
+ for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) {
178
185
  _arr.push(_s.value);
179
186
 
180
187
  if (i && _arr.length === i) break;
@@ -197,20 +204,20 @@
197
204
  if (Array.isArray(arr)) return arr;
198
205
  }
199
206
 
200
- function _typeof$1(obj) {
207
+ function _typeof(obj) {
201
208
  "@babel/helpers - typeof";
202
209
 
203
210
  if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
204
- _typeof$1 = function _typeof(obj) {
211
+ _typeof = function _typeof(obj) {
205
212
  return typeof obj;
206
213
  };
207
214
  } else {
208
- _typeof$1 = function _typeof(obj) {
215
+ _typeof = function _typeof(obj) {
209
216
  return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
210
217
  };
211
218
  }
212
219
 
213
- return _typeof$1(obj);
220
+ return _typeof(obj);
214
221
  }
215
222
  /**
216
223
  * Decycles objects with circular references.
@@ -221,7 +228,7 @@
221
228
  function decycle(obj) {
222
229
  var seen = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new Set();
223
230
 
224
- if ( !obj || _typeof$1(obj) !== 'object') {
231
+ if (!obj || _typeof(obj) !== 'object') {
225
232
  return obj;
226
233
  }
227
234
 
@@ -279,6 +286,15 @@
279
286
  }
280
287
  }
281
288
 
289
+ var noop = function noop() {};
290
+
291
+ var version = '1.5.1';
292
+
293
+ var userAgents = [{
294
+ segment: 'autocomplete-core',
295
+ version: version
296
+ }];
297
+
282
298
  var warnCache = {
283
299
  current: {}
284
300
  };
@@ -325,7 +341,8 @@
325
341
  state: state,
326
342
  prevState: prevState
327
343
  });
328
- }
344
+ },
345
+ shouldSkipPendingUpdate: false
329
346
  };
330
347
  }
331
348
 
@@ -378,6 +395,55 @@
378
395
  };
379
396
  }
380
397
 
398
+ /**
399
+ * Creates a runner that executes promises in a concurrent-safe way.
400
+ *
401
+ * This is useful to prevent older promises to resolve after a newer promise,
402
+ * otherwise resulting in stale resolved values.
403
+ */
404
+ function createConcurrentSafePromise() {
405
+ var basePromiseId = -1;
406
+ var latestResolvedId = -1;
407
+ var latestResolvedValue = undefined;
408
+ var runningPromisesCount = 0;
409
+
410
+ function runConcurrentSafePromise(promise) {
411
+ basePromiseId++;
412
+ runningPromisesCount++;
413
+ var currentPromiseId = basePromiseId;
414
+ return Promise.resolve(promise).then(function (x) {
415
+ // The promise might take too long to resolve and get outdated. This would
416
+ // result in resolving stale values.
417
+ // When this happens, we ignore the promise value and return the one
418
+ // coming from the latest resolved value.
419
+ //
420
+ // +----------------------------------+
421
+ // | 100ms |
422
+ // | run(1) +---> R1 |
423
+ // | 300ms |
424
+ // | run(2) +-------------> R2 (SKIP) |
425
+ // | 200ms |
426
+ // | run(3) +--------> R3 |
427
+ // +----------------------------------+
428
+ if (latestResolvedValue && currentPromiseId < latestResolvedId) {
429
+ return latestResolvedValue;
430
+ }
431
+
432
+ latestResolvedId = currentPromiseId;
433
+ latestResolvedValue = x;
434
+ return x;
435
+ }).finally(function () {
436
+ return runningPromisesCount--;
437
+ });
438
+ }
439
+
440
+ runConcurrentSafePromise.isRunning = function () {
441
+ return runningPromisesCount > 0;
442
+ };
443
+
444
+ return runConcurrentSafePromise;
445
+ }
446
+
381
447
  /**
382
448
  * Returns the next active item ID from the current state.
383
449
  *
@@ -412,13 +478,11 @@
412
478
  return numericIndex;
413
479
  }
414
480
 
415
- var noop = function noop() {};
416
-
417
481
  function getNormalizedSources(getSources, params) {
418
482
  var seenSourceIds = [];
419
483
  return Promise.resolve(getSources(params)).then(function (sources) {
420
484
  invariant(Array.isArray(sources), function () {
421
- return "The `getSources` function must return an array of sources but returned type ".concat(JSON.stringify(_typeof(sources)), ":\n\n").concat(JSON.stringify(decycle(sources), null, 2));
485
+ return "The `getSources` function must return an array of sources but returned type ".concat(JSON.stringify(_typeof$1(sources)), ":\n\n").concat(JSON.stringify(decycle(sources), null, 2));
422
486
  });
423
487
  return Promise.all(sources // We allow `undefined` and `false` sources to allow users to use
424
488
  // `Boolean(query) && source` (=> `false`).
@@ -812,7 +876,7 @@
812
876
  var transform = matches[0].transformResponse;
813
877
  var items = transform ? transform(mapToAlgoliaResponse(results)) : results;
814
878
  invariant(Array.isArray(items), function () {
815
- return "The `getItems` function from source \"".concat(source.sourceId, "\" must return an array of items but returned type ").concat(JSON.stringify(_typeof(items)), ":\n\n").concat(JSON.stringify(decycle(items), null, 2), ".\n\nSee: https://www.algolia.com/doc/ui-libraries/autocomplete/core-concepts/sources/#param-getitems");
879
+ return "The `getItems` function from source \"".concat(source.sourceId, "\" must return an array of items but returned type ").concat(JSON.stringify(_typeof$1(items)), ":\n\n").concat(JSON.stringify(decycle(items), null, 2), ".\n\nSee: https://www.algolia.com/doc/ui-libraries/autocomplete/core-concepts/sources/#param-getitems");
816
880
  });
817
881
  invariant(items.every(Boolean), "The `getItems` function from source \"".concat(source.sourceId, "\" must return an array of items but returned ").concat(JSON.stringify(undefined), ".\n\nDid you forget to return items?\n\nSee: https://www.algolia.com/doc/ui-libraries/autocomplete/core-concepts/sources/#param-getitems"));
818
882
  return {
@@ -822,7 +886,9 @@
822
886
  });
823
887
  }
824
888
 
889
+ var _excluded$2 = ["event", "nextState", "props", "query", "refresh", "store"];
825
890
  var lastStalledId = null;
891
+ var runConcurrentSafePromise = createConcurrentSafePromise();
826
892
  function onInput(_ref) {
827
893
  var event = _ref.event,
828
894
  _ref$nextState = _ref.nextState,
@@ -831,7 +897,7 @@
831
897
  query = _ref.query,
832
898
  refresh = _ref.refresh,
833
899
  store = _ref.store,
834
- setters = _objectWithoutProperties(_ref, ["event", "nextState", "props", "query", "refresh", "store"]);
900
+ setters = _objectWithoutProperties(_ref, _excluded$2);
835
901
 
836
902
  if (lastStalledId) {
837
903
  props.environment.clearTimeout(lastStalledId);
@@ -848,28 +914,40 @@
848
914
  if (!query && props.openOnFocus === false) {
849
915
  var _nextState$isOpen;
850
916
 
851
- setStatus('idle');
852
- setCollections(store.getState().collections.map(function (collection) {
917
+ var collections = store.getState().collections.map(function (collection) {
853
918
  return _objectSpread2(_objectSpread2({}, collection), {}, {
854
919
  items: []
855
920
  });
856
- }));
921
+ });
922
+ setStatus('idle');
923
+ setCollections(collections);
857
924
  setIsOpen((_nextState$isOpen = nextState.isOpen) !== null && _nextState$isOpen !== void 0 ? _nextState$isOpen : props.shouldPanelOpen({
858
925
  state: store.getState()
859
- }));
860
- return Promise.resolve();
926
+ })); // We make sure to update the latest resolved value of the tracked
927
+ // promises to keep late resolving promises from "cancelling" the state
928
+ // updates performed in this code path.
929
+ // We chain with a void promise to respect `onInput`'s expected return type.
930
+
931
+ return runConcurrentSafePromise(collections).then(function () {
932
+ return Promise.resolve();
933
+ });
861
934
  }
862
935
 
863
936
  setStatus('loading');
864
937
  lastStalledId = props.environment.setTimeout(function () {
865
938
  setStatus('stalled');
866
- }, props.stallThreshold);
867
- return props.getSources(_objectSpread2({
939
+ }, props.stallThreshold); // We track the entire promise chain triggered by `onInput` before mutating
940
+ // the Autocomplete state to make sure that any state manipulation is based on
941
+ // fresh data regardless of when promises individually resolve.
942
+ // We don't track nested promises and only rely on the full chain resolution,
943
+ // meaning we should only ever manipulate the state once this concurrent-safe
944
+ // promise is resolved.
945
+
946
+ return runConcurrentSafePromise(props.getSources(_objectSpread2({
868
947
  query: query,
869
948
  refresh: refresh,
870
949
  state: store.getState()
871
950
  }, setters)).then(function (sources) {
872
- setStatus('loading');
873
951
  return Promise.all(sources.map(function (source) {
874
952
  return Promise.resolve(source.getItems(_objectSpread2({
875
953
  query: query,
@@ -886,46 +964,61 @@
886
964
  props: props,
887
965
  state: store.getState()
888
966
  });
889
- }).then(function (collections) {
890
- var _nextState$isOpen2;
967
+ });
968
+ })).then(function (collections) {
969
+ var _nextState$isOpen2;
891
970
 
892
- setStatus('idle');
893
- setCollections(collections);
894
- var isPanelOpen = props.shouldPanelOpen({
895
- state: store.getState()
896
- });
897
- setIsOpen((_nextState$isOpen2 = nextState.isOpen) !== null && _nextState$isOpen2 !== void 0 ? _nextState$isOpen2 : props.openOnFocus && !query && isPanelOpen || isPanelOpen);
898
- var highlightedItem = getActiveItem(store.getState());
971
+ // Parameters passed to `onInput` could be stale when the following code
972
+ // executes, because `onInput` calls may not resolve in order.
973
+ // If it becomes a problem we'll need to save the last passed parameters.
974
+ // See: https://codesandbox.io/s/agitated-cookies-y290z
975
+ setStatus('idle');
899
976
 
900
- if (store.getState().activeItemId !== null && highlightedItem) {
901
- var item = highlightedItem.item,
902
- itemInputValue = highlightedItem.itemInputValue,
903
- itemUrl = highlightedItem.itemUrl,
904
- source = highlightedItem.source;
905
- source.onActive(_objectSpread2({
906
- event: event,
907
- item: item,
908
- itemInputValue: itemInputValue,
909
- itemUrl: itemUrl,
910
- refresh: refresh,
911
- source: source,
912
- state: store.getState()
913
- }, setters));
914
- }
915
- }).finally(function () {
916
- if (lastStalledId) {
917
- props.environment.clearTimeout(lastStalledId);
977
+ if (store.shouldSkipPendingUpdate) {
978
+ if (!runConcurrentSafePromise.isRunning()) {
979
+ store.shouldSkipPendingUpdate = false;
918
980
  }
981
+
982
+ return;
983
+ }
984
+
985
+ setCollections(collections);
986
+ var isPanelOpen = props.shouldPanelOpen({
987
+ state: store.getState()
919
988
  });
989
+ setIsOpen((_nextState$isOpen2 = nextState.isOpen) !== null && _nextState$isOpen2 !== void 0 ? _nextState$isOpen2 : props.openOnFocus && !query && isPanelOpen || isPanelOpen);
990
+ var highlightedItem = getActiveItem(store.getState());
991
+
992
+ if (store.getState().activeItemId !== null && highlightedItem) {
993
+ var item = highlightedItem.item,
994
+ itemInputValue = highlightedItem.itemInputValue,
995
+ itemUrl = highlightedItem.itemUrl,
996
+ source = highlightedItem.source;
997
+ source.onActive(_objectSpread2({
998
+ event: event,
999
+ item: item,
1000
+ itemInputValue: itemInputValue,
1001
+ itemUrl: itemUrl,
1002
+ refresh: refresh,
1003
+ source: source,
1004
+ state: store.getState()
1005
+ }, setters));
1006
+ }
1007
+ }).finally(function () {
1008
+ if (lastStalledId) {
1009
+ props.environment.clearTimeout(lastStalledId);
1010
+ }
920
1011
  });
921
1012
  }
1013
+ onInput.isRunning = runConcurrentSafePromise.isRunning;
922
1014
 
1015
+ var _excluded$1 = ["event", "props", "refresh", "store"];
923
1016
  function onKeyDown(_ref) {
924
1017
  var event = _ref.event,
925
1018
  props = _ref.props,
926
1019
  refresh = _ref.refresh,
927
1020
  store = _ref.store,
928
- setters = _objectWithoutProperties(_ref, ["event", "props", "refresh", "store"]);
1021
+ setters = _objectWithoutProperties(_ref, _excluded$1);
929
1022
 
930
1023
  if (event.key === 'ArrowUp' || event.key === 'ArrowDown') {
931
1024
  // eslint-disable-next-line no-inner-declarations
@@ -994,7 +1087,14 @@
994
1087
  // from removing the query right away because we first want to close the
995
1088
  // panel.
996
1089
  event.preventDefault();
997
- store.dispatch(event.key, null);
1090
+ store.dispatch(event.key, null); // Hitting the `Escape` key signals the end of a user interaction with the
1091
+ // autocomplete. At this point, we should ignore any requests that are still
1092
+ // running and could reopen the panel once they resolve, because that would
1093
+ // result in an unsolicited UI behavior.
1094
+
1095
+ if (onInput.isRunning()) {
1096
+ store.shouldSkipPendingUpdate = true;
1097
+ }
998
1098
  } else if (event.key === 'Enter') {
999
1099
  // No active item, so we let the browser handle the native `onSubmit` form
1000
1100
  // event.
@@ -1091,17 +1191,22 @@
1091
1191
  }
1092
1192
  }
1093
1193
 
1194
+ var _excluded = ["props", "refresh", "store"],
1195
+ _excluded2 = ["inputElement", "formElement", "panelElement"],
1196
+ _excluded3 = ["inputElement"],
1197
+ _excluded4 = ["inputElement", "maxLength"],
1198
+ _excluded5 = ["item", "source"];
1094
1199
  function getPropGetters(_ref) {
1095
1200
  var props = _ref.props,
1096
1201
  refresh = _ref.refresh,
1097
1202
  store = _ref.store,
1098
- setters = _objectWithoutProperties(_ref, ["props", "refresh", "store"]);
1203
+ setters = _objectWithoutProperties(_ref, _excluded);
1099
1204
 
1100
1205
  var getEnvironmentProps = function getEnvironmentProps(providedProps) {
1101
1206
  var inputElement = providedProps.inputElement,
1102
1207
  formElement = providedProps.formElement,
1103
1208
  panelElement = providedProps.panelElement,
1104
- rest = _objectWithoutProperties(providedProps, ["inputElement", "formElement", "panelElement"]);
1209
+ rest = _objectWithoutProperties(providedProps, _excluded2);
1105
1210
 
1106
1211
  return _objectSpread2({
1107
1212
  // On touch devices, we do not rely on the native `blur` event of the
@@ -1109,19 +1214,34 @@
1109
1214
  // outside of the autocomplete elements.
1110
1215
  // This ensures a working experience on mobile because we blur the input
1111
1216
  // on touch devices when the user starts scrolling (`touchmove`).
1217
+ // @TODO: support cases where there are multiple Autocomplete instances.
1218
+ // Right now, a second instance makes this computation return false.
1112
1219
  onTouchStart: function onTouchStart(event) {
1113
- if (store.getState().isOpen === false || event.target === inputElement) {
1220
+ // The `onTouchStart` event shouldn't trigger the `blur` handler when
1221
+ // it's not an interaction with Autocomplete. We detect it with the
1222
+ // following heuristics:
1223
+ // - the panel is closed AND there are no running requests
1224
+ // (no interaction with the autocomplete, no future state updates)
1225
+ // - OR the touched target is the input element (should open the panel)
1226
+ var isNotAutocompleteInteraction = store.getState().isOpen === false && !onInput.isRunning();
1227
+
1228
+ if (isNotAutocompleteInteraction || event.target === inputElement) {
1114
1229
  return;
1115
- } // @TODO: support cases where there are multiple Autocomplete instances.
1116
- // Right now, a second instance makes this computation return false.
1117
-
1230
+ }
1118
1231
 
1119
1232
  var isTargetWithinAutocomplete = [formElement, panelElement].some(function (contextNode) {
1120
- return isOrContainsNode(contextNode, event.target) || isOrContainsNode(contextNode, props.environment.document.activeElement);
1233
+ return isOrContainsNode(contextNode, event.target);
1121
1234
  });
1122
1235
 
1123
1236
  if (isTargetWithinAutocomplete === false) {
1124
- store.dispatch('blur', null);
1237
+ store.dispatch('blur', null); // If requests are still running when the user closes the panel, they
1238
+ // could reopen the panel once they resolve.
1239
+ // We want to prevent any subsequent query from reopening the panel
1240
+ // because it would result in an unsolicited UI behavior.
1241
+
1242
+ if (!props.debug && onInput.isRunning()) {
1243
+ store.shouldSkipPendingUpdate = true;
1244
+ }
1125
1245
  }
1126
1246
  },
1127
1247
  // When scrolling on touch devices (mobiles, tablets, etc.), we want to
@@ -1149,8 +1269,8 @@
1149
1269
  };
1150
1270
 
1151
1271
  var getFormProps = function getFormProps(providedProps) {
1152
- var inputElement = providedProps.inputElement,
1153
- rest = _objectWithoutProperties(providedProps, ["inputElement"]);
1272
+ providedProps.inputElement;
1273
+ var rest = _objectWithoutProperties(providedProps, _excluded3);
1154
1274
 
1155
1275
  return _objectSpread2({
1156
1276
  action: '',
@@ -1202,11 +1322,11 @@
1202
1322
 
1203
1323
  var isTouchDevice = ('ontouchstart' in props.environment);
1204
1324
 
1205
- var _ref2 = providedProps || {},
1206
- inputElement = _ref2.inputElement,
1207
- _ref2$maxLength = _ref2.maxLength,
1325
+ var _ref2 = providedProps || {};
1326
+ _ref2.inputElement;
1327
+ var _ref2$maxLength = _ref2.maxLength,
1208
1328
  maxLength = _ref2$maxLength === void 0 ? 512 : _ref2$maxLength,
1209
- rest = _objectWithoutProperties(_ref2, ["inputElement", "maxLength"]);
1329
+ rest = _objectWithoutProperties(_ref2, _excluded4);
1210
1330
 
1211
1331
  var activeItem = getActiveItem(store.getState());
1212
1332
  return _objectSpread2({
@@ -1247,7 +1367,14 @@
1247
1367
  // We do rely on the `blur` event on touch devices.
1248
1368
  // See explanation in `onTouchStart`.
1249
1369
  if (!isTouchDevice) {
1250
- store.dispatch('blur', null);
1370
+ store.dispatch('blur', null); // If requests are still running when the user closes the panel, they
1371
+ // could reopen the panel once they resolve.
1372
+ // We want to prevent any subsequent query from reopening the panel
1373
+ // because it would result in an unsolicited UI behavior.
1374
+
1375
+ if (!props.debug && onInput.isRunning()) {
1376
+ store.shouldSkipPendingUpdate = true;
1377
+ }
1251
1378
  }
1252
1379
  },
1253
1380
  onClick: function onClick(event) {
@@ -1297,7 +1424,7 @@
1297
1424
  var getItemProps = function getItemProps(providedProps) {
1298
1425
  var item = providedProps.item,
1299
1426
  source = providedProps.source,
1300
- rest = _objectWithoutProperties(providedProps, ["item", "source"]);
1427
+ rest = _objectWithoutProperties(providedProps, _excluded5);
1301
1428
 
1302
1429
  return _objectSpread2({
1303
1430
  id: "".concat(props.id, "-item-").concat(item.__autocomplete_id),
@@ -1384,6 +1511,44 @@
1384
1511
  };
1385
1512
  }
1386
1513
 
1514
+ function getMetadata(_ref) {
1515
+ var _, _options$__autocomple, _options$__autocomple2, _options$__autocomple3;
1516
+
1517
+ var plugins = _ref.plugins,
1518
+ options = _ref.options;
1519
+ var optionsKey = (_ = (((_options$__autocomple = options.__autocomplete_metadata) === null || _options$__autocomple === void 0 ? void 0 : _options$__autocomple.userAgents) || [])[0]) === null || _ === void 0 ? void 0 : _.segment;
1520
+ var extraOptions = optionsKey ? _defineProperty({}, optionsKey, Object.keys(((_options$__autocomple2 = options.__autocomplete_metadata) === null || _options$__autocomple2 === void 0 ? void 0 : _options$__autocomple2.options) || {})) : {};
1521
+ return {
1522
+ plugins: plugins.map(function (plugin) {
1523
+ return {
1524
+ name: plugin.name,
1525
+ options: Object.keys(plugin.__autocomplete_pluginOptions || [])
1526
+ };
1527
+ }),
1528
+ options: _objectSpread2({
1529
+ 'autocomplete-core': Object.keys(options)
1530
+ }, extraOptions),
1531
+ ua: userAgents.concat(((_options$__autocomple3 = options.__autocomplete_metadata) === null || _options$__autocomple3 === void 0 ? void 0 : _options$__autocomple3.userAgents) || [])
1532
+ };
1533
+ }
1534
+ function injectMetadata(_ref3) {
1535
+ var _environment$navigato;
1536
+
1537
+ var metadata = _ref3.metadata,
1538
+ environment = _ref3.environment;
1539
+ var isMetadataEnabled = (_environment$navigato = environment.navigator) === null || _environment$navigato === void 0 ? void 0 : _environment$navigato.userAgent.includes('Algolia Crawler');
1540
+
1541
+ if (isMetadataEnabled) {
1542
+ var metadataContainer = environment.document.createElement('meta');
1543
+ var headRef = environment.document.querySelector('head');
1544
+ metadataContainer.name = 'algolia:metadata';
1545
+ setTimeout(function () {
1546
+ metadataContainer.content = JSON.stringify(metadata);
1547
+ headRef.appendChild(metadataContainer);
1548
+ }, 0);
1549
+ }
1550
+ }
1551
+
1387
1552
  function getCompletion(_ref) {
1388
1553
  var _getActiveItem;
1389
1554
 
@@ -1603,18 +1768,22 @@
1603
1768
  }
1604
1769
  }));
1605
1770
  });
1771
+ injectMetadata({
1772
+ metadata: getMetadata({
1773
+ plugins: props.plugins,
1774
+ options: options
1775
+ }),
1776
+ environment: props.environment
1777
+ });
1606
1778
  return _objectSpread2(_objectSpread2({
1607
1779
  refresh: refresh
1608
1780
  }, propGetters), setters);
1609
1781
  }
1610
1782
 
1611
- var version = '1.3.0';
1612
-
1613
1783
  exports.createAutocomplete = createAutocomplete;
1614
1784
  exports.getDefaultProps = getDefaultProps;
1615
- exports.version = version;
1616
1785
 
1617
1786
  Object.defineProperty(exports, '__esModule', { value: true });
1618
1787
 
1619
- })));
1788
+ }));
1620
1789
  //# sourceMappingURL=index.development.js.map