@athoscommerce/snap-store-mobx 1.0.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/LICENSE +21 -0
- package/README.md +17 -0
- package/dist/cjs/Abstract/AbstractStore.d.ts +17 -0
- package/dist/cjs/Abstract/AbstractStore.d.ts.map +1 -0
- package/dist/cjs/Abstract/AbstractStore.js +28 -0
- package/dist/cjs/Autocomplete/AutocompleteStore.d.ts +40 -0
- package/dist/cjs/Autocomplete/AutocompleteStore.d.ts.map +1 -0
- package/dist/cjs/Autocomplete/AutocompleteStore.js +246 -0
- package/dist/cjs/Autocomplete/Stores/AutocompleteFacetStore.d.ts +13 -0
- package/dist/cjs/Autocomplete/Stores/AutocompleteFacetStore.d.ts.map +1 -0
- package/dist/cjs/Autocomplete/Stores/AutocompleteFacetStore.js +67 -0
- package/dist/cjs/Autocomplete/Stores/AutocompleteHistoryStore.d.ts +19 -0
- package/dist/cjs/Autocomplete/Stores/AutocompleteHistoryStore.d.ts.map +1 -0
- package/dist/cjs/Autocomplete/Stores/AutocompleteHistoryStore.js +57 -0
- package/dist/cjs/Autocomplete/Stores/AutocompleteQueryStore.d.ts +25 -0
- package/dist/cjs/Autocomplete/Stores/AutocompleteQueryStore.d.ts.map +1 -0
- package/dist/cjs/Autocomplete/Stores/AutocompleteQueryStore.js +34 -0
- package/dist/cjs/Autocomplete/Stores/AutocompleteStateStore.d.ts +29 -0
- package/dist/cjs/Autocomplete/Stores/AutocompleteStateStore.d.ts.map +1 -0
- package/dist/cjs/Autocomplete/Stores/AutocompleteStateStore.js +54 -0
- package/dist/cjs/Autocomplete/Stores/AutocompleteTermStore.d.ts +47 -0
- package/dist/cjs/Autocomplete/Stores/AutocompleteTermStore.d.ts.map +1 -0
- package/dist/cjs/Autocomplete/Stores/AutocompleteTermStore.js +102 -0
- package/dist/cjs/Autocomplete/Stores/AutocompleteTrendingStore.d.ts +20 -0
- package/dist/cjs/Autocomplete/Stores/AutocompleteTrendingStore.d.ts.map +1 -0
- package/dist/cjs/Autocomplete/Stores/AutocompleteTrendingStore.js +58 -0
- package/dist/cjs/Autocomplete/Stores/index.d.ts +7 -0
- package/dist/cjs/Autocomplete/Stores/index.d.ts.map +1 -0
- package/dist/cjs/Autocomplete/Stores/index.js +16 -0
- package/dist/cjs/Cart/CartStore.d.ts +15 -0
- package/dist/cjs/Cart/CartStore.d.ts.map +1 -0
- package/dist/cjs/Cart/CartStore.js +109 -0
- package/dist/cjs/Finder/FinderStore.d.ts +27 -0
- package/dist/cjs/Finder/FinderStore.d.ts.map +1 -0
- package/dist/cjs/Finder/FinderStore.js +156 -0
- package/dist/cjs/Finder/Stores/FinderSelectionStore.d.ts +73 -0
- package/dist/cjs/Finder/Stores/FinderSelectionStore.d.ts.map +1 -0
- package/dist/cjs/Finder/Stores/FinderSelectionStore.js +320 -0
- package/dist/cjs/Finder/Stores/index.d.ts +2 -0
- package/dist/cjs/Finder/Stores/index.d.ts.map +1 -0
- package/dist/cjs/Finder/Stores/index.js +5 -0
- package/dist/cjs/Meta/MetaStore.d.ts +20 -0
- package/dist/cjs/Meta/MetaStore.d.ts.map +1 -0
- package/dist/cjs/Meta/MetaStore.js +55 -0
- package/dist/cjs/Recommendation/RecommendationStore.d.ts +19 -0
- package/dist/cjs/Recommendation/RecommendationStore.d.ts.map +1 -0
- package/dist/cjs/Recommendation/RecommendationStore.js +97 -0
- package/dist/cjs/Recommendation/Stores/RecommendationProfileStore.d.ts +15 -0
- package/dist/cjs/Recommendation/Stores/RecommendationProfileStore.d.ts.map +1 -0
- package/dist/cjs/Recommendation/Stores/RecommendationProfileStore.js +30 -0
- package/dist/cjs/Recommendation/Stores/index.d.ts +2 -0
- package/dist/cjs/Recommendation/Stores/index.d.ts.map +1 -0
- package/dist/cjs/Recommendation/Stores/index.js +5 -0
- package/dist/cjs/Search/SearchStore.d.ts +27 -0
- package/dist/cjs/Search/SearchStore.d.ts.map +1 -0
- package/dist/cjs/Search/SearchStore.js +125 -0
- package/dist/cjs/Search/Stores/SearchFacetStore.d.ts +89 -0
- package/dist/cjs/Search/Stores/SearchFacetStore.d.ts.map +1 -0
- package/dist/cjs/Search/Stores/SearchFacetStore.js +368 -0
- package/dist/cjs/Search/Stores/SearchFilterStore.d.ts +44 -0
- package/dist/cjs/Search/Stores/SearchFilterStore.d.ts.map +1 -0
- package/dist/cjs/Search/Stores/SearchFilterStore.js +102 -0
- package/dist/cjs/Search/Stores/SearchHistoryStore.d.ts +20 -0
- package/dist/cjs/Search/Stores/SearchHistoryStore.d.ts.map +1 -0
- package/dist/cjs/Search/Stores/SearchHistoryStore.js +106 -0
- package/dist/cjs/Search/Stores/SearchMerchandisingStore.d.ts +36 -0
- package/dist/cjs/Search/Stores/SearchMerchandisingStore.d.ts.map +1 -0
- package/dist/cjs/Search/Stores/SearchMerchandisingStore.js +90 -0
- package/dist/cjs/Search/Stores/SearchPaginationStore.d.ts +57 -0
- package/dist/cjs/Search/Stores/SearchPaginationStore.d.ts.map +1 -0
- package/dist/cjs/Search/Stores/SearchPaginationStore.js +212 -0
- package/dist/cjs/Search/Stores/SearchQueryStore.d.ts +23 -0
- package/dist/cjs/Search/Stores/SearchQueryStore.d.ts.map +1 -0
- package/dist/cjs/Search/Stores/SearchQueryStore.js +41 -0
- package/dist/cjs/Search/Stores/SearchResultStore.d.ts +175 -0
- package/dist/cjs/Search/Stores/SearchResultStore.d.ts.map +1 -0
- package/dist/cjs/Search/Stores/SearchResultStore.js +749 -0
- package/dist/cjs/Search/Stores/SearchSortingStore.d.ts +32 -0
- package/dist/cjs/Search/Stores/SearchSortingStore.d.ts.map +1 -0
- package/dist/cjs/Search/Stores/SearchSortingStore.js +77 -0
- package/dist/cjs/Search/Stores/index.d.ts +9 -0
- package/dist/cjs/Search/Stores/index.d.ts.map +1 -0
- package/dist/cjs/Search/Stores/index.js +30 -0
- package/dist/cjs/Storage/StorageStore.d.ts +27 -0
- package/dist/cjs/Storage/StorageStore.d.ts.map +1 -0
- package/dist/cjs/Storage/StorageStore.js +176 -0
- package/dist/cjs/index.d.ts +12 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +37 -0
- package/dist/cjs/types.d.ts +170 -0
- package/dist/cjs/types.d.ts.map +1 -0
- package/dist/cjs/types.js +9 -0
- package/dist/esm/Abstract/AbstractStore.d.ts +17 -0
- package/dist/esm/Abstract/AbstractStore.d.ts.map +1 -0
- package/dist/esm/Abstract/AbstractStore.js +22 -0
- package/dist/esm/Autocomplete/AutocompleteStore.d.ts +40 -0
- package/dist/esm/Autocomplete/AutocompleteStore.d.ts.map +1 -0
- package/dist/esm/Autocomplete/AutocompleteStore.js +210 -0
- package/dist/esm/Autocomplete/Stores/AutocompleteFacetStore.d.ts +13 -0
- package/dist/esm/Autocomplete/Stores/AutocompleteFacetStore.d.ts.map +1 -0
- package/dist/esm/Autocomplete/Stores/AutocompleteFacetStore.js +35 -0
- package/dist/esm/Autocomplete/Stores/AutocompleteHistoryStore.d.ts +19 -0
- package/dist/esm/Autocomplete/Stores/AutocompleteHistoryStore.d.ts.map +1 -0
- package/dist/esm/Autocomplete/Stores/AutocompleteHistoryStore.js +27 -0
- package/dist/esm/Autocomplete/Stores/AutocompleteQueryStore.d.ts +25 -0
- package/dist/esm/Autocomplete/Stores/AutocompleteQueryStore.d.ts.map +1 -0
- package/dist/esm/Autocomplete/Stores/AutocompleteQueryStore.js +28 -0
- package/dist/esm/Autocomplete/Stores/AutocompleteStateStore.d.ts +29 -0
- package/dist/esm/Autocomplete/Stores/AutocompleteStateStore.d.ts.map +1 -0
- package/dist/esm/Autocomplete/Stores/AutocompleteStateStore.js +42 -0
- package/dist/esm/Autocomplete/Stores/AutocompleteTermStore.d.ts +47 -0
- package/dist/esm/Autocomplete/Stores/AutocompleteTermStore.d.ts.map +1 -0
- package/dist/esm/Autocomplete/Stores/AutocompleteTermStore.js +56 -0
- package/dist/esm/Autocomplete/Stores/AutocompleteTrendingStore.d.ts +20 -0
- package/dist/esm/Autocomplete/Stores/AutocompleteTrendingStore.d.ts.map +1 -0
- package/dist/esm/Autocomplete/Stores/AutocompleteTrendingStore.js +27 -0
- package/dist/esm/Autocomplete/Stores/index.d.ts +7 -0
- package/dist/esm/Autocomplete/Stores/index.d.ts.map +1 -0
- package/dist/esm/Autocomplete/Stores/index.js +6 -0
- package/dist/esm/Cart/CartStore.d.ts +15 -0
- package/dist/esm/Cart/CartStore.d.ts.map +1 -0
- package/dist/esm/Cart/CartStore.js +74 -0
- package/dist/esm/Finder/FinderStore.d.ts +27 -0
- package/dist/esm/Finder/FinderStore.d.ts.map +1 -0
- package/dist/esm/Finder/FinderStore.js +118 -0
- package/dist/esm/Finder/Stores/FinderSelectionStore.d.ts +73 -0
- package/dist/esm/Finder/Stores/FinderSelectionStore.d.ts.map +1 -0
- package/dist/esm/Finder/Stores/FinderSelectionStore.js +258 -0
- package/dist/esm/Finder/Stores/index.d.ts +2 -0
- package/dist/esm/Finder/Stores/index.d.ts.map +1 -0
- package/dist/esm/Finder/Stores/index.js +1 -0
- package/dist/esm/Meta/MetaStore.d.ts +20 -0
- package/dist/esm/Meta/MetaStore.d.ts.map +1 -0
- package/dist/esm/Meta/MetaStore.js +45 -0
- package/dist/esm/Recommendation/RecommendationStore.d.ts +19 -0
- package/dist/esm/Recommendation/RecommendationStore.d.ts.map +1 -0
- package/dist/esm/Recommendation/RecommendationStore.js +71 -0
- package/dist/esm/Recommendation/Stores/RecommendationProfileStore.d.ts +15 -0
- package/dist/esm/Recommendation/Stores/RecommendationProfileStore.d.ts.map +1 -0
- package/dist/esm/Recommendation/Stores/RecommendationProfileStore.js +25 -0
- package/dist/esm/Recommendation/Stores/index.d.ts +2 -0
- package/dist/esm/Recommendation/Stores/index.d.ts.map +1 -0
- package/dist/esm/Recommendation/Stores/index.js +1 -0
- package/dist/esm/Search/SearchStore.d.ts +27 -0
- package/dist/esm/Search/SearchStore.d.ts.map +1 -0
- package/dist/esm/Search/SearchStore.js +102 -0
- package/dist/esm/Search/Stores/SearchFacetStore.d.ts +89 -0
- package/dist/esm/Search/Stores/SearchFacetStore.d.ts.map +1 -0
- package/dist/esm/Search/Stores/SearchFacetStore.js +292 -0
- package/dist/esm/Search/Stores/SearchFilterStore.d.ts +44 -0
- package/dist/esm/Search/Stores/SearchFilterStore.d.ts.map +1 -0
- package/dist/esm/Search/Stores/SearchFilterStore.js +70 -0
- package/dist/esm/Search/Stores/SearchHistoryStore.d.ts +20 -0
- package/dist/esm/Search/Stores/SearchHistoryStore.d.ts.map +1 -0
- package/dist/esm/Search/Stores/SearchHistoryStore.js +85 -0
- package/dist/esm/Search/Stores/SearchMerchandisingStore.d.ts +36 -0
- package/dist/esm/Search/Stores/SearchMerchandisingStore.d.ts.map +1 -0
- package/dist/esm/Search/Stores/SearchMerchandisingStore.js +61 -0
- package/dist/esm/Search/Stores/SearchPaginationStore.d.ts +57 -0
- package/dist/esm/Search/Stores/SearchPaginationStore.d.ts.map +1 -0
- package/dist/esm/Search/Stores/SearchPaginationStore.js +163 -0
- package/dist/esm/Search/Stores/SearchQueryStore.d.ts +23 -0
- package/dist/esm/Search/Stores/SearchQueryStore.d.ts.map +1 -0
- package/dist/esm/Search/Stores/SearchQueryStore.js +33 -0
- package/dist/esm/Search/Stores/SearchResultStore.d.ts +175 -0
- package/dist/esm/Search/Stores/SearchResultStore.d.ts.map +1 -0
- package/dist/esm/Search/Stores/SearchResultStore.js +648 -0
- package/dist/esm/Search/Stores/SearchSortingStore.d.ts +32 -0
- package/dist/esm/Search/Stores/SearchSortingStore.d.ts.map +1 -0
- package/dist/esm/Search/Stores/SearchSortingStore.js +67 -0
- package/dist/esm/Search/Stores/index.d.ts +9 -0
- package/dist/esm/Search/Stores/index.d.ts.map +1 -0
- package/dist/esm/Search/Stores/index.js +8 -0
- package/dist/esm/Storage/StorageStore.d.ts +27 -0
- package/dist/esm/Storage/StorageStore.d.ts.map +1 -0
- package/dist/esm/Storage/StorageStore.js +169 -0
- package/dist/esm/index.d.ts +12 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +11 -0
- package/dist/esm/types.d.ts +170 -0
- package/dist/esm/types.d.ts.map +1 -0
- package/dist/esm/types.js +6 -0
- package/package.json +35 -0
|
@@ -0,0 +1,648 @@
|
|
|
1
|
+
import { computed, makeObservable, observable } from 'mobx';
|
|
2
|
+
import deepmerge from 'deepmerge';
|
|
3
|
+
import { isPlainObject } from 'is-plain-object';
|
|
4
|
+
const VARIANT_ATTRIBUTE = 'ss-variant-option';
|
|
5
|
+
const VARIANT_ATTRIBUTE_SELECTED = 'ss-variant-option-selected';
|
|
6
|
+
export class SearchResultStore extends Array {
|
|
7
|
+
static get [Symbol.species]() {
|
|
8
|
+
return Array;
|
|
9
|
+
}
|
|
10
|
+
constructor(params) {
|
|
11
|
+
const { config, data, state, stores } = params || {};
|
|
12
|
+
const { search, meta, previousSearch } = data || {};
|
|
13
|
+
const { results, merchandising, pagination } = search || {};
|
|
14
|
+
const { previousResults } = stores || {};
|
|
15
|
+
const { loaded } = state || {};
|
|
16
|
+
let resultsArr = (results || []).map((result, idx) => {
|
|
17
|
+
return new Product({
|
|
18
|
+
config,
|
|
19
|
+
data: { result, meta },
|
|
20
|
+
position: idx + 1,
|
|
21
|
+
responseId: params.data.search?.tracking?.responseId || '',
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
const variantConfig = config?.settings?.variants;
|
|
25
|
+
// preselected variant options
|
|
26
|
+
if (variantConfig?.realtime?.enabled) {
|
|
27
|
+
// attach click events - ONLY happens once (known limitation)
|
|
28
|
+
if (!loaded && resultsArr?.length) {
|
|
29
|
+
const processedSelects = new Set();
|
|
30
|
+
document.querySelectorAll(`[${VARIANT_ATTRIBUTE}]`).forEach((elem) => {
|
|
31
|
+
if (elem.tagName == 'OPTION') {
|
|
32
|
+
const parentSelect = elem.closest('select');
|
|
33
|
+
if (parentSelect) {
|
|
34
|
+
if (!processedSelects.has(parentSelect)) {
|
|
35
|
+
processedSelects.add(parentSelect);
|
|
36
|
+
parentSelect.addEventListener('change', (e) => {
|
|
37
|
+
const val = e.target?.value;
|
|
38
|
+
const clickedOption = Array.from(parentSelect.querySelectorAll(`[${VARIANT_ATTRIBUTE}]`)).filter((elem) => elem.value == val);
|
|
39
|
+
if (clickedOption.length > 0) {
|
|
40
|
+
variantOptionClick(clickedOption[0], variantConfig, resultsArr);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
console.warn('Warning: unable to add realtime variant event listener for element - ', elem);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
elem.addEventListener('click', () => {
|
|
51
|
+
variantOptionClick(elem, variantConfig, resultsArr);
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
// check for attributes for preselection
|
|
57
|
+
if (resultsArr.length) {
|
|
58
|
+
const options = {};
|
|
59
|
+
// grab values from elements on the page to form preselected elements
|
|
60
|
+
document.querySelectorAll(`[${VARIANT_ATTRIBUTE_SELECTED}]`).forEach((elem) => {
|
|
61
|
+
const attr = elem.getAttribute(VARIANT_ATTRIBUTE);
|
|
62
|
+
if (attr) {
|
|
63
|
+
const [option, value] = attr.split(':');
|
|
64
|
+
if (option && value) {
|
|
65
|
+
options[option.toLowerCase()] = [value.toLowerCase()];
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
makeVariantSelections(variantConfig, options, resultsArr);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
// only when infinite is enabled
|
|
73
|
+
if (config?.settings?.infinite?.enabled && previousResults) {
|
|
74
|
+
// logic to determine when to concatenate previous results
|
|
75
|
+
// this logic is not bullet proof, but it is highly unlikely that the current and previous pagination data would ever be sequential unless paginating
|
|
76
|
+
// TODO: implement "load previous" with a limit to how many total products can be displayed
|
|
77
|
+
// the limit would be enforced by unloading (trimming) results based on the direction of the new search (previous page, next page)
|
|
78
|
+
if (pagination?.page && previousSearch?.pagination?.page && pagination.page == previousSearch.pagination.page + 1) {
|
|
79
|
+
resultsArr = (previousResults || []).concat(resultsArr);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
// add banners to results - banner data includes ALL banners within the entire search results (even if not on the current page)
|
|
83
|
+
if (pagination && merchandising?.content?.inline) {
|
|
84
|
+
// ensure banners are sorted by index
|
|
85
|
+
const banners = merchandising.content.inline
|
|
86
|
+
.sort(function (a, b) {
|
|
87
|
+
return a.config.position.index - b.config.position.index;
|
|
88
|
+
})
|
|
89
|
+
.map((banner) => {
|
|
90
|
+
return new Banner({
|
|
91
|
+
data: { banner, responseId: params.data.search?.tracking?.responseId || '' },
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
if (banners && pagination.totalResults) {
|
|
95
|
+
resultsArr = addBannersToResults(config, resultsArr, banners, pagination);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
super(...resultsArr);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
export class Banner {
|
|
102
|
+
constructor(bannerData) {
|
|
103
|
+
this.type = 'banner';
|
|
104
|
+
this.attributes = {};
|
|
105
|
+
this.mappings = {
|
|
106
|
+
core: {},
|
|
107
|
+
};
|
|
108
|
+
this.custom = {};
|
|
109
|
+
const { banner, responseId } = bannerData?.data || {};
|
|
110
|
+
const htmlString = banner.value;
|
|
111
|
+
const match = typeof htmlString === 'string' && htmlString.match(/data-banner-id="(\d+)"/);
|
|
112
|
+
const uid = match ? match[1] : 'ss-ib-' + banner.config.position.index;
|
|
113
|
+
this.id = uid;
|
|
114
|
+
this.responseId = responseId;
|
|
115
|
+
this.config = banner.config;
|
|
116
|
+
this.value = banner.value;
|
|
117
|
+
makeObservable(this, {
|
|
118
|
+
id: observable,
|
|
119
|
+
mappings: observable,
|
|
120
|
+
attributes: observable,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
export class Product {
|
|
125
|
+
constructor(productData) {
|
|
126
|
+
this.type = 'product';
|
|
127
|
+
this.attributes = {};
|
|
128
|
+
this.mappings = {
|
|
129
|
+
core: {},
|
|
130
|
+
};
|
|
131
|
+
this.custom = {};
|
|
132
|
+
this.quantity = 1;
|
|
133
|
+
this.mask = new ProductMask();
|
|
134
|
+
const { config } = productData || {};
|
|
135
|
+
const { result, meta } = productData?.data || {};
|
|
136
|
+
this.id = result.id;
|
|
137
|
+
this.attributes = result.attributes;
|
|
138
|
+
this.mappings = result.mappings;
|
|
139
|
+
this.position = productData.position;
|
|
140
|
+
this.badges = new Badges({
|
|
141
|
+
data: {
|
|
142
|
+
meta,
|
|
143
|
+
result,
|
|
144
|
+
},
|
|
145
|
+
});
|
|
146
|
+
this.responseId = result.responseId || productData.responseId;
|
|
147
|
+
// @ts-ignore - need to add bundleSeed to snapi-types
|
|
148
|
+
if (result.bundleSeed) {
|
|
149
|
+
// @ts-ignore - need to add bundleSeed to snapi-types
|
|
150
|
+
this.bundleSeed = Boolean(result.bundleSeed);
|
|
151
|
+
}
|
|
152
|
+
if (result.variants && result.variants.data) {
|
|
153
|
+
// if variants are already parsed, use them
|
|
154
|
+
this.variants = new Variants({
|
|
155
|
+
data: {
|
|
156
|
+
mask: this.mask,
|
|
157
|
+
variants: result.variants.data,
|
|
158
|
+
optionConfig: result.variants.optionConfig,
|
|
159
|
+
preferences: result.variants?.preferences, // TODO: fix typing
|
|
160
|
+
meta: meta,
|
|
161
|
+
},
|
|
162
|
+
config: config?.settings?.variants,
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
makeObservable(this, {
|
|
166
|
+
id: observable,
|
|
167
|
+
display: computed,
|
|
168
|
+
mappings: observable,
|
|
169
|
+
attributes: observable,
|
|
170
|
+
custom: observable,
|
|
171
|
+
quantity: observable,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
get display() {
|
|
175
|
+
return deepmerge({ id: this.id, mappings: this.mappings, attributes: this.attributes, badges: this.badges }, this.mask.data, {
|
|
176
|
+
isMergeableObject: isPlainObject,
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
export class Badges {
|
|
181
|
+
constructor(badgesData) {
|
|
182
|
+
this.all = [];
|
|
183
|
+
const { data } = badgesData || {};
|
|
184
|
+
const { meta, result } = data || {};
|
|
185
|
+
this.all = (result.badges || [])
|
|
186
|
+
.filter((badge) => {
|
|
187
|
+
// remove badges that are not in the meta or are disabled
|
|
188
|
+
return !!(badge?.tag && meta.badges?.tags && meta.badges?.tags[badge.tag] && meta.badges?.tags[badge.tag].enabled);
|
|
189
|
+
})
|
|
190
|
+
.map((badge) => {
|
|
191
|
+
// merge badge with badge meta data
|
|
192
|
+
const metaBadgeData = meta.badges?.tags?.[badge.tag];
|
|
193
|
+
return {
|
|
194
|
+
...badge,
|
|
195
|
+
...metaBadgeData,
|
|
196
|
+
};
|
|
197
|
+
})
|
|
198
|
+
.sort((a, b) => {
|
|
199
|
+
return a.priority - b.priority;
|
|
200
|
+
});
|
|
201
|
+
makeObservable(this, {
|
|
202
|
+
all: observable,
|
|
203
|
+
tags: computed,
|
|
204
|
+
locations: computed,
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
// get all the result badges that are in a specific location
|
|
208
|
+
atLocation(location) {
|
|
209
|
+
const locations = Array.isArray(location) ? location : [location];
|
|
210
|
+
return this.all.filter((badge) => {
|
|
211
|
+
// filter location
|
|
212
|
+
return locations.some((location) => badge.location.startsWith(`${location}/`) || badge.location == location);
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
get tags() {
|
|
216
|
+
return this.all.reduce((badgeMap, badge) => {
|
|
217
|
+
badgeMap[badge.tag] = badge;
|
|
218
|
+
return badgeMap;
|
|
219
|
+
}, {});
|
|
220
|
+
}
|
|
221
|
+
get locations() {
|
|
222
|
+
return this.all.reduce((locationMap, badge) => {
|
|
223
|
+
// put badge in location by path
|
|
224
|
+
const [section, tag] = badge.location.split('/');
|
|
225
|
+
locationMap[section] = locationMap[section] || {};
|
|
226
|
+
locationMap[section][tag] = (locationMap[section][tag] || []).concat(badge);
|
|
227
|
+
return locationMap;
|
|
228
|
+
}, {});
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
// Mask is used to power the product display for quick attribute swapping
|
|
232
|
+
export class ProductMask {
|
|
233
|
+
constructor() {
|
|
234
|
+
this.data = {};
|
|
235
|
+
makeObservable(this, {
|
|
236
|
+
data: observable,
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
merge(mask) {
|
|
240
|
+
// TODO: look into making more performant
|
|
241
|
+
// needed to prevent infinite re-render on merge with same data
|
|
242
|
+
if (JSON.stringify(deepmerge(this.data, mask)) != JSON.stringify(this.data)) {
|
|
243
|
+
this.data = deepmerge(this.data, mask);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
set(mask) {
|
|
247
|
+
// TODO: look into making more performant
|
|
248
|
+
// needed to prevent infinite re-render on set with same data
|
|
249
|
+
if (JSON.stringify(mask) != JSON.stringify(this.data)) {
|
|
250
|
+
this.data = mask;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
reset() {
|
|
254
|
+
this.data = {};
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
export class Variants {
|
|
258
|
+
constructor(variantData) {
|
|
259
|
+
this.data = [];
|
|
260
|
+
this.selections = [];
|
|
261
|
+
const { config, data } = variantData || {};
|
|
262
|
+
const { variants, mask, meta } = data || {};
|
|
263
|
+
const preferences = variantData?.data?.preferences || {};
|
|
264
|
+
// setting function in constructor to prevent exposing mask as class property
|
|
265
|
+
this.setActive = (variant) => {
|
|
266
|
+
this.active = variant;
|
|
267
|
+
const activeBadges = new Badges({
|
|
268
|
+
data: {
|
|
269
|
+
meta: meta,
|
|
270
|
+
result: variant,
|
|
271
|
+
},
|
|
272
|
+
});
|
|
273
|
+
mask.set({ mappings: this.active.mappings, attributes: this.active.attributes, badges: activeBadges });
|
|
274
|
+
};
|
|
275
|
+
if (config) {
|
|
276
|
+
this.config = config;
|
|
277
|
+
}
|
|
278
|
+
if (data.optionConfig) {
|
|
279
|
+
this.optionConfig = data.optionConfig;
|
|
280
|
+
}
|
|
281
|
+
this.update(variants, config, preferences);
|
|
282
|
+
}
|
|
283
|
+
update(variantData, config = this.config, preferences) {
|
|
284
|
+
try {
|
|
285
|
+
const options = [];
|
|
286
|
+
// create variants objects
|
|
287
|
+
this.data = variantData
|
|
288
|
+
.filter((variant) => this.config?.showDisabledSelectionValues || variant.mappings.core?.available !== false)
|
|
289
|
+
.map((variant) => {
|
|
290
|
+
// normalize price fields ensuring they are numbers
|
|
291
|
+
if (variant.mappings.core?.price) {
|
|
292
|
+
variant.mappings.core.price = Number(variant.mappings.core?.price);
|
|
293
|
+
}
|
|
294
|
+
if (variant.mappings.core?.msrp) {
|
|
295
|
+
variant.mappings.core.msrp = Number(variant.mappings.core?.msrp);
|
|
296
|
+
}
|
|
297
|
+
return variant;
|
|
298
|
+
})
|
|
299
|
+
.map((variant) => {
|
|
300
|
+
Object.keys(variant.options).forEach((variantOption) => {
|
|
301
|
+
if (!options.includes(variantOption)) {
|
|
302
|
+
options.push(variantOption);
|
|
303
|
+
}
|
|
304
|
+
});
|
|
305
|
+
return new Variant({
|
|
306
|
+
data: { variant },
|
|
307
|
+
});
|
|
308
|
+
});
|
|
309
|
+
//need to reset this.selections first
|
|
310
|
+
this.selections = [];
|
|
311
|
+
options.map((option) => {
|
|
312
|
+
const variantOptionConfig = this.config?.options && this.config.options[option];
|
|
313
|
+
this.selections.push(new VariantSelection({
|
|
314
|
+
config: variantOptionConfig,
|
|
315
|
+
optionConfig: this.optionConfig?.[option],
|
|
316
|
+
data: {
|
|
317
|
+
variants: this,
|
|
318
|
+
selectorField: option,
|
|
319
|
+
},
|
|
320
|
+
}));
|
|
321
|
+
});
|
|
322
|
+
const preselectedOptions = {};
|
|
323
|
+
//api preferences
|
|
324
|
+
if (preferences) {
|
|
325
|
+
Object.keys(preferences).forEach((option) => {
|
|
326
|
+
preselectedOptions[option] = preferences[option];
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
//user defined preferences - these override API preferences
|
|
330
|
+
if (config?.options) {
|
|
331
|
+
Object.keys(config?.options).forEach((option) => {
|
|
332
|
+
if (config.options[option].preSelected) {
|
|
333
|
+
preselectedOptions[option] = config.options[option].preSelected;
|
|
334
|
+
}
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
if (config?.autoSelect) {
|
|
338
|
+
// select first available
|
|
339
|
+
this.makeSelections(preselectedOptions);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
catch (err) {
|
|
343
|
+
// failed to parse the variant JSON
|
|
344
|
+
console.error(err, `Invalid variant JSON for: ${variantData}`);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
makeSelections(options) {
|
|
348
|
+
// options = {color: 'Blue', size: 'L'};
|
|
349
|
+
if (!options || !Object.keys(options).length) {
|
|
350
|
+
// select first available for each selection
|
|
351
|
+
this.selections.forEach((selection) => {
|
|
352
|
+
const firstAvailableOption = selection.values.find((value) => value.available);
|
|
353
|
+
if (firstAvailableOption) {
|
|
354
|
+
selection.select(firstAvailableOption.value, true);
|
|
355
|
+
}
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
else {
|
|
359
|
+
this.selections.forEach((selection, idx) => {
|
|
360
|
+
// filter by first available, then by preselected option preference
|
|
361
|
+
//make all options available for first selection.
|
|
362
|
+
const availableOptions = selection.values.filter((value) => (idx == 0 ? true : value.available));
|
|
363
|
+
const preferedOptions = options[selection.field.toLowerCase()];
|
|
364
|
+
let preferencedOption = selection.selected || availableOptions[0];
|
|
365
|
+
// if theres a preference for that field
|
|
366
|
+
if (preferedOptions) {
|
|
367
|
+
const checkIfAvailable = (preference) => {
|
|
368
|
+
//see if that option is in the available options
|
|
369
|
+
const availablePreferedOptions = availableOptions.find((value) => value.value.toString().toLowerCase() == preference?.toString().toLowerCase());
|
|
370
|
+
//use it
|
|
371
|
+
if (availablePreferedOptions) {
|
|
372
|
+
preferencedOption = availablePreferedOptions;
|
|
373
|
+
}
|
|
374
|
+
};
|
|
375
|
+
if (Array.isArray(preferedOptions)) {
|
|
376
|
+
//loop through each preference option
|
|
377
|
+
preferedOptions.forEach((preference) => {
|
|
378
|
+
checkIfAvailable(preference);
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
else {
|
|
382
|
+
checkIfAvailable(preferedOptions);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
if (preferencedOption) {
|
|
386
|
+
selection.select(preferencedOption.value, true);
|
|
387
|
+
}
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
refineSelections(fromSelection) {
|
|
392
|
+
// need to ensure the update originator is at the BOTTOM of the list for refinement
|
|
393
|
+
const orderedSelections = [...this.selections];
|
|
394
|
+
orderedSelections.sort((a) => {
|
|
395
|
+
if (a.field == fromSelection.field) {
|
|
396
|
+
return 1;
|
|
397
|
+
}
|
|
398
|
+
return -1;
|
|
399
|
+
});
|
|
400
|
+
// refine selections ensuring that the selection that triggered the update refines LAST
|
|
401
|
+
orderedSelections.forEach((selection) => selection.refineValues(this));
|
|
402
|
+
// check to see if we have enough selections made to update the display
|
|
403
|
+
const selectedSelections = this.selections.filter((selection) => selection.selected?.value?.length);
|
|
404
|
+
if (selectedSelections.length) {
|
|
405
|
+
let availableVariants = this.data;
|
|
406
|
+
// loop through selectedSelections and only include available products that match current selections
|
|
407
|
+
for (const selectedSelection of selectedSelections) {
|
|
408
|
+
availableVariants = availableVariants.filter((variant) => selectedSelection.selected?.value == variant.options[selectedSelection.field]?.value && variant.available);
|
|
409
|
+
}
|
|
410
|
+
// set active variant
|
|
411
|
+
if (availableVariants.length == 1) {
|
|
412
|
+
const variantToSet = availableVariants[0];
|
|
413
|
+
//need to update all unselected selections to match the single result
|
|
414
|
+
const unselectedSelections = this.selections.filter((selection) => !selection.selected);
|
|
415
|
+
unselectedSelections.forEach((selection) => {
|
|
416
|
+
const field = selection.field;
|
|
417
|
+
const valueToSet = variantToSet.options[field].value;
|
|
418
|
+
selection.select(valueToSet);
|
|
419
|
+
});
|
|
420
|
+
this.setActive(variantToSet);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
export class VariantSelection {
|
|
426
|
+
constructor(variantSelectionData) {
|
|
427
|
+
this.selected = undefined;
|
|
428
|
+
this.previouslySelected = undefined;
|
|
429
|
+
this.values = [];
|
|
430
|
+
const { data, config, optionConfig } = variantSelectionData || {};
|
|
431
|
+
const { variants, selectorField } = data || {};
|
|
432
|
+
this.field = selectorField;
|
|
433
|
+
this.type = optionConfig?.type;
|
|
434
|
+
this.count = optionConfig?.count;
|
|
435
|
+
this.label = config?.label || selectorField;
|
|
436
|
+
this.config = config || {};
|
|
437
|
+
// needed to prevent attaching variants as class property
|
|
438
|
+
this.variantsUpdate = () => variants.refineSelections(this);
|
|
439
|
+
// create possible values from the data and refine them
|
|
440
|
+
this.refineValues(variants);
|
|
441
|
+
makeObservable(this, {
|
|
442
|
+
selected: observable,
|
|
443
|
+
values: observable,
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
refineValues(variants) {
|
|
447
|
+
// current selection should only consider OTHER selections for availability
|
|
448
|
+
const selectedSelections = variants.selections.filter((selection) => selection.field != this.field && selection.selected);
|
|
449
|
+
let availableVariants = variants.data.filter((variant) => variant.available);
|
|
450
|
+
// loop through selectedSelections and remove products that do not match
|
|
451
|
+
for (const selectedSelection of selectedSelections) {
|
|
452
|
+
availableVariants = availableVariants.filter((variant) => selectedSelection.selected?.value == variant.options?.[selectedSelection.field]?.value && variant.available);
|
|
453
|
+
}
|
|
454
|
+
const newValues = variants.data
|
|
455
|
+
.filter((variant) => variant.options[this.field])
|
|
456
|
+
.reduce((values, variant) => {
|
|
457
|
+
if (!values.some((val) => variant.options[this.field].value == val.value)) {
|
|
458
|
+
const value = variant.options[this.field].value;
|
|
459
|
+
const thumbnailImageUrl = variant.mappings.core?.thumbnailImageUrl;
|
|
460
|
+
// A value should only be disabled if there are NO available variants in the entire dataset that have this value for the current field
|
|
461
|
+
const allAvailableVariants = variants.data.filter((variant) => variant.available);
|
|
462
|
+
const disabledValue = !allAvailableVariants.some((availableVariant) => {
|
|
463
|
+
return availableVariant.options[this.field].value === value;
|
|
464
|
+
});
|
|
465
|
+
const mappedValue = {
|
|
466
|
+
value: value,
|
|
467
|
+
label: value,
|
|
468
|
+
thumbnailImageUrl: thumbnailImageUrl,
|
|
469
|
+
available: Boolean(availableVariants.some((availableVariant) => availableVariant.options[this.field].value == variant.options[this.field].value)),
|
|
470
|
+
disabled: disabledValue,
|
|
471
|
+
};
|
|
472
|
+
if (this.config.thumbnailBackgroundImages) {
|
|
473
|
+
mappedValue.backgroundImageUrl = thumbnailImageUrl;
|
|
474
|
+
}
|
|
475
|
+
else if (variant.options[this.field].backgroundImageUrl) {
|
|
476
|
+
mappedValue.backgroundImageUrl = variant.options[this.field].backgroundImageUrl;
|
|
477
|
+
}
|
|
478
|
+
if (variant.options[this.field].background) {
|
|
479
|
+
mappedValue.background = variant.options[this.field].background;
|
|
480
|
+
}
|
|
481
|
+
if (this.config.mappings && this.config.mappings && this.config.mappings[value.toString().toLowerCase()]) {
|
|
482
|
+
const mapping = this.config.mappings[value.toString().toLowerCase()];
|
|
483
|
+
if (mapping.label) {
|
|
484
|
+
mappedValue.label = mapping.label;
|
|
485
|
+
}
|
|
486
|
+
if (mapping.background) {
|
|
487
|
+
mappedValue.background = mapping.background;
|
|
488
|
+
}
|
|
489
|
+
if (mapping.backgroundImageUrl) {
|
|
490
|
+
mappedValue.backgroundImageUrl = mapping.backgroundImageUrl;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
values.push(mappedValue);
|
|
494
|
+
}
|
|
495
|
+
// TODO: use sorting function from config
|
|
496
|
+
return values;
|
|
497
|
+
}, []);
|
|
498
|
+
// if selection has been made
|
|
499
|
+
if (this.selected) {
|
|
500
|
+
// check if the selection is stil available
|
|
501
|
+
if (!newValues.some((val) => val.value == this.selected?.value && val.available)) {
|
|
502
|
+
// the selection is no longer available, attempt to select previous selection
|
|
503
|
+
if (this.selected !== this.previouslySelected &&
|
|
504
|
+
this.previouslySelected &&
|
|
505
|
+
newValues.some((val) => val.value == this.previouslySelected?.value && val.available)) {
|
|
506
|
+
this.select(this.previouslySelected.value, true);
|
|
507
|
+
}
|
|
508
|
+
else {
|
|
509
|
+
// choose the first available option if previous seletions are unavailable
|
|
510
|
+
const availableValues = newValues.filter((val) => val.available);
|
|
511
|
+
if (newValues.length && availableValues.length) {
|
|
512
|
+
const nextAvailableValue = availableValues[0].value;
|
|
513
|
+
if (this.selected.value !== nextAvailableValue) {
|
|
514
|
+
this.select(nextAvailableValue, true);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
this.values = newValues;
|
|
521
|
+
}
|
|
522
|
+
reset() {
|
|
523
|
+
this.selected = undefined;
|
|
524
|
+
this.values.forEach((val) => (val.available = false));
|
|
525
|
+
}
|
|
526
|
+
select(value, internalSelection = false) {
|
|
527
|
+
const valueExist = this.values.find((val) => val.value == value);
|
|
528
|
+
if (valueExist) {
|
|
529
|
+
if (!internalSelection) {
|
|
530
|
+
this.previouslySelected = this.selected;
|
|
531
|
+
}
|
|
532
|
+
this.selected = valueExist;
|
|
533
|
+
this.variantsUpdate();
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
export class Variant {
|
|
538
|
+
constructor(variantData) {
|
|
539
|
+
this.type = 'variant';
|
|
540
|
+
this.attributes = {};
|
|
541
|
+
this.mappings = {
|
|
542
|
+
core: {},
|
|
543
|
+
};
|
|
544
|
+
this.custom = {};
|
|
545
|
+
const { data } = variantData || {};
|
|
546
|
+
const { variant } = data || {};
|
|
547
|
+
this.attributes = variant.attributes || {};
|
|
548
|
+
this.mappings = variant.mappings;
|
|
549
|
+
this.options = variant.options;
|
|
550
|
+
// construct badges from data (need meta)
|
|
551
|
+
this.badges = variant.badges || [];
|
|
552
|
+
this.available = this.mappings.core?.available ?? true;
|
|
553
|
+
makeObservable(this, {
|
|
554
|
+
attributes: observable,
|
|
555
|
+
mappings: observable,
|
|
556
|
+
custom: observable,
|
|
557
|
+
available: observable,
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
function addBannersToResults(config, results, allBanners, paginationData) {
|
|
562
|
+
const bannersAndResults = [...results];
|
|
563
|
+
let paginationBegin = paginationData.pageSize * (paginationData.page - 1) + 1;
|
|
564
|
+
let paginationEnd = paginationData.pageSize * paginationData.page;
|
|
565
|
+
// if infinite scroll is enabled, we need to adjust the begin position
|
|
566
|
+
if (config?.settings?.infinite?.enabled)
|
|
567
|
+
paginationBegin = 1;
|
|
568
|
+
// if the end of the page is greater than the total results, adjust the end
|
|
569
|
+
if (paginationData.pageSize * paginationData.page > paginationData.totalResults)
|
|
570
|
+
paginationEnd = paginationData.totalResults;
|
|
571
|
+
// banners that have not allready been injected
|
|
572
|
+
const bannersNotInResults = allBanners.filter((banner) => !bannersAndResults.some((result) => result.id == banner.id));
|
|
573
|
+
// banners that have an index position within the current set of results
|
|
574
|
+
const bannersToInject = bannersNotInResults.filter((banner) => {
|
|
575
|
+
// find banners that are within the current pagination set
|
|
576
|
+
const index = banner.config.position.index;
|
|
577
|
+
return index >= paginationBegin - 1 && index <= paginationEnd - 1;
|
|
578
|
+
});
|
|
579
|
+
// banners can have an index greater than the total results, these should be injected at the end
|
|
580
|
+
const bannersToInjectAtEnd = bannersNotInResults.filter((banner) => {
|
|
581
|
+
const index = banner.config.position.index;
|
|
582
|
+
return index >= paginationData.totalResults;
|
|
583
|
+
});
|
|
584
|
+
// inject banners that have index position within current set into the results
|
|
585
|
+
bannersToInject.forEach((banner) => {
|
|
586
|
+
const adjustedIndex = banner.config.position.index - (paginationBegin - 1);
|
|
587
|
+
bannersAndResults.splice(adjustedIndex, 0, banner);
|
|
588
|
+
});
|
|
589
|
+
// inject banners at the end that fall within the current pagination set (but ensure there is room based on the begin and end indexes)
|
|
590
|
+
bannersToInjectAtEnd.forEach((banner, index) => {
|
|
591
|
+
const resultIndex = paginationData.totalResults - (bannersToInjectAtEnd.length - index);
|
|
592
|
+
if (resultIndex >= paginationBegin - 1 && resultIndex <= paginationEnd - 1) {
|
|
593
|
+
bannersAndResults.splice(resultIndex, 0, banner);
|
|
594
|
+
}
|
|
595
|
+
});
|
|
596
|
+
// when using infinite, need to adjust the inline banner responseIds
|
|
597
|
+
if (config?.settings?.infinite) {
|
|
598
|
+
bannersAndResults.forEach((item, index) => {
|
|
599
|
+
if (item.type === 'banner') {
|
|
600
|
+
// need to determine what the correctResponseId is based on the index position, pageSize (current page) and adjacent product responseIds
|
|
601
|
+
const pageSize = paginationData.pageSize;
|
|
602
|
+
const currentPage = Math.floor(index / pageSize) + 1;
|
|
603
|
+
const firstItemIndexOnPage = (currentPage - 1) * pageSize;
|
|
604
|
+
const lastItemIndexOnPage = firstItemIndexOnPage + pageSize - 1;
|
|
605
|
+
// find the first product on the current page to grab its responseId
|
|
606
|
+
for (let i = firstItemIndexOnPage; i < lastItemIndexOnPage; i++) {
|
|
607
|
+
if (bannersAndResults[i].type === 'product') {
|
|
608
|
+
item.responseId = bannersAndResults[i].responseId;
|
|
609
|
+
break;
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
});
|
|
614
|
+
}
|
|
615
|
+
return bannersAndResults;
|
|
616
|
+
}
|
|
617
|
+
function variantOptionClick(elem, variantConfig, results) {
|
|
618
|
+
const options = {};
|
|
619
|
+
const attr = elem.getAttribute(VARIANT_ATTRIBUTE);
|
|
620
|
+
if (attr) {
|
|
621
|
+
const [option, value] = attr.split(':');
|
|
622
|
+
if (!option || !value) {
|
|
623
|
+
console.error('Error!: realtime variant is missing option or value (option:value)!', elem, attr);
|
|
624
|
+
}
|
|
625
|
+
else {
|
|
626
|
+
options[option.toLowerCase()] = [value.toLowerCase()];
|
|
627
|
+
makeVariantSelections(variantConfig, options, results);
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
function makeVariantSelections(variantConfig, options, results) {
|
|
632
|
+
let filteredResults = results;
|
|
633
|
+
// filter based on config
|
|
634
|
+
variantConfig.realtime?.filters?.forEach((filter) => {
|
|
635
|
+
if (filter == 'first') {
|
|
636
|
+
filteredResults = [filteredResults[0]];
|
|
637
|
+
}
|
|
638
|
+
if (filter == 'unaltered') {
|
|
639
|
+
filteredResults = filteredResults.filter((result) => !result.variants?.selections.some((selection) => selection.previouslySelected));
|
|
640
|
+
}
|
|
641
|
+
});
|
|
642
|
+
filteredResults.forEach((result) => {
|
|
643
|
+
// no banner types
|
|
644
|
+
if (result.type == 'product') {
|
|
645
|
+
result.variants?.makeSelections(options);
|
|
646
|
+
}
|
|
647
|
+
});
|
|
648
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { UrlManager } from '@athoscommerce/snap-url-manager';
|
|
2
|
+
import type { StoreServices } from '../../types';
|
|
3
|
+
import type { MetaResponseModel, MetaResponseModelSortOption, SearchResponseModel } from '@athoscommerce/snapi-types';
|
|
4
|
+
type MetaResponseModelSortOptionMutated = MetaResponseModelSortOption & {
|
|
5
|
+
active?: boolean;
|
|
6
|
+
default?: boolean;
|
|
7
|
+
};
|
|
8
|
+
type SearchSortingStoreConfig = {
|
|
9
|
+
services: StoreServices;
|
|
10
|
+
data: {
|
|
11
|
+
search?: SearchResponseModel;
|
|
12
|
+
meta: MetaResponseModel;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
export declare class SearchSortingStore {
|
|
16
|
+
options: Option[];
|
|
17
|
+
constructor(params: SearchSortingStoreConfig);
|
|
18
|
+
get current(): Option | undefined;
|
|
19
|
+
}
|
|
20
|
+
declare class Option {
|
|
21
|
+
active: boolean;
|
|
22
|
+
default: boolean;
|
|
23
|
+
field: string;
|
|
24
|
+
label: string;
|
|
25
|
+
direction: string;
|
|
26
|
+
type: string;
|
|
27
|
+
value: string;
|
|
28
|
+
url: UrlManager;
|
|
29
|
+
constructor(services: StoreServices, option: MetaResponseModelSortOptionMutated, index: number);
|
|
30
|
+
}
|
|
31
|
+
export {};
|
|
32
|
+
//# sourceMappingURL=SearchSortingStore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SearchSortingStore.d.ts","sourceRoot":"","sources":["../../../../src/Search/Stores/SearchSortingStore.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAClE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,KAAK,EAAE,iBAAiB,EAAE,2BAA2B,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAEtH,KAAK,kCAAkC,GAAG,2BAA2B,GAAG;IACvE,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF,KAAK,wBAAwB,GAAG;IAC/B,QAAQ,EAAE,aAAa,CAAC;IACxB,IAAI,EAAE;QACL,MAAM,CAAC,EAAE,mBAAmB,CAAC;QAC7B,IAAI,EAAE,iBAAiB,CAAC;KACxB,CAAC;CACF,CAAC;AAEF,qBAAa,kBAAkB;IACvB,OAAO,EAAE,MAAM,EAAE,CAAM;gBAElB,MAAM,EAAE,wBAAwB;IA0C5C,IAAW,OAAO,IAAI,MAAM,GAAG,SAAS,CAEvC;CACD;AAED,cAAM,MAAM;IACJ,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,UAAU,CAAC;gBAEX,QAAQ,EAAE,aAAa,EAAE,MAAM,EAAE,kCAAkC,EAAE,KAAK,EAAE,MAAM;CAuB9F"}
|