instantsearch.js 4.85.2 → 4.86.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cjs/connectors/sort-by/connectSortBy.js +118 -18
- package/cjs/lib/version.js +1 -1
- package/cjs/widgets/autocomplete/autocomplete.js +140 -103
- package/dist/instantsearch.development.d.ts +76 -11
- package/dist/instantsearch.development.js +311 -131
- package/dist/instantsearch.development.js.map +1 -1
- package/dist/instantsearch.production.d.ts +76 -11
- package/dist/instantsearch.production.min.d.ts +76 -11
- package/dist/instantsearch.production.min.js +2 -2
- package/dist/instantsearch.production.min.js.map +1 -1
- package/es/connectors/sort-by/connectSortBy.d.ts +41 -9
- package/es/connectors/sort-by/connectSortBy.js +118 -18
- package/es/lib/version.d.ts +1 -1
- package/es/lib/version.js +1 -1
- package/es/widgets/autocomplete/autocomplete.d.ts +9 -0
- package/es/widgets/autocomplete/autocomplete.js +140 -103
- package/es/widgets/sort-by/sort-by.d.ts +22 -2
- package/package.json +8 -8
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
import type { Connector, TransformItems, WidgetRenderState } from '../../types';
|
|
2
2
|
/**
|
|
3
3
|
* The **SortBy** connector provides the logic to build a custom widget that will display a
|
|
4
|
-
* list of indices. With Algolia, this is most commonly used for changing
|
|
5
|
-
* a user to change how the hits are being sorted.
|
|
4
|
+
* list of indices or sorting strategies. With Algolia, this is most commonly used for changing
|
|
5
|
+
* ranking strategy. This allows a user to change how the hits are being sorted.
|
|
6
|
+
*
|
|
7
|
+
* This connector supports two sorting modes:
|
|
8
|
+
* 1. **Index-based (traditional)**: Uses the `value` property to switch between different indices.
|
|
9
|
+
* This is the standard behavior for non-composition setups.
|
|
10
|
+
*
|
|
11
|
+
* 2. **Strategy-based (composition mode)**: Uses the `strategy` property to apply sorting strategies
|
|
12
|
+
* via the `sortBy` search parameter. This is only available when using Algolia Compositions.
|
|
13
|
+
*
|
|
14
|
+
* Items can mix both types in the same widget, allowing for flexible sorting options.
|
|
6
15
|
*/
|
|
7
|
-
export type
|
|
16
|
+
export type SortByIndexItem = {
|
|
8
17
|
/**
|
|
9
18
|
* The name of the index to target.
|
|
10
19
|
*/
|
|
@@ -13,10 +22,30 @@ export type SortByItem = {
|
|
|
13
22
|
* The label of the index to display.
|
|
14
23
|
*/
|
|
15
24
|
label: string;
|
|
25
|
+
/**
|
|
26
|
+
* Ensures mutual exclusivity with strategy.
|
|
27
|
+
*/
|
|
28
|
+
strategy?: never;
|
|
29
|
+
};
|
|
30
|
+
export type SortByStrategyItem = {
|
|
31
|
+
/**
|
|
32
|
+
* The name of the sorting strategy to use.
|
|
33
|
+
* Only available in composition mode.
|
|
34
|
+
*/
|
|
35
|
+
strategy: string;
|
|
36
|
+
/**
|
|
37
|
+
* The label of the strategy to display.
|
|
38
|
+
*/
|
|
39
|
+
label: string;
|
|
40
|
+
/**
|
|
41
|
+
* Ensures mutual exclusivity with value.
|
|
42
|
+
*/
|
|
43
|
+
value?: never;
|
|
16
44
|
};
|
|
45
|
+
export type SortByItem = SortByIndexItem | SortByStrategyItem;
|
|
17
46
|
export type SortByConnectorParams = {
|
|
18
47
|
/**
|
|
19
|
-
* Array of objects defining the different indices to choose from.
|
|
48
|
+
* Array of objects defining the different indices or strategies to choose from.
|
|
20
49
|
*/
|
|
21
50
|
items: SortByItem[];
|
|
22
51
|
/**
|
|
@@ -26,19 +55,22 @@ export type SortByConnectorParams = {
|
|
|
26
55
|
};
|
|
27
56
|
export type SortByRenderState = {
|
|
28
57
|
/**
|
|
29
|
-
* The initially selected index.
|
|
58
|
+
* The initially selected index or strategy.
|
|
30
59
|
*/
|
|
31
60
|
initialIndex?: string;
|
|
32
61
|
/**
|
|
33
|
-
* The currently selected index.
|
|
62
|
+
* The currently selected index or strategy.
|
|
34
63
|
*/
|
|
35
64
|
currentRefinement: string;
|
|
36
65
|
/**
|
|
37
|
-
* All the available indices
|
|
66
|
+
* All the available indices and strategies
|
|
38
67
|
*/
|
|
39
|
-
options:
|
|
68
|
+
options: Array<{
|
|
69
|
+
value: string;
|
|
70
|
+
label: string;
|
|
71
|
+
}>;
|
|
40
72
|
/**
|
|
41
|
-
* Switches indices and triggers a new search.
|
|
73
|
+
* Switches indices or strategies and triggers a new search.
|
|
42
74
|
*/
|
|
43
75
|
refine: (value: string) => void;
|
|
44
76
|
/**
|
|
@@ -12,10 +12,33 @@ var withUsage = createDocumentationMessageGenerator({
|
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* The **SortBy** connector provides the logic to build a custom widget that will display a
|
|
15
|
-
* list of indices. With Algolia, this is most commonly used for changing
|
|
16
|
-
* a user to change how the hits are being sorted.
|
|
15
|
+
* list of indices or sorting strategies. With Algolia, this is most commonly used for changing
|
|
16
|
+
* ranking strategy. This allows a user to change how the hits are being sorted.
|
|
17
|
+
*
|
|
18
|
+
* This connector supports two sorting modes:
|
|
19
|
+
* 1. **Index-based (traditional)**: Uses the `value` property to switch between different indices.
|
|
20
|
+
* This is the standard behavior for non-composition setups.
|
|
21
|
+
*
|
|
22
|
+
* 2. **Strategy-based (composition mode)**: Uses the `strategy` property to apply sorting strategies
|
|
23
|
+
* via the `sortBy` search parameter. This is only available when using Algolia Compositions.
|
|
24
|
+
*
|
|
25
|
+
* Items can mix both types in the same widget, allowing for flexible sorting options.
|
|
17
26
|
*/
|
|
18
27
|
|
|
28
|
+
function isStrategyItem(item) {
|
|
29
|
+
return 'strategy' in item && item.strategy !== undefined;
|
|
30
|
+
}
|
|
31
|
+
function getItemValue(item) {
|
|
32
|
+
if (isStrategyItem(item)) {
|
|
33
|
+
return item.strategy;
|
|
34
|
+
}
|
|
35
|
+
return item.value;
|
|
36
|
+
}
|
|
37
|
+
function isValidStrategy(itemsLookup, value) {
|
|
38
|
+
if (!value) return false;
|
|
39
|
+
var item = itemsLookup[value];
|
|
40
|
+
return item !== undefined && isStrategyItem(item);
|
|
41
|
+
}
|
|
19
42
|
var connectSortBy = function connectSortBy(renderFn) {
|
|
20
43
|
var unmountFn = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : noop;
|
|
21
44
|
checkRendering(renderFn, withUsage());
|
|
@@ -30,14 +53,38 @@ var connectSortBy = function connectSortBy(renderFn) {
|
|
|
30
53
|
if (!Array.isArray(items)) {
|
|
31
54
|
throw new Error(withUsage('The `items` option expects an array of objects.'));
|
|
32
55
|
}
|
|
56
|
+
var itemsLookup = {};
|
|
57
|
+
items.forEach(function (item, index) {
|
|
58
|
+
var hasValue = 'value' in item && item.value !== undefined;
|
|
59
|
+
var hasStrategy = 'strategy' in item && item.strategy !== undefined;
|
|
60
|
+
|
|
61
|
+
// Validate mutual exclusivity
|
|
62
|
+
if (hasValue && hasStrategy) {
|
|
63
|
+
throw new Error(withUsage("Item at index ".concat(index, " cannot have both \"value\" and \"strategy\" properties.")));
|
|
64
|
+
}
|
|
65
|
+
if (!hasValue && !hasStrategy) {
|
|
66
|
+
throw new Error(withUsage("Item at index ".concat(index, " must have either a \"value\" or \"strategy\" property.")));
|
|
67
|
+
}
|
|
68
|
+
var itemValue = getItemValue(item);
|
|
69
|
+
itemsLookup[itemValue] = item;
|
|
70
|
+
});
|
|
71
|
+
connectorState.itemsLookup = itemsLookup;
|
|
33
72
|
return {
|
|
34
73
|
$$type: 'ais.sortBy',
|
|
35
74
|
init: function init(initOptions) {
|
|
36
75
|
var instantSearchInstance = initOptions.instantSearchInstance;
|
|
76
|
+
|
|
77
|
+
// Check if strategies are used outside composition mode
|
|
78
|
+
var hasStrategyItems = items.some(function (item) {
|
|
79
|
+
return 'strategy' in item && item.strategy;
|
|
80
|
+
});
|
|
81
|
+
if (hasStrategyItems && !instantSearchInstance.compositionID) {
|
|
82
|
+
throw new Error(withUsage('Sorting strategies can only be used in composition mode. Please provide a "compositionID" to your InstantSearch instance.'));
|
|
83
|
+
}
|
|
37
84
|
var widgetRenderState = this.getWidgetRenderState(initOptions);
|
|
38
85
|
var currentIndex = widgetRenderState.currentRefinement;
|
|
39
86
|
var isCurrentIndexInItems = find(items, function (item) {
|
|
40
|
-
return item
|
|
87
|
+
return getItemValue(item) === currentIndex;
|
|
41
88
|
});
|
|
42
89
|
process.env.NODE_ENV === 'development' ? warning(isCurrentIndexInItems !== undefined, "The index named \"".concat(currentIndex, "\" is not listed in the `items` of `sortBy`.")) : void 0;
|
|
43
90
|
renderFn(_objectSpread(_objectSpread({}, widgetRenderState), {}, {
|
|
@@ -53,7 +100,17 @@ var connectSortBy = function connectSortBy(renderFn) {
|
|
|
53
100
|
dispose: function dispose(_ref2) {
|
|
54
101
|
var state = _ref2.state;
|
|
55
102
|
unmountFn();
|
|
56
|
-
|
|
103
|
+
|
|
104
|
+
// Clear sortBy parameter if it was set
|
|
105
|
+
if (connectorState.isUsingComposition && state.sortBy) {
|
|
106
|
+
state = state.setQueryParameter('sortBy', undefined);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Restore initial index if changed
|
|
110
|
+
if (connectorState.initialValue && state.index !== connectorState.initialValue) {
|
|
111
|
+
return state.setIndex(connectorState.initialValue);
|
|
112
|
+
}
|
|
113
|
+
return state;
|
|
57
114
|
},
|
|
58
115
|
getRenderState: function getRenderState(renderState, renderOptions) {
|
|
59
116
|
return _objectSpread(_objectSpread({}, renderState), {}, {
|
|
@@ -64,22 +121,54 @@ var connectSortBy = function connectSortBy(renderFn) {
|
|
|
64
121
|
var results = _ref3.results,
|
|
65
122
|
helper = _ref3.helper,
|
|
66
123
|
state = _ref3.state,
|
|
67
|
-
parent = _ref3.parent
|
|
68
|
-
|
|
69
|
-
|
|
124
|
+
parent = _ref3.parent,
|
|
125
|
+
instantSearchInstance = _ref3.instantSearchInstance;
|
|
126
|
+
// Capture initial value (composition ID or main index)
|
|
127
|
+
if (!connectorState.initialValue && parent) {
|
|
128
|
+
connectorState.initialValue = parent.getIndexName();
|
|
70
129
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
130
|
+
|
|
131
|
+
// Create refine function if not exists
|
|
132
|
+
if (!connectorState.refine) {
|
|
133
|
+
// Cache composition mode status for lifecycle methods that don't have access to instantSearchInstance
|
|
134
|
+
connectorState.isUsingComposition = Boolean(instantSearchInstance === null || instantSearchInstance === void 0 ? void 0 : instantSearchInstance.compositionID);
|
|
135
|
+
connectorState.refine = function (value) {
|
|
136
|
+
// O(1) lookup using the items lookup table
|
|
137
|
+
var item = connectorState.itemsLookup[value];
|
|
138
|
+
if (item && isStrategyItem(item)) {
|
|
139
|
+
// Strategy-based: set sortBy parameter for composition API
|
|
140
|
+
// The composition backend will interpret this and apply the sorting strategy
|
|
141
|
+
helper.setQueryParameter('sortBy', item.strategy).search();
|
|
142
|
+
} else {
|
|
143
|
+
// Index-based: clear any existing sortBy parameter and switch to the new index
|
|
144
|
+
// Clearing sortBy is critical when transitioning from strategy to index-based sorting
|
|
145
|
+
helper.setQueryParameter('sortBy', undefined).setIndex(value).search();
|
|
146
|
+
}
|
|
74
147
|
};
|
|
75
148
|
}
|
|
149
|
+
|
|
150
|
+
// Transform items first (on original structure)
|
|
151
|
+
var transformedItems = transformItems(items, {
|
|
152
|
+
results: results
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
// Normalize items: all get a 'value' property for the render state
|
|
156
|
+
var normalizedItems = transformedItems.map(function (item) {
|
|
157
|
+
return {
|
|
158
|
+
label: item.label,
|
|
159
|
+
value: getItemValue(item)
|
|
160
|
+
};
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
// Determine current refinement
|
|
164
|
+
// In composition mode, prefer sortBy parameter if it corresponds to a valid strategy item
|
|
165
|
+
// Otherwise use the index (for index-based items or when no valid strategy is active)
|
|
166
|
+
var currentRefinement = connectorState.isUsingComposition && isValidStrategy(connectorState.itemsLookup, state.sortBy) ? state.sortBy : state.index;
|
|
76
167
|
var hasNoResults = results ? results.nbHits === 0 : true;
|
|
77
168
|
return {
|
|
78
|
-
currentRefinement:
|
|
79
|
-
options:
|
|
80
|
-
|
|
81
|
-
}),
|
|
82
|
-
refine: connectorState.setIndex,
|
|
169
|
+
currentRefinement: currentRefinement,
|
|
170
|
+
options: normalizedItems,
|
|
171
|
+
refine: connectorState.refine,
|
|
83
172
|
hasNoResults: hasNoResults,
|
|
84
173
|
canRefine: !hasNoResults && items.length > 0,
|
|
85
174
|
widgetParams: widgetParams
|
|
@@ -87,14 +176,25 @@ var connectSortBy = function connectSortBy(renderFn) {
|
|
|
87
176
|
},
|
|
88
177
|
getWidgetUiState: function getWidgetUiState(uiState, _ref4) {
|
|
89
178
|
var searchParameters = _ref4.searchParameters;
|
|
90
|
-
|
|
179
|
+
// In composition mode with an active strategy, use sortBy parameter
|
|
180
|
+
// Otherwise use index-based behavior (traditional mode)
|
|
181
|
+
var currentValue = connectorState.isUsingComposition && isValidStrategy(connectorState.itemsLookup, searchParameters.sortBy) ? searchParameters.sortBy : searchParameters.index;
|
|
91
182
|
return _objectSpread(_objectSpread({}, uiState), {}, {
|
|
92
|
-
sortBy:
|
|
183
|
+
sortBy: currentValue !== connectorState.initialValue ? currentValue : undefined
|
|
93
184
|
});
|
|
94
185
|
},
|
|
95
186
|
getWidgetSearchParameters: function getWidgetSearchParameters(searchParameters, _ref5) {
|
|
96
187
|
var uiState = _ref5.uiState;
|
|
97
|
-
|
|
188
|
+
var sortByValue = uiState.sortBy || connectorState.initialValue || searchParameters.index;
|
|
189
|
+
if (isValidStrategy(connectorState.itemsLookup, sortByValue)) {
|
|
190
|
+
var item = connectorState.itemsLookup[sortByValue];
|
|
191
|
+
// Strategy-based: set the sortBy parameter for composition API
|
|
192
|
+
// The index remains as the compositionID
|
|
193
|
+
return searchParameters.setQueryParameter('sortBy', item.strategy);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Index-based: set the index parameter (traditional behavior)
|
|
197
|
+
return searchParameters.setQueryParameter('index', sortByValue);
|
|
98
198
|
}
|
|
99
199
|
};
|
|
100
200
|
};
|
package/es/lib/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const _default: "4.
|
|
1
|
+
declare const _default: "4.86.1";
|
|
2
2
|
export default _default;
|
package/es/lib/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export default '4.
|
|
1
|
+
export default '4.86.1';
|
|
@@ -58,6 +58,14 @@ type AutocompleteWidgetParams<TItem extends BaseHit> = {
|
|
|
58
58
|
*/
|
|
59
59
|
storageKey?: string;
|
|
60
60
|
templates?: Partial<{
|
|
61
|
+
/**
|
|
62
|
+
* Template to use for the header, before the list of items.
|
|
63
|
+
*/
|
|
64
|
+
header: Template<{
|
|
65
|
+
items: Array<{
|
|
66
|
+
query: string;
|
|
67
|
+
}>;
|
|
68
|
+
}>;
|
|
61
69
|
/**
|
|
62
70
|
* Template to use for each result. This template will receive an object containing a single record.
|
|
63
71
|
*/
|
|
@@ -69,6 +77,7 @@ type AutocompleteWidgetParams<TItem extends BaseHit> = {
|
|
|
69
77
|
onRemoveRecentSearch: () => void;
|
|
70
78
|
}>;
|
|
71
79
|
}>;
|
|
80
|
+
cssClasses?: Partial<AutocompleteIndexClassNames>;
|
|
72
81
|
};
|
|
73
82
|
/**
|
|
74
83
|
* Search parameters to apply to the autocomplete indices.
|