@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.
- package/dist/esm/createStore.js +2 -1
- package/dist/esm/getPropGetters.js +9 -9
- package/dist/esm/onInput.d.ts +2 -4
- package/dist/esm/onInput.js +11 -16
- package/dist/esm/onKeyDown.js +2 -4
- package/dist/esm/types/AutocompleteStore.d.ts +2 -1
- package/dist/esm/utils/createCancelablePromise.d.ts +15 -0
- package/dist/esm/utils/createCancelablePromise.js +70 -0
- package/dist/esm/utils/createCancelablePromiseList.d.ts +7 -0
- package/dist/esm/utils/createCancelablePromiseList.js +21 -0
- package/dist/esm/utils/createConcurrentSafePromise.d.ts +1 -4
- package/dist/esm/utils/createConcurrentSafePromise.js +1 -12
- package/dist/esm/utils/index.d.ts +2 -0
- package/dist/esm/utils/index.js +2 -0
- package/dist/umd/index.development.js +154 -96
- package/dist/umd/index.development.js.map +1 -1
- package/dist/umd/index.production.js +2 -2
- package/dist/umd/index.production.js.map +1 -1
- package/package.json +3 -3
package/dist/esm/createStore.js
CHANGED
|
@@ -4,6 +4,7 @@ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { va
|
|
|
4
4
|
|
|
5
5
|
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
6
6
|
|
|
7
|
+
import { createCancelablePromiseList } from './utils';
|
|
7
8
|
export function createStore(reducer, props, onStoreStateChange) {
|
|
8
9
|
var state = props.initialState;
|
|
9
10
|
return {
|
|
@@ -23,6 +24,6 @@ export function createStore(reducer, props, onStoreStateChange) {
|
|
|
23
24
|
prevState: prevState
|
|
24
25
|
});
|
|
25
26
|
},
|
|
26
|
-
|
|
27
|
+
pendingRequests: createCancelablePromiseList()
|
|
27
28
|
};
|
|
28
29
|
}
|
|
@@ -41,12 +41,12 @@ export function getPropGetters(_ref) {
|
|
|
41
41
|
// The `onTouchStart` event shouldn't trigger the `blur` handler when
|
|
42
42
|
// it's not an interaction with Autocomplete. We detect it with the
|
|
43
43
|
// following heuristics:
|
|
44
|
-
// - the panel is closed AND there are no
|
|
44
|
+
// - the panel is closed AND there are no pending requests
|
|
45
45
|
// (no interaction with the autocomplete, no future state updates)
|
|
46
46
|
// - OR the touched target is the input element (should open the panel)
|
|
47
|
-
var
|
|
47
|
+
var isAutocompleteInteraction = store.getState().isOpen || !store.pendingRequests.isEmpty();
|
|
48
48
|
|
|
49
|
-
if (
|
|
49
|
+
if (!isAutocompleteInteraction || event.target === inputElement) {
|
|
50
50
|
return;
|
|
51
51
|
}
|
|
52
52
|
|
|
@@ -55,13 +55,13 @@ export function getPropGetters(_ref) {
|
|
|
55
55
|
});
|
|
56
56
|
|
|
57
57
|
if (isTargetWithinAutocomplete === false) {
|
|
58
|
-
store.dispatch('blur', null); // If requests are still
|
|
58
|
+
store.dispatch('blur', null); // If requests are still pending when the user closes the panel, they
|
|
59
59
|
// could reopen the panel once they resolve.
|
|
60
60
|
// We want to prevent any subsequent query from reopening the panel
|
|
61
61
|
// because it would result in an unsolicited UI behavior.
|
|
62
62
|
|
|
63
|
-
if (!props.debug
|
|
64
|
-
store.
|
|
63
|
+
if (!props.debug) {
|
|
64
|
+
store.pendingRequests.cancelAll();
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
67
|
},
|
|
@@ -188,13 +188,13 @@ export function getPropGetters(_ref) {
|
|
|
188
188
|
// We do rely on the `blur` event on touch devices.
|
|
189
189
|
// See explanation in `onTouchStart`.
|
|
190
190
|
if (!isTouchDevice) {
|
|
191
|
-
store.dispatch('blur', null); // If requests are still
|
|
191
|
+
store.dispatch('blur', null); // If requests are still pending when the user closes the panel, they
|
|
192
192
|
// could reopen the panel once they resolve.
|
|
193
193
|
// We want to prevent any subsequent query from reopening the panel
|
|
194
194
|
// because it would result in an unsolicited UI behavior.
|
|
195
195
|
|
|
196
|
-
if (!props.debug
|
|
197
|
-
store.
|
|
196
|
+
if (!props.debug) {
|
|
197
|
+
store.pendingRequests.cancelAll();
|
|
198
198
|
}
|
|
199
199
|
}
|
|
200
200
|
},
|
package/dist/esm/onInput.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { AutocompleteScopeApi, AutocompleteState, AutocompleteStore, BaseItem, InternalAutocompleteOptions } from './types';
|
|
2
|
+
import { CancelablePromise } from './utils';
|
|
2
3
|
interface OnInputParams<TItem extends BaseItem> extends AutocompleteScopeApi<TItem> {
|
|
3
4
|
event: any;
|
|
4
5
|
/**
|
|
@@ -13,8 +14,5 @@ interface OnInputParams<TItem extends BaseItem> extends AutocompleteScopeApi<TIt
|
|
|
13
14
|
query: string;
|
|
14
15
|
store: AutocompleteStore<TItem>;
|
|
15
16
|
}
|
|
16
|
-
export declare function onInput<TItem extends BaseItem>({ event, nextState, props, query, refresh, store, ...setters }: OnInputParams<TItem>):
|
|
17
|
-
export declare namespace onInput {
|
|
18
|
-
var isRunning: () => boolean;
|
|
19
|
-
}
|
|
17
|
+
export declare function onInput<TItem extends BaseItem>({ event, nextState, props, query, refresh, store, ...setters }: OnInputParams<TItem>): CancelablePromise<void>;
|
|
20
18
|
export {};
|
package/dist/esm/onInput.js
CHANGED
|
@@ -12,7 +12,7 @@ function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) r
|
|
|
12
12
|
|
|
13
13
|
import { reshape } from './reshape';
|
|
14
14
|
import { preResolve, resolve, postResolve } from './resolve';
|
|
15
|
-
import { createConcurrentSafePromise, getActiveItem } from './utils';
|
|
15
|
+
import { cancelable, createConcurrentSafePromise, getActiveItem } from './utils';
|
|
16
16
|
var lastStalledId = null;
|
|
17
17
|
var runConcurrentSafePromise = createConcurrentSafePromise();
|
|
18
18
|
export function onInput(_ref) {
|
|
@@ -54,9 +54,11 @@ export function onInput(_ref) {
|
|
|
54
54
|
// updates performed in this code path.
|
|
55
55
|
// We chain with a void promise to respect `onInput`'s expected return type.
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
var _request = cancelable(runConcurrentSafePromise(collections).then(function () {
|
|
58
58
|
return Promise.resolve();
|
|
59
|
-
});
|
|
59
|
+
}));
|
|
60
|
+
|
|
61
|
+
return store.pendingRequests.add(_request);
|
|
60
62
|
}
|
|
61
63
|
|
|
62
64
|
setStatus('loading');
|
|
@@ -69,7 +71,7 @@ export function onInput(_ref) {
|
|
|
69
71
|
// meaning we should only ever manipulate the state once this concurrent-safe
|
|
70
72
|
// promise is resolved.
|
|
71
73
|
|
|
72
|
-
|
|
74
|
+
var request = cancelable(runConcurrentSafePromise(props.getSources(_objectSpread({
|
|
73
75
|
query: query,
|
|
74
76
|
refresh: refresh,
|
|
75
77
|
state: store.getState()
|
|
@@ -91,7 +93,7 @@ export function onInput(_ref) {
|
|
|
91
93
|
state: store.getState()
|
|
92
94
|
});
|
|
93
95
|
});
|
|
94
|
-
})).then(function (collections) {
|
|
96
|
+
}))).then(function (collections) {
|
|
95
97
|
var _nextState$isOpen2;
|
|
96
98
|
|
|
97
99
|
// Parameters passed to `onInput` could be stale when the following code
|
|
@@ -99,15 +101,6 @@ export function onInput(_ref) {
|
|
|
99
101
|
// If it becomes a problem we'll need to save the last passed parameters.
|
|
100
102
|
// See: https://codesandbox.io/s/agitated-cookies-y290z
|
|
101
103
|
setStatus('idle');
|
|
102
|
-
|
|
103
|
-
if (store.shouldSkipPendingUpdate) {
|
|
104
|
-
if (!runConcurrentSafePromise.isRunning()) {
|
|
105
|
-
store.shouldSkipPendingUpdate = false;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
104
|
setCollections(collections);
|
|
112
105
|
var isPanelOpen = props.shouldPanelOpen({
|
|
113
106
|
state: store.getState()
|
|
@@ -131,9 +124,11 @@ export function onInput(_ref) {
|
|
|
131
124
|
}, setters));
|
|
132
125
|
}
|
|
133
126
|
}).finally(function () {
|
|
127
|
+
setStatus('idle');
|
|
128
|
+
|
|
134
129
|
if (lastStalledId) {
|
|
135
130
|
props.environment.clearTimeout(lastStalledId);
|
|
136
131
|
}
|
|
137
132
|
});
|
|
138
|
-
|
|
139
|
-
|
|
133
|
+
return store.pendingRequests.add(request);
|
|
134
|
+
}
|
package/dist/esm/onKeyDown.js
CHANGED
|
@@ -88,12 +88,10 @@ export function onKeyDown(_ref) {
|
|
|
88
88
|
event.preventDefault();
|
|
89
89
|
store.dispatch(event.key, null); // Hitting the `Escape` key signals the end of a user interaction with the
|
|
90
90
|
// autocomplete. At this point, we should ignore any requests that are still
|
|
91
|
-
//
|
|
91
|
+
// pending and could reopen the panel once they resolve, because that would
|
|
92
92
|
// result in an unsolicited UI behavior.
|
|
93
93
|
|
|
94
|
-
|
|
95
|
-
store.shouldSkipPendingUpdate = true;
|
|
96
|
-
}
|
|
94
|
+
store.pendingRequests.cancelAll();
|
|
97
95
|
} else if (event.key === 'Enter') {
|
|
98
96
|
// No active item, so we let the browser handle the native `onSubmit` form
|
|
99
97
|
// event.
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
import { CancelablePromiseList } from '../utils';
|
|
1
2
|
import { BaseItem } from './AutocompleteApi';
|
|
2
3
|
import { InternalAutocompleteOptions } from './AutocompleteOptions';
|
|
3
4
|
import { AutocompleteState } from './AutocompleteState';
|
|
4
5
|
export interface AutocompleteStore<TItem extends BaseItem> {
|
|
5
6
|
getState(): AutocompleteState<TItem>;
|
|
6
7
|
dispatch(action: ActionType, payload: any): void;
|
|
7
|
-
|
|
8
|
+
pendingRequests: CancelablePromiseList<void>;
|
|
8
9
|
}
|
|
9
10
|
export declare type Reducer = <TItem extends BaseItem>(state: AutocompleteState<TItem>, action: Action<TItem, any>) => AutocompleteState<TItem>;
|
|
10
11
|
declare type Action<TItem extends BaseItem, TPayload> = {
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
declare type PromiseExecutor<TValue> = (resolve: (value: TValue | PromiseLike<TValue>) => void, reject: (reason?: any) => void) => void;
|
|
2
|
+
export declare type CancelablePromise<TValue> = {
|
|
3
|
+
then<TResultFulfilled = TValue, TResultRejected = never>(onfulfilled?: ((value: TValue) => TResultFulfilled | PromiseLike<TResultFulfilled> | CancelablePromise<TResultFulfilled>) | undefined | null, onrejected?: ((reason: any) => TResultRejected | PromiseLike<TResultRejected> | CancelablePromise<TResultRejected>) | undefined | null): CancelablePromise<TResultFulfilled | TResultRejected>;
|
|
4
|
+
catch<TResult = never>(onrejected?: ((reason: any) => TResult | PromiseLike<TResult> | CancelablePromise<TResult>) | undefined | null): CancelablePromise<TValue | TResult>;
|
|
5
|
+
finally(onfinally?: (() => void) | undefined | null): CancelablePromise<TValue>;
|
|
6
|
+
cancel(): void;
|
|
7
|
+
isCanceled(): boolean;
|
|
8
|
+
};
|
|
9
|
+
export declare function createCancelablePromise<TValue>(executor: PromiseExecutor<TValue>): CancelablePromise<TValue>;
|
|
10
|
+
export declare namespace createCancelablePromise {
|
|
11
|
+
var resolve: <TValue>(value?: TValue | PromiseLike<TValue> | CancelablePromise<TValue> | undefined) => CancelablePromise<TValue | CancelablePromise<TValue> | undefined>;
|
|
12
|
+
var reject: (reason?: any) => CancelablePromise<never>;
|
|
13
|
+
}
|
|
14
|
+
export declare function cancelable<TValue>(promise: Promise<TValue>): CancelablePromise<TValue>;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
function createInternalCancelablePromise(promise, initialState) {
|
|
2
|
+
var state = initialState;
|
|
3
|
+
return {
|
|
4
|
+
then: function then(onfulfilled, onrejected) {
|
|
5
|
+
return createInternalCancelablePromise(promise.then(createCallback(onfulfilled, state, promise), createCallback(onrejected, state, promise)), state);
|
|
6
|
+
},
|
|
7
|
+
catch: function _catch(onrejected) {
|
|
8
|
+
return createInternalCancelablePromise(promise.catch(createCallback(onrejected, state, promise)), state);
|
|
9
|
+
},
|
|
10
|
+
finally: function _finally(onfinally) {
|
|
11
|
+
if (onfinally) {
|
|
12
|
+
state.onCancelList.push(onfinally);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return createInternalCancelablePromise(promise.finally(createCallback(onfinally && function () {
|
|
16
|
+
state.onCancelList = [];
|
|
17
|
+
return onfinally();
|
|
18
|
+
}, state, promise)), state);
|
|
19
|
+
},
|
|
20
|
+
cancel: function cancel() {
|
|
21
|
+
state.isCanceled = true;
|
|
22
|
+
var callbacks = state.onCancelList;
|
|
23
|
+
state.onCancelList = [];
|
|
24
|
+
callbacks.forEach(function (callback) {
|
|
25
|
+
callback();
|
|
26
|
+
});
|
|
27
|
+
},
|
|
28
|
+
isCanceled: function isCanceled() {
|
|
29
|
+
return state.isCanceled === true;
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function createCancelablePromise(executor) {
|
|
35
|
+
return createInternalCancelablePromise(new Promise(function (resolve, reject) {
|
|
36
|
+
return executor(resolve, reject);
|
|
37
|
+
}), {
|
|
38
|
+
isCanceled: false,
|
|
39
|
+
onCancelList: []
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
createCancelablePromise.resolve = function (value) {
|
|
44
|
+
return cancelable(Promise.resolve(value));
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
createCancelablePromise.reject = function (reason) {
|
|
48
|
+
return cancelable(Promise.reject(reason));
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export function cancelable(promise) {
|
|
52
|
+
return createInternalCancelablePromise(promise, {
|
|
53
|
+
isCanceled: false,
|
|
54
|
+
onCancelList: []
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function createCallback(onResult, state, fallback) {
|
|
59
|
+
if (!onResult) {
|
|
60
|
+
return fallback;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return function callback(arg) {
|
|
64
|
+
if (state.isCanceled) {
|
|
65
|
+
return arg;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return onResult(arg);
|
|
69
|
+
};
|
|
70
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { CancelablePromise } from '.';
|
|
2
|
+
export declare type CancelablePromiseList<TValue> = {
|
|
3
|
+
add(cancelablePromise: CancelablePromise<TValue>): CancelablePromise<TValue>;
|
|
4
|
+
cancelAll(): void;
|
|
5
|
+
isEmpty(): boolean;
|
|
6
|
+
};
|
|
7
|
+
export declare function createCancelablePromiseList<TValue>(): CancelablePromiseList<TValue>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export function createCancelablePromiseList() {
|
|
2
|
+
var list = [];
|
|
3
|
+
return {
|
|
4
|
+
add: function add(cancelablePromise) {
|
|
5
|
+
list.push(cancelablePromise);
|
|
6
|
+
return cancelablePromise.finally(function () {
|
|
7
|
+
list = list.filter(function (item) {
|
|
8
|
+
return item !== cancelablePromise;
|
|
9
|
+
});
|
|
10
|
+
});
|
|
11
|
+
},
|
|
12
|
+
cancelAll: function cancelAll() {
|
|
13
|
+
list.forEach(function (promise) {
|
|
14
|
+
return promise.cancel();
|
|
15
|
+
});
|
|
16
|
+
},
|
|
17
|
+
isEmpty: function isEmpty() {
|
|
18
|
+
return list.length === 0;
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
}
|
|
@@ -5,7 +5,4 @@ import { MaybePromise } from '@algolia/autocomplete-shared';
|
|
|
5
5
|
* This is useful to prevent older promises to resolve after a newer promise,
|
|
6
6
|
* otherwise resulting in stale resolved values.
|
|
7
7
|
*/
|
|
8
|
-
export declare function createConcurrentSafePromise():
|
|
9
|
-
<TValue>(promise: MaybePromise<TValue>): Promise<TValue>;
|
|
10
|
-
isRunning(): boolean;
|
|
11
|
-
};
|
|
8
|
+
export declare function createConcurrentSafePromise(): <TValue>(promise: MaybePromise<TValue>) => Promise<TValue>;
|
|
@@ -8,11 +8,8 @@ export function createConcurrentSafePromise() {
|
|
|
8
8
|
var basePromiseId = -1;
|
|
9
9
|
var latestResolvedId = -1;
|
|
10
10
|
var latestResolvedValue = undefined;
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
function runConcurrentSafePromise(promise) {
|
|
11
|
+
return function runConcurrentSafePromise(promise) {
|
|
14
12
|
basePromiseId++;
|
|
15
|
-
runningPromisesCount++;
|
|
16
13
|
var currentPromiseId = basePromiseId;
|
|
17
14
|
return Promise.resolve(promise).then(function (x) {
|
|
18
15
|
// The promise might take too long to resolve and get outdated. This would
|
|
@@ -35,14 +32,6 @@ export function createConcurrentSafePromise() {
|
|
|
35
32
|
latestResolvedId = currentPromiseId;
|
|
36
33
|
latestResolvedValue = x;
|
|
37
34
|
return x;
|
|
38
|
-
}).finally(function () {
|
|
39
|
-
return runningPromisesCount--;
|
|
40
35
|
});
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
runConcurrentSafePromise.isRunning = function () {
|
|
44
|
-
return runningPromisesCount > 0;
|
|
45
36
|
};
|
|
46
|
-
|
|
47
|
-
return runConcurrentSafePromise;
|
|
48
37
|
}
|