@depict-ai/ui 4.7.33 → 4.7.34

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/latest/index.cjs CHANGED
@@ -12,6 +12,7 @@ function $parcel$export(e, n, v, s) {
12
12
  configurable: true
13
13
  });
14
14
  }
15
+ $parcel$export(module.exports, "ModernResponsiveImage", () => $d212a67662c5cf38$export$140423fa4bfb6d63);
15
16
  $parcel$export(module.exports, "setExtraDisplayTransformers", () => $606c221d98ba4d92$export$f98b8d5ea7207a1);
16
17
  $parcel$export(module.exports, "getExtraDisplayTransformers", () => $606c221d98ba4d92$export$b2d94be4f02525b2);
17
18
  $parcel$export(module.exports, "remap_blocks_to_index", () => $d92f338ba8f9da9b$export$92aa5d691d588f3f);
@@ -54,7 +55,6 @@ $parcel$export(module.exports, "make_accurate_width_accessor", () => $f13855d1b9
54
55
  $parcel$export(module.exports, "RecommendationSlider", () => $72f96c4782db0b1b$export$9574e176201e5e8d);
55
56
  $parcel$export(module.exports, "RecommendationGrid", () => $1f8dbf7e28b3eff2$export$9756467d34d91cdb);
56
57
  $parcel$export(module.exports, "ShopifyResponsiveImg", () => $19950faaac026e09$export$6e5611bc7e57e477);
57
- $parcel$export(module.exports, "makeSizeAccessor", () => $19950faaac026e09$export$e6fffe67d4cdd848);
58
58
  $parcel$export(module.exports, "ImageContainer", () => $93304d59a9747aa6$export$94026ee33dbabc26);
59
59
  $parcel$export(module.exports, "SentryErrorBoundary", () => $edafe91daf3091d7$export$b69d0d92862de18b);
60
60
  $parcel$export(module.exports, "ExtraSentryContextProvider", () => $edafe91daf3091d7$export$240ecf5363d76c0f);
@@ -106,194 +106,32 @@ $parcel$export(module.exports, "SearchField", () => $c0a0659cf97e8eb2$export$316
106
106
  $parcel$export(module.exports, "BreadCrumbs", () => $1f4925833f07df29$export$5b7c3ce0ebe51f86);
107
107
  $parcel$export(module.exports, "QuickLinks", () => $1f4925833f07df29$export$de035477d6fef996);
108
108
  $parcel$export(module.exports, "CategoryPage", () => $14f289047713d67a$export$e2bf8ce7779e827e);
109
+ $parcel$export(module.exports, "makeSizeAccessors", () => $128302ab61dab8f1$export$54c6d6ff54e72743);
109
110
  /*! Depict.ai search and product listing SDK */
110
- const $b757f75811f3261f$export$83d89fbfd8236492 = "4.7.33";
111
-
112
- // Value first so shopify merchants can set (extra) display transformers in the shopify app
113
- // Calling these functions normally doesn't do anything, but our shopify app respects them
114
- // Them being here is value first too because this can be easily imported in the product card editor
115
- let $606c221d98ba4d92$var$extraDisplayTransformers;
116
- const $606c221d98ba4d92$export$b2d94be4f02525b2 = () => $606c221d98ba4d92$var$extraDisplayTransformers;
117
- const $606c221d98ba4d92$export$f98b8d5ea7207a1 = displayTransformers => $606c221d98ba4d92$var$extraDisplayTransformers = displayTransformers;
118
- function $b54e6192d0caa293$export$d9f36cf74e24d595(media_query) {
119
- const signal = $gVhhF$solidjs.createSignal(false);
120
- $gVhhF$solidjs.onCleanup($gVhhF$depictaiutilishared.javascript_media_query(media_query, ({
121
- matches: matches
122
- }) => signal[1](matches)).remove);
123
- return signal[0];
124
- }
125
- function $17d06b3bcb035500$export$a79e666719b3647c(cols_at_size_) {
126
- const size_active = $gVhhF$solidjs.createMemo(() => cols_at_size_().map(([n_cols, min_width, max_width]) => {
127
- const media_query = $gVhhF$depictaiutilishared.layout_media_query_header(min_width, max_width).replaceAll("@media", "");
128
- const matches = $b54e6192d0caa293$export$d9f36cf74e24d595(media_query);
129
- return [matches, n_cols];
130
- }));
131
- const n_cols_currently_showing = $gVhhF$solidjs.createMemo(() => {
132
- try {
133
- const [, n_cols_currently_showing] = size_active().find(([media_query]) => media_query());
134
- return n_cols_currently_showing;
135
- } catch (e) {
136
- $gVhhF$depictaiutilishared.dwarn("No active size found in columnsAtSize - can't render content blocks or looks");
137
- return null;
138
- }
139
- });
140
- return n_cols_currently_showing;
141
- }
142
-
143
- // Errors caused by SDK consuming code interfacing with the SDK, not the SDK itself or other Depict packages
144
- class $7dd1edb2ee2b43ad$export$e1d52e4f0c7557a1 extends Error {}
145
- class $7dd1edb2ee2b43ad$export$aa14a9f43128f61f extends $7dd1edb2ee2b43ad$export$e1d52e4f0c7557a1 {
146
- constructor(templateError) {
147
- super("Uncaught error in product card template", {
148
- cause: templateError
149
- });
150
- }
151
- }
152
- var $edafe91daf3091d7$var$_tmpl$ = /*#__PURE__*/$gVhhF$solidjsweb.template(`<div><!$><!/><!$><!/>`);
153
- const $edafe91daf3091d7$var$ExtraInfoContext = /*@__PURE__*/$gVhhF$solidjs.createContext();
154
- function $edafe91daf3091d7$export$240ecf5363d76c0f(props) {
155
- return $gVhhF$solidjsweb.createComponent($edafe91daf3091d7$var$ExtraInfoContext.Provider, {
156
- get value() {
157
- return props.getContext_;
158
- },
159
- get children() {
160
- return props.children;
161
- }
162
- });
163
- }
164
- function $edafe91daf3091d7$export$b69d0d92862de18b(props) {
165
- const getContextFunction = $gVhhF$solidjs.useContext($edafe91daf3091d7$var$ExtraInfoContext);
166
- return $gVhhF$solidjs.ErrorBoundary({
167
- fallback(err, retry) {
168
- const {
169
- message_: message_,
170
- severity_: severity_
171
- } = props;
172
- let message = message_;
173
- if (err instanceof (0, $7dd1edb2ee2b43ad$export$aa14a9f43128f61f)) {
174
- console.error(err.cause);
175
- message = `${message}: ${err.message}`;
176
- } else $gVhhF$depictaiutilishared.report([err, message_], severity_, getContextFunction?.());
177
- return (() => {
178
- var _el$ = $gVhhF$solidjsweb.getNextElement($edafe91daf3091d7$var$_tmpl$),
179
- _el$2 = _el$.firstChild,
180
- [_el$3, _co$] = $gVhhF$solidjsweb.getNextMarker(_el$2.nextSibling),
181
- _el$4 = _el$3.nextSibling,
182
- [_el$5, _co$2] = $gVhhF$solidjsweb.getNextMarker(_el$4.nextSibling);
183
- $gVhhF$solidjsweb.insert(_el$, message, _el$3, _co$);
184
- $gVhhF$solidjsweb.insert(_el$, () => props.on_error_?.(retry), _el$5, _co$2);
185
- $gVhhF$solidjsweb.effect(_$p => $gVhhF$solidjsweb.classList(_el$, Object.fromEntries(props.class_list_?.map(c => [c, true]) || []), _$p));
186
- return _el$;
187
- })();
188
- },
111
+ const $b757f75811f3261f$export$83d89fbfd8236492 = "4.7.34";
112
+ const $84798aaa863a4909$var$ListingContext = /*@__PURE__*/$gVhhF$solidjs.createContext();
113
+ function $84798aaa863a4909$export$e68cff63f76bd15b(props) {
114
+ const our_object = {};
115
+ return $gVhhF$solidjsweb.createComponent($84798aaa863a4909$var$ListingContext.Provider, {
116
+ value: our_object,
189
117
  get children() {
190
118
  return props.children;
191
119
  }
192
120
  });
193
121
  }
194
- var $75777da1687ba3f6$var$_tmpl$ = /*#__PURE__*/$gVhhF$solidjsweb.template(`<div>`),
195
- $75777da1687ba3f6$var$_tmpl$2 = /*#__PURE__*/$gVhhF$solidjsweb.template(`<style type=text/css>`);
196
- const $75777da1687ba3f6$export$c500183c31c2b22e = "--spans-columns";
197
- function $75777da1687ba3f6$export$ab6b147eec64fea5(props) {
198
- const container_class = "x" + $gVhhF$solidjs.createUniqueId();
199
- const container_element_ = (() => {
200
- var _el$ = $gVhhF$solidjsweb.getNextElement($75777da1687ba3f6$var$_tmpl$);
201
- $gVhhF$solidjsweb.spread(_el$, $gVhhF$solidjsweb.mergeProps(() => props.element_attributes, {
202
- "classList": {
203
- [container_class]: true
204
- }
205
- }), false, true);
206
- $gVhhF$solidjsweb.insert(_el$, () => props.children);
207
- $gVhhF$solidjsweb.runHydrationEvents();
208
- return _el$;
209
- })(); // Don't want this in the error boundary below, it should only be for the actual layouting
210
- return $gVhhF$solidjsweb.createComponent((0, $edafe91daf3091d7$export$b69d0d92862de18b), {
211
- message_: "Failed calculating css grid layout",
212
- severity_: "error",
213
- get children() {
214
- return $75777da1687ba3f6$export$e2f22da34aaf310a(props, container_element_, false, container_class);
215
- }
216
- });
217
- }
218
- function $75777da1687ba3f6$export$e2f22da34aaf310a(props, container_element, insert_after_container = false, container_class) {
219
- if (!container_class) {
220
- // only try to add if it wasn't provided, so that we have a code path without classList when doing SSR
221
- container_class = $gVhhF$depictaiutilishared.make_random_classname();
222
- container_element.classList.add(container_class);
223
- }
224
- const container_selector = `.depict .${container_class}`; // class so ppl can have id if needed
225
- const col_styling = $gVhhF$solidjs.createMemo(() => $75777da1687ba3f6$var$generate_media_queried_declarations({
226
- container_selector_: container_selector,
227
- default_grid_spacing_text: props.grid_spacing,
228
- cols_at_size_: props.cols_at_size,
229
- override_vertical_spacing_: props.override_vertical_spacing,
230
- top_row_shortened_by_: props.top_row_shortened_by_()
231
- }));
232
- const base_styling = $gVhhF$solidjs.createMemo(() => {
233
- return `${container_selector}{display:grid;}`;
234
- });
235
- const get_styling = $gVhhF$solidjs.createMemo(() => base_styling() + col_styling());
236
- const style_element = (() => {
237
- var _el$2 = $gVhhF$solidjsweb.getNextElement($75777da1687ba3f6$var$_tmpl$2);
238
- $gVhhF$solidjsweb.insert(_el$2, get_styling);
239
- return _el$2;
240
- })(); // need to create this after set_styling has been called or else there will be no styling in the SSR'd output
241
- if (insert_after_container) container_element.after(style_element);
242
- return [container_element, style_element];
243
- }
244
- function $75777da1687ba3f6$var$generate_media_queried_declarations({
245
- container_selector_: container_selector_,
246
- default_grid_spacing_text: default_grid_spacing_text,
247
- cols_at_size_: cols_at_size_,
248
- override_vertical_spacing_: override_vertical_spacing_,
249
- top_row_shortened_by_: top_row_shortened_by_
250
- }) {
251
- return cols_at_size_.map(options => {
252
- const [n_cols, min, max, grid_spacing_for_size] = options;
253
- const main_spacing_to_use = grid_spacing_for_size || default_grid_spacing_text;
254
- // See comment in else block below
255
- const can_use_gap = override_vertical_spacing_ ? $75777da1687ba3f6$var$can_use_gap_for_unit(override_vertical_spacing_) : $75777da1687ba3f6$var$can_use_gap_for_unit(main_spacing_to_use);
256
- let spacing_styling;
257
- let additional_styling = ""; // outside the media query
258
- if (can_use_gap) spacing_styling = `gap:${main_spacing_to_use};${override_vertical_spacing_ ? `row-gap:${override_vertical_spacing_};` : ``}`;else {
259
- // When using percent units, which our default styling does (because convenient) and our interface relies on (hard to specify different gaps for different screen sizes)
260
- // row gaps in grids are relative to the whole height of the grid, maybe see also https://stackoverflow.com/a/53563865. The issue with that is that as we load more products, the gap grows. Also, it breaks existing integrations.
261
- // Logical thought: use margins for the vertical spacing. But margins in grids are apparently relative to the size of the individual items in grids.
262
- // Given that, and that content blocks can be of different width, we have to do some complicated calculations to convert the provided gap, which is in gap of the total container width, to a margin of the individual items.
263
- const vertical_spacing_to_use = override_vertical_spacing_ ?? main_spacing_to_use;
264
- const width_multiplier = `var(${$75777da1687ba3f6$export$c500183c31c2b22e}, 1)`; // How many cards wide the current item is (needed because content blocks)
265
- const one_item_width_exclusing_gaps = `calc(calc(100 / ${n_cols}) - calc(calc(${n_cols * 2 - 2} * calc(${$75777da1687ba3f6$var$strip_percent_unit(vertical_spacing_to_use)} / 2)) / ${n_cols}))`; // Width of one item in percent of the whole
266
- // Equation from layout.tsx, also used in CheckboxGrid. Needs half spacing for some reason
267
- const this_item_width_meat = `calc(${one_item_width_exclusing_gaps} * ${width_multiplier})`; // Width of just the card
268
- const this_item_width_gaps = `calc(calc(${width_multiplier} - 1) * ${$75777da1687ba3f6$var$strip_percent_unit(vertical_spacing_to_use)})`; // Width of the gaps
269
- const this_item_width = `calc(${this_item_width_meat} + ${this_item_width_gaps})`; // Width of this item including gaps
270
- // this_item_width needs to be unitless, and since plain css can't do calc(40% / 5%) which would become unitless we need to strip all units in the calculations before
271
- const convert_top_spacing_to_item_width_percentage = spacing => `calc(calc(100 / ${this_item_width}) * ${spacing})`; // Now finally we can convert the desired spacing to a percentage of this individual item's width (including gaps that would be there but aren't because content blocks span them)
272
- spacing_styling = `column-gap:${main_spacing_to_use};`; // Still use gaps in the horizontal direction because that works flawlessly
273
- // Re-use old layout code to get the correct :nth-child declarations for the top margins
274
- additional_styling = $gVhhF$depictaiutilishared.n_col_styling_with_dist(container_selector_ + ">", convert_top_spacing_to_item_width_percentage(default_grid_spacing_text), [[n_cols, min, max, grid_spacing_for_size ? convert_top_spacing_to_item_width_percentage(grid_spacing_for_size) : null]], "grid", override_vertical_spacing_ ? convert_top_spacing_to_item_width_percentage(override_vertical_spacing_) : override_vertical_spacing_, top_row_shortened_by_);
275
- }
276
- const media_query_body = `${container_selector_}{${spacing_styling}grid-template-columns:repeat(${n_cols},minmax(0,1fr));}`;
277
- return `${$gVhhF$depictaiutilishared.layout_media_query_header(min, max)}{${media_query_body}}${additional_styling}`;
278
- }).join("");
279
- }
280
- function $75777da1687ba3f6$var$can_use_gap_for_unit(spacing) {
281
- const trimmed_spacing = spacing.trim();
282
- return !trimmed_spacing.includes("%");
283
- }
284
- function $75777da1687ba3f6$var$strip_percent_unit(css_value) {
285
- return css_value.replaceAll("%", "");
122
+ function $84798aaa863a4909$export$8d0c4fdbb05a2f2e() {
123
+ return $gVhhF$solidjs.useContext($84798aaa863a4909$var$ListingContext);
286
124
  }
287
- var $9fe9883b1d8ecccc$var$_tmpl$ = /*#__PURE__*/$gVhhF$solidjsweb.template(`<span>`);
288
- function $9fe9883b1d8ecccc$export$acb3eaade4b23afd(props) {
289
- // Work around https://github.com/solidjs/solid/issues/1980
125
+ var $93304d59a9747aa6$var$_tmpl$ = /*#__PURE__*/$gVhhF$solidjsweb.template(`<div>`);
126
+ function $93304d59a9747aa6$export$94026ee33dbabc26(props) {
290
127
  return (() => {
291
- var _el$ = $gVhhF$solidjsweb.getNextElement($9fe9883b1d8ecccc$var$_tmpl$);
128
+ var _el$ = $gVhhF$solidjsweb.getNextElement($93304d59a9747aa6$var$_tmpl$);
129
+ $gVhhF$solidjsweb.insert(_el$, () => props.children);
292
130
  $gVhhF$solidjsweb.effect(_p$ => {
293
- var _v$ = `${props.class || ""} depict-placeholder`,
294
- _v$2 = props.style;
295
- _v$ !== _p$.e && $gVhhF$solidjsweb.className(_el$, _p$.e = _v$);
296
- _p$.t = $gVhhF$solidjsweb.style(_el$, _v$2, _p$.t);
131
+ var _v$ = `padding-bottom:calc(calc(1 / ${props.aspectRatio}) * 100%)`,
132
+ _v$2 = "s-image-container" + (props.class ? " " + props.class : "");
133
+ _p$.e = $gVhhF$solidjsweb.style(_el$, _v$, _p$.e);
134
+ _v$2 !== _p$.t && $gVhhF$solidjsweb.className(_el$, _p$.t = _v$2);
297
135
  return _p$;
298
136
  }, {
299
137
  e: undefined,
@@ -302,298 +140,236 @@ function $9fe9883b1d8ecccc$export$acb3eaade4b23afd(props) {
302
140
  return _el$;
303
141
  })();
304
142
  }
305
- function $3bc4a89b971a3af5$export$3f37af283f8fb843(props) {
306
- const style = $gVhhF$solidjs.createMemo(() => {
307
- const {
308
- style: style,
309
- height: height,
310
- width: width,
311
- aspectRatio: aspectRatio
312
- } = props;
313
- const is_image = aspectRatio != undefined;
314
- const size_data = is_image ? {
315
- width: "100%",
316
- "padding-bottom": `calc(calc(1 / ${aspectRatio}) * 100%)`
317
- } : {
318
- width: width,
319
- height: height
320
- };
321
- return {
322
- display: "block",
323
- ...(style || {}),
324
- ...size_data
325
- };
326
- });
327
- return $gVhhF$solidjsweb.createComponent((0, $9fe9883b1d8ecccc$export$acb3eaade4b23afd), {
328
- get style() {
329
- return style();
143
+ const $129b9b9c010039d0$var$ProductCardIndexContext = /*@__PURE__*/$gVhhF$solidjs.createContext();
144
+ function $129b9b9c010039d0$export$11354f017ddc9790(props) {
145
+ return $gVhhF$solidjsweb.createComponent($129b9b9c010039d0$var$ProductCardIndexContext.Provider, {
146
+ get value() {
147
+ return props.index_;
330
148
  },
331
- get ["class"]() {
332
- return props.class;
149
+ get children() {
150
+ return props.children;
333
151
  }
334
152
  });
335
153
  }
336
- var $d92f338ba8f9da9b$var$_tmpl$ = /*#__PURE__*/$gVhhF$solidjsweb.template(`<div class=content-block-wrapper>`);
337
- function $d92f338ba8f9da9b$export$791ef57e62e21c3f({
338
- content_blocks_: incoming_content_blocks,
339
- cols_at_size_: cols_at_size_,
340
- set_top_row_shortened_by_: set_top_row_shortened_by_
154
+ function $129b9b9c010039d0$export$e3faf4d95300054f() {
155
+ return $gVhhF$solidjs.useContext($129b9b9c010039d0$var$ProductCardIndexContext);
156
+ }
157
+ let $3ea8e0af95b5b7d2$var$browserIsBugged = false;
158
+ function $3ea8e0af95b5b7d2$export$2972b890694ced75(callback, {
159
+ timeout: timeout
341
160
  }) {
342
- // these are by row
343
- const [content_blocks, set_content_blocks] = $gVhhF$solidjsstore.createStore([]);
344
- const n_cols_currently_showing_ = $17d06b3bcb035500$export$a79e666719b3647c(cols_at_size_);
345
- $gVhhF$solidjs.createEffect(() => {
346
- const incoming_value = incoming_content_blocks();
347
- if (!incoming_value) {
348
- set_content_blocks([]);
349
- return;
350
- }
351
- set_content_blocks($gVhhF$solidjsstore.reconcile(incoming_value, {
352
- key: "content"
353
- })); // key: Make moving blocks faster, see https://github.com/solidjs/solid/issues/1925#issuecomment-1769671596
354
- });
355
-
356
- const get_wrapped_blocks_ = $d92f338ba8f9da9b$var$wrap_blocks_in_div(content_blocks, n_cols_currently_showing_);
357
- $gVhhF$solidjs.createComputed(() => {
358
- // Update our styling if a content block forces some items that should be in the top row into a lower one, so we can generate margin-top declarations for them
359
- const top_row_block = get_wrapped_blocks_()[0];
360
- const blocks_in_top_row_taken = top_row_block?.span_columns;
361
- if (blocks_in_top_row_taken == undefined) {
362
- // Fall back to zero if no content block in top row, since then no blocks in top row are taken
363
- set_top_row_shortened_by_(0);
364
- return;
365
- }
366
- set_top_row_shortened_by_(blocks_in_top_row_taken - 1); // minus one because the content block wrapper also is an element
161
+ if ($3ea8e0af95b5b7d2$var$browserIsBugged) timeout /= 4;
162
+ const cancel = () => {
163
+ cancelIdleCallback(idleHandle);
164
+ clearTimeout(racingTimeout);
165
+ };
166
+ const idleHandle = requestIdleCallback(deadline => {
167
+ cancel();
168
+ return callback(deadline);
169
+ }, {
170
+ timeout: timeout
367
171
  });
368
-
369
- return $gVhhF$solidjs.createMemo(() => $d92f338ba8f9da9b$export$92aa5d691d588f3f({
370
- wrapped_blocks_: get_wrapped_blocks_(),
371
- n_cols_currently_showing_: n_cols_currently_showing_()
372
- }).content_blocks_by_index_);
172
+ const racingTimeout = setTimeout(() => {
173
+ $gVhhF$depictaiutilishared.dwarn("Browser did not respect requestIdleCallback timeout");
174
+ $3ea8e0af95b5b7d2$var$browserIsBugged = true;
175
+ cancel();
176
+ callback({
177
+ didTimeout: false,
178
+ timeRemaining() {
179
+ return 500;
180
+ }
181
+ });
182
+ }, timeout + 1000);
183
+ return cancel;
373
184
  }
374
- function $d92f338ba8f9da9b$export$92aa5d691d588f3f({
375
- wrapped_blocks_: wrapped_blocks_,
376
- n_cols_currently_showing_: n_cols_currently_showing_
377
- }) {
378
- // This computation should be quite cheap since it just moves around things into an array
379
- const content_blocks_by_index_ = [];
380
- const constructed_block_arrays = new WeakSet();
381
- // Need to keep track of products displaced by blocks above when mapping rows to indexes in the actual grid
382
- const displaced_products_ = []; // undefined = is product, true = is content block if number instead of tuple number indicates content blocks in that row. Index in first array is row, index in second array is column
383
- if (!n_cols_currently_showing_)
384
- // Nope out of here if we don't know how many columns are showing
185
+ const $19950faaac026e09$var$onIdleFunctionsSymbol = /*@__PURE__*/Symbol("onIdleFunctions");
186
+ const $19950faaac026e09$export$f8945274f08a3105 = /*@__PURE__*/Symbol("listingContextAspectRatios");
187
+ const $19950faaac026e09$export$6066eb417ed2e9f7 = /*@__PURE__*/Symbol("listingContextAspectRatios");
188
+ function $19950faaac026e09$export$6e5611bc7e57e477(props) {
189
+ const img = new Image(); // Need to use new Image or browser might not start loading image before it's in the DOM
190
+ const [ourRatioKeyed, setOurRatioKeyed] = $gVhhF$solidjs.createSignal();
191
+ const sizeToUse = $gVhhF$solidjs.createMemo(() => {
192
+ // props.size could be a getter that expects a cleanup so get it in this memo instead of where we set img.sizes (also we need to multiply, and it's good with some diffing here so people calling the comp don't have to)
193
+ const incomingSize = props.size();
194
+ const ratio = ourRatioKeyed();
195
+ const containerHeight = props.sizeHeight?.();
196
+ if (!incomingSize || !ratio || !containerHeight || getComputedStyle(img).objectFit !== "cover") return incomingSize;
197
+ const maybeCorrectedSize = ratio * containerHeight;
198
+ return Math.round(Math.max(incomingSize, maybeCorrectedSize));
199
+ });
200
+ const src_options = $gVhhF$solidjs.createMemo(() => {
201
+ const {
202
+ src: src,
203
+ customUrls: customUrls
204
+ } = props;
205
+ const fallback_src = (customUrls || $19950faaac026e09$export$1d6267828870adda)(src, 400);
206
+ const srcset = $19950faaac026e09$var$make_srcset(props.src, props.resolutions || [64, 128, 256, 300, 400, 512, 640, 768, 1024, 1280, 1536, 2048, 2560, 3072, 3584, 4096], customUrls);
207
+ // The order here *might* matter - I've seen mobile safari always start with loading the 400px version when it was reversed
385
208
  return {
386
- content_blocks_by_index_: content_blocks_by_index_
209
+ srcset: srcset,
210
+ src: fallback_src
387
211
  };
388
- for (let i = 0; i < wrapped_blocks_.length; i++) {
389
- // i = row to show at
390
- const block = wrapped_blocks_[i];
391
- if (!block) continue; // Array can have holes/not defined places
392
- const index_of_first_item_in_row = i * n_cols_currently_showing_;
393
- const index_of_last_item_in_row = index_of_first_item_in_row + n_cols_currently_showing_;
212
+ });
213
+ // Work around https://github.com/solidjs/solid/issues/1828
214
+ const [, otherAttributes] = $gVhhF$solidjs.splitProps(props, ["resolutions", "size", "src", "srcSet", "loading", "keyRatioUpdating", "sizeHeight", "customUrls" // Can't have properties ending on underscore because the property name mangling will get confused by this <-
215
+ ]);
216
+
217
+ const [hasSetSize, setHasSetSize] = $gVhhF$solidjs.createSignal(false);
218
+ const [hasDecoded, setHasDecoded] = $gVhhF$solidjs.createSignal(false);
219
+ const productCardIndexAccessor = $129b9b9c010039d0$export$e3faf4d95300054f();
220
+ const listingContext = $84798aaa863a4909$export$8d0c4fdbb05a2f2e();
221
+ const isPrioritisedImage = $gVhhF$solidjs.createMemo(() => productCardIndexAccessor?.() < 8);
222
+ const shouldActuallyEnableLazyLoading = $gVhhF$solidjs.createMemo(() => {
394
223
  const {
395
- span_rows: span_rows,
396
- position: position,
397
- span_columns: span_columns
398
- } = block; // span columns is already capped in the block wrapping step (wrap_blocks_in_div)
399
- let target_index;
400
- if (position === "left") target_index = index_of_first_item_in_row;else if (position === "right") target_index = index_of_last_item_in_row - span_columns;else if (position === "center") target_index = index_of_first_item_in_row + Math.floor((n_cols_currently_showing_ - span_columns) / 2);else throw new Error("Invalid position for content block - must be left, right or center: " + JSON.stringify(position));
401
- // Calculate displacement of us by content blocks above us
402
- let displacement_by_added_blocks = 0;
403
- for (let j = 0; j < i; j++) {
404
- const row = displaced_products_[j];
405
- if (row === undefined) continue; // there were no blocks in this row
406
- let displaced_items_in_row;
407
- if (typeof row === "number") displaced_items_in_row = row;else {
408
- displaced_items_in_row = row.filter(item => item).length;
409
- displaced_products_[j] = displaced_items_in_row;
410
- }
411
- displacement_by_added_blocks += displaced_items_in_row;
224
+ loading: loading
225
+ } = props;
226
+ if (loading === "force-lazy") return true;
227
+ return loading === "lazy" && !isPrioritisedImage();
228
+ });
229
+ const modifiedOtherAttributes = $gVhhF$solidjs.mergeProps(otherAttributes, {
230
+ get loading() {
231
+ return shouldActuallyEnableLazyLoading() ? "lazy" : undefined;
412
232
  }
413
- const index_of_us_in_row = target_index - index_of_first_item_in_row;
414
- for (let j = 0; j < index_of_us_in_row; j++) if (displaced_products_[i]?.[j])
415
- // Add blocks in our *current* row to the displacement, but only the ones to our left
416
- displacement_by_added_blocks++;
417
- // Add block to output array at the correct index
418
- const final_target_index = target_index - displacement_by_added_blocks;
419
- const existing_block = content_blocks_by_index_[final_target_index];
420
- const block_content = block.content;
421
- // If two blocks want to exist at the same location (can happen, for example if one block wants to be at the end of a row and the other one at the start of the next row) add an array with both
422
- if (existing_block) {
423
- if (constructed_block_arrays.has(existing_block))
424
- // Only push to arrays we have created to not create funky bugs when there's an array in JSX.Element
425
- existing_block.push(block_content);else {
426
- const array = [existing_block, block_content];
427
- constructed_block_arrays.add(array);
428
- content_blocks_by_index_[final_target_index] = array;
233
+ });
234
+ // Batch idle callbacks so that MutationObserver can batch the resulting mutations better, so we don't lose tons of CPU time to MutationObservers doing lots of things for each image
235
+ // Happened on singular, they have some weird observer (+we also have one but ours is at least 3x more efficient)
236
+ const executeWhenIdleBatched = (fn, isSizesUpdate) => {
237
+ if (typeof requestIdleCallback !== "function") return setTimeout(fn); // Old browsers don't get optimisation
238
+ if ((!isSizesUpdate || !sizeSetFastPath) && $gVhhF$solidjs.untrack(isPrioritisedImage)) return fn(); // If we're one of the first 8 images, execute the callbacks immediately (improves lighthouse metrics)
239
+ // unless we're just a `sizes` update after the initial sizes setting (this optimisation makes the looks animation a lot smoother on low-cpu devices)
240
+ const existingStack = listingContext?.[$19950faaac026e09$var$onIdleFunctionsSymbol];
241
+ if (existingStack) return existingStack.add(fn);
242
+ const newStack = new Set([fn]);
243
+ if (listingContext) listingContext[$19950faaac026e09$var$onIdleFunctionsSymbol] = newStack;
244
+ const processStackWhenIdle = () => $3ea8e0af95b5b7d2$export$2972b890694ced75($gVhhF$depictaiutilishared.catchify(deadline => {
245
+ const start = performance.now();
246
+ const thirdDeadlineTime = deadline.timeRemaining() / 3; // observers coming after this take up 2-3x the time so don't use all of it
247
+ for (const fn of newStack) {
248
+ newStack.delete(fn);
249
+ fn();
250
+ const timeTaken = performance.now() - start;
251
+ if ((!deadline.timeRemaining() || timeTaken > thirdDeadlineTime) && newStack.size) {
252
+ // There's still work to be done but no time, so schedule another idle callback
253
+ processStackWhenIdle();
254
+ return;
255
+ }
429
256
  }
430
- } else content_blocks_by_index_[final_target_index] = block_content;
431
- // Update displacement
432
- for (let j = 0; j < span_rows; j++) {
433
- const row = displaced_products_[i + j] ||= [];
434
- for (let k = index_of_us_in_row; k < index_of_us_in_row + span_columns; k++) row[k] = true;
435
- }
436
- }
437
- return {
438
- content_blocks_by_index_: content_blocks_by_index_,
439
- displaced_products_: displaced_products_
257
+ // Stack is empty
258
+ if (listingContext) delete listingContext[$19950faaac026e09$var$onIdleFunctionsSymbol];
259
+ }), {
260
+ timeout: 15000
261
+ } // long timeout, better to not have images than to freeze the browser. But also we don't want to confuse user why no iamges are loading
262
+ );
263
+
264
+ processStackWhenIdle();
440
265
  };
441
- }
442
- /**
443
- * Takes a store that contains an array from where index is number of row and value is the result of executing the content block rendering function.
444
- * Then renders the content and wraps it into a div with the correct styling.
445
- * Also caps span_columns to max available columns
446
- */
447
- function $d92f338ba8f9da9b$var$wrap_blocks_in_div(rendered_content_blocks, max_span_columns) {
448
- const get_wrapped_blocks = $gVhhF$solidjs.mapArray(() => rendered_content_blocks, block => {
449
- if (!block) return;
450
- const final_span_columns = $gVhhF$solidjs.createMemo(() => Math.min(block.span_columns, max_span_columns() ?? Infinity));
451
- // If the item spans an entire row, limit the height to 1 row since there's no concept of "rows" if there are no products beside the block
452
- const final_span_rows = $gVhhF$solidjs.createMemo(() => final_span_columns() === max_span_columns() ? 1 : block.span_rows);
453
- const [rendering_delayed, set_rendering_delayed_] = $gVhhF$solidjs.createSignal(true);
454
- const content = $gVhhF$solidjsweb.createComponent((0, $gVhhF$solidjs.Show), {
455
- get when() {
456
- return !rendering_delayed();
457
- },
458
- get children() {
459
- var _el$ = $gVhhF$solidjsweb.getNextElement($d92f338ba8f9da9b$var$_tmpl$);
460
- $gVhhF$solidjsweb.insert(_el$, $gVhhF$solidjsweb.createComponent($d92f338ba8f9da9b$var$RenderContent, {
461
- get content() {
462
- return block.content;
463
- }
464
- }));
465
- $gVhhF$solidjsweb.effect(_p$ => {
466
- var _v$ = !!(final_span_columns() === max_span_columns()),
467
- _v$2 = {
468
- [(0, $75777da1687ba3f6$export$c500183c31c2b22e)]: final_span_columns()
469
- },
470
- _v$3 = `span ${final_span_columns()}`,
471
- _v$4 = `span ${final_span_rows()}`;
472
- _v$ !== _p$.e && _el$.classList.toggle("spans-whole-row", _p$.e = _v$);
473
- _p$.t = $gVhhF$solidjsweb.style(_el$, _v$2, _p$.t);
474
- _v$3 !== _p$.a && ((_p$.a = _v$3) != null ? _el$.style.setProperty("grid-column", _v$3) : _el$.style.removeProperty("grid-column"));
475
- _v$4 !== _p$.o && ((_p$.o = _v$4) != null ? _el$.style.setProperty("grid-row", _v$4) : _el$.style.removeProperty("grid-row"));
476
- return _p$;
477
- }, {
478
- e: undefined,
479
- t: undefined,
480
- a: undefined,
481
- o: undefined
482
- });
483
- return _el$;
484
- }
485
- });
486
- return $gVhhF$solidjs.mergeProps(block, {
487
- // Create content divs for the blocks with as much fine-grained diffing as possible?
488
- content: () => {
489
- // Sneaky on-demand rendering, so we don't render more blocks than we can show: we have the rendered value here and don't return a function that renders because if our block would be moved around we'd be guaranteed to re-render
490
- // However, no need to count products to know if we should render: the <For> loops in Placeholders and render_displays will only call this function if there's actually a product here that wants to display this block
491
- set_rendering_delayed_(false);
492
- return content;
493
- },
494
- get span_columns() {
495
- // Cap max width of block to the number of columns available, for everyone downstream
496
- return final_span_columns();
497
- },
498
- get span_rows() {
499
- return final_span_rows();
266
+ let sizeSetFastPath = false;
267
+ let hasDecodedFastPath = false;
268
+ let timeoutSet = false;
269
+ let gotCleanedUp = false;
270
+ let srcJustChanged = true;
271
+ img.classList.add("s-responsive-image");
272
+ if (listingContext) {
273
+ let prevValue;
274
+ const aspectRatios = listingContext[$19950faaac026e09$export$f8945274f08a3105] ||= new Set();
275
+ const [, setMostPrevalentAspectRatio] = listingContext[$19950faaac026e09$export$6066eb417ed2e9f7] ||= $gVhhF$solidjs.createSignal();
276
+ img.addEventListener("load", $gVhhF$depictaiutilishared.catchify(() => {
277
+ const aspectRatio_ = img.naturalWidth / img.naturalHeight;
278
+ if (srcJustChanged)
279
+ // Always key object-fit-cover compensation to avoid loading-different-images-infinite-loop
280
+ setOurRatioKeyed(aspectRatio_);else if (props.keyRatioUpdating) return;
281
+ srcJustChanged = false;
282
+ // Save our aspect ratio whenever it changes, so that we can base the containers on the most dominant aspect ratio, used in SearchModalV2 and LookItem
283
+ if (prevValue) aspectRatios.delete(prevValue);
284
+ const newValue = !isNaN(aspectRatio_) ? {
285
+ aspectRatio_: aspectRatio_
286
+ } : undefined;
287
+ if (newValue) aspectRatios.add(newValue);
288
+ prevValue = newValue;
289
+ // Do all calculations resulting of an update directly, for performance
290
+ const asArray = [...aspectRatios].map(({
291
+ aspectRatio_: aspectRatio_
292
+ }) => aspectRatio_);
293
+ const mostPrevalentAspectRatio = $gVhhF$depictaiutilishared.findMostFrequentItem(asArray);
294
+ setMostPrevalentAspectRatio(mostPrevalentAspectRatio);
295
+ }));
296
+ $gVhhF$solidjs.onCleanup(() => prevValue && aspectRatios.delete(prevValue));
297
+ }
298
+ $gVhhF$solidjs.onCleanup(() => gotCleanedUp = true);
299
+ $gVhhF$solidjsweb.spread(img, $gVhhF$solidjs.mergeProps(modifiedOtherAttributes), false, true);
300
+ $gVhhF$solidjs.createRenderEffect(() => {
301
+ props.src;
302
+ srcJustChanged = true;
303
+ });
304
+ $gVhhF$solidjs.createEffect(() => {
305
+ if (!sizeToUse()) return; // wait until we have a size, run every time the size changes
306
+ if (timeoutSet) return;
307
+ const handler = $gVhhF$depictaiutilishared.catchify(() => {
308
+ if (gotCleanedUp) return;
309
+ img.sizes = `${sizeToUse()}px`; // use latest available value of size in case it has changed
310
+ if (!sizeSetFastPath) {
311
+ setHasSetSize(true);
312
+ sizeSetFastPath = true;
500
313
  }
314
+ timeoutSet = false;
501
315
  });
316
+ // Throttle the updating of sizes to avoid unnecessary image re-fetches during resize
317
+ // And also because we first set src after sizes has been set, wait with loading and decoding images until browser is idle
318
+ // This greatly improves performance when loading (more) products
319
+ // See https://depictaiworkspace.slack.com/archives/C05NJ0K3WRL/p1698220617663139
320
+ timeoutSet = true; // Important to set timeoutSet before the call to executeWhenIdleBatched since if this is an important image executeWhenIdleBatched will execute fn immediately
321
+ executeWhenIdleBatched(handler, true);
502
322
  });
503
- return get_wrapped_blocks;
504
- }
505
- /**
506
- * Basically adds support for async content block functions and shows a rudimentary placeholder if rendering is delayed. Required due to react having a rendering delay.
507
- */
508
- function $d92f338ba8f9da9b$var$RenderContent(props) {
509
- const [resource] = $gVhhF$solidjs.createResource(() => props.content,
510
- // Could be reactive
511
- fn => fn());
323
+ const dontLoadImageYet = $gVhhF$solidjs.createMemo(() => !sizeToUse() || !hasSetSize()); // Moving this condition into a memo saves us from re-setting src and srcset (expensive) if just props.size changes
512
324
  $gVhhF$solidjs.createEffect(() => {
513
- if (resource.error) console.error("Error in content block rendering", resource.error);
325
+ if (dontLoadImageYet()) return; // If size isn't known or set, don't assign src yet since it would cause the most low res image to load
326
+ Object.assign(img, src_options());
327
+ // Add to DOM once decoded to not require/block any paints before image has completed loading and avoid showing broken image symbol
328
+ // We can do this because there has to be a container around us giving us a size
329
+ // No need to listen if we're already in the DOM
330
+ if (hasDecodedFastPath) return;
331
+ if (shouldActuallyEnableLazyLoading()) {
332
+ // For lazy loading there's no decoding to wait for as it only occurs when the image is visible
333
+ // Make sure src is set before we put the image into the DOM though so no broken image symbol is shown
334
+ setHasDecoded(true);
335
+ hasDecodedFastPath = true;
336
+ return;
337
+ }
338
+ img.decode().catch(e => $gVhhF$depictaiutilishared.dwarn("Failed to decode image", e, img)) // Display the image also if loading failed so webdev can debug and doesn't get "no image"
339
+ .then(async () => {
340
+ // Also wait with inserting image (i.e. paint) until browser is idle
341
+ await new Promise(r => executeWhenIdleBatched(r, false));
342
+ if (gotCleanedUp) return;
343
+ setHasDecoded(true);
344
+ hasDecodedFastPath = true;
345
+ });
514
346
  });
515
- // The suspense is for when there's an async content block (which we have to support due to react having a rendering delay) so that we can show some height while it's loading (should kinda never be visible)
516
- return $gVhhF$solidjsweb.createComponent((0, $gVhhF$solidjs.Suspense), {
517
- get fallback() {
518
- return $gVhhF$solidjsweb.createComponent((0, $3bc4a89b971a3af5$export$3f37af283f8fb843), {
519
- width: "100%",
520
- height: "max(100%, 400px)"
521
- });
347
+ return $gVhhF$solidjsweb.createComponent((0, $gVhhF$solidjs.Show), {
348
+ get when() {
349
+ return hasDecoded();
522
350
  },
523
- children: resource
524
- });
525
- }
526
- let $8bf678c45948a297$var$elementsHoveredSignal;
527
- let $8bf678c45948a297$var$consumersSignal;
528
- function $8bf678c45948a297$export$d4d96868197be50e() {
529
- if (!$8bf678c45948a297$var$elementsHoveredSignal || !$8bf678c45948a297$var$consumersSignal) $gVhhF$solidjs.createRoot(dispose => {
530
- $8bf678c45948a297$var$elementsHoveredSignal = $gVhhF$solidjs.createSignal(new Set());
531
- $8bf678c45948a297$var$consumersSignal = $gVhhF$solidjs.createSignal(0);
532
- const [consumers] = $8bf678c45948a297$var$consumersSignal;
533
- // No matter how often useMobileHover is called, only add one listener and get hovered elements once per touchmove event, for efficiency
534
- $8bf678c45948a297$var$doHeavyCalculations();
535
- $gVhhF$solidjs.createEffect(() => {
536
- // No-one needs information anymore, stop listening
537
- const users = consumers();
538
- if (users < 1) {
539
- $gVhhF$depictaiutilisharedlatest.dlog("Stopping to listen for mobile hover");
540
- dispose();
541
- $8bf678c45948a297$var$elementsHoveredSignal = undefined;
542
- $8bf678c45948a297$var$consumersSignal = undefined;
543
- }
544
- });
351
+ children: img
545
352
  });
546
- const [elementsHovered] = $8bf678c45948a297$var$elementsHoveredSignal;
547
- const [, setConsumers] = $8bf678c45948a297$var$consumersSignal;
548
- setConsumers(prev => prev + 1);
549
- $gVhhF$solidjs.onCleanup(() => setConsumers(prev => prev - 1));
550
- return elementsHovered;
551
353
  }
552
- function $8bf678c45948a297$var$doHeavyCalculations() {
553
- $gVhhF$depictaiutilisharedlatest.dlog("Starting to listen for mobile hover");
554
- const [, setElementsHovered] = $8bf678c45948a297$var$elementsHoveredSignal;
555
- const elementAtTouch = new Map();
556
- const updateSet = () => setElementsHovered(new Set([...elementAtTouch.values()].filter(v => v)));
557
- const setPositionHandler = $gVhhF$depictaiutilisharedlatest.catchify(evt => {
558
- for (const touch of evt.changedTouches) elementAtTouch.set(touch.identifier, document.elementFromPoint(touch.clientX, touch.clientY));
559
- updateSet();
560
- });
561
- const deleteTouchHandler = $gVhhF$depictaiutilisharedlatest.catchify(evt => {
562
- for (const touch of evt.changedTouches) elementAtTouch.delete(touch.identifier);
563
- updateSet();
564
- });
565
- addEventListener("touchstart", setPositionHandler, {
566
- passive: true
567
- });
568
- addEventListener("touchend", deleteTouchHandler, {
569
- passive: true
570
- });
571
- addEventListener("touchcancel", deleteTouchHandler, {
572
- passive: true
573
- });
574
- $gVhhF$solidjs.onCleanup(() => {
575
- removeEventListener("touchstart", setPositionHandler);
576
- removeEventListener("touchend", deleteTouchHandler);
577
- removeEventListener("touchcancel", deleteTouchHandler);
578
- });
354
+ function $19950faaac026e09$var$make_srcset(url, resolutions, customGenerateImageUrl_) {
355
+ return resolutions.map(width => `${(customGenerateImageUrl_ || $19950faaac026e09$export$1d6267828870adda)(url, width)} ${width}w`).join(", ");
579
356
  }
580
- function $55cbacb79d3e0cea$export$54299a3c81f5425() {
581
- const d = document;
582
- const [isVisible, setIsVisible] = $gVhhF$solidjs.createSignal(d.visibilityState !== "hidden");
583
- const handleVisibilityChange = () => setIsVisible(d.visibilityState !== "hidden");
584
- d.addEventListener("visibilitychange", handleVisibilityChange);
585
- $gVhhF$solidjs.onCleanup(() => d.removeEventListener("visibilitychange", handleVisibilityChange));
586
- return isVisible;
357
+ function $19950faaac026e09$export$1d6267828870adda(url, width) {
358
+ // Even just creating one `URL` object per image src and then setting the search parameter is quite slow (0.25ms to set the width param)
359
+ // We're making the assumptions that the url has already at least one query parameter and that the image resizing service will choose the last one out of a duplicate width parameter (shopify's does)
360
+ // That way we can just to string concatenation which is insanely fast
361
+ return url + "&width=" + encodeURIComponent(width);
587
362
  }
588
- var $0f313083912e447a$var$_tmpl$ = /*#__PURE__*/$gVhhF$solidjsweb.template(`<svg xmlns=http://www.w3.org/2000/svg viewBox="0 0 14 26"><path d="M2 24.656c-.078 0-.156-.016-.234-.047s-.133-.066-.187-.129c-.117-.117-.176-.258-.176-.422s.059-.305.176-.422l10.371-10.371L1.578 2.883c-.117-.117-.176-.258-.176-.422s.059-.305.176-.422.258-.176.422-.176.305.059.422.176l10.805 10.793a.59.59 0 0 1 .176.434c0 .172-.059.305-.176.422L2.422 24.48a.42.42 0 0 1-.199.129.54.54 0 0 1-.223.047zm0 0"fill=#0f0f0f>`);
589
- function $0f313083912e447a$export$8be2714ec7f169ee(props = {}) {
363
+ var $9fe9883b1d8ecccc$var$_tmpl$ = /*#__PURE__*/$gVhhF$solidjsweb.template(`<span>`);
364
+ function $9fe9883b1d8ecccc$export$acb3eaade4b23afd(props) {
365
+ // Work around https://github.com/solidjs/solid/issues/1980
590
366
  return (() => {
591
- var _el$ = $gVhhF$solidjsweb.getNextElement($0f313083912e447a$var$_tmpl$);
367
+ var _el$ = $gVhhF$solidjsweb.getNextElement($9fe9883b1d8ecccc$var$_tmpl$);
592
368
  $gVhhF$solidjsweb.effect(_p$ => {
593
- var _v$ = props.width_ ?? 14,
594
- _v$2 = props.height_ ?? 26;
595
- _v$ !== _p$.e && $gVhhF$solidjsweb.setAttribute(_el$, "width", _p$.e = _v$);
596
- _v$2 !== _p$.t && $gVhhF$solidjsweb.setAttribute(_el$, "height", _p$.t = _v$2);
369
+ var _v$ = `${props.class || ""} depict-placeholder`,
370
+ _v$2 = props.style;
371
+ _v$ !== _p$.e && $gVhhF$solidjsweb.className(_el$, _p$.e = _v$);
372
+ _p$.t = $gVhhF$solidjsweb.style(_el$, _v$2, _p$.t);
597
373
  return _p$;
598
374
  }, {
599
375
  e: undefined,
@@ -602,1205 +378,1442 @@ function $0f313083912e447a$export$8be2714ec7f169ee(props = {}) {
602
378
  return _el$;
603
379
  })();
604
380
  }
605
- var $a0e53f99354809d9$var$_tmpl$ = /*#__PURE__*/$gVhhF$solidjsweb.template(`<div>`),
606
- $a0e53f99354809d9$var$_tmpl$2 = /*#__PURE__*/$gVhhF$solidjsweb.template(`<div class="depict-slider depict"><button aria-label="Scroll left"class="left d-navbutton"type=button></button><button aria-label="Scroll right"class="right d-navbutton"type=button></button><div class=sliding>`);
607
- const $a0e53f99354809d9$var$SlidableContext = /*@__PURE__*/$gVhhF$solidjs.createContext();
608
- function $a0e53f99354809d9$export$6649caad96ef8870(props) {
609
- const {
610
- slider_ref_: slider_ref_,
611
- faded_opacity_ = 0.7,
612
- mutation_plugins_ = [],
613
- disable_fading_: disable_fading_,
614
- snapAlign_: snapAlign_,
615
- fadingThreshold_ = 0.96
616
- } = props;
617
- const [hasHydrated, setHasHydrated] = $gVhhF$solidjs.createSignal(false);
618
- $gVhhF$solidjs.createEffect(() => setHasHydrated(true));
619
- const MakeButton = () => (() => {
620
- var _el$ = $gVhhF$solidjsweb.getNextElement($a0e53f99354809d9$var$_tmpl$);
621
- $gVhhF$solidjsweb.insert(_el$, $gVhhF$solidjsweb.createComponent((0, $0f313083912e447a$export$8be2714ec7f169ee), {
622
- get height_() {
623
- return props.arrow_height_;
624
- },
625
- get width_() {
626
- return props.arrow_width_;
627
- }
628
- }));
629
- return _el$;
630
- })();
631
- // To support SSR it would be very hard to make Amazing_Slider compatible with solid's SSR because it would have to be rewritten in a more JSX way
632
- // However, all it would do on the server (when used by SlidableItems) is creating the following 5 div elements
633
- // Therefore, we just bail on the server (and first render on the client which is the hydration) and return a static version of the slider
634
- // Once we have hydrated, we construct and insert the actual slider
635
- return $gVhhF$solidjsweb.createComponent((0, $gVhhF$solidjs.Show), {
636
- get when() {
637
- return hasHydrated() && !(0, $gVhhF$solidjsweb.isServer);
638
- },
639
- get fallback() {
640
- return (() => {
641
- var _el$2 = $gVhhF$solidjsweb.getNextElement($a0e53f99354809d9$var$_tmpl$2),
642
- _el$3 = _el$2.firstChild,
643
- _el$4 = _el$3.nextSibling,
644
- _el$5 = _el$4.nextSibling;
645
- $gVhhF$solidjsweb.insert(_el$3, $gVhhF$solidjsweb.createComponent(MakeButton, {}));
646
- $gVhhF$solidjsweb.insert(_el$4, $gVhhF$solidjsweb.createComponent(MakeButton, {}));
647
- $gVhhF$solidjsweb.insert(_el$5, () => props.children);
648
- return _el$2;
649
- })();
381
+ function $3bc4a89b971a3af5$export$3f37af283f8fb843(props) {
382
+ const style = $gVhhF$solidjs.createMemo(() => {
383
+ const {
384
+ style: style,
385
+ height: height,
386
+ width: width,
387
+ aspectRatio: aspectRatio
388
+ } = props;
389
+ const is_image = aspectRatio != undefined;
390
+ const size_data = is_image ? {
391
+ width: "100%",
392
+ "padding-bottom": `calc(calc(1 / ${aspectRatio}) * 100%)`
393
+ } : {
394
+ width: width,
395
+ height: height
396
+ };
397
+ return {
398
+ display: "block",
399
+ ...(style || {}),
400
+ ...size_data
401
+ };
402
+ });
403
+ return $gVhhF$solidjsweb.createComponent((0, $9fe9883b1d8ecccc$export$acb3eaade4b23afd), {
404
+ get style() {
405
+ return style();
650
406
  },
651
- get children() {
652
- return (() => {
653
- const [intersectionEntries_, setIntersectionEntries] = $gVhhF$solidjs.createSignal([]);
654
- const fading_plugin = disable_fading_ ? undefined : $gVhhF$depictaiutilishared.slider_fading_factory({
655
- threshold_: fadingThreshold_,
656
- faded_opacity_: faded_opacity_
657
- });
658
- const slider = new (0, $gVhhF$depictaiutilishared.Amazing_Slider)({
659
- button_contents: {
660
- left: [MakeButton()],
661
- right: [MakeButton()]
662
- },
663
- constructor_plugins: [$gVhhF$depictaiutilishared.slider_snap_css(0, {
664
- align: snapAlign_
665
- }).constructor_plugin, ...(fading_plugin ? [fading_plugin.constructor_plugin] : [])],
666
- mutation_plugins: [...(fading_plugin ? [fading_plugin.mutation_plugin_] : []), ...mutation_plugins_, entries => setIntersectionEntries(entries)]
667
- });
668
- $gVhhF$solidjsweb.insert(slider.insert_here, () => $gVhhF$solidjsweb.createComponent($a0e53f99354809d9$var$SlidableContext.Provider, {
669
- value: {
670
- slider_: slider,
671
- intersectionEntries_: intersectionEntries_
672
- },
673
- get children() {
674
- return props.children;
675
- }
676
- }));
677
- $gVhhF$solidjs.onCleanup(() => slider.observer.disconnect());
678
- slider_ref_?.(slider);
679
- return slider.container;
680
- })();
407
+ get ["class"]() {
408
+ return props.class;
681
409
  }
682
410
  });
683
411
  }
684
- function $a0e53f99354809d9$export$eaa625fb39439e37() {
685
- return $gVhhF$solidjs.useContext($a0e53f99354809d9$var$SlidableContext);
686
- }
687
-
688
- /**
689
- * Gets product_id (the thing we key the rendering on) from a display
690
- */
691
- function $a5871dacf261adf8$export$548d053aba053d79(display) {
692
- if ("variant_index" in display) {
693
- const actual_display = display.variant_displays[display.variant_index];
694
- return actual_display.product_id;
695
- }
696
- if ("listing_id" in display) return display.listing_id;
697
- return display.product_id;
698
- }
699
- function $5f7d55c710fd1bc2$export$2f0cbfbb4354bb52(elements) {
700
- // FYI this probably doesn't work on hugely deep nestings or large amount of elements
701
- return $gVhhF$solidjs.createMemo(() => $5f7d55c710fd1bc2$var$unwrapping_helper(elements));
702
- }
703
- function $5f7d55c710fd1bc2$var$unwrapping_helper(elements) {
704
- const result = [];
705
- if (Array.isArray(elements)) for (let i = 0; i < elements.length; i++) {
706
- const element = elements[i];
707
- result.push(...$5f7d55c710fd1bc2$var$unwrapping_helper(element));
708
- } else if (typeof elements == "function") result.push(...$5f7d55c710fd1bc2$var$unwrapping_helper(elements()));else if (elements != undefined && typeof elements != "boolean") result.push(elements);
709
- return result;
710
- }
711
- const $129b9b9c010039d0$var$ProductCardIndexContext = /*@__PURE__*/$gVhhF$solidjs.createContext();
712
- function $129b9b9c010039d0$export$11354f017ddc9790(props) {
713
- return $gVhhF$solidjsweb.createComponent($129b9b9c010039d0$var$ProductCardIndexContext.Provider, {
714
- get value() {
715
- return props.index_;
412
+ function $061ff5b4c3d36171$export$89c6019e4baa40b1(props) {
413
+ const style = $gVhhF$solidjs.createMemo(() => {
414
+ const {
415
+ style: style,
416
+ height: height,
417
+ width: width
418
+ } = props;
419
+ return {
420
+ ...(style || {}),
421
+ height: height ?? "1em",
422
+ ...(width && {
423
+ width: width
424
+ })
425
+ };
426
+ });
427
+ return $gVhhF$solidjsweb.createComponent((0, $9fe9883b1d8ecccc$export$acb3eaade4b23afd), {
428
+ get style() {
429
+ return style();
716
430
  },
717
- get children() {
718
- return props.children;
431
+ get ["class"]() {
432
+ return props.class;
719
433
  }
720
434
  });
721
435
  }
722
- function $129b9b9c010039d0$export$e3faf4d95300054f() {
723
- return $gVhhF$solidjs.useContext($129b9b9c010039d0$var$ProductCardIndexContext);
436
+ var $6f604560873e4f69$var$_tmpl$ = /*#__PURE__*/$gVhhF$solidjsweb.template(`<div class=instant-card><div class=img-part></div><div class=right-part><div class=title-brand-tagline><span class=title></span></div><div class=price-container>`);
437
+ const $6f604560873e4f69$export$8a2234b5b1a5ebd4 = "--image-aspect-ratio";
438
+ function $6f604560873e4f69$export$ee1733d9a8495996() {
439
+ return (() => {
440
+ var _el$ = $gVhhF$solidjsweb.getNextElement($6f604560873e4f69$var$_tmpl$),
441
+ _el$2 = _el$.firstChild,
442
+ _el$3 = _el$2.nextSibling,
443
+ _el$4 = _el$3.firstChild,
444
+ _el$5 = _el$4.firstChild,
445
+ _el$6 = _el$4.nextSibling;
446
+ $gVhhF$solidjsweb.insert(_el$2, $gVhhF$solidjsweb.createComponent((0, $3bc4a89b971a3af5$export$3f37af283f8fb843), {
447
+ aspectRatio: `var(${$6f604560873e4f69$export$8a2234b5b1a5ebd4}, 1)`
448
+ }));
449
+ $gVhhF$solidjsweb.insert(_el$5, $gVhhF$solidjsweb.createComponent((0, $061ff5b4c3d36171$export$89c6019e4baa40b1), {
450
+ width: "20ch",
451
+ height: "1em"
452
+ }));
453
+ $gVhhF$solidjsweb.insert(_el$6, $gVhhF$solidjsweb.createComponent((0, $061ff5b4c3d36171$export$89c6019e4baa40b1), {
454
+ width: "5ch",
455
+ height: "1em",
456
+ "class": "price"
457
+ }));
458
+ return _el$;
459
+ })();
724
460
  }
725
-
726
- /**
727
- * This is a function that will safely clone an object that may contain promises.
728
- * It will use structuredClone to clone the object and then re-apply (copy) any promises that were in the original object to the cloned object.
729
- * @param obj Anything to clone
730
- */
731
- function $ac946c6b782bb900$export$83bb9af5671e1dbe(obj) {
732
- if (obj instanceof Promise) return obj;
733
- // Extract all promises, keep a map of them, and then re-apply them to both the original object and the target object after cloning
734
- const seen = new Set();
735
- const keyStack = [[]];
736
- const valueStack = [obj];
737
- const promiseLocationStack = [];
738
- while (valueStack.length) {
739
- const value = valueStack.pop();
740
- const keyPath = keyStack.pop();
741
- // Promises can only be in objects and we don't need to check for objects we've already seen (like in circular references)
742
- if (typeof value !== "object" || value === null || seen.has(value)) continue;
743
- seen.add(value);
744
- if (Array.isArray(value)) {
745
- for (let i = 0; i < value.length; i++) {
746
- const ourValue = value[i];
747
- const valuesKeyPath = [...keyPath, i];
748
- if (ourValue instanceof Promise) {
749
- promiseLocationStack.push([valuesKeyPath, ourValue]);
750
- delete value[i]; // This will leave a hole in the array, but it's fine since we'll put it back soon
751
- // This way both the indexes will be correct when putting the value back and our for loop doesn't get messed up
752
- } else {
753
- keyStack.push(valuesKeyPath);
754
- valueStack.push(ourValue);
755
- }
756
- }
757
- continue;
461
+ function $128302ab61dab8f1$export$54c6d6ff54e72743(whatToWatch) {
462
+ const [width, setWidth] = $gVhhF$solidjs.createSignal(0);
463
+ const [height, setHeight] = $gVhhF$solidjs.createSignal(0);
464
+ const resizeObserver = new ResizeObserver($gVhhF$depictaiutilishared.catchify(records => $gVhhF$solidjs.batch(() => {
465
+ for (let j = 0; j < records.length; j++) {
466
+ const {
467
+ width: width,
468
+ height: height
469
+ } = records[j].contentRect;
470
+ // Padding hack makes that we can't use contentRect to get the height since that's apparently without padding
471
+ const definitiveHeight = height || whatToWatch.getBoundingClientRect().height;
472
+ if (definitiveHeight) setHeight(Math.ceil(definitiveHeight));
473
+ if (width) setWidth(Math.ceil(width));
758
474
  }
759
- for (const innerKey in value) {
760
- const ourValue = value[innerKey];
761
- const valuesKeyPath = [...keyPath, innerKey];
762
- if (ourValue instanceof Promise) {
763
- promiseLocationStack.push([valuesKeyPath, ourValue]);
764
- delete value[innerKey];
765
- } else {
766
- keyStack.push(valuesKeyPath);
767
- valueStack.push(ourValue);
475
+ })));
476
+ // If we start observing elements before they're in the DOM Chrome (Version 117.0.5938.92 (Official Build) (arm64)) sometimes gives us a resize event with a width of 0 and then doesn't dispatch one with the actual width once it's in the DOM. So we wait until the element is in the DOM before we start observing it.
477
+ // It doesn't seem to work even when waiting for it to be in the DOM, also wait an animation frame
478
+ const disconnect = (0, $gVhhF$depictaiutilishared.observer).onexists(whatToWatch, ({
479
+ element: element
480
+ }) => requestAnimationFrame($gVhhF$depictaiutilishared.catchify(() => resizeObserver.observe(element))));
481
+ $gVhhF$solidjs.onCleanup(() => {
482
+ resizeObserver.disconnect();
483
+ disconnect();
484
+ });
485
+ return [width, height];
486
+ }
487
+ const $a33065cfed827fc0$var$backupSizeSignal = /*@__PURE__*/$gVhhF$solidjs.createSignal();
488
+ const $a33065cfed827fc0$export$d7b7ab832c20e2f4 = /*@__PURE__*/Symbol("ShopifyContainedImage");
489
+ function $a33065cfed827fc0$export$e4b984597b71e989(props) {
490
+ const context = $84798aaa863a4909$export$8d0c4fdbb05a2f2e();
491
+ if (context && !context[$a33065cfed827fc0$export$d7b7ab832c20e2f4])
492
+ // create a signal that's the same for all items in this listing
493
+ context[$a33065cfed827fc0$export$d7b7ab832c20e2f4] = $gVhhF$solidjs.createSignal();
494
+ const [currentImageSize, setCurrentImageSize] = context?.[$a33065cfed827fc0$export$d7b7ab832c20e2f4] || $a33065cfed827fc0$var$backupSizeSignal; // use global fallback signal (might lead to blurry images) if no context is available
495
+ const [mostPrevalentAspectRatio] = context ?
496
+ // If the context exists we have to create the signal here as getting it here would be too late when it's created by ShopifyResponsiveImg below
497
+ context[(0, $19950faaac026e09$export$6066eb417ed2e9f7)] ||= $gVhhF$solidjs.createSignal() : [];
498
+ const container = $93304d59a9747aa6$export$94026ee33dbabc26({
499
+ // Write getters manually instead of using JSX, so we can do more complex logic in the aspectRatio getter without increasing bundle size
500
+ get class() {
501
+ return props.class;
502
+ },
503
+ get aspectRatio() {
504
+ if (props.autoAdjustAspectRatio) {
505
+ const targetRatio = mostPrevalentAspectRatio?.();
506
+ if (targetRatio != undefined) return `var(${(0, $6f604560873e4f69$export$8a2234b5b1a5ebd4)}, ${targetRatio})`;
768
507
  }
508
+ return props.aspectRatio;
769
509
  }
770
- }
771
- let structuredClonedObject;
772
- let error;
773
- try {
774
- structuredClonedObject = structuredClone(obj);
775
- } catch (e) {
776
- // We still need to put back the promises in the original object, even if we threw an error
777
- error = e;
778
- }
779
- // Put back the promises in both the original object and the cloned object
780
- while (promiseLocationStack.length) {
781
- const [keyPath, promise] = promiseLocationStack.pop();
782
- let targetInClone = structuredClonedObject;
783
- let targetInOriginal = obj;
784
- for (let i = 0; i < keyPath.length - 1; i++) {
785
- if (!error) targetInClone = targetInClone[keyPath[i]];
786
- targetInOriginal = targetInOriginal[keyPath[i]];
510
+ });
511
+ const owner = $gVhhF$solidjs.getOwner();
512
+ let createdSizeAccessor = false;
513
+ let wasCleanedUp = false;
514
+ $gVhhF$solidjs.createEffect(() => {
515
+ if (currentImageSize() || wasCleanedUp) return; // We get into the effect when onCleanup is called if we don't have wasCleanedUp
516
+ // ensure we always have a size accessor
517
+ // what this whole thing does is set the sizes attribute to the actual size of one of the existing image containers, therefore the value doesn't have to manually be specified
518
+ createdSizeAccessor = true;
519
+ setCurrentImageSize(() => $gVhhF$solidjs.runWithOwner(owner, () => $128302ab61dab8f1$export$54c6d6ff54e72743(container)));
520
+ });
521
+ $gVhhF$solidjs.onCleanup(() => {
522
+ wasCleanedUp = true; // important to set this before the function call below and always, so the dead effects don't create dead size accessors
523
+ if (!createdSizeAccessor) return;
524
+ $gVhhF$depictaiutilishared.queueMacroTask(() => setCurrentImageSize()); // unset the size accessor next event loop iteration, so that all onCleanup's have time to run, since the effects run more aggressively and can create a size accessor with a cleaned up owner otherwise
525
+ });
526
+
527
+ $gVhhF$solidjsweb.insert(container, $gVhhF$solidjsweb.createComponent((0, $19950faaac026e09$export$6e5611bc7e57e477), $gVhhF$solidjsweb.mergeProps({
528
+ size: () => currentImageSize()?.[0]?.() || 0,
529
+ sizeHeight: () => currentImageSize()?.[1]?.() || 0,
530
+ get src() {
531
+ return props.src;
787
532
  }
788
- if (!error) targetInClone[keyPath[keyPath.length - 1]] = promise;
789
- targetInOriginal[keyPath[keyPath.length - 1]] = promise;
790
- }
791
- if (error) throw error;
792
- return structuredClonedObject;
533
+ }, () => props.imgProps, {
534
+ get style() {
535
+ return props.imgProps?.style;
536
+ }
537
+ })));
538
+ return container;
793
539
  }
794
- let $a73063cc642f3e5f$var$printedCloneError = false;
795
- function $a73063cc642f3e5f$export$7f8bd308237ab5a5(get_displays_) {
796
- const displays_by_key_ = $gVhhF$solidjs.createMemo(() => {
797
- const displays = get_displays_();
798
- // We key by product_id because that's what can be used to uniquely identify products
799
- // This way <For> can optimize the rendering
800
- const map = new Map();
801
- for (let i = 0; i < displays.length; i++) {
802
- const display = displays[i];
803
- const product_id = $a5871dacf261adf8$export$548d053aba053d79(display);
804
- map.set(product_id, display);
540
+ const $d212a67662c5cf38$var$customUrls = (url, width) => {
541
+ if (url?.startsWith?.("https://cdn.shopify.com"))
542
+ // The shopify image resizing CDN blocks Cloudflare's image resizer. It's possible that we can't detect shopify image URLs sometimes because they go to the merchant's domain,
543
+ // but it seems like the URLs returned by the Depict API always goes to the cdn.shopify.com version of an image, so this simple workaround works.
544
+ return $19950faaac026e09$export$1d6267828870adda(url, width);
545
+ // Use cloudflare image resizer instead of img.depict.ai because it's a lot faster (and also auto-chooses other formats if webp is not supported)
546
+ return `https://app.depict.ai/cdn-cgi/image/width=${width},format=auto/${url}`;
547
+ };
548
+ function $d212a67662c5cf38$export$9d4a4b669b1929ab(props) {
549
+ return $gVhhF$solidjsweb.createComponent((0, $a33065cfed827fc0$export$e4b984597b71e989), $gVhhF$solidjsweb.mergeProps(props, {
550
+ get imgProps() {
551
+ return {
552
+ ...props.imgProps,
553
+ customUrls: $d212a67662c5cf38$var$customUrls
554
+ };
555
+ }
556
+ }));
557
+ }
558
+ function $d212a67662c5cf38$export$140423fa4bfb6d63(props) {
559
+ return $gVhhF$solidjsweb.createComponent((0, $19950faaac026e09$export$6e5611bc7e57e477), $gVhhF$solidjsweb.mergeProps(props, {
560
+ customUrls: $d212a67662c5cf38$var$customUrls
561
+ }));
562
+ }
563
+
564
+ // Value first so shopify merchants can set (extra) display transformers in the shopify app
565
+ // Calling these functions normally doesn't do anything, but our shopify app respects them
566
+ // Them being here is value first too because this can be easily imported in the product card editor
567
+ let $606c221d98ba4d92$var$extraDisplayTransformers;
568
+ const $606c221d98ba4d92$export$b2d94be4f02525b2 = () => $606c221d98ba4d92$var$extraDisplayTransformers;
569
+ const $606c221d98ba4d92$export$f98b8d5ea7207a1 = displayTransformers => $606c221d98ba4d92$var$extraDisplayTransformers = displayTransformers;
570
+ function $b54e6192d0caa293$export$d9f36cf74e24d595(media_query) {
571
+ const signal = $gVhhF$solidjs.createSignal(false);
572
+ $gVhhF$solidjs.onCleanup($gVhhF$depictaiutilishared.javascript_media_query(media_query, ({
573
+ matches: matches
574
+ }) => signal[1](matches)).remove);
575
+ return signal[0];
576
+ }
577
+ function $17d06b3bcb035500$export$a79e666719b3647c(cols_at_size_) {
578
+ const size_active = $gVhhF$solidjs.createMemo(() => cols_at_size_().map(([n_cols, min_width, max_width]) => {
579
+ const media_query = $gVhhF$depictaiutilishared.layout_media_query_header(min_width, max_width).replaceAll("@media", "");
580
+ const matches = $b54e6192d0caa293$export$d9f36cf74e24d595(media_query);
581
+ return [matches, n_cols];
582
+ }));
583
+ const n_cols_currently_showing = $gVhhF$solidjs.createMemo(() => {
584
+ try {
585
+ const [, n_cols_currently_showing] = size_active().find(([media_query]) => media_query());
586
+ return n_cols_currently_showing;
587
+ } catch (e) {
588
+ $gVhhF$depictaiutilishared.dwarn("No active size found in columnsAtSize - can't render content blocks or looks");
589
+ return null;
805
590
  }
806
- return map;
807
591
  });
808
- return displays_by_key_;
592
+ return n_cols_currently_showing;
809
593
  }
810
- const $a73063cc642f3e5f$var$result_ids = ["search_result_id", "product_listing_result_id", "recommendation_id"];
811
- function $a73063cc642f3e5f$export$43f0fb951fcb0c54({
812
- displays_by_key_: displays_by_key_,
813
- product_card_template_: product_card_template_,
814
- do_per_elements_: do_per_elements_,
815
- content_blocks_: content_blocks_
816
- }) {
817
- return $gVhhF$solidjsweb.createComponent((0, $gVhhF$solidjs.For), {
818
- get each() {
819
- return [...displays_by_key_().keys()];
594
+
595
+ // Errors caused by SDK consuming code interfacing with the SDK, not the SDK itself or other Depict packages
596
+ class $7dd1edb2ee2b43ad$export$e1d52e4f0c7557a1 extends Error {}
597
+ class $7dd1edb2ee2b43ad$export$aa14a9f43128f61f extends $7dd1edb2ee2b43ad$export$e1d52e4f0c7557a1 {
598
+ constructor(templateError) {
599
+ super("Uncaught error in product card template", {
600
+ cause: templateError
601
+ });
602
+ }
603
+ }
604
+ var $edafe91daf3091d7$var$_tmpl$ = /*#__PURE__*/$gVhhF$solidjsweb.template(`<div><!$><!/><!$><!/>`);
605
+ const $edafe91daf3091d7$var$ExtraInfoContext = /*@__PURE__*/$gVhhF$solidjs.createContext();
606
+ function $edafe91daf3091d7$export$240ecf5363d76c0f(props) {
607
+ return $gVhhF$solidjsweb.createComponent($edafe91daf3091d7$var$ExtraInfoContext.Provider, {
608
+ get value() {
609
+ return props.getContext_;
820
610
  },
821
- children: (product_id, index) => {
822
- const owner = $gVhhF$solidjs.getOwner();
823
- const possible_content_block = $gVhhF$solidjs.createMemo(() => content_blocks_?.()?.()?.[index()]);
824
- const display_accessor = $gVhhF$solidjs.createMemo(prev => {
825
- const new_display = displays_by_key_().get(product_id);
826
- if (prev && $a73063cc642f3e5f$var$deep_equal(prev, new_display))
827
- // If displays are identical, they haven't changed due to merchant/market changing OR we made a new query and the displays are the same. Return the old display (same object reference) to avoid re-rendering.
828
- // This doesn't work if people mutate the displays from within the template but we hope they don't
829
- // (We can't know if the display changing is due to merchant/market changing or a new query otherwise and don't want to run into https://gitlab.com/depict-ai/depict.ai/-/merge_requests/6890/diffs)
830
- return prev;
831
- return new_display;
832
- });
833
- const [product_card_resource] = $gVhhF$solidjs.createResource(display_accessor,
834
- // re-render when the reference to the display object changes
835
- display => {
836
- // It's easy for merchants to accidentally mutate the display or some sub-object thereof
837
- // We had a case on singular where they popped an image url and due to use caching after some time there was no image
838
- // Attempt cloning the displays if possible (99.9% of the cases probably) and if not, just use the original display
839
- try {
840
- // Use promiseSafeStructuredClone so that:
841
- // 1. When fetching for example prices from an API, one can start that API request
842
- // 2. Add the promise to the display
843
- // 3. Already render the product card with placeholders for prices while the price loads
844
- // 4. When the price loads, re-render the display with the new price
845
- display = $ac946c6b782bb900$export$83bb9af5671e1dbe(display);
846
- } catch (e) {
847
- if (!$a73063cc642f3e5f$var$printedCloneError) {
848
- $a73063cc642f3e5f$var$printedCloneError = true;
849
- $gVhhF$depictaiutilishared.dlog("Can't clone display", display, " (uncloneable objects in it), make sure you don't mutate it in the template since we cache.", e);
850
- }
851
- }
852
- let renderingResult;
853
- // Wrap the product card in a ProductCardIndexProvider so that the index is available to the product card
854
- // This is wo we can do intelligent image loading in ShopifyContainedImage without shopify devs having to pass in the index
855
- // We don't care about the return value of ProductCardIndexProvider because our rendering result might be a promise which solid doesn't support until it has gone through createResource
856
- $129b9b9c010039d0$export$11354f017ddc9790({
857
- index_: index,
858
- get children() {
859
- // This is the key - the functions called inside of the children getter of the provider are the one that have the context defined in their solid-internal Owner
860
- renderingResult = product_card_template_(display, {
861
- set_on_index_change: fn => $gVhhF$solidjs.runWithOwner(owner, () => $gVhhF$solidjs.createEffect(() => fn(index())))
862
- });
863
- return undefined; // just to satisfy the typing
864
- }
865
- });
866
- // renderingResult is guaranteed to be defined here (if rendering returned something) because the children getter of ProductCardIndexProvider is called synchronously (createRenderEffect is used solid-internally)
867
- return renderingResult;
868
- });
869
- const node_accessor = $5f7d55c710fd1bc2$export$2f0cbfbb4354bb52(product_card_resource);
870
- $gVhhF$solidjs.createEffect(() => {
871
- const product_card_nodes = node_accessor();
872
- const display = display_accessor();
873
- const elements = [];
874
- for (let i = 0; i < product_card_nodes.length; i++) {
875
- const element = product_card_nodes[i];
876
- if (!(element instanceof Element)) continue;
877
- elements.push(element);
878
- // Add tracking ids
879
- if (!(element instanceof HTMLElement)) continue; // Apparently only HTMLElements have dataset
880
- for (let i = 0; i < $a73063cc642f3e5f$var$result_ids.length; i++) {
881
- const id = $a73063cc642f3e5f$var$result_ids[i];
882
- const value = display[id];
883
- if (value) Object.assign(element.dataset, $gVhhF$depictaiutilishared.camelCasifyObject({
884
- [id]: value
885
- }));
886
- }
887
- }
888
- do_per_elements_?.(elements, index);
889
- });
890
- return [
891
- // Show content block before product card, so it's *at* that index
892
- possible_content_block, node_accessor // Don't return product_card_resource as calling it could be creating the nodes which would mean we'd be double-creating the nodes,
893
- ]; // Regarding the as unknown shenanigans: https://github.com/solidjs/solid/releases/tag/v1.7.0
611
+ get children() {
612
+ return props.children;
894
613
  }
895
614
  });
896
615
  }
897
- // source: https://medium.com/@pancemarko/deep-equality-in-javascript-determining-if-two-objects-are-equal-bf98cf47e934
898
- // deeply diffs two objects
899
- // Modified by chatGPT to not be recursive, since stiga was getting a stack overflow just due to the size of their data
900
- // Modified futher to bias towards saying objects are equal non-primitive objects differ
901
- function $a73063cc642f3e5f$var$deep_equal(obj1, obj2) {
902
- const stack = [[obj1, obj2]];
903
- while (stack.length) {
904
- const [currentObj1, currentObj2] = stack.pop();
905
- if (currentObj1 === currentObj2) continue;
906
- if (Array.isArray(currentObj1) && Array.isArray(currentObj2)) {
907
- if (currentObj1.length !== currentObj2.length) return false;
908
- for (let i = 0; i < currentObj1.length; i++) stack.push([currentObj1[i], currentObj2[i]]);
909
- } else if (typeof currentObj1 === "object" && typeof currentObj2 === "object" && currentObj1 !== null && currentObj2 !== null) {
910
- if (Array.isArray(currentObj1) || Array.isArray(currentObj2)) return false;
911
- const keys1 = Object.keys(currentObj1);
912
- const keys2 = Object.keys(currentObj2);
913
- if (keys1.length !== keys2.length || !keys1.every(key => keys2.includes(key))) return false;
914
- for (let key in currentObj1) {
915
- if ($a73063cc642f3e5f$var$result_ids.includes(key)) continue; // ignore comparing values for keys we know are changing with every request
916
- stack.push([currentObj1[key], currentObj2[key]]);
616
+ function $edafe91daf3091d7$export$b69d0d92862de18b(props) {
617
+ const getContextFunction = $gVhhF$solidjs.useContext($edafe91daf3091d7$var$ExtraInfoContext);
618
+ return $gVhhF$solidjs.ErrorBoundary({
619
+ fallback(err, retry) {
620
+ const {
621
+ message_: message_,
622
+ severity_: severity_
623
+ } = props;
624
+ let message = message_;
625
+ if (err instanceof (0, $7dd1edb2ee2b43ad$export$aa14a9f43128f61f)) {
626
+ console.error(err.cause);
627
+ message = `${message}: ${err.message}`;
628
+ } else $gVhhF$depictaiutilishared.report([err, message_], severity_, getContextFunction?.());
629
+ return (() => {
630
+ var _el$ = $gVhhF$solidjsweb.getNextElement($edafe91daf3091d7$var$_tmpl$),
631
+ _el$2 = _el$.firstChild,
632
+ [_el$3, _co$] = $gVhhF$solidjsweb.getNextMarker(_el$2.nextSibling),
633
+ _el$4 = _el$3.nextSibling,
634
+ [_el$5, _co$2] = $gVhhF$solidjsweb.getNextMarker(_el$4.nextSibling);
635
+ $gVhhF$solidjsweb.insert(_el$, message, _el$3, _co$);
636
+ $gVhhF$solidjsweb.insert(_el$, () => props.on_error_?.(retry), _el$5, _co$2);
637
+ $gVhhF$solidjsweb.effect(_$p => $gVhhF$solidjsweb.classList(_el$, Object.fromEntries(props.class_list_?.map(c => [c, true]) || []), _$p));
638
+ return _el$;
639
+ })();
640
+ },
641
+ get children() {
642
+ return props.children;
643
+ }
644
+ });
645
+ }
646
+ var $75777da1687ba3f6$var$_tmpl$ = /*#__PURE__*/$gVhhF$solidjsweb.template(`<div>`),
647
+ $75777da1687ba3f6$var$_tmpl$2 = /*#__PURE__*/$gVhhF$solidjsweb.template(`<style type=text/css>`);
648
+ const $75777da1687ba3f6$export$c500183c31c2b22e = "--spans-columns";
649
+ function $75777da1687ba3f6$export$ab6b147eec64fea5(props) {
650
+ const container_class = "x" + $gVhhF$solidjs.createUniqueId();
651
+ const container_element_ = (() => {
652
+ var _el$ = $gVhhF$solidjsweb.getNextElement($75777da1687ba3f6$var$_tmpl$);
653
+ $gVhhF$solidjsweb.spread(_el$, $gVhhF$solidjsweb.mergeProps(() => props.element_attributes, {
654
+ "classList": {
655
+ [container_class]: true
917
656
  }
918
- } else if (typeof currentObj1 !== "function" && typeof currentObj2 !== "function")
919
- // Claim that all functions are equal, since the only way a function ends up here is because someone added it in the displayTransformer and that shouldn't force us to re-render, right?
920
- return false;
657
+ }), false, true);
658
+ $gVhhF$solidjsweb.insert(_el$, () => props.children);
659
+ $gVhhF$solidjsweb.runHydrationEvents();
660
+ return _el$;
661
+ })(); // Don't want this in the error boundary below, it should only be for the actual layouting
662
+ return $gVhhF$solidjsweb.createComponent((0, $edafe91daf3091d7$export$b69d0d92862de18b), {
663
+ message_: "Failed calculating css grid layout",
664
+ severity_: "error",
665
+ get children() {
666
+ return $75777da1687ba3f6$export$e2f22da34aaf310a(props, container_element_, false, container_class);
667
+ }
668
+ });
669
+ }
670
+ function $75777da1687ba3f6$export$e2f22da34aaf310a(props, container_element, insert_after_container = false, container_class) {
671
+ if (!container_class) {
672
+ // only try to add if it wasn't provided, so that we have a code path without classList when doing SSR
673
+ container_class = $gVhhF$depictaiutilishared.make_random_classname();
674
+ container_element.classList.add(container_class);
921
675
  }
922
- return true;
676
+ const container_selector = `.depict .${container_class}`; // class so ppl can have id if needed
677
+ const col_styling = $gVhhF$solidjs.createMemo(() => $75777da1687ba3f6$var$generate_media_queried_declarations({
678
+ container_selector_: container_selector,
679
+ default_grid_spacing_text: props.grid_spacing,
680
+ cols_at_size_: props.cols_at_size,
681
+ override_vertical_spacing_: props.override_vertical_spacing,
682
+ top_row_shortened_by_: props.top_row_shortened_by_()
683
+ }));
684
+ const base_styling = $gVhhF$solidjs.createMemo(() => {
685
+ return `${container_selector}{display:grid;}`;
686
+ });
687
+ const get_styling = $gVhhF$solidjs.createMemo(() => base_styling() + col_styling());
688
+ const style_element = (() => {
689
+ var _el$2 = $gVhhF$solidjsweb.getNextElement($75777da1687ba3f6$var$_tmpl$2);
690
+ $gVhhF$solidjsweb.insert(_el$2, get_styling);
691
+ return _el$2;
692
+ })(); // need to create this after set_styling has been called or else there will be no styling in the SSR'd output
693
+ if (insert_after_container) container_element.after(style_element);
694
+ return [container_element, style_element];
923
695
  }
924
- const $67155e1aeb7073f6$export$f223d69ed12f5fb7 = /*@__PURE__*/$gVhhF$solidjs.createContext();
925
- function $67155e1aeb7073f6$export$4a14175d33d81a96({
926
- displays_by_key_: displays_by_key_,
927
- product_card_template_: product_card_template_,
928
- content_blocks_: content_blocks_,
929
- alwaysObserveCards_: alwaysObserveCards_
696
+ function $75777da1687ba3f6$var$generate_media_queried_declarations({
697
+ container_selector_: container_selector_,
698
+ default_grid_spacing_text: default_grid_spacing_text,
699
+ cols_at_size_: cols_at_size_,
700
+ override_vertical_spacing_: override_vertical_spacing_,
701
+ top_row_shortened_by_: top_row_shortened_by_
930
702
  }) {
931
- const targetToIndexMap_ = new Map();
932
- const indexToTargetMap_ = new Map();
933
- const [listenersForIntersecting, setListenersForIntersecting] = $gVhhF$solidjs.createSignal(0);
934
- const [currentlyIntersecting, setCurrentlyIntersecting] = $gVhhF$solidjs.createSignal(new Set(), {
935
- equals: false
936
- });
937
- if (0, $gVhhF$solidjsweb.isServer) return {
938
- renderedDisplays_: $a73063cc642f3e5f$export$43f0fb951fcb0c54({
939
- displays_by_key_: displays_by_key_,
940
- product_card_template_: product_card_template_,
941
- content_blocks_: content_blocks_
942
- }),
943
- indexToTargetMap_: indexToTargetMap_,
944
- currentlyIntersecting: currentlyIntersecting
945
- };
946
- // The following sets up an intersection observer to determine which results are in the viewport, used for the ScrollStatus indicator, knowing when to load more and any other consumer of the useProductCardIsVisible hook.
947
- const shouldObserve = $gVhhF$solidjs.createMemo(() => alwaysObserveCards_ || listenersForIntersecting() > 0);
948
- const intersection_observer = new IntersectionObserver($gVhhF$depictaiutilishared.catchify(records => {
949
- const intersectingValue = currentlyIntersecting();
950
- for (let i = 0; i < records.length; i++) {
951
- const record = records[i];
952
- const {
953
- target: target,
954
- intersectionRatio: intersectionRatio
955
- } = record;
956
- const index_of_target = targetToIndexMap_.get(target);
957
- if (index_of_target == undefined) return; // It seems like it can happen that we get IntersectionObserverEntries after the onCleanup function for a recommendation card has run, in Firefox. Probably a bug in Firefox? If I understood the bug correctly, this is a workaround.
958
- const is_in_set = intersectingValue.has(index_of_target);
959
- if (intersectionRatio > 0) {
960
- if (!is_in_set) intersectingValue.add(index_of_target);
961
- } else if (is_in_set) intersectingValue.delete(index_of_target);
703
+ return cols_at_size_.map(options => {
704
+ const [n_cols, min, max, grid_spacing_for_size] = options;
705
+ const main_spacing_to_use = grid_spacing_for_size || default_grid_spacing_text;
706
+ // See comment in else block below
707
+ const can_use_gap = override_vertical_spacing_ ? $75777da1687ba3f6$var$can_use_gap_for_unit(override_vertical_spacing_) : $75777da1687ba3f6$var$can_use_gap_for_unit(main_spacing_to_use);
708
+ let spacing_styling;
709
+ let additional_styling = ""; // outside the media query
710
+ if (can_use_gap) spacing_styling = `gap:${main_spacing_to_use};${override_vertical_spacing_ ? `row-gap:${override_vertical_spacing_};` : ``}`;else {
711
+ // When using percent units, which our default styling does (because convenient) and our interface relies on (hard to specify different gaps for different screen sizes)
712
+ // row gaps in grids are relative to the whole height of the grid, maybe see also https://stackoverflow.com/a/53563865. The issue with that is that as we load more products, the gap grows. Also, it breaks existing integrations.
713
+ // Logical thought: use margins for the vertical spacing. But margins in grids are apparently relative to the size of the individual items in grids.
714
+ // Given that, and that content blocks can be of different width, we have to do some complicated calculations to convert the provided gap, which is in gap of the total container width, to a margin of the individual items.
715
+ const vertical_spacing_to_use = override_vertical_spacing_ ?? main_spacing_to_use;
716
+ const width_multiplier = `var(${$75777da1687ba3f6$export$c500183c31c2b22e}, 1)`; // How many cards wide the current item is (needed because content blocks)
717
+ const one_item_width_exclusing_gaps = `calc(calc(100 / ${n_cols}) - calc(calc(${n_cols * 2 - 2} * calc(${$75777da1687ba3f6$var$strip_percent_unit(vertical_spacing_to_use)} / 2)) / ${n_cols}))`; // Width of one item in percent of the whole
718
+ // Equation from layout.tsx, also used in CheckboxGrid. Needs half spacing for some reason
719
+ const this_item_width_meat = `calc(${one_item_width_exclusing_gaps} * ${width_multiplier})`; // Width of just the card
720
+ const this_item_width_gaps = `calc(calc(${width_multiplier} - 1) * ${$75777da1687ba3f6$var$strip_percent_unit(vertical_spacing_to_use)})`; // Width of the gaps
721
+ const this_item_width = `calc(${this_item_width_meat} + ${this_item_width_gaps})`; // Width of this item including gaps
722
+ // this_item_width needs to be unitless, and since plain css can't do calc(40% / 5%) which would become unitless we need to strip all units in the calculations before
723
+ const convert_top_spacing_to_item_width_percentage = spacing => `calc(calc(100 / ${this_item_width}) * ${spacing})`; // Now finally we can convert the desired spacing to a percentage of this individual item's width (including gaps that would be there but aren't because content blocks span them)
724
+ spacing_styling = `column-gap:${main_spacing_to_use};`; // Still use gaps in the horizontal direction because that works flawlessly
725
+ // Re-use old layout code to get the correct :nth-child declarations for the top margins
726
+ additional_styling = $gVhhF$depictaiutilishared.n_col_styling_with_dist(container_selector_ + ">", convert_top_spacing_to_item_width_percentage(default_grid_spacing_text), [[n_cols, min, max, grid_spacing_for_size ? convert_top_spacing_to_item_width_percentage(grid_spacing_for_size) : null]], "grid", override_vertical_spacing_ ? convert_top_spacing_to_item_width_percentage(override_vertical_spacing_) : override_vertical_spacing_, top_row_shortened_by_);
962
727
  }
963
- setCurrentlyIntersecting(intersectingValue);
964
- }, "renderDisplays IO failed"));
965
- const renderedDisplays_ = $gVhhF$solidjsweb.createComponent($67155e1aeb7073f6$export$f223d69ed12f5fb7.Provider, {
966
- value: {
967
- currentlyIntersecting_: currentlyIntersecting,
968
- setIsListening_: setListenersForIntersecting
969
- },
970
- get children() {
971
- return $a73063cc642f3e5f$export$43f0fb951fcb0c54({
972
- displays_by_key_: displays_by_key_,
973
- product_card_template_: product_card_template_,
974
- content_blocks_: content_blocks_,
975
- do_per_elements_: (elements, index_accessor) =>
976
- // Adds/removes elements from the "results in viewport" intersection observer above
977
- $gVhhF$solidjs.createEffect(() => {
978
- if (!shouldObserve()) return;
979
- const display_index = index_accessor();
980
- // Nested createEffect because we only want to re-run this when the index changes (or we should/shouldn't observe)
981
- // We need to save in a map which index the elements of this template are so that we can know that in the IntersectionObserver callback
982
- for (let i = 0; i < elements.length; i++) {
983
- const element = elements[i];
984
- targetToIndexMap_.set(element, display_index);
985
- intersection_observer.observe(element);
986
- $gVhhF$solidjs.onCleanup(() => {
987
- intersection_observer.unobserve(element);
988
- targetToIndexMap_.delete(element);
989
- const intersecting = $gVhhF$solidjs.untrack(currentlyIntersecting);
990
- const wasDeleted = intersecting.delete(display_index);
991
- if (wasDeleted) setCurrentlyIntersecting(intersecting);
992
- });
993
- }
994
- indexToTargetMap_.set(display_index, elements);
995
- $gVhhF$solidjs.onCleanup(() => indexToTargetMap_.delete(display_index));
996
- })
997
- });
728
+ const media_query_body = `${container_selector_}{${spacing_styling}grid-template-columns:repeat(${n_cols},minmax(0,1fr));}`;
729
+ return `${$gVhhF$depictaiutilishared.layout_media_query_header(min, max)}{${media_query_body}}${additional_styling}`;
730
+ }).join("");
731
+ }
732
+ function $75777da1687ba3f6$var$can_use_gap_for_unit(spacing) {
733
+ const trimmed_spacing = spacing.trim();
734
+ return !trimmed_spacing.includes("%");
735
+ }
736
+ function $75777da1687ba3f6$var$strip_percent_unit(css_value) {
737
+ return css_value.replaceAll("%", "");
738
+ }
739
+ var $d92f338ba8f9da9b$var$_tmpl$ = /*#__PURE__*/$gVhhF$solidjsweb.template(`<div class=content-block-wrapper>`);
740
+ function $d92f338ba8f9da9b$export$791ef57e62e21c3f({
741
+ content_blocks_: incoming_content_blocks,
742
+ cols_at_size_: cols_at_size_,
743
+ set_top_row_shortened_by_: set_top_row_shortened_by_
744
+ }) {
745
+ // these are by row
746
+ const [content_blocks, set_content_blocks] = $gVhhF$solidjsstore.createStore([]);
747
+ const n_cols_currently_showing_ = $17d06b3bcb035500$export$a79e666719b3647c(cols_at_size_);
748
+ $gVhhF$solidjs.createEffect(() => {
749
+ const incoming_value = incoming_content_blocks();
750
+ if (!incoming_value) {
751
+ set_content_blocks([]);
752
+ return;
998
753
  }
754
+ set_content_blocks($gVhhF$solidjsstore.reconcile(incoming_value, {
755
+ key: "content"
756
+ })); // key: Make moving blocks faster, see https://github.com/solidjs/solid/issues/1925#issuecomment-1769671596
999
757
  });
1000
- return {
1001
- renderedDisplays_: renderedDisplays_,
1002
- indexToTargetMap_: indexToTargetMap_,
1003
- currentlyIntersecting: currentlyIntersecting
1004
- };
1005
- }
1006
- function $67155e1aeb7073f6$export$80bb7300ce6a8be3() {
1007
- const {
1008
- setIsListening_: setIsListening_,
1009
- currentlyIntersecting_: currentlyIntersecting_
1010
- } = $gVhhF$solidjs.useContext($67155e1aeb7073f6$export$f223d69ed12f5fb7) || {};
1011
- const getOurIndex = $129b9b9c010039d0$export$e3faf4d95300054f();
1012
- const isVisible = $gVhhF$solidjs.createMemo(() => currentlyIntersecting_?.().has(getOurIndex?.()));
1013
- setIsListening_?.(prev => prev + 1);
1014
- $gVhhF$solidjs.onCleanup(() => setIsListening_?.(prev => prev + 1));
1015
- return isVisible;
758
+
759
+ const get_wrapped_blocks_ = $d92f338ba8f9da9b$var$wrap_blocks_in_div(content_blocks, n_cols_currently_showing_);
760
+ $gVhhF$solidjs.createComputed(() => {
761
+ // Update our styling if a content block forces some items that should be in the top row into a lower one, so we can generate margin-top declarations for them
762
+ const top_row_block = get_wrapped_blocks_()[0];
763
+ const blocks_in_top_row_taken = top_row_block?.span_columns;
764
+ if (blocks_in_top_row_taken == undefined) {
765
+ // Fall back to zero if no content block in top row, since then no blocks in top row are taken
766
+ set_top_row_shortened_by_(0);
767
+ return;
768
+ }
769
+ set_top_row_shortened_by_(blocks_in_top_row_taken - 1); // minus one because the content block wrapper also is an element
770
+ });
771
+
772
+ return $gVhhF$solidjs.createMemo(() => $d92f338ba8f9da9b$export$92aa5d691d588f3f({
773
+ wrapped_blocks_: get_wrapped_blocks_(),
774
+ n_cols_currently_showing_: n_cols_currently_showing_()
775
+ }).content_blocks_by_index_);
1016
776
  }
1017
- function $4b455f60dadde8b8$export$5bad61ecde080b1f(...params) {
1018
- const {
1019
- collapse: collapse,
1020
- expand: expand,
1021
- ExpandingContainer: ExpandingContainer
1022
- } = $gVhhF$depictaiutilishared.useExpandingContainer(...params);
1023
- return {
1024
- collapse: collapse,
1025
- expand: expand,
1026
- ExpandingContainer: ({
1027
- children: children
1028
- }) => {
1029
- // get reactive signals/solid's jsx elements into `ExpandingContainer`
1030
- const fake_div = document.createElement("div");
1031
- const outer_expanding = $gVhhF$solidjsweb.createComponent(ExpandingContainer, {
1032
- children: [fake_div]
1033
- });
1034
- const parent = fake_div.parentElement;
1035
- fake_div.remove();
1036
- $gVhhF$solidjsweb.insert(parent, children);
1037
- return outer_expanding;
777
+ function $d92f338ba8f9da9b$export$92aa5d691d588f3f({
778
+ wrapped_blocks_: wrapped_blocks_,
779
+ n_cols_currently_showing_: n_cols_currently_showing_
780
+ }) {
781
+ // This computation should be quite cheap since it just moves around things into an array
782
+ const content_blocks_by_index_ = [];
783
+ const constructed_block_arrays = new WeakSet();
784
+ // Need to keep track of products displaced by blocks above when mapping rows to indexes in the actual grid
785
+ const displaced_products_ = []; // undefined = is product, true = is content block if number instead of tuple number indicates content blocks in that row. Index in first array is row, index in second array is column
786
+ if (!n_cols_currently_showing_)
787
+ // Nope out of here if we don't know how many columns are showing
788
+ return {
789
+ content_blocks_by_index_: content_blocks_by_index_
790
+ };
791
+ for (let i = 0; i < wrapped_blocks_.length; i++) {
792
+ // i = row to show at
793
+ const block = wrapped_blocks_[i];
794
+ if (!block) continue; // Array can have holes/not defined places
795
+ const index_of_first_item_in_row = i * n_cols_currently_showing_;
796
+ const index_of_last_item_in_row = index_of_first_item_in_row + n_cols_currently_showing_;
797
+ const {
798
+ span_rows: span_rows,
799
+ position: position,
800
+ span_columns: span_columns
801
+ } = block; // span columns is already capped in the block wrapping step (wrap_blocks_in_div)
802
+ let target_index;
803
+ if (position === "left") target_index = index_of_first_item_in_row;else if (position === "right") target_index = index_of_last_item_in_row - span_columns;else if (position === "center") target_index = index_of_first_item_in_row + Math.floor((n_cols_currently_showing_ - span_columns) / 2);else throw new Error("Invalid position for content block - must be left, right or center: " + JSON.stringify(position));
804
+ // Calculate displacement of us by content blocks above us
805
+ let displacement_by_added_blocks = 0;
806
+ for (let j = 0; j < i; j++) {
807
+ const row = displaced_products_[j];
808
+ if (row === undefined) continue; // there were no blocks in this row
809
+ let displaced_items_in_row;
810
+ if (typeof row === "number") displaced_items_in_row = row;else {
811
+ displaced_items_in_row = row.filter(item => item).length;
812
+ displaced_products_[j] = displaced_items_in_row;
813
+ }
814
+ displacement_by_added_blocks += displaced_items_in_row;
815
+ }
816
+ const index_of_us_in_row = target_index - index_of_first_item_in_row;
817
+ for (let j = 0; j < index_of_us_in_row; j++) if (displaced_products_[i]?.[j])
818
+ // Add blocks in our *current* row to the displacement, but only the ones to our left
819
+ displacement_by_added_blocks++;
820
+ // Add block to output array at the correct index
821
+ const final_target_index = target_index - displacement_by_added_blocks;
822
+ const existing_block = content_blocks_by_index_[final_target_index];
823
+ const block_content = block.content;
824
+ // If two blocks want to exist at the same location (can happen, for example if one block wants to be at the end of a row and the other one at the start of the next row) add an array with both
825
+ if (existing_block) {
826
+ if (constructed_block_arrays.has(existing_block))
827
+ // Only push to arrays we have created to not create funky bugs when there's an array in JSX.Element
828
+ existing_block.push(block_content);else {
829
+ const array = [existing_block, block_content];
830
+ constructed_block_arrays.add(array);
831
+ content_blocks_by_index_[final_target_index] = array;
832
+ }
833
+ } else content_blocks_by_index_[final_target_index] = block_content;
834
+ // Update displacement
835
+ for (let j = 0; j < span_rows; j++) {
836
+ const row = displaced_products_[i + j] ||= [];
837
+ for (let k = index_of_us_in_row; k < index_of_us_in_row + span_columns; k++) row[k] = true;
1038
838
  }
839
+ }
840
+ return {
841
+ content_blocks_by_index_: content_blocks_by_index_,
842
+ displaced_products_: displaced_products_
1039
843
  };
1040
844
  }
1041
- const $4e04be41243c3d80$var$transform_elements = sorted_cols_at_size => {
1042
- const cols_at_size = [];
1043
- for (let i = 0; i < sorted_cols_at_size.length; i++) {
1044
- const [nb_cols, width] = sorted_cols_at_size[i];
1045
- const next_width = sorted_cols_at_size[i + 1]?.[1];
1046
- const max_width = i === 0 ? "" : `${width}px`;
1047
- const min_width = next_width !== undefined ? `${next_width + 1}px` : "";
1048
- const col_at_size = [nb_cols, min_width, max_width];
1049
- cols_at_size.push(col_at_size);
1050
- }
1051
- return cols_at_size;
1052
- };
1053
- const $4e04be41243c3d80$export$6daf8749b65a1b77 = cols_at_size => {
1054
- // Check that every element is an array
1055
- const are_arrays = cols_at_size?.every?.(element => Array.isArray(element));
1056
- if (!are_arrays) throw new Error("Every element in cols_at_size must be an array");
1057
- // Check width duplicates
1058
- const widths = cols_at_size.map(([_, width]) => width);
1059
- const widths_set = new Set(widths);
1060
- if (widths_set.size < widths.length) throw new Error("There are duplicate widths in cols_at_size");
1061
- if (cols_at_size.length === 1) {
1062
- const [nb_cols, width] = cols_at_size[0];
1063
- const max_width = width !== undefined ? `${width}px` : "";
1064
- return [[nb_cols, "", max_width]];
1065
- }
1066
- // Sort widths in ascending order
1067
- const sorted_cols_at_size = [...cols_at_size].sort((a, b) => {
1068
- const [_b, width_A] = a;
1069
- const [_a, width_b] = b;
1070
- if (width_A === undefined) return -1;
1071
- if (width_b === undefined) return 1;
1072
- return width_b - width_A;
845
+ /**
846
+ * Takes a store that contains an array from where index is number of row and value is the result of executing the content block rendering function.
847
+ * Then renders the content and wraps it into a div with the correct styling.
848
+ * Also caps span_columns to max available columns
849
+ */
850
+ function $d92f338ba8f9da9b$var$wrap_blocks_in_div(rendered_content_blocks, max_span_columns) {
851
+ const get_wrapped_blocks = $gVhhF$solidjs.mapArray(() => rendered_content_blocks, block => {
852
+ if (!block) return;
853
+ const final_span_columns = $gVhhF$solidjs.createMemo(() => Math.min(block.span_columns, max_span_columns() ?? Infinity));
854
+ // If the item spans an entire row, limit the height to 1 row since there's no concept of "rows" if there are no products beside the block
855
+ const final_span_rows = $gVhhF$solidjs.createMemo(() => final_span_columns() === max_span_columns() ? 1 : block.span_rows);
856
+ const [rendering_delayed, set_rendering_delayed_] = $gVhhF$solidjs.createSignal(true);
857
+ const content = $gVhhF$solidjsweb.createComponent((0, $gVhhF$solidjs.Show), {
858
+ get when() {
859
+ return !rendering_delayed();
860
+ },
861
+ get children() {
862
+ var _el$ = $gVhhF$solidjsweb.getNextElement($d92f338ba8f9da9b$var$_tmpl$);
863
+ $gVhhF$solidjsweb.insert(_el$, $gVhhF$solidjsweb.createComponent($d92f338ba8f9da9b$var$RenderContent, {
864
+ get content() {
865
+ return block.content;
866
+ }
867
+ }));
868
+ $gVhhF$solidjsweb.effect(_p$ => {
869
+ var _v$ = !!(final_span_columns() === max_span_columns()),
870
+ _v$2 = {
871
+ [(0, $75777da1687ba3f6$export$c500183c31c2b22e)]: final_span_columns()
872
+ },
873
+ _v$3 = `span ${final_span_columns()}`,
874
+ _v$4 = `span ${final_span_rows()}`;
875
+ _v$ !== _p$.e && _el$.classList.toggle("spans-whole-row", _p$.e = _v$);
876
+ _p$.t = $gVhhF$solidjsweb.style(_el$, _v$2, _p$.t);
877
+ _v$3 !== _p$.a && ((_p$.a = _v$3) != null ? _el$.style.setProperty("grid-column", _v$3) : _el$.style.removeProperty("grid-column"));
878
+ _v$4 !== _p$.o && ((_p$.o = _v$4) != null ? _el$.style.setProperty("grid-row", _v$4) : _el$.style.removeProperty("grid-row"));
879
+ return _p$;
880
+ }, {
881
+ e: undefined,
882
+ t: undefined,
883
+ a: undefined,
884
+ o: undefined
885
+ });
886
+ return _el$;
887
+ }
888
+ });
889
+ return $gVhhF$solidjs.mergeProps(block, {
890
+ // Create content divs for the blocks with as much fine-grained diffing as possible?
891
+ content: () => {
892
+ // Sneaky on-demand rendering, so we don't render more blocks than we can show: we have the rendered value here and don't return a function that renders because if our block would be moved around we'd be guaranteed to re-render
893
+ // However, no need to count products to know if we should render: the <For> loops in Placeholders and render_displays will only call this function if there's actually a product here that wants to display this block
894
+ set_rendering_delayed_(false);
895
+ return content;
896
+ },
897
+ get span_columns() {
898
+ // Cap max width of block to the number of columns available, for everyone downstream
899
+ return final_span_columns();
900
+ },
901
+ get span_rows() {
902
+ return final_span_rows();
903
+ }
904
+ });
1073
905
  });
1074
- return $4e04be41243c3d80$var$transform_elements(sorted_cols_at_size);
1075
- };
1076
- function $fc23d7668ae0011d$export$a2cc176055f8f08(what = "grid") {
1077
- const text = "Please try setting `columnsAtSize`/`gridSpacing` on the `SearchPage`/`CategoryPage` components to change the " + what + " before setting it with CSS. If you do with CSS, set `columnsAtSize` to an empty array to ensure no declarations created by the SDK leak through.";
1078
- return (0, $gVhhF$solidjsweb.isServer) ? {
1079
- t: `<!-- ${text} -->`
1080
- } : document.createComment(text);
906
+ return get_wrapped_blocks;
1081
907
  }
1082
- const $0caf87eaa19079da$var$defaultCacheKey = /*@__PURE__*/Symbol();
1083
- const $0caf87eaa19079da$var$cachedPlaceholderElements = {};
1084
- const $0caf87eaa19079da$var$renderingCachedPlaceholderElement = {};
1085
- function $0caf87eaa19079da$export$f33414a4df0afd3f({
1086
- product_card_template_: product_card_template_,
1087
- num_placeholders_: num_placeholders_,
1088
- content_blocks_: content_blocks_,
1089
- placeholderCacheKey_: placeholderCacheKey_
1090
- }) {
1091
- const [num_to_actually_show, set_num_to_actually_show] = $gVhhF$solidjs.createSignal($gVhhF$solidjs.untrack(num_placeholders_));
1092
- if (0, $gVhhF$solidjsweb.isServer)
1093
- // want to render simple placeholders on server, effects don't run there
1094
- // FIXME when adding proper SSR: also SSR content blocks
1095
- return $gVhhF$solidjs.createMemo(() => Array.from({
1096
- length: num_placeholders_()
1097
- }).map(() => {
1098
- const template_result = product_card_template_(null, undefined);
1099
- // @ts-ignore
1100
- if (typeof template_result?.then !== "function") return template_result;
1101
- }));
1102
- $gVhhF$solidjs.createRenderEffect(() => {
1103
- const existing_num = num_to_actually_show();
1104
- const to_add = num_placeholders_() - existing_num;
1105
- if (to_add <= 0) return;
1106
- // Below comment is from when we used recommendation renderer and manual DOM manipulation for this, but I'm still scared to remove placeholders and mess up scroll restoration, so I'm keeping the way it worked. Otherwise, <Index> could be used which would be far more elegant.
1107
- // We don't need to remove placeholders, only need to add. The reason for that is that chrome restores the scroll position once all tasks queued as reaction of the "popstate" events have been completed in the task queue. That means that we need to get the placeholders in fast, or we might lose scroll position if the previous site was shorter.
1108
- // The query id and the amount might update in separate tasks because one comes from href_change_ipns and the other one from another "popstate" listener. Therefore, we need to be able to dynamically add more placeholders. I'm not entirely sure about this anymore though, since we have the scroll position on a per-id basis it might work anyway but THIS WORKS, and I've been working on this bug for 3 hours so let's just keep it this way.
1109
- // Above comments were from when we added elements in this createRenderEffect. Now I changed it to just increment the number and render them from an <Index> instead, so I can add content blocks better
1110
- // Functionality should be the same
1111
- set_num_to_actually_show(existing_num + to_add);
908
+ /**
909
+ * Basically adds support for async content block functions and shows a rudimentary placeholder if rendering is delayed. Required due to react having a rendering delay.
910
+ */
911
+ function $d92f338ba8f9da9b$var$RenderContent(props) {
912
+ const [resource] = $gVhhF$solidjs.createResource(() => props.content,
913
+ // Could be reactive
914
+ fn => fn());
915
+ $gVhhF$solidjs.createEffect(() => {
916
+ if (resource.error) console.error("Error in content block rendering", resource.error);
1112
917
  });
1113
- return $gVhhF$solidjsweb.createComponent((0, $gVhhF$solidjs.Index), {
1114
- get each() {
1115
- return Array.from({
1116
- length: num_to_actually_show()
918
+ // The suspense is for when there's an async content block (which we have to support due to react having a rendering delay) so that we can show some height while it's loading (should kinda never be visible)
919
+ return $gVhhF$solidjsweb.createComponent((0, $gVhhF$solidjs.Suspense), {
920
+ get fallback() {
921
+ return $gVhhF$solidjsweb.createComponent((0, $3bc4a89b971a3af5$export$3f37af283f8fb843), {
922
+ width: "100%",
923
+ height: "max(100%, 400px)"
1117
924
  });
1118
925
  },
1119
- children: (_item, index) => {
1120
- const possible_content_block = $gVhhF$solidjs.createMemo(() => content_blocks_?.()?.()?.[index]);
1121
- // Show content block before product card, so it's *at* that index
1122
- return [possible_content_block, $0caf87eaa19079da$var$Placeholder({
1123
- product_card_template_: product_card_template_,
1124
- placeholderCacheKey_: placeholderCacheKey_
1125
- })]; // https://github.com/solidjs/solid/releases/tag/v1.7.0
1126
- }
926
+ children: resource
1127
927
  });
1128
928
  }
1129
- /**
1130
- * Get one placeholder element. It will be cloned for each placeholder for ultimate performance even when solid-js isn't rendering the placeholders.
1131
- */
1132
- function $0caf87eaa19079da$var$Placeholder({
1133
- product_card_template_: product_card_template_,
1134
- placeholderCacheKey_ = $0caf87eaa19079da$var$defaultCacheKey
1135
- }) {
1136
- const get_cloned_placeholders = (element_to_clone = $0caf87eaa19079da$var$cachedPlaceholderElements[placeholderCacheKey_]) => Array.isArray(element_to_clone) ? element_to_clone.map(el => el instanceof Node ? el.cloneNode(true) : el) : element_to_clone;
1137
- if ($0caf87eaa19079da$var$cachedPlaceholderElements[placeholderCacheKey_]) return get_cloned_placeholders();
1138
- let dispose_original_copy;
1139
- const get_unwrapped_element = $gVhhF$solidjs.createRoot(dispose => {
1140
- dispose_original_copy = dispose;
1141
- // if product_card_template_ is async and rejects the resource will take care of throwing upwards here in the solid-js error handling system
1142
- const [resource] = $gVhhF$solidjs.createResource(() => {
1143
- const rendering_cached_placeholder_element = $0caf87eaa19079da$var$renderingCachedPlaceholderElement[placeholderCacheKey_];
1144
- if (rendering_cached_placeholder_element) return rendering_cached_placeholder_element;
1145
- const return_value = product_card_template_(null, undefined);
1146
- if (typeof return_value === "object" && typeof return_value?.then === "function")
1147
- // Is Promise or promise-like object
1148
- $0caf87eaa19079da$var$renderingCachedPlaceholderElement[placeholderCacheKey_] = return_value;
1149
- return return_value;
1150
- });
1151
- return $gVhhF$solidjs.createMemo(() => {
1152
- if (resource.loading) return () => [];
1153
- return $5f7d55c710fd1bc2$export$2f0cbfbb4354bb52(resource);
929
+ let $8bf678c45948a297$var$elementsHoveredSignal;
930
+ let $8bf678c45948a297$var$consumersSignal;
931
+ function $8bf678c45948a297$export$d4d96868197be50e() {
932
+ if (!$8bf678c45948a297$var$elementsHoveredSignal || !$8bf678c45948a297$var$consumersSignal) $gVhhF$solidjs.createRoot(dispose => {
933
+ $8bf678c45948a297$var$elementsHoveredSignal = $gVhhF$solidjs.createSignal(new Set());
934
+ $8bf678c45948a297$var$consumersSignal = $gVhhF$solidjs.createSignal(0);
935
+ const [consumers] = $8bf678c45948a297$var$consumersSignal;
936
+ // No matter how often useMobileHover is called, only add one listener and get hovered elements once per touchmove event, for efficiency
937
+ $8bf678c45948a297$var$doHeavyCalculations();
938
+ $gVhhF$solidjs.createEffect(() => {
939
+ // No-one needs information anymore, stop listening
940
+ const users = consumers();
941
+ if (users < 1) {
942
+ $gVhhF$depictaiutilisharedlatest.dlog("Stopping to listen for mobile hover");
943
+ dispose();
944
+ $8bf678c45948a297$var$elementsHoveredSignal = undefined;
945
+ $8bf678c45948a297$var$consumersSignal = undefined;
946
+ }
1154
947
  });
1155
948
  });
1156
- $gVhhF$solidjs.onCleanup(() => {
1157
- const rendering = $0caf87eaa19079da$var$renderingCachedPlaceholderElement[placeholderCacheKey_];
1158
- if (rendering) {
1159
- // If we're still rendering in our separate root, let the rendering finish or other <Placeholder> components waiting for it might get an incomplete result
1160
- const unset_current_rendering = () => {
1161
- dispose_original_copy();
1162
- $0caf87eaa19079da$var$renderingCachedPlaceholderElement[placeholderCacheKey_] = undefined;
1163
- };
1164
- rendering?.then(unset_current_rendering, unset_current_rendering);
1165
- } else
1166
- // Otherwise, just make sure we've disposed our root
1167
- dispose_original_copy();
949
+ const [elementsHovered] = $8bf678c45948a297$var$elementsHoveredSignal;
950
+ const [, setConsumers] = $8bf678c45948a297$var$consumersSignal;
951
+ setConsumers(prev => prev + 1);
952
+ $gVhhF$solidjs.onCleanup(() => setConsumers(prev => prev - 1));
953
+ return elementsHovered;
954
+ }
955
+ function $8bf678c45948a297$var$doHeavyCalculations() {
956
+ $gVhhF$depictaiutilisharedlatest.dlog("Starting to listen for mobile hover");
957
+ const [, setElementsHovered] = $8bf678c45948a297$var$elementsHoveredSignal;
958
+ const elementAtTouch = new Map();
959
+ const updateSet = () => setElementsHovered(new Set([...elementAtTouch.values()].filter(v => v)));
960
+ const setPositionHandler = $gVhhF$depictaiutilisharedlatest.catchify(evt => {
961
+ for (const touch of evt.changedTouches) elementAtTouch.set(touch.identifier, document.elementFromPoint(touch.clientX, touch.clientY));
962
+ updateSet();
1168
963
  });
1169
- return $gVhhF$solidjs.createMemo(() => {
1170
- const unwrapped_array = get_unwrapped_element()();
1171
- if (!unwrapped_array?.length || !unwrapped_array.every(item => item))
1172
- // Haven't finished rendering yet in case of "async" rendering, so don't freeze the elements yet
1173
- return;
1174
- // placeholders have finished rendering, freeze the elements
1175
- // here we might receive the result of a promise that has already been received, tore down and put into cached_placeholder_element, so don't overwrite that more valid result if it's already set
1176
- $0caf87eaa19079da$var$cachedPlaceholderElements[placeholderCacheKey_] ||= get_cloned_placeholders(unwrapped_array); // Clone initial return value so that who gave us the elements can't mutate them, which has caused issues before with react
1177
- $0caf87eaa19079da$var$renderingCachedPlaceholderElement[placeholderCacheKey_] = undefined;
1178
- dispose_original_copy(); // So that this memo will never re-run and the placeholder rendering function can clean up
1179
- return get_cloned_placeholders(); // clone, just in case. If we wouldn't, someone could modify the copy in the DOM, and it would affect the cached version
964
+ const deleteTouchHandler = $gVhhF$depictaiutilisharedlatest.catchify(evt => {
965
+ for (const touch of evt.changedTouches) elementAtTouch.delete(touch.identifier);
966
+ updateSet();
967
+ });
968
+ addEventListener("touchstart", setPositionHandler, {
969
+ passive: true
970
+ });
971
+ addEventListener("touchend", deleteTouchHandler, {
972
+ passive: true
973
+ });
974
+ addEventListener("touchcancel", deleteTouchHandler, {
975
+ passive: true
976
+ });
977
+ $gVhhF$solidjs.onCleanup(() => {
978
+ removeEventListener("touchstart", setPositionHandler);
979
+ removeEventListener("touchend", deleteTouchHandler);
980
+ removeEventListener("touchcancel", deleteTouchHandler);
1180
981
  });
1181
982
  }
1182
-
1183
- var $f48d0fbb00835aaa$var$_tmpl$ = /*#__PURE__*/$gVhhF$solidjsweb.template(`<div>`),
1184
- $f48d0fbb00835aaa$var$_tmpl$2 = /*#__PURE__*/$gVhhF$solidjsweb.template(`<style type=text/css>`);
1185
- function $f48d0fbb00835aaa$export$d21e32dfedac04be(props) {
1186
- const container_class = "x" + $gVhhF$solidjs.createUniqueId();
1187
- const container_element_ = (() => {
1188
- var _el$ = $gVhhF$solidjsweb.getNextElement($f48d0fbb00835aaa$var$_tmpl$);
1189
- $gVhhF$solidjsweb.spread(_el$, $gVhhF$solidjsweb.mergeProps(() => props.element_attributes, {
1190
- "classList": {
1191
- [container_class]: true
983
+ function $55cbacb79d3e0cea$export$54299a3c81f5425() {
984
+ const d = document;
985
+ const [isVisible, setIsVisible] = $gVhhF$solidjs.createSignal(d.visibilityState !== "hidden");
986
+ const handleVisibilityChange = () => setIsVisible(d.visibilityState !== "hidden");
987
+ d.addEventListener("visibilitychange", handleVisibilityChange);
988
+ $gVhhF$solidjs.onCleanup(() => d.removeEventListener("visibilitychange", handleVisibilityChange));
989
+ return isVisible;
990
+ }
991
+ var $0f313083912e447a$var$_tmpl$ = /*#__PURE__*/$gVhhF$solidjsweb.template(`<svg xmlns=http://www.w3.org/2000/svg viewBox="0 0 14 26"><path d="M2 24.656c-.078 0-.156-.016-.234-.047s-.133-.066-.187-.129c-.117-.117-.176-.258-.176-.422s.059-.305.176-.422l10.371-10.371L1.578 2.883c-.117-.117-.176-.258-.176-.422s.059-.305.176-.422.258-.176.422-.176.305.059.422.176l10.805 10.793a.59.59 0 0 1 .176.434c0 .172-.059.305-.176.422L2.422 24.48a.42.42 0 0 1-.199.129.54.54 0 0 1-.223.047zm0 0"fill=#0f0f0f>`);
992
+ function $0f313083912e447a$export$8be2714ec7f169ee(props = {}) {
993
+ return (() => {
994
+ var _el$ = $gVhhF$solidjsweb.getNextElement($0f313083912e447a$var$_tmpl$);
995
+ $gVhhF$solidjsweb.effect(_p$ => {
996
+ var _v$ = props.width_ ?? 14,
997
+ _v$2 = props.height_ ?? 26;
998
+ _v$ !== _p$.e && $gVhhF$solidjsweb.setAttribute(_el$, "width", _p$.e = _v$);
999
+ _v$2 !== _p$.t && $gVhhF$solidjsweb.setAttribute(_el$, "height", _p$.t = _v$2);
1000
+ return _p$;
1001
+ }, {
1002
+ e: undefined,
1003
+ t: undefined
1004
+ });
1005
+ return _el$;
1006
+ })();
1007
+ }
1008
+ var $a0e53f99354809d9$var$_tmpl$ = /*#__PURE__*/$gVhhF$solidjsweb.template(`<div>`),
1009
+ $a0e53f99354809d9$var$_tmpl$2 = /*#__PURE__*/$gVhhF$solidjsweb.template(`<div class="depict-slider depict"><button aria-label="Scroll left"class="left d-navbutton"type=button></button><button aria-label="Scroll right"class="right d-navbutton"type=button></button><div class=sliding>`);
1010
+ const $a0e53f99354809d9$var$SlidableContext = /*@__PURE__*/$gVhhF$solidjs.createContext();
1011
+ function $a0e53f99354809d9$export$6649caad96ef8870(props) {
1012
+ const {
1013
+ slider_ref_: slider_ref_,
1014
+ faded_opacity_ = 0.7,
1015
+ mutation_plugins_ = [],
1016
+ disable_fading_: disable_fading_,
1017
+ snapAlign_: snapAlign_,
1018
+ fadingThreshold_ = 0.96
1019
+ } = props;
1020
+ const [hasHydrated, setHasHydrated] = $gVhhF$solidjs.createSignal(false);
1021
+ $gVhhF$solidjs.createEffect(() => setHasHydrated(true));
1022
+ const MakeButton = () => (() => {
1023
+ var _el$ = $gVhhF$solidjsweb.getNextElement($a0e53f99354809d9$var$_tmpl$);
1024
+ $gVhhF$solidjsweb.insert(_el$, $gVhhF$solidjsweb.createComponent((0, $0f313083912e447a$export$8be2714ec7f169ee), {
1025
+ get height_() {
1026
+ return props.arrow_height_;
1027
+ },
1028
+ get width_() {
1029
+ return props.arrow_width_;
1192
1030
  }
1193
- }), false, true);
1194
- $gVhhF$solidjsweb.insert(_el$, () => props.children);
1195
- $gVhhF$solidjsweb.runHydrationEvents();
1031
+ }));
1196
1032
  return _el$;
1197
- })(); // Don't want this in the error boundary below, it should only be for the actual layouting
1198
- return $gVhhF$solidjsweb.createComponent((0, $edafe91daf3091d7$export$b69d0d92862de18b), {
1199
- get message_() {
1200
- return "Failed calculating " + props.layout + " layout";
1033
+ })();
1034
+ // To support SSR it would be very hard to make Amazing_Slider compatible with solid's SSR because it would have to be rewritten in a more JSX way
1035
+ // However, all it would do on the server (when used by SlidableItems) is creating the following 5 div elements
1036
+ // Therefore, we just bail on the server (and first render on the client which is the hydration) and return a static version of the slider
1037
+ // Once we have hydrated, we construct and insert the actual slider
1038
+ return $gVhhF$solidjsweb.createComponent((0, $gVhhF$solidjs.Show), {
1039
+ get when() {
1040
+ return hasHydrated() && !(0, $gVhhF$solidjsweb.isServer);
1041
+ },
1042
+ get fallback() {
1043
+ return (() => {
1044
+ var _el$2 = $gVhhF$solidjsweb.getNextElement($a0e53f99354809d9$var$_tmpl$2),
1045
+ _el$3 = _el$2.firstChild,
1046
+ _el$4 = _el$3.nextSibling,
1047
+ _el$5 = _el$4.nextSibling;
1048
+ $gVhhF$solidjsweb.insert(_el$3, $gVhhF$solidjsweb.createComponent(MakeButton, {}));
1049
+ $gVhhF$solidjsweb.insert(_el$4, $gVhhF$solidjsweb.createComponent(MakeButton, {}));
1050
+ $gVhhF$solidjsweb.insert(_el$5, () => props.children);
1051
+ return _el$2;
1052
+ })();
1201
1053
  },
1202
- severity_: "error",
1203
1054
  get children() {
1204
- return $f48d0fbb00835aaa$export$445d8d3f5f706a40(props, container_element_, false, container_class);
1055
+ return (() => {
1056
+ const [intersectionEntries_, setIntersectionEntries] = $gVhhF$solidjs.createSignal([]);
1057
+ const fading_plugin = disable_fading_ ? undefined : $gVhhF$depictaiutilishared.slider_fading_factory({
1058
+ threshold_: fadingThreshold_,
1059
+ faded_opacity_: faded_opacity_
1060
+ });
1061
+ const slider = new (0, $gVhhF$depictaiutilishared.Amazing_Slider)({
1062
+ button_contents: {
1063
+ left: [MakeButton()],
1064
+ right: [MakeButton()]
1065
+ },
1066
+ constructor_plugins: [$gVhhF$depictaiutilishared.slider_snap_css(0, {
1067
+ align: snapAlign_
1068
+ }).constructor_plugin, ...(fading_plugin ? [fading_plugin.constructor_plugin] : [])],
1069
+ mutation_plugins: [...(fading_plugin ? [fading_plugin.mutation_plugin_] : []), ...mutation_plugins_, entries => setIntersectionEntries(entries)]
1070
+ });
1071
+ $gVhhF$solidjsweb.insert(slider.insert_here, () => $gVhhF$solidjsweb.createComponent($a0e53f99354809d9$var$SlidableContext.Provider, {
1072
+ value: {
1073
+ slider_: slider,
1074
+ intersectionEntries_: intersectionEntries_
1075
+ },
1076
+ get children() {
1077
+ return props.children;
1078
+ }
1079
+ }));
1080
+ $gVhhF$solidjs.onCleanup(() => slider.observer.disconnect());
1081
+ slider_ref_?.(slider);
1082
+ return slider.container;
1083
+ })();
1205
1084
  }
1206
1085
  });
1207
1086
  }
1208
- function $f48d0fbb00835aaa$export$445d8d3f5f706a40(props, container_element, insert_after_container = false, container_class) {
1209
- if (!container_class) {
1210
- // only try to add if it wasn't provided, so that we have a code path without classList when doing SSR
1211
- container_class = $gVhhF$depictaiutilishared.make_random_classname();
1212
- container_element.classList.add(container_class);
1087
+ function $a0e53f99354809d9$export$eaa625fb39439e37() {
1088
+ return $gVhhF$solidjs.useContext($a0e53f99354809d9$var$SlidableContext);
1089
+ }
1090
+
1091
+ /**
1092
+ * Gets product_id (the thing we key the rendering on) from a display
1093
+ */
1094
+ function $a5871dacf261adf8$export$548d053aba053d79(display) {
1095
+ if ("variant_index" in display) {
1096
+ const actual_display = display.variant_displays[display.variant_index];
1097
+ return actual_display.product_id;
1213
1098
  }
1214
- // Copy of Layout from utilishared but using solid-js signals instead of async iterable IPNS
1215
- const show_all_rows = $gVhhF$solidjs.createMemo(() => props.rows === "all");
1216
- const rows_or_empty_object = $gVhhF$solidjs.createMemo(() => show_all_rows() ? {} : props.rows);
1217
- const view_more_button_elements = $5f7d55c710fd1bc2$export$2f0cbfbb4354bb52($gVhhF$solidjs.createMemo(() => rows_or_empty_object().button));
1218
- const view_less_button_elements = $5f7d55c710fd1bc2$export$2f0cbfbb4354bb52($gVhhF$solidjs.createMemo(() => rows_or_empty_object().view_less_button));
1219
- const have_a_view_more_button = $gVhhF$solidjs.createMemo(() => {
1220
- const button = view_more_button_elements();
1221
- return !(!button || Array.isArray(button) && !button.length);
1222
- });
1223
- const have_a_view_less_button = $gVhhF$solidjs.createMemo(() => {
1224
- const button = view_less_button_elements();
1225
- return !(!button || Array.isArray(button) && !button.length);
1226
- });
1227
- const [get_styling, set_styling] = $gVhhF$solidjs.createSignal();
1228
- const selector_beginning = `.depict .${container_class}>`; // class so ppl can have id if needed
1229
- const [get_children_count, set_children_count] = $gVhhF$solidjs.createSignal(0);
1230
- const update_children_count = () => (0, $gVhhF$solidjsweb.isServer) ? 20 : set_children_count(container_element.children.length);
1231
- const col_styling = $gVhhF$solidjs.createMemo(() => $gVhhF$depictaiutilishared.n_col_styling_with_dist(selector_beginning, props.grid_spacing, props.cols_at_size, props.layout, props.override_vertical_spacing));
1232
- const default_currently_visible_signal = $gVhhF$solidjs.createSignal($gVhhF$solidjs.untrack(() => rows_or_empty_object().start_rows ?? 1)); // Keep this signal here, so it doesn't change when rows_or_empty_object and I think it's to be expected that the "internal default signal" stays the same
1233
- const rows_visible_signal = $gVhhF$solidjs.createMemo(() => rows_or_empty_object().currently_visible || default_currently_visible_signal);
1234
- const get_rows_visible = $gVhhF$solidjs.createMemo(() => rows_visible_signal()[0]());
1235
- const set_rows_visible = $gVhhF$solidjs.createMemo(() => rows_visible_signal()[1]);
1236
- const view_more_button_class = "m" + $gVhhF$solidjs.createUniqueId();
1237
- const view_less_button_class = "l" + $gVhhF$solidjs.createUniqueId(); // https://github.com/solidjs/solid-docs/issues/263#issuecomment-1622388079
1238
- const view_more_button_container = $gVhhF$solidjs.createMemo(() => have_a_view_more_button() && (() => {
1239
- var _el$2 = $gVhhF$solidjsweb.getNextElement($f48d0fbb00835aaa$var$_tmpl$);
1240
- $gVhhF$solidjsweb.addEventListener(_el$2, "click", $gVhhF$depictaiutilishared.catchify(() => set_rows_visible()(prev => prev + (rows_or_empty_object().rows_per_click ?? 1))), true);
1241
- $gVhhF$solidjsweb.className(_el$2, `load-more-container ${view_more_button_class}`);
1242
- $gVhhF$solidjsweb.insert(_el$2, () => rows_or_empty_object().button);
1243
- $gVhhF$solidjsweb.runHydrationEvents();
1244
- return _el$2;
1245
- })());
1246
- // We need to have duplicate buttons because which ones is shown depends on the number of rows visible and therefore on the viewport size and therefore on the css media queries
1247
- const view_less_button_container = $gVhhF$solidjs.createMemo(() => have_a_view_less_button() && (() => {
1248
- var _el$3 = $gVhhF$solidjsweb.getNextElement($f48d0fbb00835aaa$var$_tmpl$);
1249
- $gVhhF$solidjsweb.addEventListener(_el$3, "click", $gVhhF$depictaiutilishared.catchify(() => set_rows_visible()(() => rows_or_empty_object().start_rows ?? 1)), true);
1250
- $gVhhF$solidjsweb.className(_el$3, `load-less-container ${view_less_button_class}`);
1251
- $gVhhF$solidjsweb.insert(_el$3, () => rows_or_empty_object().view_less_button);
1252
- $gVhhF$solidjsweb.runHydrationEvents();
1253
- return _el$3;
1254
- })());
1255
- const [force_style_update, set_force_style_update] = $gVhhF$solidjs.createSignal("");
1256
- $gVhhF$solidjs.createComputed(() => {
1257
- // Make sure we don't have more rows visible than max_rows (this could happen by max_rows being decreased)
1258
- const rows_visible = get_rows_visible();
1259
- const max_rows = rows_or_empty_object().max_rows;
1260
- if (!isNaN(max_rows) && max_rows >= 0 && rows_visible > max_rows) set_rows_visible()(max_rows);
1261
- });
1262
- $gVhhF$solidjs.createEffect(() => set_force_style_update(" ")); // force style update after hydration due to https://discord.com/channels/722131463138705510/722167424186843267/1151544212517552168
1263
- $gVhhF$solidjs.createRenderEffect(() => {
1264
- // Be careful to only read show_all_rows in here (ofc doesn't apply to nested effects)
1265
- if (show_all_rows()) {
1266
- // no view more functionality requested
1267
- set_styling(() => col_styling);
1268
- return;
1269
- }
1270
- update_children_count();
1271
- if (!(0, $gVhhF$solidjsweb.isServer)) {
1272
- const mutation_observer = new MutationObserver($gVhhF$depictaiutilishared.catchify(update_children_count));
1273
- mutation_observer.observe(container_element, {
1274
- childList: true
1275
- });
1276
- $gVhhF$solidjs.onCleanup(() => mutation_observer.disconnect());
1277
- }
1278
- set_styling(
1279
- // this is needed because solid will think that the memo-accessor is a setter function otherwise
1280
- () => $gVhhF$solidjs.createMemo(() => {
1281
- const {
1282
- rows_per_click: rows_per_click,
1283
- max_rows = -1,
1284
- start_rows: start_rows
1285
- } = rows_or_empty_object();
1286
- const rows_visible = get_rows_visible();
1287
- const children_count = get_children_count();
1288
- return force_style_update() + col_styling() + props.cols_at_size.map(options => {
1289
- const [recs_per_row, min, max] = options;
1290
- let should_hide_view_more_button;
1291
- let visible_recs;
1292
- if (props.disable_partial_rows) {
1293
- visible_recs = $gVhhF$depictaiutilishared.layout_get_visible_recs_without_partial_rows(rows_visible, children_count, options);
1294
- const visible_recs_next_show_more = $gVhhF$depictaiutilishared.layout_get_visible_recs_without_partial_rows(rows_visible + (rows_per_click || 0),
1295
- // Should this really be zero when we default rows_per_click to 1?
1296
- children_count, options);
1297
- should_hide_view_more_button = visible_recs === visible_recs_next_show_more;
1099
+ if ("listing_id" in display) return display.listing_id;
1100
+ return display.product_id;
1101
+ }
1102
+ function $5f7d55c710fd1bc2$export$2f0cbfbb4354bb52(elements) {
1103
+ // FYI this probably doesn't work on hugely deep nestings or large amount of elements
1104
+ return $gVhhF$solidjs.createMemo(() => $5f7d55c710fd1bc2$var$unwrapping_helper(elements));
1105
+ }
1106
+ function $5f7d55c710fd1bc2$var$unwrapping_helper(elements) {
1107
+ const result = [];
1108
+ if (Array.isArray(elements)) for (let i = 0; i < elements.length; i++) {
1109
+ const element = elements[i];
1110
+ result.push(...$5f7d55c710fd1bc2$var$unwrapping_helper(element));
1111
+ } else if (typeof elements == "function") result.push(...$5f7d55c710fd1bc2$var$unwrapping_helper(elements()));else if (elements != undefined && typeof elements != "boolean") result.push(elements);
1112
+ return result;
1113
+ }
1114
+
1115
+ /**
1116
+ * This is a function that will safely clone an object that may contain promises.
1117
+ * It will use structuredClone to clone the object and then re-apply (copy) any promises that were in the original object to the cloned object.
1118
+ * @param obj Anything to clone
1119
+ */
1120
+ function $ac946c6b782bb900$export$83bb9af5671e1dbe(obj) {
1121
+ if (obj instanceof Promise) return obj;
1122
+ // Extract all promises, keep a map of them, and then re-apply them to both the original object and the target object after cloning
1123
+ const seen = new Set();
1124
+ const keyStack = [[]];
1125
+ const valueStack = [obj];
1126
+ const promiseLocationStack = [];
1127
+ while (valueStack.length) {
1128
+ const value = valueStack.pop();
1129
+ const keyPath = keyStack.pop();
1130
+ // Promises can only be in objects and we don't need to check for objects we've already seen (like in circular references)
1131
+ if (typeof value !== "object" || value === null || seen.has(value)) continue;
1132
+ seen.add(value);
1133
+ if (Array.isArray(value)) {
1134
+ for (let i = 0; i < value.length; i++) {
1135
+ const ourValue = value[i];
1136
+ const valuesKeyPath = [...keyPath, i];
1137
+ if (ourValue instanceof Promise) {
1138
+ promiseLocationStack.push([valuesKeyPath, ourValue]);
1139
+ delete value[i]; // This will leave a hole in the array, but it's fine since we'll put it back soon
1140
+ // This way both the indexes will be correct when putting the value back and our for loop doesn't get messed up
1298
1141
  } else {
1299
- visible_recs = $gVhhF$depictaiutilishared.layout_get_total_rec_amount_for_row(rows_visible, options);
1300
- should_hide_view_more_button = visible_recs >= children_count;
1142
+ keyStack.push(valuesKeyPath);
1143
+ valueStack.push(ourValue);
1301
1144
  }
1302
- const should_hide_button_final = should_hide_view_more_button || max_rows !== -1 && rows_visible >= max_rows;
1303
- // I don't really understand the logic for when the view more button gets hidden so IDK if below is correct, but it works how it should for now
1304
- const should_hide_view_less_button = !should_hide_view_more_button || start_rows == undefined || rows_visible <= start_rows || children_count <= recs_per_row * start_rows;
1305
- return `${$gVhhF$depictaiutilishared.layout_media_query_header(min, max)}{${selector_beginning}:nth-child(n+${visible_recs + 1}){display:none}${props.layout == "slider" ? `${selector_beginning}:nth-child(n+${visible_recs}){margin-right:0}` : ""}${should_hide_button_final ? `.depict .${view_more_button_class}{display:none!important}` : ``}${should_hide_view_less_button ? `.depict .${view_less_button_class}{display:none!important}` : ``}}`;
1306
- }).join("");
1307
- }));
1145
+ }
1146
+ continue;
1147
+ }
1148
+ for (const innerKey in value) {
1149
+ const ourValue = value[innerKey];
1150
+ const valuesKeyPath = [...keyPath, innerKey];
1151
+ if (ourValue instanceof Promise) {
1152
+ promiseLocationStack.push([valuesKeyPath, ourValue]);
1153
+ delete value[innerKey];
1154
+ } else {
1155
+ keyStack.push(valuesKeyPath);
1156
+ valueStack.push(ourValue);
1157
+ }
1158
+ }
1159
+ }
1160
+ let structuredClonedObject;
1161
+ let error;
1162
+ try {
1163
+ structuredClonedObject = structuredClone(obj);
1164
+ } catch (e) {
1165
+ // We still need to put back the promises in the original object, even if we threw an error
1166
+ error = e;
1167
+ }
1168
+ // Put back the promises in both the original object and the cloned object
1169
+ while (promiseLocationStack.length) {
1170
+ const [keyPath, promise] = promiseLocationStack.pop();
1171
+ let targetInClone = structuredClonedObject;
1172
+ let targetInOriginal = obj;
1173
+ for (let i = 0; i < keyPath.length - 1; i++) {
1174
+ if (!error) targetInClone = targetInClone[keyPath[i]];
1175
+ targetInOriginal = targetInOriginal[keyPath[i]];
1176
+ }
1177
+ if (!error) targetInClone[keyPath[keyPath.length - 1]] = promise;
1178
+ targetInOriginal[keyPath[keyPath.length - 1]] = promise;
1179
+ }
1180
+ if (error) throw error;
1181
+ return structuredClonedObject;
1182
+ }
1183
+ let $a73063cc642f3e5f$var$printedCloneError = false;
1184
+ function $a73063cc642f3e5f$export$7f8bd308237ab5a5(get_displays_) {
1185
+ const displays_by_key_ = $gVhhF$solidjs.createMemo(() => {
1186
+ const displays = get_displays_();
1187
+ // We key by product_id because that's what can be used to uniquely identify products
1188
+ // This way <For> can optimize the rendering
1189
+ const map = new Map();
1190
+ for (let i = 0; i < displays.length; i++) {
1191
+ const display = displays[i];
1192
+ const product_id = $a5871dacf261adf8$export$548d053aba053d79(display);
1193
+ map.set(product_id, display);
1194
+ }
1195
+ return map;
1308
1196
  });
1309
- const style_element = (() => {
1310
- var _el$4 = $gVhhF$solidjsweb.getNextElement($f48d0fbb00835aaa$var$_tmpl$2);
1311
- $gVhhF$solidjsweb.insert(_el$4, get_styling);
1312
- return _el$4;
1313
- })(); // need to create this after set_styling has been called or else there will be no styling in the SSR'd output
1314
- if (insert_after_container) container_element.after(style_element);
1315
- return [container_element, $gVhhF$solidjsweb.memo(view_more_button_container), $gVhhF$solidjsweb.memo(view_less_button_container), style_element];
1197
+ return displays_by_key_;
1316
1198
  }
1317
- $gVhhF$solidjsweb.delegateEvents(["click"]);
1318
- var $363ce4d1d71041aa$var$_tmpl$ = /*#__PURE__*/$gVhhF$solidjsweb.template(`<h2 class=recs-title>`);
1319
- function $363ce4d1d71041aa$export$d41d19e89346fa2({
1320
- recommendations_resource_: recommendations_resource_,
1199
+ const $a73063cc642f3e5f$var$result_ids = ["search_result_id", "product_listing_result_id", "recommendation_id"];
1200
+ function $a73063cc642f3e5f$export$43f0fb951fcb0c54({
1201
+ displays_by_key_: displays_by_key_,
1321
1202
  product_card_template_: product_card_template_,
1322
- layout_options_: layout_options_,
1323
- title_: title_,
1324
- NoResultsFallback_: NoResultsFallback_,
1325
- placeholderCacheKey_: placeholderCacheKey_
1203
+ do_per_elements_: do_per_elements_,
1204
+ content_blocks_: content_blocks_
1326
1205
  }) {
1327
- const is_showing_something = $gVhhF$solidjs.createMemo(() => recommendations_resource_.loading || recommendations_resource_()?.length);
1328
- const title = $gVhhF$solidjs.createMemo(() => title_?.()); // In case getter/accessor creates elements, don't create them twice
1329
- const num_placeholders_ = () => 20;
1330
- const slider = [$gVhhF$solidjsweb.createComponent((0, $gVhhF$solidjs.Show), {
1331
- get when() {
1332
- return title();
1333
- },
1334
- get children() {
1335
- var _el$ = $gVhhF$solidjsweb.getNextElement($363ce4d1d71041aa$var$_tmpl$);
1336
- $gVhhF$solidjsweb.insert(_el$, title);
1337
- return _el$;
1338
- }
1339
- }), $gVhhF$solidjsweb.createComponent((0, $a0e53f99354809d9$export$6649caad96ef8870), {
1340
- arrow_height_: 25,
1341
- arrow_width_: 12.5,
1342
- disable_fading_: true,
1343
- slider_ref_: slider => {
1344
- const {
1345
- insert_here: insert_here,
1346
- container: container
1347
- } = slider;
1348
- const Layout = props => $f48d0fbb00835aaa$export$445d8d3f5f706a40(props, insert_here, true);
1349
- // Do like this, so we don't have to call mergeProps ourselves
1350
- $gVhhF$solidjsweb.createComponent(Layout, $gVhhF$solidjsweb.mergeProps({
1351
- rows: "all",
1352
- layout: "slider"
1353
- }, layout_options_));
1354
- $gVhhF$depictaiutilishared.slider_fractional_factory(0, 0.75).constructor_plugin.call(slider, {}); // enable fractional scrolling
1355
- insert_here.before($fc23d7668ae0011d$export$a2cc176055f8f08());
1356
- container.classList.add("products");
1206
+ return $gVhhF$solidjsweb.createComponent((0, $gVhhF$solidjs.For), {
1207
+ get each() {
1208
+ return [...displays_by_key_().keys()];
1357
1209
  },
1358
- get children() {
1359
- return $gVhhF$solidjsweb.createComponent((0, $gVhhF$solidjs.Suspense), {
1360
- get fallback() {
1361
- return $0caf87eaa19079da$export$f33414a4df0afd3f({
1362
- num_placeholders_: num_placeholders_,
1363
- product_card_template_: product_card_template_,
1364
- placeholderCacheKey_: placeholderCacheKey_
1365
- });
1366
- },
1367
- get children() {
1368
- return (() => {
1369
- // IIFE to make sure we access both the recommendations_resource and the resources containing the product card rendering results themselves within the Suspense boundary
1370
- const displays_by_key_ = $a73063cc642f3e5f$export$7f8bd308237ab5a5($gVhhF$solidjs.createMemo(() => recommendations_resource_() || []));
1371
- const {
1372
- renderedDisplays_: renderedDisplays_
1373
- } = $67155e1aeb7073f6$export$4a14175d33d81a96({
1374
- displays_by_key_: displays_by_key_,
1375
- product_card_template_: product_card_template_
1210
+ children: (product_id, index) => {
1211
+ const owner = $gVhhF$solidjs.getOwner();
1212
+ const possible_content_block = $gVhhF$solidjs.createMemo(() => content_blocks_?.()?.()?.[index()]);
1213
+ const display_accessor = $gVhhF$solidjs.createMemo(prev => {
1214
+ const new_display = displays_by_key_().get(product_id);
1215
+ if (prev && $a73063cc642f3e5f$var$deep_equal(prev, new_display))
1216
+ // If displays are identical, they haven't changed due to merchant/market changing OR we made a new query and the displays are the same. Return the old display (same object reference) to avoid re-rendering.
1217
+ // This doesn't work if people mutate the displays from within the template but we hope they don't
1218
+ // (We can't know if the display changing is due to merchant/market changing or a new query otherwise and don't want to run into https://gitlab.com/depict-ai/depict.ai/-/merge_requests/6890/diffs)
1219
+ return prev;
1220
+ return new_display;
1221
+ });
1222
+ const [product_card_resource] = $gVhhF$solidjs.createResource(display_accessor,
1223
+ // re-render when the reference to the display object changes
1224
+ display => {
1225
+ // It's easy for merchants to accidentally mutate the display or some sub-object thereof
1226
+ // We had a case on singular where they popped an image url and due to use caching after some time there was no image
1227
+ // Attempt cloning the displays if possible (99.9% of the cases probably) and if not, just use the original display
1228
+ try {
1229
+ // Use promiseSafeStructuredClone so that:
1230
+ // 1. When fetching for example prices from an API, one can start that API request
1231
+ // 2. Add the promise to the display
1232
+ // 3. Already render the product card with placeholders for prices while the price loads
1233
+ // 4. When the price loads, re-render the display with the new price
1234
+ display = $ac946c6b782bb900$export$83bb9af5671e1dbe(display);
1235
+ } catch (e) {
1236
+ if (!$a73063cc642f3e5f$var$printedCloneError) {
1237
+ $a73063cc642f3e5f$var$printedCloneError = true;
1238
+ $gVhhF$depictaiutilishared.dlog("Can't clone display", display, " (uncloneable objects in it), make sure you don't mutate it in the template since we cache.", e);
1239
+ }
1240
+ }
1241
+ let renderingResult;
1242
+ // Wrap the product card in a ProductCardIndexProvider so that the index is available to the product card
1243
+ // This is wo we can do intelligent image loading in ShopifyContainedImage without shopify devs having to pass in the index
1244
+ // We don't care about the return value of ProductCardIndexProvider because our rendering result might be a promise which solid doesn't support until it has gone through createResource
1245
+ $129b9b9c010039d0$export$11354f017ddc9790({
1246
+ index_: index,
1247
+ get children() {
1248
+ // This is the key - the functions called inside of the children getter of the provider are the one that have the context defined in their solid-internal Owner
1249
+ renderingResult = product_card_template_(display, {
1250
+ set_on_index_change: fn => $gVhhF$solidjs.runWithOwner(owner, () => $gVhhF$solidjs.createEffect(() => fn(index())))
1376
1251
  });
1377
- return renderedDisplays_;
1378
- })();
1252
+ return undefined; // just to satisfy the typing
1253
+ }
1254
+ });
1255
+ // renderingResult is guaranteed to be defined here (if rendering returned something) because the children getter of ProductCardIndexProvider is called synchronously (createRenderEffect is used solid-internally)
1256
+ return renderingResult;
1257
+ });
1258
+ const node_accessor = $5f7d55c710fd1bc2$export$2f0cbfbb4354bb52(product_card_resource);
1259
+ $gVhhF$solidjs.createEffect(() => {
1260
+ const product_card_nodes = node_accessor();
1261
+ const display = display_accessor();
1262
+ const elements = [];
1263
+ for (let i = 0; i < product_card_nodes.length; i++) {
1264
+ const element = product_card_nodes[i];
1265
+ if (!(element instanceof Element)) continue;
1266
+ elements.push(element);
1267
+ // Add tracking ids
1268
+ if (!(element instanceof HTMLElement)) continue; // Apparently only HTMLElements have dataset
1269
+ for (let i = 0; i < $a73063cc642f3e5f$var$result_ids.length; i++) {
1270
+ const id = $a73063cc642f3e5f$var$result_ids[i];
1271
+ const value = display[id];
1272
+ if (value) Object.assign(element.dataset, $gVhhF$depictaiutilishared.camelCasifyObject({
1273
+ [id]: value
1274
+ }));
1275
+ }
1379
1276
  }
1277
+ do_per_elements_?.(elements, index);
1380
1278
  });
1279
+ return [
1280
+ // Show content block before product card, so it's *at* that index
1281
+ possible_content_block, node_accessor // Don't return product_card_resource as calling it could be creating the nodes which would mean we'd be double-creating the nodes,
1282
+ ]; // Regarding the as unknown shenanigans: https://github.com/solidjs/solid/releases/tag/v1.7.0
1381
1283
  }
1382
- })];
1383
- return $gVhhF$solidjsweb.createComponent((0, $gVhhF$solidjs.Show), {
1384
- get when() {
1385
- return is_showing_something();
1386
- },
1387
- get fallback() {
1388
- return NoResultsFallback_?.();
1389
- },
1390
- children: slider
1391
1284
  });
1392
1285
  }
1393
- function $95837413165cc2df$export$23fca46f3143581f(component, error_message) {
1394
- const component_in_error_boundary = do_with_elements => {
1395
- const error_boundary_wrapped = $edafe91daf3091d7$export$b69d0d92862de18b({
1396
- severity_: "error",
1397
- message_: error_message,
1398
- get children() {
1399
- const elements = component();
1400
- do_with_elements?.(elements);
1401
- return elements;
1286
+ // source: https://medium.com/@pancemarko/deep-equality-in-javascript-determining-if-two-objects-are-equal-bf98cf47e934
1287
+ // deeply diffs two objects
1288
+ // Modified by chatGPT to not be recursive, since stiga was getting a stack overflow just due to the size of their data
1289
+ // Modified futher to bias towards saying objects are equal non-primitive objects differ
1290
+ function $a73063cc642f3e5f$var$deep_equal(obj1, obj2) {
1291
+ const stack = [[obj1, obj2]];
1292
+ while (stack.length) {
1293
+ const [currentObj1, currentObj2] = stack.pop();
1294
+ if (currentObj1 === currentObj2) continue;
1295
+ if (Array.isArray(currentObj1) && Array.isArray(currentObj2)) {
1296
+ if (currentObj1.length !== currentObj2.length) return false;
1297
+ for (let i = 0; i < currentObj1.length; i++) stack.push([currentObj1[i], currentObj2[i]]);
1298
+ } else if (typeof currentObj1 === "object" && typeof currentObj2 === "object" && currentObj1 !== null && currentObj2 !== null) {
1299
+ if (Array.isArray(currentObj1) || Array.isArray(currentObj2)) return false;
1300
+ const keys1 = Object.keys(currentObj1);
1301
+ const keys2 = Object.keys(currentObj2);
1302
+ if (keys1.length !== keys2.length || !keys1.every(key => keys2.includes(key))) return false;
1303
+ for (let key in currentObj1) {
1304
+ if ($a73063cc642f3e5f$var$result_ids.includes(key)) continue; // ignore comparing values for keys we know are changing with every request
1305
+ stack.push([currentObj1[key], currentObj2[key]]);
1402
1306
  }
1403
- });
1404
- // This might return an accessor, in that case we need to "unwrap" it since our SDK consumers expect HTML elements or arrays thereof
1405
- if (typeof error_boundary_wrapped === "function")
1406
- // @ts-ignore
1407
- return error_boundary_wrapped();
1408
- // On the server it returns a string, so we just return that
1409
- return error_boundary_wrapped;
1410
- };
1411
- const owner = $gVhhF$solidjs.getOwner();
1412
- if (owner == null) return $gVhhF$solidjs.createRoot(dispose => {
1413
- // if we're not already run in a root, create a root and automatically dispose it when the first returned element is removed from the DOM
1414
- return component_in_error_boundary(elements => {
1415
- const have_multiple = Array.isArray(elements);
1416
- if (have_multiple ? !elements?.length : !elements) return;
1417
- (0, $gVhhF$depictaiutilishared.observer).onremoved(have_multiple ? elements[0] : elements, ({
1418
- element: element
1419
- }) => {
1420
- if (document.documentElement.contains(element)) return; // If element has instantly been re-inserted, don't dispose
1421
- // See https://depictaiworkspace.slack.com/archives/C050GHEPBTR/p1695809102791259?thread_ts=1695106805.459729&cid=C050GHEPBTR
1422
- dispose();
1423
- });
1424
- });
1425
- });else
1426
- // we're already in a root so cleanups are handled
1427
- return component_in_error_boundary();
1307
+ } else if (typeof currentObj1 !== "function" && typeof currentObj2 !== "function")
1308
+ // Claim that all functions are equal, since the only way a function ends up here is because someone added it in the displayTransformer and that shouldn't force us to re-render, right?
1309
+ return false;
1310
+ }
1311
+ return true;
1428
1312
  }
1429
- const $84798aaa863a4909$var$ListingContext = /*@__PURE__*/$gVhhF$solidjs.createContext();
1430
- function $84798aaa863a4909$export$e68cff63f76bd15b(props) {
1431
- const our_object = {};
1432
- return $gVhhF$solidjsweb.createComponent($84798aaa863a4909$var$ListingContext.Provider, {
1433
- value: our_object,
1313
+ const $67155e1aeb7073f6$export$f223d69ed12f5fb7 = /*@__PURE__*/$gVhhF$solidjs.createContext();
1314
+ function $67155e1aeb7073f6$export$4a14175d33d81a96({
1315
+ displays_by_key_: displays_by_key_,
1316
+ product_card_template_: product_card_template_,
1317
+ content_blocks_: content_blocks_,
1318
+ alwaysObserveCards_: alwaysObserveCards_
1319
+ }) {
1320
+ const targetToIndexMap_ = new Map();
1321
+ const indexToTargetMap_ = new Map();
1322
+ const [listenersForIntersecting, setListenersForIntersecting] = $gVhhF$solidjs.createSignal(0);
1323
+ const [currentlyIntersecting, setCurrentlyIntersecting] = $gVhhF$solidjs.createSignal(new Set(), {
1324
+ equals: false
1325
+ });
1326
+ if (0, $gVhhF$solidjsweb.isServer) return {
1327
+ renderedDisplays_: $a73063cc642f3e5f$export$43f0fb951fcb0c54({
1328
+ displays_by_key_: displays_by_key_,
1329
+ product_card_template_: product_card_template_,
1330
+ content_blocks_: content_blocks_
1331
+ }),
1332
+ indexToTargetMap_: indexToTargetMap_,
1333
+ currentlyIntersecting: currentlyIntersecting
1334
+ };
1335
+ // The following sets up an intersection observer to determine which results are in the viewport, used for the ScrollStatus indicator, knowing when to load more and any other consumer of the useProductCardIsVisible hook.
1336
+ const shouldObserve = $gVhhF$solidjs.createMemo(() => alwaysObserveCards_ || listenersForIntersecting() > 0);
1337
+ const intersection_observer = new IntersectionObserver($gVhhF$depictaiutilishared.catchify(records => {
1338
+ const intersectingValue = currentlyIntersecting();
1339
+ for (let i = 0; i < records.length; i++) {
1340
+ const record = records[i];
1341
+ const {
1342
+ target: target,
1343
+ intersectionRatio: intersectionRatio
1344
+ } = record;
1345
+ const index_of_target = targetToIndexMap_.get(target);
1346
+ if (index_of_target == undefined) return; // It seems like it can happen that we get IntersectionObserverEntries after the onCleanup function for a recommendation card has run, in Firefox. Probably a bug in Firefox? If I understood the bug correctly, this is a workaround.
1347
+ const is_in_set = intersectingValue.has(index_of_target);
1348
+ if (intersectionRatio > 0) {
1349
+ if (!is_in_set) intersectingValue.add(index_of_target);
1350
+ } else if (is_in_set) intersectingValue.delete(index_of_target);
1351
+ }
1352
+ setCurrentlyIntersecting(intersectingValue);
1353
+ }, "renderDisplays IO failed"));
1354
+ const renderedDisplays_ = $gVhhF$solidjsweb.createComponent($67155e1aeb7073f6$export$f223d69ed12f5fb7.Provider, {
1355
+ value: {
1356
+ currentlyIntersecting_: currentlyIntersecting,
1357
+ setIsListening_: setListenersForIntersecting
1358
+ },
1434
1359
  get children() {
1435
- return props.children;
1360
+ return $a73063cc642f3e5f$export$43f0fb951fcb0c54({
1361
+ displays_by_key_: displays_by_key_,
1362
+ product_card_template_: product_card_template_,
1363
+ content_blocks_: content_blocks_,
1364
+ do_per_elements_: (elements, index_accessor) =>
1365
+ // Adds/removes elements from the "results in viewport" intersection observer above
1366
+ $gVhhF$solidjs.createEffect(() => {
1367
+ if (!shouldObserve()) return;
1368
+ const display_index = index_accessor();
1369
+ // Nested createEffect because we only want to re-run this when the index changes (or we should/shouldn't observe)
1370
+ // We need to save in a map which index the elements of this template are so that we can know that in the IntersectionObserver callback
1371
+ for (let i = 0; i < elements.length; i++) {
1372
+ const element = elements[i];
1373
+ targetToIndexMap_.set(element, display_index);
1374
+ intersection_observer.observe(element);
1375
+ $gVhhF$solidjs.onCleanup(() => {
1376
+ intersection_observer.unobserve(element);
1377
+ targetToIndexMap_.delete(element);
1378
+ const intersecting = $gVhhF$solidjs.untrack(currentlyIntersecting);
1379
+ const wasDeleted = intersecting.delete(display_index);
1380
+ if (wasDeleted) setCurrentlyIntersecting(intersecting);
1381
+ });
1382
+ }
1383
+ indexToTargetMap_.set(display_index, elements);
1384
+ $gVhhF$solidjs.onCleanup(() => indexToTargetMap_.delete(display_index));
1385
+ })
1386
+ });
1436
1387
  }
1437
1388
  });
1389
+ return {
1390
+ renderedDisplays_: renderedDisplays_,
1391
+ indexToTargetMap_: indexToTargetMap_,
1392
+ currentlyIntersecting: currentlyIntersecting
1393
+ };
1438
1394
  }
1439
- function $84798aaa863a4909$export$8d0c4fdbb05a2f2e() {
1440
- return $gVhhF$solidjs.useContext($84798aaa863a4909$var$ListingContext);
1395
+ function $67155e1aeb7073f6$export$80bb7300ce6a8be3() {
1396
+ const {
1397
+ setIsListening_: setIsListening_,
1398
+ currentlyIntersecting_: currentlyIntersecting_
1399
+ } = $gVhhF$solidjs.useContext($67155e1aeb7073f6$export$f223d69ed12f5fb7) || {};
1400
+ const getOurIndex = $129b9b9c010039d0$export$e3faf4d95300054f();
1401
+ const isVisible = $gVhhF$solidjs.createMemo(() => currentlyIntersecting_?.().has(getOurIndex?.()));
1402
+ setIsListening_?.(prev => prev + 1);
1403
+ $gVhhF$solidjs.onCleanup(() => setIsListening_?.(prev => prev + 1));
1404
+ return isVisible;
1441
1405
  }
1442
- function $061ff5b4c3d36171$export$89c6019e4baa40b1(props) {
1443
- const style = $gVhhF$solidjs.createMemo(() => {
1444
- const {
1445
- style: style,
1446
- height: height,
1447
- width: width
1448
- } = props;
1449
- return {
1450
- ...(style || {}),
1451
- height: height ?? "1em",
1452
- ...(width && {
1453
- width: width
1454
- })
1455
- };
1456
- });
1457
- return $gVhhF$solidjsweb.createComponent((0, $9fe9883b1d8ecccc$export$acb3eaade4b23afd), {
1458
- get style() {
1459
- return style();
1460
- },
1461
- get ["class"]() {
1462
- return props.class;
1406
+ function $4b455f60dadde8b8$export$5bad61ecde080b1f(...params) {
1407
+ const {
1408
+ collapse: collapse,
1409
+ expand: expand,
1410
+ ExpandingContainer: ExpandingContainer
1411
+ } = $gVhhF$depictaiutilishared.useExpandingContainer(...params);
1412
+ return {
1413
+ collapse: collapse,
1414
+ expand: expand,
1415
+ ExpandingContainer: ({
1416
+ children: children
1417
+ }) => {
1418
+ // get reactive signals/solid's jsx elements into `ExpandingContainer`
1419
+ const fake_div = document.createElement("div");
1420
+ const outer_expanding = $gVhhF$solidjsweb.createComponent(ExpandingContainer, {
1421
+ children: [fake_div]
1422
+ });
1423
+ const parent = fake_div.parentElement;
1424
+ fake_div.remove();
1425
+ $gVhhF$solidjsweb.insert(parent, children);
1426
+ return outer_expanding;
1463
1427
  }
1464
- });
1465
- }
1466
- var $6f604560873e4f69$var$_tmpl$ = /*#__PURE__*/$gVhhF$solidjsweb.template(`<div class=instant-card><div class=img-part></div><div class=right-part><div class=title-brand-tagline><span class=title></span></div><div class=price-container>`);
1467
- const $6f604560873e4f69$export$8a2234b5b1a5ebd4 = "--image-aspect-ratio";
1468
- function $6f604560873e4f69$export$ee1733d9a8495996() {
1469
- return (() => {
1470
- var _el$ = $gVhhF$solidjsweb.getNextElement($6f604560873e4f69$var$_tmpl$),
1471
- _el$2 = _el$.firstChild,
1472
- _el$3 = _el$2.nextSibling,
1473
- _el$4 = _el$3.firstChild,
1474
- _el$5 = _el$4.firstChild,
1475
- _el$6 = _el$4.nextSibling;
1476
- $gVhhF$solidjsweb.insert(_el$2, $gVhhF$solidjsweb.createComponent((0, $3bc4a89b971a3af5$export$3f37af283f8fb843), {
1477
- aspectRatio: `var(${$6f604560873e4f69$export$8a2234b5b1a5ebd4}, 1)`
1478
- }));
1479
- $gVhhF$solidjsweb.insert(_el$5, $gVhhF$solidjsweb.createComponent((0, $061ff5b4c3d36171$export$89c6019e4baa40b1), {
1480
- width: "20ch",
1481
- height: "1em"
1482
- }));
1483
- $gVhhF$solidjsweb.insert(_el$6, $gVhhF$solidjsweb.createComponent((0, $061ff5b4c3d36171$export$89c6019e4baa40b1), {
1484
- width: "5ch",
1485
- height: "1em",
1486
- "class": "price"
1487
- }));
1488
- return _el$;
1489
- })();
1428
+ };
1490
1429
  }
1491
- var $93304d59a9747aa6$var$_tmpl$ = /*#__PURE__*/$gVhhF$solidjsweb.template(`<div>`);
1492
- function $93304d59a9747aa6$export$94026ee33dbabc26(props) {
1493
- return (() => {
1494
- var _el$ = $gVhhF$solidjsweb.getNextElement($93304d59a9747aa6$var$_tmpl$);
1495
- $gVhhF$solidjsweb.insert(_el$, () => props.children);
1496
- $gVhhF$solidjsweb.effect(_p$ => {
1497
- var _v$ = `padding-bottom:calc(calc(1 / ${props.aspectRatio}) * 100%)`,
1498
- _v$2 = "s-image-container" + (props.class ? " " + props.class : "");
1499
- _p$.e = $gVhhF$solidjsweb.style(_el$, _v$, _p$.e);
1500
- _v$2 !== _p$.t && $gVhhF$solidjsweb.className(_el$, _p$.t = _v$2);
1501
- return _p$;
1502
- }, {
1503
- e: undefined,
1504
- t: undefined
1505
- });
1506
- return _el$;
1507
- })();
1430
+ const $4e04be41243c3d80$var$transform_elements = sorted_cols_at_size => {
1431
+ const cols_at_size = [];
1432
+ for (let i = 0; i < sorted_cols_at_size.length; i++) {
1433
+ const [nb_cols, width] = sorted_cols_at_size[i];
1434
+ const next_width = sorted_cols_at_size[i + 1]?.[1];
1435
+ const max_width = i === 0 ? "" : `${width}px`;
1436
+ const min_width = next_width !== undefined ? `${next_width + 1}px` : "";
1437
+ const col_at_size = [nb_cols, min_width, max_width];
1438
+ cols_at_size.push(col_at_size);
1439
+ }
1440
+ return cols_at_size;
1441
+ };
1442
+ const $4e04be41243c3d80$export$6daf8749b65a1b77 = cols_at_size => {
1443
+ // Check that every element is an array
1444
+ const are_arrays = cols_at_size?.every?.(element => Array.isArray(element));
1445
+ if (!are_arrays) throw new Error("Every element in cols_at_size must be an array");
1446
+ // Check width duplicates
1447
+ const widths = cols_at_size.map(([_, width]) => width);
1448
+ const widths_set = new Set(widths);
1449
+ if (widths_set.size < widths.length) throw new Error("There are duplicate widths in cols_at_size");
1450
+ if (cols_at_size.length === 1) {
1451
+ const [nb_cols, width] = cols_at_size[0];
1452
+ const max_width = width !== undefined ? `${width}px` : "";
1453
+ return [[nb_cols, "", max_width]];
1454
+ }
1455
+ // Sort widths in ascending order
1456
+ const sorted_cols_at_size = [...cols_at_size].sort((a, b) => {
1457
+ const [_b, width_A] = a;
1458
+ const [_a, width_b] = b;
1459
+ if (width_A === undefined) return -1;
1460
+ if (width_b === undefined) return 1;
1461
+ return width_b - width_A;
1462
+ });
1463
+ return $4e04be41243c3d80$var$transform_elements(sorted_cols_at_size);
1464
+ };
1465
+ function $fc23d7668ae0011d$export$a2cc176055f8f08(what = "grid") {
1466
+ const text = "Please try setting `columnsAtSize`/`gridSpacing` on the `SearchPage`/`CategoryPage` components to change the " + what + " before setting it with CSS. If you do with CSS, set `columnsAtSize` to an empty array to ensure no declarations created by the SDK leak through.";
1467
+ return (0, $gVhhF$solidjsweb.isServer) ? {
1468
+ t: `<!-- ${text} -->`
1469
+ } : document.createComment(text);
1508
1470
  }
1509
- let $3ea8e0af95b5b7d2$var$browserIsBugged = false;
1510
- function $3ea8e0af95b5b7d2$export$2972b890694ced75(callback, {
1511
- timeout: timeout
1471
+ const $0caf87eaa19079da$var$defaultCacheKey = /*@__PURE__*/Symbol();
1472
+ const $0caf87eaa19079da$var$cachedPlaceholderElements = {};
1473
+ const $0caf87eaa19079da$var$renderingCachedPlaceholderElement = {};
1474
+ function $0caf87eaa19079da$export$f33414a4df0afd3f({
1475
+ product_card_template_: product_card_template_,
1476
+ num_placeholders_: num_placeholders_,
1477
+ content_blocks_: content_blocks_,
1478
+ placeholderCacheKey_: placeholderCacheKey_
1512
1479
  }) {
1513
- if ($3ea8e0af95b5b7d2$var$browserIsBugged) timeout /= 4;
1514
- const cancel = () => {
1515
- cancelIdleCallback(idleHandle);
1516
- clearTimeout(racingTimeout);
1517
- };
1518
- const idleHandle = requestIdleCallback(deadline => {
1519
- cancel();
1520
- return callback(deadline);
1521
- }, {
1522
- timeout: timeout
1480
+ const [num_to_actually_show, set_num_to_actually_show] = $gVhhF$solidjs.createSignal($gVhhF$solidjs.untrack(num_placeholders_));
1481
+ if (0, $gVhhF$solidjsweb.isServer)
1482
+ // want to render simple placeholders on server, effects don't run there
1483
+ // FIXME when adding proper SSR: also SSR content blocks
1484
+ return $gVhhF$solidjs.createMemo(() => Array.from({
1485
+ length: num_placeholders_()
1486
+ }).map(() => {
1487
+ const template_result = product_card_template_(null, undefined);
1488
+ // @ts-ignore
1489
+ if (typeof template_result?.then !== "function") return template_result;
1490
+ }));
1491
+ $gVhhF$solidjs.createRenderEffect(() => {
1492
+ const existing_num = num_to_actually_show();
1493
+ const to_add = num_placeholders_() - existing_num;
1494
+ if (to_add <= 0) return;
1495
+ // Below comment is from when we used recommendation renderer and manual DOM manipulation for this, but I'm still scared to remove placeholders and mess up scroll restoration, so I'm keeping the way it worked. Otherwise, <Index> could be used which would be far more elegant.
1496
+ // We don't need to remove placeholders, only need to add. The reason for that is that chrome restores the scroll position once all tasks queued as reaction of the "popstate" events have been completed in the task queue. That means that we need to get the placeholders in fast, or we might lose scroll position if the previous site was shorter.
1497
+ // The query id and the amount might update in separate tasks because one comes from href_change_ipns and the other one from another "popstate" listener. Therefore, we need to be able to dynamically add more placeholders. I'm not entirely sure about this anymore though, since we have the scroll position on a per-id basis it might work anyway but THIS WORKS, and I've been working on this bug for 3 hours so let's just keep it this way.
1498
+ // Above comments were from when we added elements in this createRenderEffect. Now I changed it to just increment the number and render them from an <Index> instead, so I can add content blocks better
1499
+ // Functionality should be the same
1500
+ set_num_to_actually_show(existing_num + to_add);
1523
1501
  });
1524
- const racingTimeout = setTimeout(() => {
1525
- $gVhhF$depictaiutilishared.dwarn("Browser did not respect requestIdleCallback timeout");
1526
- $3ea8e0af95b5b7d2$var$browserIsBugged = true;
1527
- cancel();
1528
- callback({
1529
- didTimeout: false,
1530
- timeRemaining() {
1531
- return 500;
1532
- }
1533
- });
1534
- }, timeout + 1000);
1535
- return cancel;
1536
- }
1537
- function $19950faaac026e09$export$e6fffe67d4cdd848(whatToWatch) {
1538
- const [size, setSize] = $gVhhF$solidjs.createSignal(0);
1539
- const resizeObserver = new ResizeObserver($gVhhF$depictaiutilishared.catchify(records => {
1540
- for (let j = 0; j < records.length; j++) {
1541
- const {
1542
- width: width
1543
- } = records[j].contentRect;
1544
- if (!width) continue;
1545
- setSize(Math.ceil(width));
1502
+ return $gVhhF$solidjsweb.createComponent((0, $gVhhF$solidjs.Index), {
1503
+ get each() {
1504
+ return Array.from({
1505
+ length: num_to_actually_show()
1506
+ });
1507
+ },
1508
+ children: (_item, index) => {
1509
+ const possible_content_block = $gVhhF$solidjs.createMemo(() => content_blocks_?.()?.()?.[index]);
1510
+ // Show content block before product card, so it's *at* that index
1511
+ return [possible_content_block, $0caf87eaa19079da$var$Placeholder({
1512
+ product_card_template_: product_card_template_,
1513
+ placeholderCacheKey_: placeholderCacheKey_
1514
+ })]; // https://github.com/solidjs/solid/releases/tag/v1.7.0
1546
1515
  }
1547
- }));
1548
- // If we start observing elements before they're in the DOM Chrome (Version 117.0.5938.92 (Official Build) (arm64)) sometimes gives us a resize event with a width of 0 and then doesn't dispatch one with the actual width once it's in the DOM. So we wait until the element is in the DOM before we start observing it.
1549
- // It doesn't seem to work even when waiting for it to be in the DOM, also wait an animation frame
1550
- const disconnect = (0, $gVhhF$depictaiutilishared.observer).onexists(whatToWatch, ({
1551
- element: element
1552
- }) => requestAnimationFrame($gVhhF$depictaiutilishared.catchify(() => resizeObserver.observe(element))));
1553
- $gVhhF$solidjs.onCleanup(() => {
1554
- resizeObserver.disconnect();
1555
- disconnect();
1556
1516
  });
1557
- return size;
1558
1517
  }
1559
- const $19950faaac026e09$var$onIdleFunctionsSymbol = /*@__PURE__*/Symbol("onIdleFunctions");
1560
- const $19950faaac026e09$export$f8945274f08a3105 = /*@__PURE__*/Symbol("listingContextAspectRatios");
1561
- const $19950faaac026e09$export$6066eb417ed2e9f7 = /*@__PURE__*/Symbol("listingContextAspectRatios");
1562
- function $19950faaac026e09$export$6e5611bc7e57e477(props) {
1563
- const src_options = $gVhhF$solidjs.createMemo(() => {
1564
- const {
1565
- src: src,
1566
- customUrls: customUrls
1567
- } = props;
1568
- const fallback_src = (customUrls || $19950faaac026e09$export$1d6267828870adda)(src, 400);
1569
- const srcset = $19950faaac026e09$var$make_srcset(props.src, props.resolutions || [64, 128, 256, 300, 400, 512, 640, 768, 1024, 1280, 1536, 2048, 2560, 3072, 3584, 4096], customUrls);
1570
- // The order here *might* matter - I've seen mobile safari always start with loading the 400px version when it was reversed
1571
- return {
1572
- srcset: srcset,
1573
- src: fallback_src
1574
- };
1518
+ /**
1519
+ * Get one placeholder element. It will be cloned for each placeholder for ultimate performance even when solid-js isn't rendering the placeholders.
1520
+ */
1521
+ function $0caf87eaa19079da$var$Placeholder({
1522
+ product_card_template_: product_card_template_,
1523
+ placeholderCacheKey_ = $0caf87eaa19079da$var$defaultCacheKey
1524
+ }) {
1525
+ const get_cloned_placeholders = (element_to_clone = $0caf87eaa19079da$var$cachedPlaceholderElements[placeholderCacheKey_]) => Array.isArray(element_to_clone) ? element_to_clone.map(el => el instanceof Node ? el.cloneNode(true) : el) : element_to_clone;
1526
+ if ($0caf87eaa19079da$var$cachedPlaceholderElements[placeholderCacheKey_]) return get_cloned_placeholders();
1527
+ let dispose_original_copy;
1528
+ const get_unwrapped_element = $gVhhF$solidjs.createRoot(dispose => {
1529
+ dispose_original_copy = dispose;
1530
+ // if product_card_template_ is async and rejects the resource will take care of throwing upwards here in the solid-js error handling system
1531
+ const [resource] = $gVhhF$solidjs.createResource(() => {
1532
+ const rendering_cached_placeholder_element = $0caf87eaa19079da$var$renderingCachedPlaceholderElement[placeholderCacheKey_];
1533
+ if (rendering_cached_placeholder_element) return rendering_cached_placeholder_element;
1534
+ const return_value = product_card_template_(null, undefined);
1535
+ if (typeof return_value === "object" && typeof return_value?.then === "function")
1536
+ // Is Promise or promise-like object
1537
+ $0caf87eaa19079da$var$renderingCachedPlaceholderElement[placeholderCacheKey_] = return_value;
1538
+ return return_value;
1539
+ });
1540
+ return $gVhhF$solidjs.createMemo(() => {
1541
+ if (resource.loading) return () => [];
1542
+ return $5f7d55c710fd1bc2$export$2f0cbfbb4354bb52(resource);
1543
+ });
1575
1544
  });
1576
- // Work around https://github.com/solidjs/solid/issues/1828
1577
- const img = new Image(); // Need to use new Image or browser might not start loading image before it's in the DOM
1578
- const [, otherAttributes] = $gVhhF$solidjs.splitProps(props, ["resolutions", "size", "src", "srcSet", "loading", "keyRatioUpdating", "customUrls" // Can't have properties ending on underscore because the property name mangling will get confused by this <-
1579
- ]);
1580
-
1581
- const [hasSetSize, setHasSetSize] = $gVhhF$solidjs.createSignal(false);
1582
- const [hasDecoded, setHasDecoded] = $gVhhF$solidjs.createSignal(false);
1583
- const productCardIndexAccessor = $129b9b9c010039d0$export$e3faf4d95300054f();
1584
- const sizePX = $gVhhF$solidjs.createMemo(() => `${props.size()}px`); // props.size could be a getter that expects a cleanup so get it in this memo instead of where we set img.sizes
1585
- const listingContext = $84798aaa863a4909$export$8d0c4fdbb05a2f2e();
1586
- const isPrioritisedImage = $gVhhF$solidjs.createMemo(() => productCardIndexAccessor?.() < 8);
1587
- const shouldActuallyEnableLazyLoading = $gVhhF$solidjs.createMemo(() => {
1588
- const {
1589
- loading: loading
1590
- } = props;
1591
- if (loading === "force-lazy") return true;
1592
- return loading === "lazy" && !isPrioritisedImage();
1545
+ $gVhhF$solidjs.onCleanup(() => {
1546
+ const rendering = $0caf87eaa19079da$var$renderingCachedPlaceholderElement[placeholderCacheKey_];
1547
+ if (rendering) {
1548
+ // If we're still rendering in our separate root, let the rendering finish or other <Placeholder> components waiting for it might get an incomplete result
1549
+ const unset_current_rendering = () => {
1550
+ dispose_original_copy();
1551
+ $0caf87eaa19079da$var$renderingCachedPlaceholderElement[placeholderCacheKey_] = undefined;
1552
+ };
1553
+ rendering?.then(unset_current_rendering, unset_current_rendering);
1554
+ } else
1555
+ // Otherwise, just make sure we've disposed our root
1556
+ dispose_original_copy();
1593
1557
  });
1594
- const modifiedOtherAttributes = $gVhhF$solidjs.mergeProps(otherAttributes, {
1595
- get loading() {
1596
- return shouldActuallyEnableLazyLoading() ? "lazy" : undefined;
1597
- }
1558
+ return $gVhhF$solidjs.createMemo(() => {
1559
+ const unwrapped_array = get_unwrapped_element()();
1560
+ if (!unwrapped_array?.length || !unwrapped_array.every(item => item))
1561
+ // Haven't finished rendering yet in case of "async" rendering, so don't freeze the elements yet
1562
+ return;
1563
+ // placeholders have finished rendering, freeze the elements
1564
+ // here we might receive the result of a promise that has already been received, tore down and put into cached_placeholder_element, so don't overwrite that more valid result if it's already set
1565
+ $0caf87eaa19079da$var$cachedPlaceholderElements[placeholderCacheKey_] ||= get_cloned_placeholders(unwrapped_array); // Clone initial return value so that who gave us the elements can't mutate them, which has caused issues before with react
1566
+ $0caf87eaa19079da$var$renderingCachedPlaceholderElement[placeholderCacheKey_] = undefined;
1567
+ dispose_original_copy(); // So that this memo will never re-run and the placeholder rendering function can clean up
1568
+ return get_cloned_placeholders(); // clone, just in case. If we wouldn't, someone could modify the copy in the DOM, and it would affect the cached version
1598
1569
  });
1599
- // Batch idle callbacks so that MutationObserver can batch the resulting mutations better, so we don't lose tons of CPU time to MutationObservers doing lots of things for each image
1600
- // Happened on singular, they have some weird observer (+we also have one but ours is at least 3x more efficient)
1601
- const executeWhenIdleBatched = (fn, isSizesUpdate) => {
1602
- if (typeof requestIdleCallback !== "function") return setTimeout(fn); // Old browsers don't get optimisation
1603
- if ((!isSizesUpdate || !sizeSetFastPath) && $gVhhF$solidjs.untrack(isPrioritisedImage)) return fn(); // If we're one of the first 8 images, execute the callbacks immediately (improves lighthouse metrics)
1604
- // unless we're just a `sizes` update after the initial sizes setting (this optimisation makes the looks animation a lot smoother on low-cpu devices)
1605
- const existingStack = listingContext?.[$19950faaac026e09$var$onIdleFunctionsSymbol];
1606
- if (existingStack) return existingStack.add(fn);
1607
- const newStack = new Set([fn]);
1608
- if (listingContext) listingContext[$19950faaac026e09$var$onIdleFunctionsSymbol] = newStack;
1609
- const processStackWhenIdle = () => $3ea8e0af95b5b7d2$export$2972b890694ced75($gVhhF$depictaiutilishared.catchify(deadline => {
1610
- const start = performance.now();
1611
- const thirdDeadlineTime = deadline.timeRemaining() / 3; // observers coming after this take up 2-3x the time so don't use all of it
1612
- for (const fn of newStack) {
1613
- newStack.delete(fn);
1614
- fn();
1615
- const timeTaken = performance.now() - start;
1616
- if ((!deadline.timeRemaining() || timeTaken > thirdDeadlineTime) && newStack.size) {
1617
- // There's still work to be done but no time, so schedule another idle callback
1618
- processStackWhenIdle();
1619
- return;
1620
- }
1621
- }
1622
- // Stack is empty
1623
- if (listingContext) delete listingContext[$19950faaac026e09$var$onIdleFunctionsSymbol];
1624
- }), {
1625
- timeout: 15000
1626
- } // long timeout, better to not have images than to freeze the browser. But also we don't want to confuse user why no iamges are loading
1627
- );
1570
+ }
1628
1571
 
1629
- processStackWhenIdle();
1630
- };
1631
- let sizeSetFastPath = false;
1632
- let hasDecodedFastPath = false;
1633
- let timeoutSet = false;
1634
- let gotCleanedUp = false;
1635
- let srcJustChanged = true;
1636
- img.classList.add("s-responsive-image");
1637
- if (listingContext) {
1638
- let prevValue;
1639
- const aspectRatios = listingContext[$19950faaac026e09$export$f8945274f08a3105] ||= new Set();
1640
- const [, setMostPrevalentAspectRatio] = listingContext[$19950faaac026e09$export$6066eb417ed2e9f7] ||= $gVhhF$solidjs.createSignal();
1641
- img.addEventListener("load", $gVhhF$depictaiutilishared.catchify(() => {
1642
- if (props.keyRatioUpdating) {
1643
- if (!srcJustChanged) return;
1644
- srcJustChanged = false;
1572
+ var $f48d0fbb00835aaa$var$_tmpl$ = /*#__PURE__*/$gVhhF$solidjsweb.template(`<div>`),
1573
+ $f48d0fbb00835aaa$var$_tmpl$2 = /*#__PURE__*/$gVhhF$solidjsweb.template(`<style type=text/css>`);
1574
+ function $f48d0fbb00835aaa$export$d21e32dfedac04be(props) {
1575
+ const container_class = "x" + $gVhhF$solidjs.createUniqueId();
1576
+ const container_element_ = (() => {
1577
+ var _el$ = $gVhhF$solidjsweb.getNextElement($f48d0fbb00835aaa$var$_tmpl$);
1578
+ $gVhhF$solidjsweb.spread(_el$, $gVhhF$solidjsweb.mergeProps(() => props.element_attributes, {
1579
+ "classList": {
1580
+ [container_class]: true
1645
1581
  }
1646
- // Save our aspect ratio whenever it changes, so that we can base the containers on the most dominant aspect ratio, used in SearchModalV2 and LookItem
1647
- const aspectRatio_ = img.naturalWidth / img.naturalHeight;
1648
- if (prevValue) aspectRatios.delete(prevValue);
1649
- const newValue = !isNaN(aspectRatio_) ? {
1650
- aspectRatio_: aspectRatio_
1651
- } : undefined;
1652
- if (newValue) aspectRatios.add(newValue);
1653
- prevValue = newValue;
1654
- // Do all calculations resulting of an update directly, for performance
1655
- const asArray = [...aspectRatios].map(({
1656
- aspectRatio_: aspectRatio_
1657
- }) => aspectRatio_);
1658
- const mostPrevalentAspectRatio = $gVhhF$depictaiutilishared.findMostFrequentItem(asArray);
1659
- setMostPrevalentAspectRatio(mostPrevalentAspectRatio);
1660
- }));
1661
- $gVhhF$solidjs.onCleanup(() => prevValue && aspectRatios.delete(prevValue));
1582
+ }), false, true);
1583
+ $gVhhF$solidjsweb.insert(_el$, () => props.children);
1584
+ $gVhhF$solidjsweb.runHydrationEvents();
1585
+ return _el$;
1586
+ })(); // Don't want this in the error boundary below, it should only be for the actual layouting
1587
+ return $gVhhF$solidjsweb.createComponent((0, $edafe91daf3091d7$export$b69d0d92862de18b), {
1588
+ get message_() {
1589
+ return "Failed calculating " + props.layout + " layout";
1590
+ },
1591
+ severity_: "error",
1592
+ get children() {
1593
+ return $f48d0fbb00835aaa$export$445d8d3f5f706a40(props, container_element_, false, container_class);
1594
+ }
1595
+ });
1596
+ }
1597
+ function $f48d0fbb00835aaa$export$445d8d3f5f706a40(props, container_element, insert_after_container = false, container_class) {
1598
+ if (!container_class) {
1599
+ // only try to add if it wasn't provided, so that we have a code path without classList when doing SSR
1600
+ container_class = $gVhhF$depictaiutilishared.make_random_classname();
1601
+ container_element.classList.add(container_class);
1662
1602
  }
1663
- $gVhhF$solidjs.onCleanup(() => gotCleanedUp = true);
1664
- $gVhhF$solidjsweb.spread(img, $gVhhF$solidjs.mergeProps(modifiedOtherAttributes), false, true);
1665
- $gVhhF$solidjs.createRenderEffect(() => {
1666
- props.src;
1667
- srcJustChanged = true;
1603
+ // Copy of Layout from utilishared but using solid-js signals instead of async iterable IPNS
1604
+ const show_all_rows = $gVhhF$solidjs.createMemo(() => props.rows === "all");
1605
+ const rows_or_empty_object = $gVhhF$solidjs.createMemo(() => show_all_rows() ? {} : props.rows);
1606
+ const view_more_button_elements = $5f7d55c710fd1bc2$export$2f0cbfbb4354bb52($gVhhF$solidjs.createMemo(() => rows_or_empty_object().button));
1607
+ const view_less_button_elements = $5f7d55c710fd1bc2$export$2f0cbfbb4354bb52($gVhhF$solidjs.createMemo(() => rows_or_empty_object().view_less_button));
1608
+ const have_a_view_more_button = $gVhhF$solidjs.createMemo(() => {
1609
+ const button = view_more_button_elements();
1610
+ return !(!button || Array.isArray(button) && !button.length);
1668
1611
  });
1669
- $gVhhF$solidjs.createEffect(() => {
1670
- if (!props.size()) return; // wait until we have a size, run every time the size changes
1671
- if (timeoutSet) return;
1672
- const handler = $gVhhF$depictaiutilishared.catchify(() => {
1673
- if (gotCleanedUp) return;
1674
- img.sizes = sizePX(); // use latest available value of props.size
1675
- if (!sizeSetFastPath) {
1676
- setHasSetSize(true);
1677
- sizeSetFastPath = true;
1678
- }
1679
- timeoutSet = false;
1680
- });
1681
- // Throttle the updating of sizes to avoid unnecessary image re-fetches during resize
1682
- // And also because we first set src after sizes has been set, wait with loading and decoding images until browser is idle
1683
- // This greatly improves performance when loading (more) products
1684
- // See https://depictaiworkspace.slack.com/archives/C05NJ0K3WRL/p1698220617663139
1685
- timeoutSet = true; // Important to set timeoutSet before the call to executeWhenIdleBatched since if this is an important image executeWhenIdleBatched will execute fn immediately
1686
- executeWhenIdleBatched(handler, true);
1612
+ const have_a_view_less_button = $gVhhF$solidjs.createMemo(() => {
1613
+ const button = view_less_button_elements();
1614
+ return !(!button || Array.isArray(button) && !button.length);
1687
1615
  });
1688
- const dontLoadImageYet = $gVhhF$solidjs.createMemo(() => !props.size() || !hasSetSize()); // Moving this condition into a memo saves us from re-setting src and srcset (expensive) if just props.size changes
1689
- $gVhhF$solidjs.createEffect(() => {
1690
- if (dontLoadImageYet()) return; // If size isn't known or set, don't assign src yet since it would cause the most low res image to load
1691
- Object.assign(img, src_options());
1692
- // Add to DOM once decoded to not require/block any paints before image has completed loading and avoid showing broken image symbol
1693
- // We can do this because there has to be a container around us giving us a size
1694
- // No need to listen if we're already in the DOM
1695
- if (hasDecodedFastPath) return;
1696
- if (shouldActuallyEnableLazyLoading()) {
1697
- // For lazy loading there's no decoding to wait for as it only occurs when the image is visible
1698
- // Make sure src is set before we put the image into the DOM though so no broken image symbol is shown
1699
- setHasDecoded(true);
1700
- hasDecodedFastPath = true;
1616
+ const [get_styling, set_styling] = $gVhhF$solidjs.createSignal();
1617
+ const selector_beginning = `.depict .${container_class}>`; // class so ppl can have id if needed
1618
+ const [get_children_count, set_children_count] = $gVhhF$solidjs.createSignal(0);
1619
+ const update_children_count = () => (0, $gVhhF$solidjsweb.isServer) ? 20 : set_children_count(container_element.children.length);
1620
+ const col_styling = $gVhhF$solidjs.createMemo(() => $gVhhF$depictaiutilishared.n_col_styling_with_dist(selector_beginning, props.grid_spacing, props.cols_at_size, props.layout, props.override_vertical_spacing));
1621
+ const default_currently_visible_signal = $gVhhF$solidjs.createSignal($gVhhF$solidjs.untrack(() => rows_or_empty_object().start_rows ?? 1)); // Keep this signal here, so it doesn't change when rows_or_empty_object and I think it's to be expected that the "internal default signal" stays the same
1622
+ const rows_visible_signal = $gVhhF$solidjs.createMemo(() => rows_or_empty_object().currently_visible || default_currently_visible_signal);
1623
+ const get_rows_visible = $gVhhF$solidjs.createMemo(() => rows_visible_signal()[0]());
1624
+ const set_rows_visible = $gVhhF$solidjs.createMemo(() => rows_visible_signal()[1]);
1625
+ const view_more_button_class = "m" + $gVhhF$solidjs.createUniqueId();
1626
+ const view_less_button_class = "l" + $gVhhF$solidjs.createUniqueId(); // https://github.com/solidjs/solid-docs/issues/263#issuecomment-1622388079
1627
+ const view_more_button_container = $gVhhF$solidjs.createMemo(() => have_a_view_more_button() && (() => {
1628
+ var _el$2 = $gVhhF$solidjsweb.getNextElement($f48d0fbb00835aaa$var$_tmpl$);
1629
+ $gVhhF$solidjsweb.addEventListener(_el$2, "click", $gVhhF$depictaiutilishared.catchify(() => set_rows_visible()(prev => prev + (rows_or_empty_object().rows_per_click ?? 1))), true);
1630
+ $gVhhF$solidjsweb.className(_el$2, `load-more-container ${view_more_button_class}`);
1631
+ $gVhhF$solidjsweb.insert(_el$2, () => rows_or_empty_object().button);
1632
+ $gVhhF$solidjsweb.runHydrationEvents();
1633
+ return _el$2;
1634
+ })());
1635
+ // We need to have duplicate buttons because which ones is shown depends on the number of rows visible and therefore on the viewport size and therefore on the css media queries
1636
+ const view_less_button_container = $gVhhF$solidjs.createMemo(() => have_a_view_less_button() && (() => {
1637
+ var _el$3 = $gVhhF$solidjsweb.getNextElement($f48d0fbb00835aaa$var$_tmpl$);
1638
+ $gVhhF$solidjsweb.addEventListener(_el$3, "click", $gVhhF$depictaiutilishared.catchify(() => set_rows_visible()(() => rows_or_empty_object().start_rows ?? 1)), true);
1639
+ $gVhhF$solidjsweb.className(_el$3, `load-less-container ${view_less_button_class}`);
1640
+ $gVhhF$solidjsweb.insert(_el$3, () => rows_or_empty_object().view_less_button);
1641
+ $gVhhF$solidjsweb.runHydrationEvents();
1642
+ return _el$3;
1643
+ })());
1644
+ const [force_style_update, set_force_style_update] = $gVhhF$solidjs.createSignal("");
1645
+ $gVhhF$solidjs.createComputed(() => {
1646
+ // Make sure we don't have more rows visible than max_rows (this could happen by max_rows being decreased)
1647
+ const rows_visible = get_rows_visible();
1648
+ const max_rows = rows_or_empty_object().max_rows;
1649
+ if (!isNaN(max_rows) && max_rows >= 0 && rows_visible > max_rows) set_rows_visible()(max_rows);
1650
+ });
1651
+ $gVhhF$solidjs.createEffect(() => set_force_style_update(" ")); // force style update after hydration due to https://discord.com/channels/722131463138705510/722167424186843267/1151544212517552168
1652
+ $gVhhF$solidjs.createRenderEffect(() => {
1653
+ // Be careful to only read show_all_rows in here (ofc doesn't apply to nested effects)
1654
+ if (show_all_rows()) {
1655
+ // no view more functionality requested
1656
+ set_styling(() => col_styling);
1701
1657
  return;
1702
1658
  }
1703
- img.decode().catch(e => $gVhhF$depictaiutilishared.dwarn("Failed to decode image", e, img)) // Display the image also if loading failed so webdev can debug and doesn't get "no image"
1704
- .then(async () => {
1705
- // Also wait with inserting image (i.e. paint) until browser is idle
1706
- await new Promise(r => executeWhenIdleBatched(r, false));
1707
- if (gotCleanedUp) return;
1708
- setHasDecoded(true);
1709
- hasDecodedFastPath = true;
1710
- });
1659
+ update_children_count();
1660
+ if (!(0, $gVhhF$solidjsweb.isServer)) {
1661
+ const mutation_observer = new MutationObserver($gVhhF$depictaiutilishared.catchify(update_children_count));
1662
+ mutation_observer.observe(container_element, {
1663
+ childList: true
1664
+ });
1665
+ $gVhhF$solidjs.onCleanup(() => mutation_observer.disconnect());
1666
+ }
1667
+ set_styling(
1668
+ // this is needed because solid will think that the memo-accessor is a setter function otherwise
1669
+ () => $gVhhF$solidjs.createMemo(() => {
1670
+ const {
1671
+ rows_per_click: rows_per_click,
1672
+ max_rows = -1,
1673
+ start_rows: start_rows
1674
+ } = rows_or_empty_object();
1675
+ const rows_visible = get_rows_visible();
1676
+ const children_count = get_children_count();
1677
+ return force_style_update() + col_styling() + props.cols_at_size.map(options => {
1678
+ const [recs_per_row, min, max] = options;
1679
+ let should_hide_view_more_button;
1680
+ let visible_recs;
1681
+ if (props.disable_partial_rows) {
1682
+ visible_recs = $gVhhF$depictaiutilishared.layout_get_visible_recs_without_partial_rows(rows_visible, children_count, options);
1683
+ const visible_recs_next_show_more = $gVhhF$depictaiutilishared.layout_get_visible_recs_without_partial_rows(rows_visible + (rows_per_click || 0),
1684
+ // Should this really be zero when we default rows_per_click to 1?
1685
+ children_count, options);
1686
+ should_hide_view_more_button = visible_recs === visible_recs_next_show_more;
1687
+ } else {
1688
+ visible_recs = $gVhhF$depictaiutilishared.layout_get_total_rec_amount_for_row(rows_visible, options);
1689
+ should_hide_view_more_button = visible_recs >= children_count;
1690
+ }
1691
+ const should_hide_button_final = should_hide_view_more_button || max_rows !== -1 && rows_visible >= max_rows;
1692
+ // I don't really understand the logic for when the view more button gets hidden so IDK if below is correct, but it works how it should for now
1693
+ const should_hide_view_less_button = !should_hide_view_more_button || start_rows == undefined || rows_visible <= start_rows || children_count <= recs_per_row * start_rows;
1694
+ return `${$gVhhF$depictaiutilishared.layout_media_query_header(min, max)}{${selector_beginning}:nth-child(n+${visible_recs + 1}){display:none}${props.layout == "slider" ? `${selector_beginning}:nth-child(n+${visible_recs}){margin-right:0}` : ""}${should_hide_button_final ? `.depict .${view_more_button_class}{display:none!important}` : ``}${should_hide_view_less_button ? `.depict .${view_less_button_class}{display:none!important}` : ``}}`;
1695
+ }).join("");
1696
+ }));
1711
1697
  });
1698
+ const style_element = (() => {
1699
+ var _el$4 = $gVhhF$solidjsweb.getNextElement($f48d0fbb00835aaa$var$_tmpl$2);
1700
+ $gVhhF$solidjsweb.insert(_el$4, get_styling);
1701
+ return _el$4;
1702
+ })(); // need to create this after set_styling has been called or else there will be no styling in the SSR'd output
1703
+ if (insert_after_container) container_element.after(style_element);
1704
+ return [container_element, $gVhhF$solidjsweb.memo(view_more_button_container), $gVhhF$solidjsweb.memo(view_less_button_container), style_element];
1705
+ }
1706
+ $gVhhF$solidjsweb.delegateEvents(["click"]);
1707
+ var $363ce4d1d71041aa$var$_tmpl$ = /*#__PURE__*/$gVhhF$solidjsweb.template(`<h2 class=recs-title>`);
1708
+ function $363ce4d1d71041aa$export$d41d19e89346fa2({
1709
+ recommendations_resource_: recommendations_resource_,
1710
+ product_card_template_: product_card_template_,
1711
+ layout_options_: layout_options_,
1712
+ title_: title_,
1713
+ NoResultsFallback_: NoResultsFallback_,
1714
+ placeholderCacheKey_: placeholderCacheKey_
1715
+ }) {
1716
+ const is_showing_something = $gVhhF$solidjs.createMemo(() => recommendations_resource_.loading || recommendations_resource_()?.length);
1717
+ const title = $gVhhF$solidjs.createMemo(() => title_?.()); // In case getter/accessor creates elements, don't create them twice
1718
+ const num_placeholders_ = () => 20;
1719
+ const slider = [$gVhhF$solidjsweb.createComponent((0, $gVhhF$solidjs.Show), {
1720
+ get when() {
1721
+ return title();
1722
+ },
1723
+ get children() {
1724
+ var _el$ = $gVhhF$solidjsweb.getNextElement($363ce4d1d71041aa$var$_tmpl$);
1725
+ $gVhhF$solidjsweb.insert(_el$, title);
1726
+ return _el$;
1727
+ }
1728
+ }), $gVhhF$solidjsweb.createComponent((0, $a0e53f99354809d9$export$6649caad96ef8870), {
1729
+ arrow_height_: 25,
1730
+ arrow_width_: 12.5,
1731
+ disable_fading_: true,
1732
+ slider_ref_: slider => {
1733
+ const {
1734
+ insert_here: insert_here,
1735
+ container: container
1736
+ } = slider;
1737
+ const Layout = props => $f48d0fbb00835aaa$export$445d8d3f5f706a40(props, insert_here, true);
1738
+ // Do like this, so we don't have to call mergeProps ourselves
1739
+ $gVhhF$solidjsweb.createComponent(Layout, $gVhhF$solidjsweb.mergeProps({
1740
+ rows: "all",
1741
+ layout: "slider"
1742
+ }, layout_options_));
1743
+ $gVhhF$depictaiutilishared.slider_fractional_factory(0, 0.75).constructor_plugin.call(slider, {}); // enable fractional scrolling
1744
+ insert_here.before($fc23d7668ae0011d$export$a2cc176055f8f08());
1745
+ container.classList.add("products");
1746
+ },
1747
+ get children() {
1748
+ return $gVhhF$solidjsweb.createComponent((0, $gVhhF$solidjs.Suspense), {
1749
+ get fallback() {
1750
+ return $0caf87eaa19079da$export$f33414a4df0afd3f({
1751
+ num_placeholders_: num_placeholders_,
1752
+ product_card_template_: product_card_template_,
1753
+ placeholderCacheKey_: placeholderCacheKey_
1754
+ });
1755
+ },
1756
+ get children() {
1757
+ return (() => {
1758
+ // IIFE to make sure we access both the recommendations_resource and the resources containing the product card rendering results themselves within the Suspense boundary
1759
+ const displays_by_key_ = $a73063cc642f3e5f$export$7f8bd308237ab5a5($gVhhF$solidjs.createMemo(() => recommendations_resource_() || []));
1760
+ const {
1761
+ renderedDisplays_: renderedDisplays_
1762
+ } = $67155e1aeb7073f6$export$4a14175d33d81a96({
1763
+ displays_by_key_: displays_by_key_,
1764
+ product_card_template_: product_card_template_
1765
+ });
1766
+ return renderedDisplays_;
1767
+ })();
1768
+ }
1769
+ });
1770
+ }
1771
+ })];
1712
1772
  return $gVhhF$solidjsweb.createComponent((0, $gVhhF$solidjs.Show), {
1713
1773
  get when() {
1714
- return hasDecoded();
1774
+ return is_showing_something();
1715
1775
  },
1716
- children: img
1776
+ get fallback() {
1777
+ return NoResultsFallback_?.();
1778
+ },
1779
+ children: slider
1717
1780
  });
1718
1781
  }
1719
- function $19950faaac026e09$var$make_srcset(url, resolutions, customGenerateImageUrl_) {
1720
- return resolutions.map(width => `${(customGenerateImageUrl_ || $19950faaac026e09$export$1d6267828870adda)(url, width)} ${width}w`).join(", ");
1721
- }
1722
- function $19950faaac026e09$export$1d6267828870adda(url, width) {
1723
- // Even just creating one `URL` object per image src and then setting the search parameter is quite slow (0.25ms to set the width param)
1724
- // We're making the assumptions that the url has already at least one query parameter and that the image resizing service will choose the last one out of a duplicate width parameter (shopify's does)
1725
- // That way we can just to string concatenation which is insanely fast
1726
- return url + "&width=" + encodeURIComponent(width);
1727
- }
1728
- const $a33065cfed827fc0$var$backupSizeSignal = /*@__PURE__*/$gVhhF$solidjs.createSignal();
1729
- const $a33065cfed827fc0$export$d7b7ab832c20e2f4 = /*@__PURE__*/Symbol("ShopifyContainedImage");
1730
- function $a33065cfed827fc0$export$e4b984597b71e989(props) {
1731
- const context = $84798aaa863a4909$export$8d0c4fdbb05a2f2e();
1732
- if (context && !context[$a33065cfed827fc0$export$d7b7ab832c20e2f4])
1733
- // create a signal that's the same for all items in this listing
1734
- context[$a33065cfed827fc0$export$d7b7ab832c20e2f4] = $gVhhF$solidjs.createSignal();
1735
- const [currentImageSize, setCurrentImageSize] = context?.[$a33065cfed827fc0$export$d7b7ab832c20e2f4] || $a33065cfed827fc0$var$backupSizeSignal; // use global fallback signal (might lead to blurry images) if no context is available
1736
- const [mostPrevalentAspectRatio] = context ?
1737
- // If the context exists we have to create the signal here as getting it here would be too late when it's created by ShopifyResponsiveImg below
1738
- context[(0, $19950faaac026e09$export$6066eb417ed2e9f7)] ||= $gVhhF$solidjs.createSignal() : [];
1739
- const container = $93304d59a9747aa6$export$94026ee33dbabc26({
1740
- // Write getters manually instead of using JSX, so we can do more complex logic in the aspectRatio getter without increasing bundle size
1741
- get class() {
1742
- return props.class;
1743
- },
1744
- get aspectRatio() {
1745
- if (props.autoAdjustAspectRatio) {
1746
- const targetRatio = mostPrevalentAspectRatio?.();
1747
- if (targetRatio != undefined) return `var(${(0, $6f604560873e4f69$export$8a2234b5b1a5ebd4)}, ${targetRatio})`;
1782
+ function $95837413165cc2df$export$23fca46f3143581f(component, error_message) {
1783
+ const component_in_error_boundary = do_with_elements => {
1784
+ const error_boundary_wrapped = $edafe91daf3091d7$export$b69d0d92862de18b({
1785
+ severity_: "error",
1786
+ message_: error_message,
1787
+ get children() {
1788
+ const elements = component();
1789
+ do_with_elements?.(elements);
1790
+ return elements;
1748
1791
  }
1749
- return props.aspectRatio;
1750
- }
1751
- });
1792
+ });
1793
+ // This might return an accessor, in that case we need to "unwrap" it since our SDK consumers expect HTML elements or arrays thereof
1794
+ if (typeof error_boundary_wrapped === "function")
1795
+ // @ts-ignore
1796
+ return error_boundary_wrapped();
1797
+ // On the server it returns a string, so we just return that
1798
+ return error_boundary_wrapped;
1799
+ };
1752
1800
  const owner = $gVhhF$solidjs.getOwner();
1753
- let createdSizeAccessor = false;
1754
- let wasCleanedUp = false;
1755
- $gVhhF$solidjs.createEffect(() => {
1756
- if (currentImageSize() || wasCleanedUp) return; // We get into the effect when onCleanup is called if we don't have wasCleanedUp
1757
- // ensure we always have a size accessor
1758
- // what this whole thing does is set the sizes attribute to the actual size of one of the existing image containers, therefore the value doesn't have to manually be specified
1759
- createdSizeAccessor = true;
1760
- setCurrentImageSize(() => $gVhhF$solidjs.runWithOwner(owner, () => $19950faaac026e09$export$e6fffe67d4cdd848(container)));
1761
- });
1762
- $gVhhF$solidjs.onCleanup(() => {
1763
- wasCleanedUp = true; // important to set this before the function call below and always, so the dead effects don't create dead size accessors
1764
- if (!createdSizeAccessor) return;
1765
- $gVhhF$depictaiutilishared.queueMacroTask(() => setCurrentImageSize()); // unset the size accessor next event loop iteration, so that all onCleanup's have time to run, since the effects run more aggressively and can create a size accessor with a cleaned up owner otherwise
1766
- });
1767
-
1768
- $gVhhF$solidjsweb.insert(container, $gVhhF$solidjsweb.createComponent((0, $19950faaac026e09$export$6e5611bc7e57e477), $gVhhF$solidjsweb.mergeProps({
1769
- get size() {
1770
- return $gVhhF$solidjs.createMemo(() => currentImageSize()?.() || 0);
1771
- },
1772
- get src() {
1773
- return props.src;
1774
- }
1775
- }, () => props.imgProps, {
1776
- get style() {
1777
- return props.imgProps?.style;
1778
- }
1779
- })));
1780
- return container;
1781
- }
1782
- const $d212a67662c5cf38$var$customUrls = (url, width) => {
1783
- if (url?.startsWith?.("https://cdn.shopify.com"))
1784
- // The shopify image resizing CDN blocks Cloudflare's image resizer. It's possible that we can't detect shopify image URLs sometimes because they go to the merchant's domain,
1785
- // but it seems like the URLs returned by the Depict API always goes to the cdn.shopify.com version of an image, so this simple workaround works.
1786
- return $19950faaac026e09$export$1d6267828870adda(url, width);
1787
- // Use cloudflare image resizer instead of img.depict.ai because it's a lot faster (and also auto-chooses other formats if webp is not supported)
1788
- return `https://app.depict.ai/cdn-cgi/image/width=${width},format=auto/${url}`;
1789
- };
1790
- function $d212a67662c5cf38$export$9d4a4b669b1929ab(props) {
1791
- return $gVhhF$solidjsweb.createComponent((0, $a33065cfed827fc0$export$e4b984597b71e989), $gVhhF$solidjsweb.mergeProps(props, {
1792
- get imgProps() {
1793
- return {
1794
- ...props.imgProps,
1795
- customUrls: $d212a67662c5cf38$var$customUrls
1796
- };
1797
- }
1798
- }));
1799
- }
1800
- function $d212a67662c5cf38$export$140423fa4bfb6d63(props) {
1801
- return $gVhhF$solidjsweb.createComponent((0, $19950faaac026e09$export$6e5611bc7e57e477), $gVhhF$solidjsweb.mergeProps(props, {
1802
- customUrls: $d212a67662c5cf38$var$customUrls
1803
- }));
1801
+ if (owner == null) return $gVhhF$solidjs.createRoot(dispose => {
1802
+ // if we're not already run in a root, create a root and automatically dispose it when the first returned element is removed from the DOM
1803
+ return component_in_error_boundary(elements => {
1804
+ const have_multiple = Array.isArray(elements);
1805
+ if (have_multiple ? !elements?.length : !elements) return;
1806
+ (0, $gVhhF$depictaiutilishared.observer).onremoved(have_multiple ? elements[0] : elements, ({
1807
+ element: element
1808
+ }) => {
1809
+ if (document.documentElement.contains(element)) return; // If element has instantly been re-inserted, don't dispose
1810
+ // See https://depictaiworkspace.slack.com/archives/C050GHEPBTR/p1695809102791259?thread_ts=1695106805.459729&cid=C050GHEPBTR
1811
+ dispose();
1812
+ });
1813
+ });
1814
+ });else
1815
+ // we're already in a root so cleanups are handled
1816
+ return component_in_error_boundary();
1804
1817
  }
1805
1818
  function $d659702496cb72ea$export$a71e92cfbb498adc(props) {
1806
1819
  return $gVhhF$solidjs.createMemo(() => {
@@ -14921,7 +14934,7 @@ function $32c662f618f5d80d$export$780a853d15174484({
14921
14934
  return $gVhhF$solidjsweb.createComponent((0, $gVhhF$solidjsweb.Dynamic), $gVhhF$solidjsweb.mergeProps({
14922
14935
  component: link ? "a" : "div",
14923
14936
  "class": "d-standard-content-wrapper",
14924
- ref: el => setWrapperSize(() => $19950faaac026e09$export$e6fffe67d4cdd848(el)),
14937
+ ref: el => setWrapperSize(() => $128302ab61dab8f1$export$54c6d6ff54e72743(el)),
14925
14938
  get onClick() {
14926
14939
  return $gVhhF$depictaiutilisharedlatest.catchify(ev => link && router_.navigate_.go_to_({
14927
14940
  new_url_: link,
@@ -14934,9 +14947,8 @@ function $32c662f618f5d80d$export$780a853d15174484({
14934
14947
  }, {
14935
14948
  get children() {
14936
14949
  return $gVhhF$solidjsweb.createComponent((0, $d212a67662c5cf38$export$140423fa4bfb6d63), {
14937
- get size() {
14938
- return $gVhhF$solidjs.createMemo(() => wrapperSize()?.() || 0);
14939
- },
14950
+ size: () => wrapperSize()?.[0]?.() || 0,
14951
+ sizeHeight: () => wrapperSize()?.[1]?.() || 0,
14940
14952
  src: mediaUrl,
14941
14953
  "class": "d-standard-content"
14942
14954
  });