@algolia/autocomplete-core 1.5.1 → 1.5.2

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.
@@ -1,4 +1,4 @@
1
- /*! @algolia/autocomplete-core 1.5.1 | MIT License | © Algolia, Inc. and contributors | https://github.com/algolia/autocomplete */
1
+ /*! @algolia/autocomplete-core 1.5.2 | 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) :
@@ -288,7 +288,7 @@
288
288
 
289
289
  var noop = function noop() {};
290
290
 
291
- var version = '1.5.1';
291
+ var version = '1.5.2';
292
292
 
293
293
  var userAgents = [{
294
294
  segment: 'autocomplete-core',
@@ -323,75 +323,79 @@
323
323
  "development" !== 'production' ? warn(!options.debug, 'The `debug` option is meant for development debugging and should not be used in production.') : void 0;
324
324
  }
325
325
 
326
- function createStore(reducer, props, onStoreStateChange) {
327
- var state = props.initialState;
326
+ function createInternalCancelablePromise(promise, initialState) {
327
+ var state = initialState;
328
328
  return {
329
- getState: function getState() {
330
- return state;
329
+ then: function then(onfulfilled, onrejected) {
330
+ return createInternalCancelablePromise(promise.then(createCallback(onfulfilled, state, promise), createCallback(onrejected, state, promise)), state);
331
331
  },
332
- dispatch: function dispatch(action, payload) {
333
- var prevState = _objectSpread2({}, state);
332
+ catch: function _catch(onrejected) {
333
+ return createInternalCancelablePromise(promise.catch(createCallback(onrejected, state, promise)), state);
334
+ },
335
+ finally: function _finally(onfinally) {
336
+ if (onfinally) {
337
+ state.onCancelList.push(onfinally);
338
+ }
334
339
 
335
- state = reducer(state, {
336
- type: action,
337
- props: props,
338
- payload: payload
339
- });
340
- onStoreStateChange({
341
- state: state,
342
- prevState: prevState
340
+ return createInternalCancelablePromise(promise.finally(createCallback(onfinally && function () {
341
+ state.onCancelList = [];
342
+ return onfinally();
343
+ }, state, promise)), state);
344
+ },
345
+ cancel: function cancel() {
346
+ state.isCanceled = true;
347
+ var callbacks = state.onCancelList;
348
+ state.onCancelList = [];
349
+ callbacks.forEach(function (callback) {
350
+ callback();
343
351
  });
344
352
  },
345
- shouldSkipPendingUpdate: false
353
+ isCanceled: function isCanceled() {
354
+ return state.isCanceled === true;
355
+ }
346
356
  };
347
357
  }
348
358
 
349
- function getAutocompleteSetters(_ref) {
350
- var store = _ref.store;
351
-
352
- var setActiveItemId = function setActiveItemId(value) {
353
- store.dispatch('setActiveItemId', value);
354
- };
355
-
356
- var setQuery = function setQuery(value) {
357
- store.dispatch('setQuery', value);
358
- };
359
-
360
- var setCollections = function setCollections(rawValue) {
361
- var baseItemId = 0;
362
- var value = rawValue.map(function (collection) {
363
- return _objectSpread2(_objectSpread2({}, collection), {}, {
364
- // We flatten the stored items to support calling `getAlgoliaResults`
365
- // from the source itself.
366
- items: flatten(collection.items).map(function (item) {
367
- return _objectSpread2(_objectSpread2({}, item), {}, {
368
- __autocomplete_id: baseItemId++
369
- });
370
- })
371
- });
372
- });
373
- store.dispatch('setCollections', value);
374
- };
359
+ function cancelable(promise) {
360
+ return createInternalCancelablePromise(promise, {
361
+ isCanceled: false,
362
+ onCancelList: []
363
+ });
364
+ }
375
365
 
376
- var setIsOpen = function setIsOpen(value) {
377
- store.dispatch('setIsOpen', value);
378
- };
366
+ function createCallback(onResult, state, fallback) {
367
+ if (!onResult) {
368
+ return fallback;
369
+ }
379
370
 
380
- var setStatus = function setStatus(value) {
381
- store.dispatch('setStatus', value);
382
- };
371
+ return function callback(arg) {
372
+ if (state.isCanceled) {
373
+ return arg;
374
+ }
383
375
 
384
- var setContext = function setContext(value) {
385
- store.dispatch('setContext', value);
376
+ return onResult(arg);
386
377
  };
378
+ }
387
379
 
380
+ function createCancelablePromiseList() {
381
+ var list = [];
388
382
  return {
389
- setActiveItemId: setActiveItemId,
390
- setQuery: setQuery,
391
- setCollections: setCollections,
392
- setIsOpen: setIsOpen,
393
- setStatus: setStatus,
394
- setContext: setContext
383
+ add: function add(cancelablePromise) {
384
+ list.push(cancelablePromise);
385
+ return cancelablePromise.finally(function () {
386
+ list = list.filter(function (item) {
387
+ return item !== cancelablePromise;
388
+ });
389
+ });
390
+ },
391
+ cancelAll: function cancelAll() {
392
+ list.forEach(function (promise) {
393
+ return promise.cancel();
394
+ });
395
+ },
396
+ isEmpty: function isEmpty() {
397
+ return list.length === 0;
398
+ }
395
399
  };
396
400
  }
397
401
 
@@ -405,11 +409,8 @@
405
409
  var basePromiseId = -1;
406
410
  var latestResolvedId = -1;
407
411
  var latestResolvedValue = undefined;
408
- var runningPromisesCount = 0;
409
-
410
- function runConcurrentSafePromise(promise) {
412
+ return function runConcurrentSafePromise(promise) {
411
413
  basePromiseId++;
412
- runningPromisesCount++;
413
414
  var currentPromiseId = basePromiseId;
414
415
  return Promise.resolve(promise).then(function (x) {
415
416
  // The promise might take too long to resolve and get outdated. This would
@@ -432,16 +433,8 @@
432
433
  latestResolvedId = currentPromiseId;
433
434
  latestResolvedValue = x;
434
435
  return x;
435
- }).finally(function () {
436
- return runningPromisesCount--;
437
436
  });
438
- }
439
-
440
- runConcurrentSafePromise.isRunning = function () {
441
- return runningPromisesCount > 0;
442
437
  };
443
-
444
- return runConcurrentSafePromise;
445
438
  }
446
439
 
447
440
  /**
@@ -646,6 +639,78 @@
646
639
  };
647
640
  }
648
641
 
642
+ function createStore(reducer, props, onStoreStateChange) {
643
+ var state = props.initialState;
644
+ return {
645
+ getState: function getState() {
646
+ return state;
647
+ },
648
+ dispatch: function dispatch(action, payload) {
649
+ var prevState = _objectSpread2({}, state);
650
+
651
+ state = reducer(state, {
652
+ type: action,
653
+ props: props,
654
+ payload: payload
655
+ });
656
+ onStoreStateChange({
657
+ state: state,
658
+ prevState: prevState
659
+ });
660
+ },
661
+ pendingRequests: createCancelablePromiseList()
662
+ };
663
+ }
664
+
665
+ function getAutocompleteSetters(_ref) {
666
+ var store = _ref.store;
667
+
668
+ var setActiveItemId = function setActiveItemId(value) {
669
+ store.dispatch('setActiveItemId', value);
670
+ };
671
+
672
+ var setQuery = function setQuery(value) {
673
+ store.dispatch('setQuery', value);
674
+ };
675
+
676
+ var setCollections = function setCollections(rawValue) {
677
+ var baseItemId = 0;
678
+ var value = rawValue.map(function (collection) {
679
+ return _objectSpread2(_objectSpread2({}, collection), {}, {
680
+ // We flatten the stored items to support calling `getAlgoliaResults`
681
+ // from the source itself.
682
+ items: flatten(collection.items).map(function (item) {
683
+ return _objectSpread2(_objectSpread2({}, item), {}, {
684
+ __autocomplete_id: baseItemId++
685
+ });
686
+ })
687
+ });
688
+ });
689
+ store.dispatch('setCollections', value);
690
+ };
691
+
692
+ var setIsOpen = function setIsOpen(value) {
693
+ store.dispatch('setIsOpen', value);
694
+ };
695
+
696
+ var setStatus = function setStatus(value) {
697
+ store.dispatch('setStatus', value);
698
+ };
699
+
700
+ var setContext = function setContext(value) {
701
+ store.dispatch('setContext', value);
702
+ };
703
+
704
+ return {
705
+ setActiveItemId: setActiveItemId,
706
+ setQuery: setQuery,
707
+ setCollections: setCollections,
708
+ setIsOpen: setIsOpen,
709
+ setStatus: setStatus,
710
+ setContext: setContext
711
+ };
712
+ }
713
+
649
714
  function getDefaultProps(props, pluginSubscribers) {
650
715
  var _props$id;
651
716
 
@@ -928,9 +993,11 @@
928
993
  // updates performed in this code path.
929
994
  // We chain with a void promise to respect `onInput`'s expected return type.
930
995
 
931
- return runConcurrentSafePromise(collections).then(function () {
996
+ var _request = cancelable(runConcurrentSafePromise(collections).then(function () {
932
997
  return Promise.resolve();
933
- });
998
+ }));
999
+
1000
+ return store.pendingRequests.add(_request);
934
1001
  }
935
1002
 
936
1003
  setStatus('loading');
@@ -943,7 +1010,7 @@
943
1010
  // meaning we should only ever manipulate the state once this concurrent-safe
944
1011
  // promise is resolved.
945
1012
 
946
- return runConcurrentSafePromise(props.getSources(_objectSpread2({
1013
+ var request = cancelable(runConcurrentSafePromise(props.getSources(_objectSpread2({
947
1014
  query: query,
948
1015
  refresh: refresh,
949
1016
  state: store.getState()
@@ -965,7 +1032,7 @@
965
1032
  state: store.getState()
966
1033
  });
967
1034
  });
968
- })).then(function (collections) {
1035
+ }))).then(function (collections) {
969
1036
  var _nextState$isOpen2;
970
1037
 
971
1038
  // Parameters passed to `onInput` could be stale when the following code
@@ -973,15 +1040,6 @@
973
1040
  // If it becomes a problem we'll need to save the last passed parameters.
974
1041
  // See: https://codesandbox.io/s/agitated-cookies-y290z
975
1042
  setStatus('idle');
976
-
977
- if (store.shouldSkipPendingUpdate) {
978
- if (!runConcurrentSafePromise.isRunning()) {
979
- store.shouldSkipPendingUpdate = false;
980
- }
981
-
982
- return;
983
- }
984
-
985
1043
  setCollections(collections);
986
1044
  var isPanelOpen = props.shouldPanelOpen({
987
1045
  state: store.getState()
@@ -1005,12 +1063,14 @@
1005
1063
  }, setters));
1006
1064
  }
1007
1065
  }).finally(function () {
1066
+ setStatus('idle');
1067
+
1008
1068
  if (lastStalledId) {
1009
1069
  props.environment.clearTimeout(lastStalledId);
1010
1070
  }
1011
1071
  });
1072
+ return store.pendingRequests.add(request);
1012
1073
  }
1013
- onInput.isRunning = runConcurrentSafePromise.isRunning;
1014
1074
 
1015
1075
  var _excluded$1 = ["event", "props", "refresh", "store"];
1016
1076
  function onKeyDown(_ref) {
@@ -1089,12 +1149,10 @@
1089
1149
  event.preventDefault();
1090
1150
  store.dispatch(event.key, null); // Hitting the `Escape` key signals the end of a user interaction with the
1091
1151
  // 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
1152
+ // pending and could reopen the panel once they resolve, because that would
1093
1153
  // result in an unsolicited UI behavior.
1094
1154
 
1095
- if (onInput.isRunning()) {
1096
- store.shouldSkipPendingUpdate = true;
1097
- }
1155
+ store.pendingRequests.cancelAll();
1098
1156
  } else if (event.key === 'Enter') {
1099
1157
  // No active item, so we let the browser handle the native `onSubmit` form
1100
1158
  // event.
@@ -1220,12 +1278,12 @@
1220
1278
  // The `onTouchStart` event shouldn't trigger the `blur` handler when
1221
1279
  // it's not an interaction with Autocomplete. We detect it with the
1222
1280
  // following heuristics:
1223
- // - the panel is closed AND there are no running requests
1281
+ // - the panel is closed AND there are no pending requests
1224
1282
  // (no interaction with the autocomplete, no future state updates)
1225
1283
  // - OR the touched target is the input element (should open the panel)
1226
- var isNotAutocompleteInteraction = store.getState().isOpen === false && !onInput.isRunning();
1284
+ var isAutocompleteInteraction = store.getState().isOpen || !store.pendingRequests.isEmpty();
1227
1285
 
1228
- if (isNotAutocompleteInteraction || event.target === inputElement) {
1286
+ if (!isAutocompleteInteraction || event.target === inputElement) {
1229
1287
  return;
1230
1288
  }
1231
1289
 
@@ -1234,13 +1292,13 @@
1234
1292
  });
1235
1293
 
1236
1294
  if (isTargetWithinAutocomplete === false) {
1237
- store.dispatch('blur', null); // If requests are still running when the user closes the panel, they
1295
+ store.dispatch('blur', null); // If requests are still pending when the user closes the panel, they
1238
1296
  // could reopen the panel once they resolve.
1239
1297
  // We want to prevent any subsequent query from reopening the panel
1240
1298
  // because it would result in an unsolicited UI behavior.
1241
1299
 
1242
- if (!props.debug && onInput.isRunning()) {
1243
- store.shouldSkipPendingUpdate = true;
1300
+ if (!props.debug) {
1301
+ store.pendingRequests.cancelAll();
1244
1302
  }
1245
1303
  }
1246
1304
  },
@@ -1367,13 +1425,13 @@
1367
1425
  // We do rely on the `blur` event on touch devices.
1368
1426
  // See explanation in `onTouchStart`.
1369
1427
  if (!isTouchDevice) {
1370
- store.dispatch('blur', null); // If requests are still running when the user closes the panel, they
1428
+ store.dispatch('blur', null); // If requests are still pending when the user closes the panel, they
1371
1429
  // could reopen the panel once they resolve.
1372
1430
  // We want to prevent any subsequent query from reopening the panel
1373
1431
  // because it would result in an unsolicited UI behavior.
1374
1432
 
1375
- if (!props.debug && onInput.isRunning()) {
1376
- store.shouldSkipPendingUpdate = true;
1433
+ if (!props.debug) {
1434
+ store.pendingRequests.cancelAll();
1377
1435
  }
1378
1436
  }
1379
1437
  },