@depict-ai/ui 4.0.0 → 4.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/latest/index.cjs CHANGED
@@ -86,7 +86,7 @@ $parcel$export(module.exports, "defaultCategoryTitlePlugin", () => $a3280f0aad99
86
86
  $parcel$export(module.exports, "version", () => $b757f75811f3261f$export$83d89fbfd8236492);
87
87
  $parcel$export(module.exports, "SearchField", () => $c0a0659cf97e8eb2$export$316f6f283ae64765);
88
88
  /*! Depict.ai search and product listing SDK */
89
- const $b757f75811f3261f$export$83d89fbfd8236492 = "4.0.0";
89
+ const $b757f75811f3261f$export$83d89fbfd8236492 = "4.0.2";
90
90
  function $53df95e470b2f8d2$export$e417c49320921907(dependency, fetcher) {
91
91
  const [promiseOfCurrentFetcherRun, setPromiseOfCurrentFetcherRun] = $gVhhF$solidjs.createSignal();
92
92
  const deparallelizedFetcher = $gVhhF$depictaiutilishared.deparallelize_no_drop(async dependencyValue => {
@@ -2423,6 +2423,7 @@ function $19950faaac026e09$export$e6fffe67d4cdd848(whatToWatch) {
2423
2423
  return size;
2424
2424
  }
2425
2425
  const $19950faaac026e09$var$onIdleFunctionsSymbol = /*@__PURE__*/Symbol("onIdleFunctions");
2426
+ const $19950faaac026e09$export$f8945274f08a3105 = /*@__PURE__*/Symbol("listingContextAspectRatios");
2426
2427
  function $19950faaac026e09$export$6e5611bc7e57e477(props) {
2427
2428
  // Enable reactive props
2428
2429
  const otherOptions = $gVhhF$solidjs.createMemo(() => {
@@ -2487,6 +2488,21 @@ function $19950faaac026e09$export$6e5611bc7e57e477(props) {
2487
2488
  let timeoutSet = false;
2488
2489
  let gotCleanedUp = false;
2489
2490
  img.classList.add("s-responsive-image");
2491
+ if (listingContext) {
2492
+ let prevValue;
2493
+ const aspectRatios = listingContext[$19950faaac026e09$export$f8945274f08a3105] ||= new (0, $gVhhF$depictaiutilishared.ListenableSet)();
2494
+ img.addEventListener("load", $gVhhF$depictaiutilishared.catchify(() => {
2495
+ // Save our aspect ratio whenever it changes, so that we can base the containers on the most dominant aspect ratio, used in SearchModalV2
2496
+ const aspectRatio_ = img.naturalWidth / img.naturalHeight;
2497
+ if (prevValue) aspectRatios.delete(prevValue);
2498
+ const newValue = !isNaN(aspectRatio_) ? {
2499
+ aspectRatio_: aspectRatio_
2500
+ } : undefined;
2501
+ if (newValue) aspectRatios.add(newValue);
2502
+ prevValue = newValue;
2503
+ }));
2504
+ $gVhhF$solidjs.onCleanup(() => prevValue && aspectRatios.delete(prevValue));
2505
+ }
2490
2506
  $gVhhF$solidjs.onCleanup(() => gotCleanedUp = true);
2491
2507
  $gVhhF$solidjsweb.spread(img, $gVhhF$solidjs.mergeProps(otherOptions), false, true);
2492
2508
  $gVhhF$solidjs.createEffect(() => {
@@ -2554,9 +2570,28 @@ function $a33065cfed827fc0$export$e4b984597b71e989(props) {
2554
2570
  // create a signal that's the same for all items in this listing
2555
2571
  context[$a33065cfed827fc0$export$d7b7ab832c20e2f4] = $gVhhF$solidjs.createSignal();
2556
2572
  const [currentImageSize, setCurrentImageSize] = context?.[$a33065cfed827fc0$export$d7b7ab832c20e2f4] || $a33065cfed827fc0$var$backupSizeSignal; // use global fallback signal (might lead to blurry images) if no context is available
2573
+ const aspectRatiosSet = context ?
2574
+ // If the context exists we have to create the set here as getting it here would be too late when it's created by ShopifyResponsiveImg below
2575
+ context[(0, $19950faaac026e09$export$f8945274f08a3105)] ||= new (0, $gVhhF$depictaiutilishared.ListenableSet)() : undefined;
2576
+ const [aspectRatios, setAspectRatios] = $gVhhF$solidjs.createSignal(aspectRatiosSet, {
2577
+ equals: false
2578
+ });
2579
+ const aspectRatioToUse = $gVhhF$solidjs.createMemo(() => {
2580
+ const aspectRatiosSet = aspectRatios(); // This signal "changes" to the same set every time the set changes, so we can use it as a dependency here
2581
+ useMostPrevalentAspectRatio: {
2582
+ if (!aspectRatiosSet || !props.autoAdjustAspectRatio) break useMostPrevalentAspectRatio;
2583
+ // Get the aspect ratio that most of the loaded images inside the ListingProvider we are, load
2584
+ const asArray = [...aspectRatiosSet].map(({
2585
+ aspectRatio_: aspectRatio_
2586
+ }) => aspectRatio_);
2587
+ const mostPrevalentAspectRatio = $gVhhF$depictaiutilishared.findMostFrequentItem(asArray);
2588
+ if (mostPrevalentAspectRatio) return `var(${(0, $6f604560873e4f69$export$8a2234b5b1a5ebd4)}, ${mostPrevalentAspectRatio})`;
2589
+ }
2590
+ return props.aspectRatio;
2591
+ });
2557
2592
  const container = $gVhhF$solidjsweb.createComponent((0, $93304d59a9747aa6$export$94026ee33dbabc26), {
2558
2593
  get aspectRatio() {
2559
- return props.aspectRatio;
2594
+ return aspectRatioToUse();
2560
2595
  },
2561
2596
  get ["class"]() {
2562
2597
  return props.class;
@@ -2565,6 +2600,10 @@ function $a33065cfed827fc0$export$e4b984597b71e989(props) {
2565
2600
  const owner = $gVhhF$solidjs.getOwner();
2566
2601
  let createdSizeAccessor = false;
2567
2602
  let wasCleanedUp = false;
2603
+ for (const event of ["add", "delete"]) {
2604
+ aspectRatiosSet?.addEventListener(event, () => setAspectRatios(aspectRatiosSet));
2605
+ $gVhhF$solidjs.onCleanup(() => aspectRatiosSet?.removeEventListener(event, () => setAspectRatios(aspectRatiosSet)));
2606
+ }
2568
2607
  $gVhhF$solidjs.createEffect(() => {
2569
2608
  if (currentImageSize() || wasCleanedUp) return; // We get into the effect when onCleanup is called if we don't have wasCleanedUp
2570
2609
  // ensure we always have a size accessor
@@ -3579,7 +3618,8 @@ function $11597ae03e703a47$var$RawSearchModalV2({
3579
3618
  get src() {
3580
3619
  return props.src_;
3581
3620
  },
3582
- aspectRatio: `var(${(0, $6f604560873e4f69$export$8a2234b5b1a5ebd4)}, 1)`
3621
+ autoAdjustAspectRatio: true,
3622
+ aspectRatio: 1
3583
3623
  }),
3584
3624
  ...instant_results_options_
3585
3625
  }), _el$4, _co$);
@@ -3920,7 +3960,15 @@ async function $9abb0a35d4b60ed3$export$79b1565500412d9b(search_modal, modal_fn_
3920
3960
  const actually_close = () => {
3921
3961
  dispose();
3922
3962
  on_dispose?.();
3923
- modal_els.forEach(el => el.remove());
3963
+ const {
3964
+ activeElement: activeElement
3965
+ } = document;
3966
+ modal_els.forEach(el => {
3967
+ if (el.contains(activeElement))
3968
+ // Fix page jumping to the bottom when closing modal with scape after animation in safari
3969
+ activeElement?.blur?.();
3970
+ el.remove();
3971
+ });
3924
3972
  };
3925
3973
  if (animation_started && +new Date() - animation_started > 1000)
3926
3974
  // Animation broke, close it this time
@@ -12658,11 +12706,22 @@ function $5cbaffcb5602e671$export$44f738469d5570cf({
12658
12706
  modalSearchFieldOuterWidth_: modalSearchFieldOuterWidth_
12659
12707
  }) {
12660
12708
  return $gVhhF$solidjs.createRoot(dispose => {
12709
+ const [innerWidthReactive, setInnerWidthReactive] = $gVhhF$solidjs.createSignal(innerWidth);
12710
+ const resizeObserver = new ResizeObserver(records => setInnerWidthReactive(records.at(-1).contentRect.width));
12711
+ const {
12712
+ left: left,
12713
+ top: top,
12714
+ width: width
12715
+ } = actualField_.getBoundingClientRect();
12661
12716
  const fakeField = makeFakeField_();
12662
12717
  const owner = $gVhhF$solidjs.getOwner();
12663
12718
  const inModalFieldWidth = $gVhhF$solidjs.createMemo(
12664
12719
  // Can't have `min(${modalSearchFieldOuterWidth_()}px, var(--field-max-width, 600px))` because the WAAPI doesn't support that, so we need ot calculate it in JS
12665
- () => Math.min(modalSearchFieldOuterWidth_(), parseFloat(getComputedStyle(actualField_).getPropertyValue("--field-max-width") || "600")) + "px");
12720
+ () => Math.min(modalSearchFieldOuterWidth_(), parseFloat(getComputedStyle(actualField_).getPropertyValue("--field-max-width") || "600")));
12721
+ const inModalFieldWidthPx = $gVhhF$solidjs.createMemo(() => inModalFieldWidth() + "px");
12722
+ let disableResizePollingTimeout;
12723
+ resizeObserver.observe(document.documentElement);
12724
+ $gVhhF$solidjs.onCleanup(() => resizeObserver.disconnect());
12666
12725
  actualField_.replaceWith(fakeField);
12667
12726
  // Don't move the wrapped div since that would confuse run_in_root_or_auto_cleanup, but do actually move the field the user clicked around so that all selection stuff etc. is easier
12668
12727
  const addedToBody = wrap_ ? (() => {
@@ -12671,21 +12730,17 @@ function $5cbaffcb5602e671$export$44f738469d5570cf({
12671
12730
  return _el$;
12672
12731
  })() : actualField_;
12673
12732
  document.body.append(addedToBody);
12674
- const {
12675
- left: left,
12676
- top: top,
12677
- width: width
12678
- } = fakeField.getBoundingClientRect();
12679
12733
  const zIndexOfFake = getComputedStyle(fakeField).zIndex;
12680
12734
  Object.assign(addedToBody.style, {
12681
12735
  position: "fixed",
12682
12736
  top: top + "px",
12737
+ left: left + "px",
12683
12738
  width: width + "px",
12684
12739
  ...(zIndexOfFake !== "auto" ? {
12685
12740
  zIndex: +zIndexOfFake + 1
12686
12741
  } : {})
12687
12742
  });
12688
- const pxOffCenter = innerWidth / 2 - (left + width / 2);
12743
+ const pxOffCenter = $gVhhF$solidjs.untrack(innerWidthReactive) / 2 - (left + width / 2);
12689
12744
  const openDuration = pxOffCenter > 100 ? 250 : 125; // faster animations if little left-right movement
12690
12745
  const closeDuration = pxOffCenter > 100 ? 150 : 100;
12691
12746
  setPollAlignment_(true);
@@ -12704,24 +12759,30 @@ function $5cbaffcb5602e671$export$44f738469d5570cf({
12704
12759
  // Wait until the ResizeObserver has delivered a value in case that the modal is small or has much padding so we know that we can't animate our fake field to be wider than that
12705
12760
  addedToBody.animate([{
12706
12761
  left: left + "px",
12707
- transform: "translateX(0%)",
12708
12762
  width: width + "px"
12709
12763
  }, {
12710
- left: "50%",
12711
- transform: "translateX(-50%)",
12712
- width: $gVhhF$solidjs.untrack(inModalFieldWidth)
12764
+ left: `${$gVhhF$solidjs.untrack(innerWidthReactive) / 2 - $gVhhF$solidjs.untrack(inModalFieldWidth) / 2}px`,
12765
+ width: $gVhhF$solidjs.untrack(inModalFieldWidthPx)
12713
12766
  }], {
12714
12767
  duration: openDuration,
12715
12768
  easing: "ease-in"
12716
12769
  }).finished.then(() => {
12717
12770
  setPollAlignment_(false);
12718
- $gVhhF$solidjs.runWithOwner(owner, () => $gVhhF$solidjs.createEffect(() =>
12719
- // When animation has finished, still dynamically set the size of the search field behind in case the user resizes the window
12720
- Object.assign(addedToBody.style, {
12721
- left: "50%",
12722
- transform: "translateX(-50%)",
12723
- width: inModalFieldWidth()
12724
- })));
12771
+ $gVhhF$solidjs.runWithOwner(owner, () => $gVhhF$solidjs.createEffect(() => {
12772
+ // When animation has finished, still dynamically set the size of the search field behind in case the user resizes the window
12773
+ Object.assign(addedToBody.style, {
12774
+ // This is left: 50%, transform: translateX(-50) with extra steps but when using it safari and firefox glitch out like crazy
12775
+ left: `${innerWidthReactive() / 2 - inModalFieldWidth() / 2}px`,
12776
+ width: inModalFieldWidthPx()
12777
+ });
12778
+ // We might only have changed the left position which align_field doesn't know about, make it poll the position for a while
12779
+ setPollAlignment_(true);
12780
+ if (disableResizePollingTimeout) clearTimeout(disableResizePollingTimeout);
12781
+ disableResizePollingTimeout = setTimeout($gVhhF$depictaiutilishared.catchify(() => {
12782
+ setPollAlignment_(false);
12783
+ disableResizePollingTimeout = undefined;
12784
+ }), 1000);
12785
+ }));
12725
12786
  });
12726
12787
  return true;
12727
12788
  }, false);
@@ -12764,13 +12825,11 @@ function $5cbaffcb5602e671$export$44f738469d5570cf({
12764
12825
  transition: `opacity ${closeDuration}ms ease-out`
12765
12826
  }));
12766
12827
  await Promise.all([addedToBody.animate([{
12767
- width: $gVhhF$solidjs.untrack(inModalFieldWidth),
12768
- left: "50%",
12769
- transform: "translateX(-50%)"
12828
+ width: $gVhhF$solidjs.untrack(inModalFieldWidthPx),
12829
+ left: `${$gVhhF$solidjs.untrack(innerWidthReactive) / 2 - $gVhhF$solidjs.untrack(inModalFieldWidth) / 2}px`
12770
12830
  }, {
12771
12831
  width: width + "px",
12772
- left: left + "px",
12773
- transform: "translateX(0%)"
12832
+ left: left + "px"
12774
12833
  }], {
12775
12834
  duration: closeDuration,
12776
12835
  fill: "forwards",
package/latest/index.d.ts CHANGED
@@ -10,7 +10,7 @@ import { ProductListingRequestV3 } from "@depict-ai/types/api/ProductListingRequ
10
10
  import { GetListingRequest } from "@depict-ai/types/api/GetListingRequest";
11
11
  import { BaseSearchRequestV3 } from "@depict-ai/types/api/BaseSearchRequestV3";
12
12
  import { SearchFilter as _SearchFilter2 } from "@depict-ai/types/api/SearchRequestV2";
13
- export const version = "4.0.0";
13
+ export const version = "4.0.2";
14
14
  export type ModalAlignmentSignals = {
15
15
  body_: Signal<JSX.CSSProperties>;
16
16
  field_: Signal<JSX.CSSProperties>;
@@ -407,6 +407,28 @@ export function ShopifyResponsiveImg(props: {
407
407
  } & Omit<JSX.ImgHTMLAttributes<HTMLImageElement>, "srcset" | "src" | "sizes"> & {
408
408
  src: string;
409
409
  }): JSX.Element;
410
+ type ImagePlaceholderBaseOptions = {
411
+ style?: JSX.CSSProperties;
412
+ class?: string;
413
+ };
414
+ type ImagePlaceholderBlockOptions = {
415
+ width: string;
416
+ height: string;
417
+ };
418
+ type ImagePlaceholderImageOptions = {
419
+ aspectRatio: number | string;
420
+ };
421
+ /**
422
+ * A placeholder that can be used instead of an image or other block of content while it's loading.
423
+ * The placeholder has `display:block`, meaning it will create a new "row" in the layout for itself. Can be used either for images with a defined aspect ratio (ContainedImage) or blocks.
424
+ * @property aspectRatio if aspectRatio is defined the placeholder will be in "image mode" and fill out the available width and then adjust the height according to the aspectRatio
425
+ * @property width If width and height is set, the defined width and height will be used.
426
+ * @property height If width and height is set, the defined width and height will be used.
427
+ * @property class extra classes to add to the placeholder
428
+ * @property style extra styles to add to the placeholder
429
+ */
430
+ export function ImagePlaceholder(props: ImagePlaceholderBaseOptions & (ImagePlaceholderBlockOptions | ImagePlaceholderImageOptions)): JSX.Element;
431
+ export function DefaultInstantCardPlaceholder(): HTMLDivElement;
410
432
  export const shopifyContainedImageContextSymbol: unique symbol;
411
433
  /**
412
434
  * Abstraction built on top of ShopifyResponsiveImg. This is your one-stop-shop If you just need a responsive image.
@@ -417,11 +439,13 @@ export const shopifyContainedImageContextSymbol: unique symbol;
417
439
  * @property aspectRatio aspect ratio of the image
418
440
  * @property class extra class to add to the container
419
441
  * @property alt alt text for the image
442
+ * @property autoAdjustAspectRatio whether to automatically adjust the aspect ratio of the image to the majority of the aspect ratios of loaded images in the current listing provider. Defaults to false. Can be overriden with css variable --image-aspect-ratio
420
443
  * @property imgProps props to pass to ShopifyResponsiveImg to add to the <img> element
421
444
  */
422
445
  export function ShopifyContainedImage(props: {
423
446
  src: string;
424
447
  aspectRatio: number | string;
448
+ autoAdjustAspectRatio?: boolean;
425
449
  class?: string;
426
450
  imgProps?: Omit<Parameters<typeof ShopifyResponsiveImg>[0], "size" | "src">;
427
451
  }): HTMLDivElement;
@@ -489,28 +513,6 @@ interface Styles {
489
513
  [selector: string]: JSX.CSSProperties;
490
514
  }
491
515
  export const styles_to_valid_css_string: (styles: Styles) => string;
492
- type ImagePlaceholderBaseOptions = {
493
- style?: JSX.CSSProperties;
494
- class?: string;
495
- };
496
- type ImagePlaceholderBlockOptions = {
497
- width: string;
498
- height: string;
499
- };
500
- type ImagePlaceholderImageOptions = {
501
- aspectRatio: number | string;
502
- };
503
- /**
504
- * A placeholder that can be used instead of an image or other block of content while it's loading.
505
- * The placeholder has `display:block`, meaning it will create a new "row" in the layout for itself. Can be used either for images with a defined aspect ratio (ContainedImage) or blocks.
506
- * @property aspectRatio if aspectRatio is defined the placeholder will be in "image mode" and fill out the available width and then adjust the height according to the aspectRatio
507
- * @property width If width and height is set, the defined width and height will be used.
508
- * @property height If width and height is set, the defined width and height will be used.
509
- * @property class extra classes to add to the placeholder
510
- * @property style extra styles to add to the placeholder
511
- */
512
- export function ImagePlaceholder(props: ImagePlaceholderBaseOptions & (ImagePlaceholderBlockOptions | ImagePlaceholderImageOptions)): JSX.Element;
513
- export function DefaultInstantCardPlaceholder(): HTMLDivElement;
514
516
  /**
515
517
  * Second generation search modal - the solid component that gets added to <body> when the search modal is opened
516
518
  */
@@ -642,7 +644,7 @@ export type BaseSearchProviderConfig<InputDisplay extends Display, PossibleTrans
642
644
  */
643
645
  enableContentSearch?: boolean;
644
646
  /**
645
- * The search modal component to use. You can switch between the classic and the new search modal. Provide one of the components here that you can import from the same package as the one you're using to see this message.
647
+ * The search modal component to use. You can switch between the classic and the new search modal. Provide one of the components here that you can import from the same package as the one you're using to see this message (`SearchModalV2` or `ClassicSearchModal`).
646
648
  * You also need to reflect the choice in SCSS, example:
647
649
  * ```scss
648
650
  * @use "@depict-ai/js-ui" as plp-styling with (
package/latest/index.js CHANGED
@@ -1,13 +1,13 @@
1
- import { add_to_version_info as $9HbP8$add_to_version_info, deparallelize_no_drop as $9HbP8$deparallelize_no_drop, catchify as $9HbP8$catchify, make_random_classname as $9HbP8$make_random_classname, ContainedImage as $9HbP8$ContainedImage, standard_price_format as $9HbP8$standard_price_format, javascript_media_query as $9HbP8$javascript_media_query, dlog as $9HbP8$dlog, camelCasifyObject as $9HbP8$camelCasifyObject, buildThresholdList as $9HbP8$buildThresholdList, make_asyncIterable_exiter as $9HbP8$make_asyncIterable_exiter, href_change_ipns as $9HbP8$href_change_ipns, observer as $9HbP8$observer, report as $9HbP8$report, base64_encode_unicode as $9HbP8$base64_encode_unicode, base64_decode_unicode as $9HbP8$base64_decode_unicode, queueMacroTask as $9HbP8$queueMacroTask, dwarn as $9HbP8$dwarn, slider_fading_factory as $9HbP8$slider_fading_factory, Amazing_Slider as $9HbP8$Amazing_Slider, slider_snap_css as $9HbP8$slider_snap_css, n_col_styling_with_dist as $9HbP8$n_col_styling_with_dist, layout_get_visible_recs_without_partial_rows as $9HbP8$layout_get_visible_recs_without_partial_rows, layout_get_total_rec_amount_for_row as $9HbP8$layout_get_total_rec_amount_for_row, layout_media_query_header as $9HbP8$layout_media_query_header, use_href_accessor as $9HbP8$use_href_accessor, get_session_id as $9HbP8$get_session_id, add_version_query_params as $9HbP8$add_version_query_params, base_url as $9HbP8$base_url, instant_exec_on_suspect_history_change as $9HbP8$instant_exec_on_suspect_history_change, slider_fractional_factory as $9HbP8$slider_fractional_factory, useExpandingContainer as $9HbP8$useExpandingContainer, hex_to_rgb as $9HbP8$hex_to_rgb, relative_luminance_from_rgb as $9HbP8$relative_luminance_from_rgb, snakeCasifyObject as $9HbP8$snakeCasifyObject, fetch_retry as $9HbP8$fetch_retry, sort_obj as $9HbP8$sort_obj, make_url_with_query_params as $9HbP8$make_url_with_query_params, is_debug as $9HbP8$is_debug, ListenableSet as $9HbP8$ListenableSet1, insert_surfaces as $9HbP8$insert_surfaces } from "@depict-ai/utilishared/latest";
1
+ import { add_to_version_info as $9HbP8$add_to_version_info, deparallelize_no_drop as $9HbP8$deparallelize_no_drop, catchify as $9HbP8$catchify, make_random_classname as $9HbP8$make_random_classname, ContainedImage as $9HbP8$ContainedImage, standard_price_format as $9HbP8$standard_price_format, javascript_media_query as $9HbP8$javascript_media_query, dlog as $9HbP8$dlog, camelCasifyObject as $9HbP8$camelCasifyObject, buildThresholdList as $9HbP8$buildThresholdList, make_asyncIterable_exiter as $9HbP8$make_asyncIterable_exiter, href_change_ipns as $9HbP8$href_change_ipns, observer as $9HbP8$observer, report as $9HbP8$report, base64_encode_unicode as $9HbP8$base64_encode_unicode, base64_decode_unicode as $9HbP8$base64_decode_unicode, ListenableSet as $9HbP8$ListenableSet, findMostFrequentItem as $9HbP8$findMostFrequentItem, queueMacroTask as $9HbP8$queueMacroTask, dwarn as $9HbP8$dwarn, slider_fading_factory as $9HbP8$slider_fading_factory, Amazing_Slider as $9HbP8$Amazing_Slider, slider_snap_css as $9HbP8$slider_snap_css, n_col_styling_with_dist as $9HbP8$n_col_styling_with_dist, layout_get_visible_recs_without_partial_rows as $9HbP8$layout_get_visible_recs_without_partial_rows, layout_get_total_rec_amount_for_row as $9HbP8$layout_get_total_rec_amount_for_row, layout_media_query_header as $9HbP8$layout_media_query_header, use_href_accessor as $9HbP8$use_href_accessor, get_session_id as $9HbP8$get_session_id, add_version_query_params as $9HbP8$add_version_query_params, base_url as $9HbP8$base_url, instant_exec_on_suspect_history_change as $9HbP8$instant_exec_on_suspect_history_change, slider_fractional_factory as $9HbP8$slider_fractional_factory, useExpandingContainer as $9HbP8$useExpandingContainer, hex_to_rgb as $9HbP8$hex_to_rgb, relative_luminance_from_rgb as $9HbP8$relative_luminance_from_rgb, snakeCasifyObject as $9HbP8$snakeCasifyObject, fetch_retry as $9HbP8$fetch_retry, sort_obj as $9HbP8$sort_obj, make_url_with_query_params as $9HbP8$make_url_with_query_params, is_debug as $9HbP8$is_debug, insert_surfaces as $9HbP8$insert_surfaces } from "@depict-ai/utilishared/latest";
2
2
  import { createSignal as $9HbP8$createSignal, createResource as $9HbP8$createResource, createEffect as $9HbP8$createEffect, untrack as $9HbP8$untrack, createMemo as $9HbP8$createMemo, onCleanup as $9HbP8$onCleanup, Show as $9HbP8$Show, createComputed as $9HbP8$createComputed, For as $9HbP8$For, getOwner as $9HbP8$getOwner, runWithOwner as $9HbP8$runWithOwner, on as $9HbP8$on, Index as $9HbP8$Index, batch as $9HbP8$batch, ErrorBoundary as $9HbP8$ErrorBoundary, createContext as $9HbP8$createContext, useContext as $9HbP8$useContext, mergeProps as $9HbP8$mergeProps1, createUniqueId as $9HbP8$createUniqueId, createRenderEffect as $9HbP8$createRenderEffect, createSelector as $9HbP8$createSelector, children as $9HbP8$children, splitProps as $9HbP8$splitProps, createRoot as $9HbP8$createRoot, Suspense as $9HbP8$Suspense, createReaction as $9HbP8$createReaction, mapArray as $9HbP8$mapArray } from "solid-js";
3
3
  import { template as $9HbP8$template, getNextElement as $9HbP8$getNextElement, addEventListener as $9HbP8$addEventListener, insert as $9HbP8$insert, memo as $9HbP8$memo, runHydrationEvents as $9HbP8$runHydrationEvents, createComponent as $9HbP8$createComponent, getNextMarker as $9HbP8$getNextMarker, use as $9HbP8$use, effect as $9HbP8$effect, classList as $9HbP8$classList, setAttribute as $9HbP8$setAttribute, delegateEvents as $9HbP8$delegateEvents, style as $9HbP8$style, className as $9HbP8$className, spread as $9HbP8$spread, mergeProps as $9HbP8$mergeProps, setProperty as $9HbP8$setProperty, isServer as $9HbP8$isServer, Dynamic as $9HbP8$Dynamic } from "solid-js/web";
4
- import { ListenableSet as $9HbP8$ListenableSet, catchify as $9HbP8$catchify1, use_href_accessor as $9HbP8$use_href_accessor1, report as $9HbP8$report1 } from "@depict-ai/utilishared/latest";
4
+ import { ListenableSet as $9HbP8$ListenableSet1, catchify as $9HbP8$catchify1, use_href_accessor as $9HbP8$use_href_accessor1, report as $9HbP8$report1 } from "@depict-ai/utilishared/latest";
5
5
  import { createStore as $9HbP8$createStore, reconcile as $9HbP8$reconcile } from "solid-js/store";
6
6
 
7
7
  /*! Depict.ai search and product listing SDK */
8
8
  import { register_es_version } from "../multi_import_deduplicator.mjs";
9
9
  register_es_version("ui", "latest");
10
- const $ec291e0010116877$export$83d89fbfd8236492 = "4.0.0";
10
+ const $ec291e0010116877$export$83d89fbfd8236492 = "4.0.2";
11
11
  function $136f1d4d6c7ba6ac$export$e417c49320921907(dependency, fetcher) {
12
12
  const [promiseOfCurrentFetcherRun, setPromiseOfCurrentFetcherRun] = $9HbP8$createSignal();
13
13
  const deparallelizedFetcher = $9HbP8$deparallelize_no_drop(async dependencyValue => {
@@ -2344,6 +2344,7 @@ function $e8ffe11ed01df0fe$export$e6fffe67d4cdd848(whatToWatch) {
2344
2344
  return size;
2345
2345
  }
2346
2346
  const $e8ffe11ed01df0fe$var$onIdleFunctionsSymbol = /*@__PURE__*/Symbol("onIdleFunctions");
2347
+ const $e8ffe11ed01df0fe$export$f8945274f08a3105 = /*@__PURE__*/Symbol("listingContextAspectRatios");
2347
2348
  function $e8ffe11ed01df0fe$export$6e5611bc7e57e477(props) {
2348
2349
  // Enable reactive props
2349
2350
  const otherOptions = $9HbP8$createMemo(() => {
@@ -2408,6 +2409,21 @@ function $e8ffe11ed01df0fe$export$6e5611bc7e57e477(props) {
2408
2409
  let timeoutSet = false;
2409
2410
  let gotCleanedUp = false;
2410
2411
  img.classList.add("s-responsive-image");
2412
+ if (listingContext) {
2413
+ let prevValue;
2414
+ const aspectRatios = listingContext[$e8ffe11ed01df0fe$export$f8945274f08a3105] ||= new (0, $9HbP8$ListenableSet)();
2415
+ img.addEventListener("load", $9HbP8$catchify(() => {
2416
+ // Save our aspect ratio whenever it changes, so that we can base the containers on the most dominant aspect ratio, used in SearchModalV2
2417
+ const aspectRatio_ = img.naturalWidth / img.naturalHeight;
2418
+ if (prevValue) aspectRatios.delete(prevValue);
2419
+ const newValue = !isNaN(aspectRatio_) ? {
2420
+ aspectRatio_: aspectRatio_
2421
+ } : undefined;
2422
+ if (newValue) aspectRatios.add(newValue);
2423
+ prevValue = newValue;
2424
+ }));
2425
+ $9HbP8$onCleanup(() => prevValue && aspectRatios.delete(prevValue));
2426
+ }
2411
2427
  $9HbP8$onCleanup(() => gotCleanedUp = true);
2412
2428
  $9HbP8$spread(img, $9HbP8$mergeProps1(otherOptions), false, true);
2413
2429
  $9HbP8$createEffect(() => {
@@ -2475,9 +2491,28 @@ function $660070e2a394b3c3$export$e4b984597b71e989(props) {
2475
2491
  // create a signal that's the same for all items in this listing
2476
2492
  context[$660070e2a394b3c3$export$d7b7ab832c20e2f4] = $9HbP8$createSignal();
2477
2493
  const [currentImageSize, setCurrentImageSize] = context?.[$660070e2a394b3c3$export$d7b7ab832c20e2f4] || $660070e2a394b3c3$var$backupSizeSignal; // use global fallback signal (might lead to blurry images) if no context is available
2494
+ const aspectRatiosSet = context ?
2495
+ // If the context exists we have to create the set here as getting it here would be too late when it's created by ShopifyResponsiveImg below
2496
+ context[(0, $e8ffe11ed01df0fe$export$f8945274f08a3105)] ||= new (0, $9HbP8$ListenableSet)() : undefined;
2497
+ const [aspectRatios, setAspectRatios] = $9HbP8$createSignal(aspectRatiosSet, {
2498
+ equals: false
2499
+ });
2500
+ const aspectRatioToUse = $9HbP8$createMemo(() => {
2501
+ const aspectRatiosSet = aspectRatios(); // This signal "changes" to the same set every time the set changes, so we can use it as a dependency here
2502
+ useMostPrevalentAspectRatio: {
2503
+ if (!aspectRatiosSet || !props.autoAdjustAspectRatio) break useMostPrevalentAspectRatio;
2504
+ // Get the aspect ratio that most of the loaded images inside the ListingProvider we are, load
2505
+ const asArray = [...aspectRatiosSet].map(({
2506
+ aspectRatio_: aspectRatio_
2507
+ }) => aspectRatio_);
2508
+ const mostPrevalentAspectRatio = $9HbP8$findMostFrequentItem(asArray);
2509
+ if (mostPrevalentAspectRatio) return `var(${(0, $bb15dfb548a7666e$export$8a2234b5b1a5ebd4)}, ${mostPrevalentAspectRatio})`;
2510
+ }
2511
+ return props.aspectRatio;
2512
+ });
2478
2513
  const container = $9HbP8$createComponent((0, $f18d425032111270$export$94026ee33dbabc26), {
2479
2514
  get aspectRatio() {
2480
- return props.aspectRatio;
2515
+ return aspectRatioToUse();
2481
2516
  },
2482
2517
  get ["class"]() {
2483
2518
  return props.class;
@@ -2486,6 +2521,10 @@ function $660070e2a394b3c3$export$e4b984597b71e989(props) {
2486
2521
  const owner = $9HbP8$getOwner();
2487
2522
  let createdSizeAccessor = false;
2488
2523
  let wasCleanedUp = false;
2524
+ for (const event of ["add", "delete"]) {
2525
+ aspectRatiosSet?.addEventListener(event, () => setAspectRatios(aspectRatiosSet));
2526
+ $9HbP8$onCleanup(() => aspectRatiosSet?.removeEventListener(event, () => setAspectRatios(aspectRatiosSet)));
2527
+ }
2489
2528
  $9HbP8$createEffect(() => {
2490
2529
  if (currentImageSize() || wasCleanedUp) return; // We get into the effect when onCleanup is called if we don't have wasCleanedUp
2491
2530
  // ensure we always have a size accessor
@@ -3500,7 +3539,8 @@ function $500a16dd18ee5a71$var$RawSearchModalV2({
3500
3539
  get src() {
3501
3540
  return props.src_;
3502
3541
  },
3503
- aspectRatio: `var(${(0, $bb15dfb548a7666e$export$8a2234b5b1a5ebd4)}, 1)`
3542
+ autoAdjustAspectRatio: true,
3543
+ aspectRatio: 1
3504
3544
  }),
3505
3545
  ...instant_results_options_
3506
3546
  }), _el$4, _co$);
@@ -3676,7 +3716,7 @@ const $024fbc0a2d796fad$var$TabsContext = /*@__PURE__*/$9HbP8$createContext();
3676
3716
  const $024fbc0a2d796fad$var$useTabs = () => $9HbP8$useContext($024fbc0a2d796fad$var$TabsContext);
3677
3717
  function $024fbc0a2d796fad$export$b2539bed5023c21c(props) {
3678
3718
  return $9HbP8$createComponent($024fbc0a2d796fad$var$TabsContext.Provider, {
3679
- value: new (0, $9HbP8$ListenableSet)(),
3719
+ value: new (0, $9HbP8$ListenableSet1)(),
3680
3720
  get children() {
3681
3721
  return $9HbP8$createComponent($024fbc0a2d796fad$var$ActualTabs, $9HbP8$mergeProps(props, {
3682
3722
  get children() {
@@ -3841,7 +3881,15 @@ async function $194bd6b8e4c59830$export$79b1565500412d9b(search_modal, modal_fn_
3841
3881
  const actually_close = () => {
3842
3882
  dispose();
3843
3883
  on_dispose?.();
3844
- modal_els.forEach(el => el.remove());
3884
+ const {
3885
+ activeElement: activeElement
3886
+ } = document;
3887
+ modal_els.forEach(el => {
3888
+ if (el.contains(activeElement))
3889
+ // Fix page jumping to the bottom when closing modal with scape after animation in safari
3890
+ activeElement?.blur?.();
3891
+ el.remove();
3892
+ });
3845
3893
  };
3846
3894
  if (animation_started && +new Date() - animation_started > 1000)
3847
3895
  // Animation broke, close it this time
@@ -12483,7 +12531,7 @@ function $49de9d31aeea1b5e$export$6d0c9c22f6652669({
12483
12531
  number_: num_results,
12484
12532
  i18n_: i18n_
12485
12533
  });
12486
- const queries_without_content_results_ = new (0, $9HbP8$ListenableSet1)();
12534
+ const queries_without_content_results_ = new (0, $9HbP8$ListenableSet)();
12487
12535
  const [check_query, set_check_query] = $9HbP8$createSignal(false);
12488
12536
  const placeholders_enabled = $9HbP8$createMemo(() => {
12489
12537
  // don't show placeholders when the user toggles filters
@@ -12579,11 +12627,22 @@ function $0e562a5b247b1fc2$export$44f738469d5570cf({
12579
12627
  modalSearchFieldOuterWidth_: modalSearchFieldOuterWidth_
12580
12628
  }) {
12581
12629
  return $9HbP8$createRoot(dispose => {
12630
+ const [innerWidthReactive, setInnerWidthReactive] = $9HbP8$createSignal(innerWidth);
12631
+ const resizeObserver = new ResizeObserver(records => setInnerWidthReactive(records.at(-1).contentRect.width));
12632
+ const {
12633
+ left: left,
12634
+ top: top,
12635
+ width: width
12636
+ } = actualField_.getBoundingClientRect();
12582
12637
  const fakeField = makeFakeField_();
12583
12638
  const owner = $9HbP8$getOwner();
12584
12639
  const inModalFieldWidth = $9HbP8$createMemo(
12585
12640
  // Can't have `min(${modalSearchFieldOuterWidth_()}px, var(--field-max-width, 600px))` because the WAAPI doesn't support that, so we need ot calculate it in JS
12586
- () => Math.min(modalSearchFieldOuterWidth_(), parseFloat(getComputedStyle(actualField_).getPropertyValue("--field-max-width") || "600")) + "px");
12641
+ () => Math.min(modalSearchFieldOuterWidth_(), parseFloat(getComputedStyle(actualField_).getPropertyValue("--field-max-width") || "600")));
12642
+ const inModalFieldWidthPx = $9HbP8$createMemo(() => inModalFieldWidth() + "px");
12643
+ let disableResizePollingTimeout;
12644
+ resizeObserver.observe(document.documentElement);
12645
+ $9HbP8$onCleanup(() => resizeObserver.disconnect());
12587
12646
  actualField_.replaceWith(fakeField);
12588
12647
  // Don't move the wrapped div since that would confuse run_in_root_or_auto_cleanup, but do actually move the field the user clicked around so that all selection stuff etc. is easier
12589
12648
  const addedToBody = wrap_ ? (() => {
@@ -12592,21 +12651,17 @@ function $0e562a5b247b1fc2$export$44f738469d5570cf({
12592
12651
  return _el$;
12593
12652
  })() : actualField_;
12594
12653
  document.body.append(addedToBody);
12595
- const {
12596
- left: left,
12597
- top: top,
12598
- width: width
12599
- } = fakeField.getBoundingClientRect();
12600
12654
  const zIndexOfFake = getComputedStyle(fakeField).zIndex;
12601
12655
  Object.assign(addedToBody.style, {
12602
12656
  position: "fixed",
12603
12657
  top: top + "px",
12658
+ left: left + "px",
12604
12659
  width: width + "px",
12605
12660
  ...(zIndexOfFake !== "auto" ? {
12606
12661
  zIndex: +zIndexOfFake + 1
12607
12662
  } : {})
12608
12663
  });
12609
- const pxOffCenter = innerWidth / 2 - (left + width / 2);
12664
+ const pxOffCenter = $9HbP8$untrack(innerWidthReactive) / 2 - (left + width / 2);
12610
12665
  const openDuration = pxOffCenter > 100 ? 250 : 125; // faster animations if little left-right movement
12611
12666
  const closeDuration = pxOffCenter > 100 ? 150 : 100;
12612
12667
  setPollAlignment_(true);
@@ -12625,24 +12680,30 @@ function $0e562a5b247b1fc2$export$44f738469d5570cf({
12625
12680
  // Wait until the ResizeObserver has delivered a value in case that the modal is small or has much padding so we know that we can't animate our fake field to be wider than that
12626
12681
  addedToBody.animate([{
12627
12682
  left: left + "px",
12628
- transform: "translateX(0%)",
12629
12683
  width: width + "px"
12630
12684
  }, {
12631
- left: "50%",
12632
- transform: "translateX(-50%)",
12633
- width: $9HbP8$untrack(inModalFieldWidth)
12685
+ left: `${$9HbP8$untrack(innerWidthReactive) / 2 - $9HbP8$untrack(inModalFieldWidth) / 2}px`,
12686
+ width: $9HbP8$untrack(inModalFieldWidthPx)
12634
12687
  }], {
12635
12688
  duration: openDuration,
12636
12689
  easing: "ease-in"
12637
12690
  }).finished.then(() => {
12638
12691
  setPollAlignment_(false);
12639
- $9HbP8$runWithOwner(owner, () => $9HbP8$createEffect(() =>
12640
- // When animation has finished, still dynamically set the size of the search field behind in case the user resizes the window
12641
- Object.assign(addedToBody.style, {
12642
- left: "50%",
12643
- transform: "translateX(-50%)",
12644
- width: inModalFieldWidth()
12645
- })));
12692
+ $9HbP8$runWithOwner(owner, () => $9HbP8$createEffect(() => {
12693
+ // When animation has finished, still dynamically set the size of the search field behind in case the user resizes the window
12694
+ Object.assign(addedToBody.style, {
12695
+ // This is left: 50%, transform: translateX(-50) with extra steps but when using it safari and firefox glitch out like crazy
12696
+ left: `${innerWidthReactive() / 2 - inModalFieldWidth() / 2}px`,
12697
+ width: inModalFieldWidthPx()
12698
+ });
12699
+ // We might only have changed the left position which align_field doesn't know about, make it poll the position for a while
12700
+ setPollAlignment_(true);
12701
+ if (disableResizePollingTimeout) clearTimeout(disableResizePollingTimeout);
12702
+ disableResizePollingTimeout = setTimeout($9HbP8$catchify(() => {
12703
+ setPollAlignment_(false);
12704
+ disableResizePollingTimeout = undefined;
12705
+ }), 1000);
12706
+ }));
12646
12707
  });
12647
12708
  return true;
12648
12709
  }, false);
@@ -12685,13 +12746,11 @@ function $0e562a5b247b1fc2$export$44f738469d5570cf({
12685
12746
  transition: `opacity ${closeDuration}ms ease-out`
12686
12747
  }));
12687
12748
  await Promise.all([addedToBody.animate([{
12688
- width: $9HbP8$untrack(inModalFieldWidth),
12689
- left: "50%",
12690
- transform: "translateX(-50%)"
12749
+ width: $9HbP8$untrack(inModalFieldWidthPx),
12750
+ left: `${$9HbP8$untrack(innerWidthReactive) / 2 - $9HbP8$untrack(inModalFieldWidth) / 2}px`
12691
12751
  }, {
12692
12752
  width: width + "px",
12693
- left: left + "px",
12694
- transform: "translateX(0%)"
12753
+ left: left + "px"
12695
12754
  }], {
12696
12755
  duration: closeDuration,
12697
12756
  fill: "forwards",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@depict-ai/ui",
3
- "version": "4.0.0",
3
+ "version": "4.0.2",
4
4
  "description": "Base package containing the actual UI of Depict UI along with translations, with a not-simplified API. We recommend using @depict-ai/js-ui or @depict-ai/react-ui instead.",
5
5
  "packageManager": "yarn@3.2.2",
6
6
  "sass": "styles.scss",
@@ -236,9 +236,9 @@
236
236
  "@babel/plugin-transform-typescript": "^7.22.9",
237
237
  "@babel/runtime": "latest",
238
238
  "@babel/runtime-corejs3": "^7.22.6",
239
- "@depict-ai/plp-styling": "^3.0.1",
239
+ "@depict-ai/plp-styling": "^3.0.4",
240
240
  "@depict-ai/types": "^5.0.3",
241
- "@depict-ai/utilishared": "^2.0.9",
241
+ "@depict-ai/utilishared": "^2.0.11",
242
242
  "solid-js": "^1.8.7"
243
243
  },
244
244
  "type": "module",