@depict-ai/react-ui 1.5.20 → 2.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/ES10/index.cjs +149 -69
- package/ES10/index.d.ts +547 -37
- package/ES10/index.js +152 -74
- package/README.md +2 -1
- package/dist/main.js +293 -0
- package/dist/module.js +285 -0
- package/dist/styles/components/PLP/Buttons.scss +124 -0
- package/dist/styles/components/PLP/MorphingSign.scss +31 -0
- package/dist/styles/components/PLP/PLPResults.scss +20 -0
- package/dist/styles/components/PLP/ProductListingPage.scss +142 -0
- package/dist/styles/components/PLP/ScrollStatus.scss +24 -0
- package/dist/styles/components/PLP/SelectedFilters.scss +74 -0
- package/dist/styles/components/PLP/SlidableItems.scss +99 -0
- package/dist/styles/components/PLP/SortAndFilter/FloatingButtons.scss +75 -0
- package/dist/styles/components/PLP/SortAndFilter/SortAndFilter.scss +728 -0
- package/dist/styles/components/PLP/SortAndFilter/SortAndFilterModal.scss +160 -0
- package/dist/styles/components/PLP/SortAndFilter/WhiteSortAndFilter.scss +148 -0
- package/dist/styles/components/PLP/Toast.scss +54 -0
- package/dist/styles/components/PLP/index.scss +45 -0
- package/dist/styles/components/category/BreadCrumbs.scss +23 -0
- package/dist/styles/components/category/CategoryTitle.scss +28 -0
- package/dist/styles/components/category/QuickLinks.scss +71 -0
- package/dist/styles/components/category/index.scss +23 -0
- package/dist/styles/components/search/Autocomplete.scss +117 -0
- package/dist/styles/components/search/CategorySuggestions.scss +74 -0
- package/dist/styles/components/search/InstantCard.scss +117 -0
- package/dist/styles/components/search/InstantResults.scss +10 -0
- package/dist/styles/components/search/PreviousSearches.scss +77 -0
- package/dist/styles/components/search/QueryInput.scss +167 -0
- package/dist/styles/components/search/SearchModal.scss +95 -0
- package/dist/styles/components/search/SearchRecommendations.scss +25 -0
- package/dist/styles/components/search/ShowingResultsFor.scss +10 -0
- package/dist/styles/components/search/index.scss +40 -0
- package/dist/styles/default.css +1 -0
- package/dist/styles/helpers/custom_input.scss +162 -0
- package/dist/styles/helpers/supports-hover.scss +5 -0
- package/dist/styles/helpers/utils.scss +82 -0
- package/dist/styles/plain-css-fallback-styling.css +1 -1
- package/dist/styles/variables.scss +47 -0
- package/latest/index.cjs +145 -68
- package/latest/index.d.ts +547 -37
- package/latest/index.js +148 -73
- package/package.json +9 -9
- package/server/ES10/index.cjs +125 -1
- package/server/ES10/index.d.ts +547 -37
- package/server/ES10/index.js +127 -5
- package/server/latest/index.cjs +122 -1
- package/server/latest/index.d.ts +547 -37
- package/server/latest/index.js +124 -5
package/dist/module.js
ADDED
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
import {SearchField as $TZSbU$SearchField, DepictSearch as $TZSbU$DepictSearch, swedish_search_translation as $TZSbU$swedish_search_translation, get_search_query_updating_blocked_signal as $TZSbU$get_search_query_updating_blocked_signal, SearchPage as $TZSbU$SearchPage} from "@depict-ai/ui";
|
|
2
|
+
import {href_change_ipns as $TZSbU$href_change_ipns, observer as $TZSbU$observer, history_replacer as $TZSbU$history_replacer, queueMacroTask as $TZSbU$queueMacroTask} from "@depict-ai/utilishared";
|
|
3
|
+
import {untrack as $TZSbU$untrack, createComponent as $TZSbU$createComponent, Show as $TZSbU$Show, createSignal as $TZSbU$createSignal, createMemo as $TZSbU$createMemo, createComputed as $TZSbU$createComputed, onCleanup as $TZSbU$onCleanup} from "solid-js";
|
|
4
|
+
import {render as $TZSbU$render} from "solid-js/web";
|
|
5
|
+
import {useState as $TZSbU$useState, useRef as $TZSbU$useRef, useEffect as $TZSbU$useEffect, createElement as $TZSbU$createElement, Fragment as $TZSbU$Fragment} from "react";
|
|
6
|
+
import {jsx as $TZSbU$jsx} from "react/jsx-runtime";
|
|
7
|
+
import {createPortal as $TZSbU$createPortal} from "react-dom";
|
|
8
|
+
|
|
9
|
+
// This is where the actual source of your React stuff should live
|
|
10
|
+
// Note, if you add an export here, also add one in src-server/main.ts
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
const $42858f023ef23dab$export$8e769ad4338a911e = {
|
|
14
|
+
search: null,
|
|
15
|
+
header_alignment: null,
|
|
16
|
+
search_page_path_: (0, $TZSbU$createSignal)(),
|
|
17
|
+
required_state_set_: (0, $TZSbU$createSignal)(false)
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
let $c83ba9ab79386687$var$run_on_href_change;
|
|
26
|
+
function $c83ba9ab79386687$export$74f3924c5027fe1d(cb) {
|
|
27
|
+
$c83ba9ab79386687$var$run_on_href_change = cb;
|
|
28
|
+
}
|
|
29
|
+
(async ()=>{
|
|
30
|
+
for await (const href of (0, $TZSbU$href_change_ipns))$c83ba9ab79386687$var$run_on_href_change?.(href);
|
|
31
|
+
})();
|
|
32
|
+
const $c83ba9ab79386687$export$6cb4d3a5c1ac9b43 = (url)=>url.pathname == (0, $TZSbU$untrack)((0, $42858f023ef23dab$export$8e769ad4338a911e).search_page_path_[0]); // might have changed therefore get it from globalState
|
|
33
|
+
function $c83ba9ab79386687$export$f6e64e17bd69eeef(solid_component, props) {
|
|
34
|
+
const [portals, set_portals] = (0, $TZSbU$useState)([]);
|
|
35
|
+
const element_ref = (0, $TZSbU$useRef)();
|
|
36
|
+
(0, $TZSbU$useEffect)(()=>{
|
|
37
|
+
const div_element = element_ref.current;
|
|
38
|
+
const dispose = (0, $TZSbU$render)(()=>solid_component(props, set_portals), div_element);
|
|
39
|
+
return dispose;
|
|
40
|
+
}, []);
|
|
41
|
+
return (0, $TZSbU$jsx)("div", {
|
|
42
|
+
ref: element_ref,
|
|
43
|
+
children: portals
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
function $c83ba9ab79386687$export$e5139bbbb9a05035(component) {
|
|
47
|
+
return (...args)=>{
|
|
48
|
+
return (0, $TZSbU$createComponent)((0, $TZSbU$Show), {
|
|
49
|
+
get when () {
|
|
50
|
+
return (0, $42858f023ef23dab$export$8e769ad4338a911e).required_state_set_[0]();
|
|
51
|
+
},
|
|
52
|
+
get children () {
|
|
53
|
+
return component(...args);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
function $54b0d21b77e719c4$export$d2c02478244f91f7() {
|
|
63
|
+
return (0, $c83ba9ab79386687$export$f6e64e17bd69eeef)((0, $c83ba9ab79386687$export$e5139bbbb9a05035)(()=>(0, $TZSbU$SearchField)({
|
|
64
|
+
depict_search: (0, $42858f023ef23dab$export$8e769ad4338a911e).search
|
|
65
|
+
})), {});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
let $8071251ee4e9ffa1$var$set = false;
|
|
73
|
+
function $8071251ee4e9ffa1$export$6453cbc34515a94d(is_search_page) {
|
|
74
|
+
if ($8071251ee4e9ffa1$var$set) return; // Just being safe
|
|
75
|
+
$8071251ee4e9ffa1$var$set = true;
|
|
76
|
+
let should_use_old_scroll = false;
|
|
77
|
+
const old_scrollTo = window.scrollTo;
|
|
78
|
+
window.scrollTo = (...args)=>{
|
|
79
|
+
if (should_use_old_scroll || process.env.__NEXT_SCROLL_RESTORATION || !is_search_page(location)) return Reflect.apply(old_scrollTo, window, args);
|
|
80
|
+
else should_use_old_scroll = true;
|
|
81
|
+
};
|
|
82
|
+
addEventListener("popstate", (e)=>{
|
|
83
|
+
if (!is_search_page(location)) {
|
|
84
|
+
$8071251ee4e9ffa1$var$removeLotsOfPadding(); // Just making sure
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
should_use_old_scroll = false;
|
|
88
|
+
$8071251ee4e9ffa1$var$addLotsOfPadding();
|
|
89
|
+
(0, $TZSbU$observer).wait_for_element(".depict.plp .listing-page").then(()=>{
|
|
90
|
+
$8071251ee4e9ffa1$var$removeLotsOfPadding();
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
(0, $TZSbU$history_replacer)(()=>{
|
|
94
|
+
(0, $TZSbU$queueMacroTask)(()=>{
|
|
95
|
+
$8071251ee4e9ffa1$var$removeLotsOfPadding();
|
|
96
|
+
});
|
|
97
|
+
}, false);
|
|
98
|
+
window?.next?.router?.events?.on("routeChangeStart", ()=>{
|
|
99
|
+
$8071251ee4e9ffa1$var$addLotsOfPadding();
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
// TODO: Make these animations so they don't collide with whatever padding is already on the body
|
|
103
|
+
function $8071251ee4e9ffa1$var$addLotsOfPadding() {
|
|
104
|
+
document.body.style.paddingBottom = (history.state?.search_scroll_restoration_data?.[0]?.min_results_loaded || 150) * 500 + "px";
|
|
105
|
+
}
|
|
106
|
+
function $8071251ee4e9ffa1$var$removeLotsOfPadding() {
|
|
107
|
+
document.body.style.paddingBottom = "";
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
function $faf794495ff8b304$export$9fac37a9c21c2f3b(props) {
|
|
115
|
+
if (!props.market || !props.merchant) {
|
|
116
|
+
(0, $42858f023ef23dab$export$8e769ad4338a911e).required_state_set_[1](false);
|
|
117
|
+
return (0, $TZSbU$createElement)((0, $TZSbU$Fragment), null, props.children);
|
|
118
|
+
}
|
|
119
|
+
const { children: children , search_page_path: search_page_path , url_param_name: url_param_name , ...assignable_options } = props;
|
|
120
|
+
const { search: search } = (0, $42858f023ef23dab$export$8e769ad4338a911e);
|
|
121
|
+
const [get_search_page_path, set_search_page_path] = (0, $42858f023ef23dab$export$8e769ad4338a911e).search_page_path_;
|
|
122
|
+
set_search_page_path(search_page_path);
|
|
123
|
+
if (!search) {
|
|
124
|
+
(0, $42858f023ef23dab$export$8e769ad4338a911e).search = new (0, $TZSbU$DepictSearch)({
|
|
125
|
+
search_query_url_param_name: url_param_name,
|
|
126
|
+
...assignable_options,
|
|
127
|
+
enable_category_suggestions: false,
|
|
128
|
+
localization: {
|
|
129
|
+
...(0, $TZSbU$swedish_search_translation),
|
|
130
|
+
price_formatting_: {
|
|
131
|
+
pre_: "kr ",
|
|
132
|
+
post_: "",
|
|
133
|
+
decimal_places_delimiter_: ",",
|
|
134
|
+
thousands_delimiter_: " ",
|
|
135
|
+
places_after_comma_: 2
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
on_navigation: (new_href)=>{
|
|
139
|
+
const shallow = (0, $c83ba9ab79386687$export$6cb4d3a5c1ac9b43)(location);
|
|
140
|
+
if (window.next) window.next.router.push(new_href, undefined, {
|
|
141
|
+
shallow: shallow,
|
|
142
|
+
scroll: true
|
|
143
|
+
});
|
|
144
|
+
else location.href = new_href;
|
|
145
|
+
},
|
|
146
|
+
url_transformer: (url)=>url.pathname = (0, $TZSbU$untrack)(get_search_page_path)
|
|
147
|
+
});
|
|
148
|
+
(0, $8071251ee4e9ffa1$export$6453cbc34515a94d)((0, $c83ba9ab79386687$export$6cb4d3a5c1ac9b43));
|
|
149
|
+
} else Object.assign(search, assignable_options);
|
|
150
|
+
(0, $42858f023ef23dab$export$8e769ad4338a911e).required_state_set_[1](true);
|
|
151
|
+
return (0, $TZSbU$createElement)((0, $TZSbU$Fragment), null, children);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
// PoC maybe-component
|
|
156
|
+
// Idea is to always give a nice "modal input in header" look with no more config than adding this component to the page header to where it can absolute-size
|
|
157
|
+
// and cover the entire height of the header. Then we know where to put the modal input top and bottom, and the left and right we just center.
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
function $f41a371216eff80b$export$cbcd1ecdb41b1d0a() {
|
|
162
|
+
return (0, $TZSbU$createElement)("div", {
|
|
163
|
+
className: "depict aligner",
|
|
164
|
+
ref: (e)=>(0, $42858f023ef23dab$export$8e769ad4338a911e).header_alignment = e
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
let $f41a371216eff80b$var$sheet;
|
|
168
|
+
function $f41a371216eff80b$export$a7f6cffb6b8ba11c() {
|
|
169
|
+
if (!(0, $42858f023ef23dab$export$8e769ad4338a911e).search) {
|
|
170
|
+
console.error("No active search in the app!");
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
if (!(0, $42858f023ef23dab$export$8e769ad4338a911e).header_alignment) {
|
|
174
|
+
console.error("No active search header aligner on the page!");
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
const style_props_ = (0, $TZSbU$createSignal)({
|
|
178
|
+
position: "fixed",
|
|
179
|
+
top: 0
|
|
180
|
+
});
|
|
181
|
+
if ((0, $42858f023ef23dab$export$8e769ad4338a911e).header_alignment?.clientHeight) {
|
|
182
|
+
let styles = `.depict.plp.in-header .depict-search-modal .query-input { height: ${(0, $42858f023ef23dab$export$8e769ad4338a911e).header_alignment?.clientHeight}px; }`;
|
|
183
|
+
styles += `.depict.plp.in-header .depict-search-modal-backdrop { top: ${(0, $42858f023ef23dab$export$8e769ad4338a911e).header_alignment?.clientHeight}px; }`;
|
|
184
|
+
if (!$f41a371216eff80b$var$sheet) {
|
|
185
|
+
const style = document.createElement("style");
|
|
186
|
+
style.append(styles);
|
|
187
|
+
document.head.appendChild(style);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
const class_list_ = (0, $TZSbU$createMemo)(()=>({
|
|
191
|
+
"in-header": true
|
|
192
|
+
}));
|
|
193
|
+
(0, $42858f023ef23dab$export$8e769ad4338a911e).search.modal_open = [
|
|
194
|
+
{
|
|
195
|
+
style_props_: style_props_,
|
|
196
|
+
class_list_: class_list_
|
|
197
|
+
}
|
|
198
|
+
];
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
function $9f8d5ef3c016c493$export$210723f5a54a7028(opts) {
|
|
209
|
+
return (0, $c83ba9ab79386687$export$f6e64e17bd69eeef)((0, $c83ba9ab79386687$export$e5139bbbb9a05035)($9f8d5ef3c016c493$var$SolidSearchPage), opts);
|
|
210
|
+
}
|
|
211
|
+
function $9f8d5ef3c016c493$var$SolidSearchPage(props, set_portals) {
|
|
212
|
+
const depict_search = (0, $42858f023ef23dab$export$8e769ad4338a911e).search;
|
|
213
|
+
if (!depict_search) throw Error("No depict_search instance when rendering SearchPage.");
|
|
214
|
+
const search_query_updating_blocked_signal = (0, $TZSbU$get_search_query_updating_blocked_signal)(depict_search);
|
|
215
|
+
if (!search_query_updating_blocked_signal) throw Error("No search_query_updating_blocked_signal when rendering SearchPage");
|
|
216
|
+
const [, set_search_query_updating_blocked_signal] = search_query_updating_blocked_signal;
|
|
217
|
+
const update_updating_blocked_signal = ()=>{
|
|
218
|
+
set_search_query_updating_blocked_signal(!(0, $c83ba9ab79386687$export$6cb4d3a5c1ac9b43)(location));
|
|
219
|
+
};
|
|
220
|
+
// react is really slow with disposing components, we want to avoid a flash of another search query when you click on a product and navigate away
|
|
221
|
+
(0, $c83ba9ab79386687$export$74f3924c5027fe1d)(update_updating_blocked_signal); // if we navigate, re-check
|
|
222
|
+
(0, $TZSbU$createComputed)(()=>{
|
|
223
|
+
(0, $42858f023ef23dab$export$8e769ad4338a911e).search_page_path_[0](); // if react defined path changes, re-check
|
|
224
|
+
update_updating_blocked_signal();
|
|
225
|
+
});
|
|
226
|
+
const component_portals = new Set();
|
|
227
|
+
return (0, $TZSbU$SearchPage)({
|
|
228
|
+
// Default config goes here
|
|
229
|
+
include_input_field: props.includeInputField ?? true,
|
|
230
|
+
cols_at_size: props.columnsAtSize ?? [
|
|
231
|
+
[
|
|
232
|
+
4
|
|
233
|
+
],
|
|
234
|
+
[
|
|
235
|
+
3,
|
|
236
|
+
1024
|
|
237
|
+
],
|
|
238
|
+
[
|
|
239
|
+
2,
|
|
240
|
+
901
|
|
241
|
+
]
|
|
242
|
+
],
|
|
243
|
+
grid_spacing: props.gridSpacing ?? "2%",
|
|
244
|
+
// Todo: Do we need this?
|
|
245
|
+
get_product_id: ()=>"",
|
|
246
|
+
depict_search: depict_search,
|
|
247
|
+
product_card_template: async (display, info)=>{
|
|
248
|
+
const solid_container = document.createElement("div");
|
|
249
|
+
const react_has_rendered = new Promise((r)=>{
|
|
250
|
+
const m_o = new MutationObserver(()=>{
|
|
251
|
+
m_o.disconnect();
|
|
252
|
+
r();
|
|
253
|
+
});
|
|
254
|
+
m_o.observe(solid_container, {
|
|
255
|
+
childList: true
|
|
256
|
+
});
|
|
257
|
+
});
|
|
258
|
+
const is_placeholder = info.rendering_options.type === "placeholder";
|
|
259
|
+
const reactProductCard = (0, $TZSbU$jsx)(props.productCard, {
|
|
260
|
+
display: is_placeholder ? null : display
|
|
261
|
+
});
|
|
262
|
+
const portal = $TZSbU$createPortal(reactProductCard, solid_container);
|
|
263
|
+
component_portals.add(portal);
|
|
264
|
+
set_portals([
|
|
265
|
+
...component_portals
|
|
266
|
+
]);
|
|
267
|
+
(0, $TZSbU$onCleanup)(()=>{
|
|
268
|
+
// this will stop working after any await so be careful to not await before this, or you'll have to carry the owner over with getOwner and runWithOwner
|
|
269
|
+
component_portals.delete(portal);
|
|
270
|
+
set_portals([
|
|
271
|
+
...component_portals
|
|
272
|
+
]);
|
|
273
|
+
});
|
|
274
|
+
await react_has_rendered; // wait for react to have finish portaling the elements so that the placeholders get removed once we have an actual result and so that the PLP code knows it has a placeholder when this is a placeholder rendering
|
|
275
|
+
return [
|
|
276
|
+
solid_container
|
|
277
|
+
];
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
export {$54b0d21b77e719c4$export$d2c02478244f91f7 as DepictSearchField, $faf794495ff8b304$export$9fac37a9c21c2f3b as DepictProvider, $f41a371216eff80b$export$a7f6cffb6b8ba11c as openModal, $f41a371216eff80b$export$cbcd1ecdb41b1d0a as DepictSearchHeader, $9f8d5ef3c016c493$export$210723f5a54a7028 as DepictSearchPage};
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
@use "../../variables";
|
|
2
|
+
@use "../../helpers/supports-hover";
|
|
3
|
+
@use "../../helpers/utils";
|
|
4
|
+
@use "sass:color";
|
|
5
|
+
|
|
6
|
+
$primary-button-background: variables.$brand-black;
|
|
7
|
+
$primary-button-color: #fff;
|
|
8
|
+
$primary-button-border: 1px solid transparent; // so that the page doesn't jump when there's half a border when switching between having sort and filters open (the buttons switch between pimary and secondary)
|
|
9
|
+
$primary-button-border-radius: variables.$border-radius;
|
|
10
|
+
$primary-button-hover-filter: drop-shadow(-2px -2px 4px rgba(255, 255, 255, 0.25))
|
|
11
|
+
drop-shadow(2px 2px 4px rgba(0, 0, 0, 0.15));
|
|
12
|
+
$primary-button-active-filter: drop-shadow(2px 2px 4px rgba(255, 255, 255, 0.25))
|
|
13
|
+
drop-shadow(-2px -2px 4px rgba(0, 0, 0, 0.15));
|
|
14
|
+
$primary-button-focus-visible-filter: drop-shadow(2px 2px 4px rgba(65, 143, 195, 0.8))
|
|
15
|
+
drop-shadow(-2px -2px 4px rgba(65, 143, 195, 0.8));
|
|
16
|
+
|
|
17
|
+
$secondary-button-background: variables.$brand-lightest-grey;
|
|
18
|
+
$secondary-button-color: variables.$brand-dark-grey;
|
|
19
|
+
$secondary-button-border: 1px solid variables.$brand-black;
|
|
20
|
+
$secondary-button-border-radius: variables.$border-radius;
|
|
21
|
+
$secondary-button-hover-filter: drop-shadow(-2px -2px 4px rgba(255, 255, 255, 0.25))
|
|
22
|
+
drop-shadow(2px 2px 4px rgba(0, 0, 0, 0.15));
|
|
23
|
+
$secondary-button-active-filter: drop-shadow(2px 2px 4px rgba(255, 255, 255, 0.25))
|
|
24
|
+
drop-shadow(-2px -2px 4px rgba(0, 0, 0, 0.15));
|
|
25
|
+
$secondary-button-focus-visible-filter: drop-shadow(-2px -2px 4px rgba(65, 143, 195, 0.8))
|
|
26
|
+
drop-shadow(2px 2px 4px rgba(65, 143, 195, 0.8));
|
|
27
|
+
|
|
28
|
+
@mixin PrimaryButton(
|
|
29
|
+
$background: $primary-button-background,
|
|
30
|
+
$color: $primary-button-color,
|
|
31
|
+
$border: $primary-button-border,
|
|
32
|
+
$border-radius: $primary-button-border-radius,
|
|
33
|
+
$hover-filter: $primary-button-hover-filter,
|
|
34
|
+
$active-filter: $primary-button-active-filter,
|
|
35
|
+
$focus-visible-filter: $primary-button-focus-visible-filter
|
|
36
|
+
) {
|
|
37
|
+
// not using .primary since that's 100% going to conflict with existing declarations on sites
|
|
38
|
+
button.major {
|
|
39
|
+
@include Button(
|
|
40
|
+
$background,
|
|
41
|
+
$color,
|
|
42
|
+
$border,
|
|
43
|
+
$border-radius,
|
|
44
|
+
$hover-filter,
|
|
45
|
+
$active-filter,
|
|
46
|
+
$focus-visible-filter
|
|
47
|
+
) {
|
|
48
|
+
@content;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
@mixin SecondaryButton(
|
|
54
|
+
$background: $secondary-button-background,
|
|
55
|
+
$color: $secondary-button-color,
|
|
56
|
+
$border: $secondary-button-border,
|
|
57
|
+
$border-radius: $secondary-button-border-radius,
|
|
58
|
+
$hover-filter: $secondary-button-hover-filter,
|
|
59
|
+
$active-filter: $secondary-button-active-filter,
|
|
60
|
+
$focus-visible-filter: $secondary-button-focus-visible-filter
|
|
61
|
+
) {
|
|
62
|
+
// not using .secondary since that's 100% going to conflict with existing declarations on sites
|
|
63
|
+
button.minor {
|
|
64
|
+
@include Button(
|
|
65
|
+
$background,
|
|
66
|
+
$color,
|
|
67
|
+
$border,
|
|
68
|
+
$border-radius,
|
|
69
|
+
$hover-filter,
|
|
70
|
+
$active-filter,
|
|
71
|
+
$focus-visible-filter
|
|
72
|
+
) {
|
|
73
|
+
@content;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
@mixin Button(
|
|
79
|
+
$background,
|
|
80
|
+
$color,
|
|
81
|
+
$border,
|
|
82
|
+
$border-radius,
|
|
83
|
+
$hover-filter,
|
|
84
|
+
$active-filter,
|
|
85
|
+
$focus-visible-filter,
|
|
86
|
+
$padding: 10px 20px
|
|
87
|
+
) {
|
|
88
|
+
background: $background;
|
|
89
|
+
color: $color;
|
|
90
|
+
border: $border;
|
|
91
|
+
border-radius: $border-radius;
|
|
92
|
+
@if $padding {
|
|
93
|
+
padding: $padding;
|
|
94
|
+
}
|
|
95
|
+
display: flex;
|
|
96
|
+
white-space: pre-wrap; // can't have spaces otherwise
|
|
97
|
+
justify-content: center;
|
|
98
|
+
align-items: center;
|
|
99
|
+
cursor: pointer;
|
|
100
|
+
transition: filter 150ms ease-in-out, background 100ms ease-in-out;
|
|
101
|
+
|
|
102
|
+
svg path {
|
|
103
|
+
fill: $color; // treat icons the same as text
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
@include supports-hover.supports-hover() {
|
|
107
|
+
&:hover {
|
|
108
|
+
filter: $hover-filter;
|
|
109
|
+
@include utils.highlight-background($normal-background: $background);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
&:active {
|
|
113
|
+
filter: $active-filter;
|
|
114
|
+
@include utils.highlight-background($normal-background: $background, $darken-factor: 10%, $lighten-factor: 20%);
|
|
115
|
+
}
|
|
116
|
+
&:focus {
|
|
117
|
+
outline-style: none;
|
|
118
|
+
}
|
|
119
|
+
&:focus-visible {
|
|
120
|
+
filter: $focus-visible-filter;
|
|
121
|
+
@include utils.highlight-background($normal-background: $background);
|
|
122
|
+
}
|
|
123
|
+
@content;
|
|
124
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
@mixin MorphingSign($color: #fff) {
|
|
2
|
+
.morphing-sign {
|
|
3
|
+
/* https://stackoverflow.com/a/38136283 */
|
|
4
|
+
border: 0;
|
|
5
|
+
font-size: 1.5em;
|
|
6
|
+
position: relative;
|
|
7
|
+
width: 1em;
|
|
8
|
+
height: 1em;
|
|
9
|
+
|
|
10
|
+
span {
|
|
11
|
+
position: absolute;
|
|
12
|
+
background: $color;
|
|
13
|
+
border-radius: 2px;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/* Create the "+" shape by positioning the spans absolutely */
|
|
17
|
+
span:first-child {
|
|
18
|
+
top: 25%;
|
|
19
|
+
bottom: 25%;
|
|
20
|
+
width: 10%;
|
|
21
|
+
left: 45%;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
span:last-child {
|
|
25
|
+
left: 25%;
|
|
26
|
+
right: 25%;
|
|
27
|
+
height: 10%;
|
|
28
|
+
top: 45%;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
@use "../../variables";
|
|
2
|
+
@use "../../helpers/utils";
|
|
3
|
+
|
|
4
|
+
@mixin PLPResults() {
|
|
5
|
+
.listing-page {
|
|
6
|
+
.no-results {
|
|
7
|
+
background: white;
|
|
8
|
+
padding: 40px;
|
|
9
|
+
display: flex;
|
|
10
|
+
justify-content: flex-start;
|
|
11
|
+
align-items: center;
|
|
12
|
+
flex-direction: column;
|
|
13
|
+
flex-grow: 2;
|
|
14
|
+
.hint {
|
|
15
|
+
text-decoration: underline;
|
|
16
|
+
cursor: pointer;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
@use "../../helpers/utils";
|
|
2
|
+
|
|
3
|
+
@mixin ProductListingPage($spacing-to-filters: 12px, $desktop-item-gap: 20px, $mobile-item-gap: 16px) {
|
|
4
|
+
.listing-page {
|
|
5
|
+
display: flex;
|
|
6
|
+
flex-direction: column;
|
|
7
|
+
padding: $desktop-item-gap calc(#{$desktop-item-gap} / 2);
|
|
8
|
+
@include mobile_sizes() {
|
|
9
|
+
padding: $mobile-item-gap calc(#{$mobile-item-gap} / 2);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
&,
|
|
13
|
+
& * {
|
|
14
|
+
box-sizing: border-box;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
& > .filter-results-for-one-row,
|
|
18
|
+
& > .results-for {
|
|
19
|
+
margin-bottom: calc(#{$desktop-item-gap} / 2);
|
|
20
|
+
@include mobile_sizes() {
|
|
21
|
+
margin-bottom: calc(#{$mobile-item-gap} / 2);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
& > .sort-and-filter-buttons {
|
|
26
|
+
margin-top: calc(#{$desktop-item-gap} / 2);
|
|
27
|
+
margin-bottom: calc(#{$desktop-item-gap} / 2);
|
|
28
|
+
@include mobile_sizes() {
|
|
29
|
+
margin-top: calc(#{$mobile-item-gap} / 2);
|
|
30
|
+
margin-bottom: calc(#{$mobile-item-gap} / 2);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
.PLP-results {
|
|
34
|
+
margin-top: calc(#{$desktop-item-gap} / 2);
|
|
35
|
+
@include mobile_sizes() {
|
|
36
|
+
margin-top: calc(#{$mobile-item-gap} / 2);
|
|
37
|
+
}
|
|
38
|
+
display: flex;
|
|
39
|
+
& > {
|
|
40
|
+
.filters,
|
|
41
|
+
.sorting {
|
|
42
|
+
margin-left: $spacing-to-filters;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
.cards {
|
|
47
|
+
// this is for all product lists, meaning PLPResults (category + search product lists) and SearchRecommendations
|
|
48
|
+
flex-wrap: wrap;
|
|
49
|
+
display: flex;
|
|
50
|
+
flex-direction: row;
|
|
51
|
+
flex-grow: 1;
|
|
52
|
+
overflow: hidden;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
@mixin SearchPage(
|
|
58
|
+
$spacing-to-filters: 12px,
|
|
59
|
+
$desktop-item-gap: 20px,
|
|
60
|
+
$mobile-item-gap: 16px,
|
|
61
|
+
$spacing-above-search-field: true,
|
|
62
|
+
$query-input-width: 800px,
|
|
63
|
+
$gap-to-recs-factor: 3
|
|
64
|
+
) {
|
|
65
|
+
.listing-page {
|
|
66
|
+
.query-input {
|
|
67
|
+
width: min(100%, $query-input-width);
|
|
68
|
+
align-self: center;
|
|
69
|
+
|
|
70
|
+
@if $spacing-above-search-field {
|
|
71
|
+
margin-top: calc(#{$desktop-item-gap} * 2);
|
|
72
|
+
}
|
|
73
|
+
margin-bottom: $desktop-item-gap;
|
|
74
|
+
@include mobile_sizes() {
|
|
75
|
+
margin-bottom: $mobile-item-gap;
|
|
76
|
+
@if $spacing-above-search-field {
|
|
77
|
+
margin-top: calc(#{$mobile-item-gap});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
~ .category-suggestions .category-suggestion {
|
|
81
|
+
&:last-child {
|
|
82
|
+
margin-right: auto;
|
|
83
|
+
}
|
|
84
|
+
&:first-child {
|
|
85
|
+
margin-left: auto;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
.category-suggestions {
|
|
90
|
+
margin-bottom: $desktop-item-gap;
|
|
91
|
+
@include mobile_sizes() {
|
|
92
|
+
margin-bottom: $mobile-item-gap;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
.query-input {
|
|
96
|
+
// if there are no category suggestions we should have 2x the gap between where they should be and what's there, only for desktop tho
|
|
97
|
+
& + .filter-results-for-one-row,
|
|
98
|
+
& + .results-for {
|
|
99
|
+
@media (min-width: 652px) {
|
|
100
|
+
margin-top: $desktop-item-gap;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
.search-recs {
|
|
105
|
+
// whole gap here so that we don't have to make the margin bottom of search-results conditional
|
|
106
|
+
margin-top: calc(#{$desktop-item-gap} * #{$gap-to-recs-factor});
|
|
107
|
+
// in between these there's the border, a visual line separating the results and recs
|
|
108
|
+
padding-top: calc(#{$desktop-item-gap} * #{$gap-to-recs-factor});
|
|
109
|
+
@include mobile_sizes() {
|
|
110
|
+
margin-top: calc(#{$mobile-item-gap} * #{$gap-to-recs-factor});
|
|
111
|
+
padding-top: calc(#{$mobile-item-gap} * #{$gap-to-recs-factor});
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
.PLP-results.nothing + .search-recs {
|
|
115
|
+
// if no results, bring us closer
|
|
116
|
+
margin-top: calc(#{$desktop-item-gap} * 1.5);
|
|
117
|
+
padding-top: calc(#{$desktop-item-gap} * 1.5);
|
|
118
|
+
@include mobile_sizes() {
|
|
119
|
+
margin-top: calc(#{$mobile-item-gap} * 1.5);
|
|
120
|
+
padding-top: calc(#{$mobile-item-gap} * 1.5);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
@mixin CategoryPage($spacing-to-filters: 12px, $desktop-item-gap: 20px, $mobile-item-gap: 16px) {
|
|
127
|
+
.listing-page {
|
|
128
|
+
.quicklinks,
|
|
129
|
+
.crumbs {
|
|
130
|
+
margin-bottom: $desktop-item-gap;
|
|
131
|
+
@include mobile_sizes() {
|
|
132
|
+
margin-bottom: $mobile-item-gap;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
@mixin mobile_sizes() {
|
|
139
|
+
@media (max-width: 651px) {
|
|
140
|
+
@content;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
@use "../../variables";
|
|
2
|
+
|
|
3
|
+
@mixin ScrollStatus(
|
|
4
|
+
$scroll-status-background: rgba(100, 100, 100, 0.9),
|
|
5
|
+
$border-radius: calc(variables.$border-radius * 5)
|
|
6
|
+
) {
|
|
7
|
+
.scroll-status {
|
|
8
|
+
pointer-events: none;
|
|
9
|
+
position: sticky;
|
|
10
|
+
bottom: 10vh;
|
|
11
|
+
white-space: nowrap;
|
|
12
|
+
display: flex;
|
|
13
|
+
justify-content: center;
|
|
14
|
+
z-index: calc(#{variables.$base-z-index} + 4);
|
|
15
|
+
align-items: center;
|
|
16
|
+
opacity: 0;
|
|
17
|
+
.text {
|
|
18
|
+
background: $scroll-status-background;
|
|
19
|
+
color: white;
|
|
20
|
+
padding: 10px 20px;
|
|
21
|
+
border-radius: $border-radius;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
@use "../../variables";
|
|
2
|
+
@use "../../helpers/utils";
|
|
3
|
+
@use "../../helpers/supports-hover";
|
|
4
|
+
@use "./Buttons";
|
|
5
|
+
|
|
6
|
+
@mixin SelectedFilters(
|
|
7
|
+
$border-radius: variables.$border-radius,
|
|
8
|
+
$title-color: variables.$brand-black,
|
|
9
|
+
$filter-title-transform: uppercase,
|
|
10
|
+
$margin-on-desktop: 0px,
|
|
11
|
+
$set-border-on-crumbs: true
|
|
12
|
+
) {
|
|
13
|
+
.listing-page .selected-filters {
|
|
14
|
+
.inner {
|
|
15
|
+
// this is in here because it's an ExpandingContainer and the animation gets borked otherwise
|
|
16
|
+
margin-top: 10px;
|
|
17
|
+
margin-bottom: 10px;
|
|
18
|
+
display: flex;
|
|
19
|
+
align-items: center;
|
|
20
|
+
.title {
|
|
21
|
+
margin-right: 10px;
|
|
22
|
+
}
|
|
23
|
+
@media (min-width: 652px) {
|
|
24
|
+
margin-top: $margin-on-desktop;
|
|
25
|
+
margin-bottom: $margin-on-desktop;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.title {
|
|
29
|
+
@media (max-width: 600px) {
|
|
30
|
+
display: none;
|
|
31
|
+
}
|
|
32
|
+
font-size: 1.1em;
|
|
33
|
+
text-transform: $filter-title-transform;
|
|
34
|
+
color: $title-color;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.depict-slider {
|
|
38
|
+
overflow: hidden;
|
|
39
|
+
width: unset;
|
|
40
|
+
|
|
41
|
+
.clear-filter {
|
|
42
|
+
@if $set-border-on-crumbs {
|
|
43
|
+
border: 1px solid Buttons.$primary-button-background;
|
|
44
|
+
}
|
|
45
|
+
svg {
|
|
46
|
+
width: 12.64px;
|
|
47
|
+
height: 12.64px;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
.clear-all-filters {
|
|
51
|
+
@if $set-border-on-crumbs {
|
|
52
|
+
border: Buttons.$secondary-button-border;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.clear-filter,
|
|
57
|
+
.clear-all-filters {
|
|
58
|
+
flex: 0 0 auto;
|
|
59
|
+
box-sizing: border-box;
|
|
60
|
+
white-space: nowrap;
|
|
61
|
+
padding: 5px 10px;
|
|
62
|
+
display: flex;
|
|
63
|
+
@include utils.flex-gap(5px, "row nowrap");
|
|
64
|
+
align-items: center;
|
|
65
|
+
border-radius: $border-radius;
|
|
66
|
+
font-size: 1em;
|
|
67
|
+
line-height: 1;
|
|
68
|
+
cursor: pointer;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
@content;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|