@depict-ai/ui 4.10.15 → 4.10.17-0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/ES10/index.cjs CHANGED
@@ -115,7 +115,7 @@ $parcel$export(module.exports, "BreadCrumbs", () => $335c02c1241a9ca8$export$5b7
115
115
  $parcel$export(module.exports, "QuickLinks", () => $335c02c1241a9ca8$export$de035477d6fef996);
116
116
  $parcel$export(module.exports, "CategoryPage", () => $c10e33c4681f8c9b$export$e2bf8ce7779e827e);
117
117
  /*! Depict.ai search and product listing SDK */
118
- const $aa1938dec50d6c12$export$83d89fbfd8236492 = "4.10.15";
118
+ const $aa1938dec50d6c12$export$83d89fbfd8236492 = "4.10.17-0";
119
119
  function $3e17e871e9d0911d$export$d9f36cf74e24d595(media_query) {
120
120
  const signal = $52FdA$solidjs.createSignal(false);
121
121
  $52FdA$solidjs.onCleanup($52FdA$depictaiutilishared.javascript_media_query(media_query, _ref => {
@@ -2681,9 +2681,18 @@ $52FdA$solidjsweb.delegateEvents(["click"]);
2681
2681
  async function $4cf7d9829d60d844$export$79b1565500412d9b(search_modal, modal_fn_opts, parent) {
2682
2682
  const body = parent || document.body || (await (0, $52FdA$depictaiutilishared.observer).wait_for_element("body"));
2683
2683
  let dispose_and_remove;
2684
+ let restoring_focus = false;
2684
2685
  const open_modal_ = $52FdA$depictaiutilishared.catchify(async function () {
2685
2686
  let extra_options_for_modal = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
2686
2687
  let on_dispose = arguments.length > 1 ? arguments[1] : undefined;
2688
+ if (restoring_focus)
2689
+ // Ignore opens triggered by our own focus restore below (a merchant can open the modal on the trigger's
2690
+ // `focus` event, and without this closing the modal would instantly reopen it, trapping keyboard users).
2691
+ // Only covers handlers that open within the same task as the restore — a handler that opens
2692
+ // asynchronously (debounce, framework effect) can still reopen the modal on close. Inversely, ANY open
2693
+ // in that task is dropped, including a programmatic close_modal_(); open_modal_() sequence when no
2694
+ // closing animation is registered — a focus-handler open is indistinguishable from one here.
2695
+ return;
2687
2696
  // Below await is very intentional,
2688
2697
  // The reason is that effects don't run in <Suspense> boundaries before they've resolved and this carries even through roots created, but if a modal is opened from a suspense boundary we still want the effects in it to run
2689
2698
  // This is because the suspense is for the content in the page and not the modal (the modal goes above it)
@@ -2692,6 +2701,11 @@ async function $4cf7d9829d60d844$export$79b1565500412d9b(search_modal, modal_fn_
2692
2701
  if (await dispose_and_remove)
2693
2702
  // modal is already open
2694
2703
  return;
2704
+ // When the modal is mounted into a shadow root, document.activeElement is the shadow host, not the real
2705
+ // focused element — focus capture/restore (and the pre-existing contains()/blur() below) are best-effort
2706
+ // there and fail closed
2707
+ const opening_active_element = document.activeElement;
2708
+ const element_focused_before_open = opening_active_element instanceof HTMLElement && opening_active_element !== document.body && opening_active_element !== document.documentElement ? opening_active_element : undefined;
2695
2709
  const modal_els = $52FdA$solidjs.createRoot(dispose => {
2696
2710
  let closing_animation;
2697
2711
  let animation_started;
@@ -2702,6 +2716,11 @@ async function $4cf7d9829d60d844$export$79b1565500412d9b(search_modal, modal_fn_
2702
2716
  const {
2703
2717
  activeElement: activeElement
2704
2718
  } = document;
2719
+ // Don't steal focus if the user has already focused something outside the modal by the time it closes
2720
+ // (for example when the modal closes due to navigating away while they're typing elsewhere).
2721
+ // !dispose_and_remove: if the modal was reopened during our closing animation, don't steal focus from
2722
+ // the new instance — its input focuses asynchronously, so activeElement can still be body here
2723
+ const should_restore_focus = (!activeElement || activeElement === document.body || activeElement === document.documentElement || modal_els.some(el => el.contains(activeElement))) && !dispose_and_remove;
2705
2724
  modal_els.forEach(el => {
2706
2725
  if (el.contains(activeElement)) {
2707
2726
  var _blur, _ref;
@@ -2710,6 +2729,18 @@ async function $4cf7d9829d60d844$export$79b1565500412d9b(search_modal, modal_fn_
2710
2729
  }
2711
2730
  el.remove();
2712
2731
  });
2732
+ if (should_restore_focus && element_focused_before_open !== null && element_focused_before_open !== void 0 && element_focused_before_open.isConnected) {
2733
+ restoring_focus = true;
2734
+ try {
2735
+ // preventScroll so this can't re-introduce the safari page-jump the blur() above works around
2736
+ element_focused_before_open.focus({
2737
+ preventScroll: true
2738
+ });
2739
+ } finally {
2740
+ // Cleared on a task instead of synchronously in case a browser delivers the focus event async
2741
+ setTimeout(() => restoring_focus = false, 0);
2742
+ }
2743
+ }
2713
2744
  };
2714
2745
  if (animation_started && +new Date() - animation_started > 1000)
2715
2746
  // Animation broke, close it this time
@@ -10259,6 +10290,9 @@ const $82d079b68975fbf7$export$b05c0ae390cf3e06 = _ref => {
10259
10290
  get src_() {
10260
10291
  var _variant$image_urls2;
10261
10292
  return ((_variant$image_urls2 = variant.image_urls) === null || _variant$image_urls2 === void 0 ? void 0 : _variant$image_urls2[0]) || variant.image_url || variant.display_image_url;
10293
+ },
10294
+ get alt_() {
10295
+ return variant.title;
10262
10296
  }
10263
10297
  }), _el$7, _co$);
10264
10298
  $52FdA$solidjsweb.insert(_el$5, $52FdA$solidjsweb.createComponent((0, $52FdA$solidjs.Show), {
@@ -10268,7 +10302,8 @@ const $82d079b68975fbf7$export$b05c0ae390cf3e06 = _ref => {
10268
10302
  get children() {
10269
10303
  return $52FdA$solidjsweb.createComponent(InstantCardImageComponent_, {
10270
10304
  src_: secondary_image_url,
10271
- class_: "secondary"
10305
+ class_: "secondary",
10306
+ alt_: ""
10272
10307
  });
10273
10308
  }
10274
10309
  }), _el$9, _co$2);
@@ -11849,17 +11884,20 @@ function $9035b24c2dbd3f36$var$RawClassicSearchModal(_ref) {
11849
11884
  market_: market_,
11850
11885
  router_: router_,
11851
11886
  set_modal_search_results_: set_modal_search_results_,
11852
- InstantCardImageComponent_: props => $52FdA$solidjs.createMemo(() => $52FdA$depictaiutilishared.ContainedImage({
11853
- loading: "lazy",
11854
- aspect_ratio: 1,
11855
- alt: "",
11856
- rendering_options: {},
11857
- "src": props.src_,
11858
- srcset_opts: {
11859
- set_dataset: false
11860
- },
11861
- sizes: "0px"
11862
- })),
11887
+ InstantCardImageComponent_: props => $52FdA$solidjs.createMemo(() => {
11888
+ var _props$alt_;
11889
+ return $52FdA$depictaiutilishared.ContainedImage({
11890
+ loading: "lazy",
11891
+ aspect_ratio: 1,
11892
+ alt: (_props$alt_ = props.alt_) !== null && _props$alt_ !== void 0 ? _props$alt_ : "",
11893
+ rendering_options: {},
11894
+ "src": props.src_,
11895
+ srcset_opts: {
11896
+ set_dataset: false
11897
+ },
11898
+ sizes: "0px"
11899
+ });
11900
+ }),
11863
11901
  ...instant_results_options_
11864
11902
  });
11865
11903
  }
@@ -12741,8 +12779,11 @@ function $43600b30502feeda$var$RawSearchModalV2(_ref) {
12741
12779
  get ["class"]() {
12742
12780
  return props.class_;
12743
12781
  },
12744
- imgProps: {
12745
- alt: "Product image"
12782
+ get imgProps() {
12783
+ var _props$alt_;
12784
+ return {
12785
+ alt: (_props$alt_ = props.alt_) !== null && _props$alt_ !== void 0 ? _props$alt_ : ""
12786
+ };
12746
12787
  },
12747
12788
  imageResizer_: imageResizer_
12748
12789
  }),
package/ES10/index.d.ts CHANGED
@@ -11,7 +11,7 @@ import { GetListingRequest } from "@depict-ai/types/api/GetListingRequest";
11
11
  import { BaseSearchRequestV3 } from "@depict-ai/types/api/BaseSearchRequestV3";
12
12
  import { FeaturedInDisplay } from "@depict-ai/types/api/FeaturedInResponseV3";
13
13
  import { SearchFilter as _SearchFilter3 } from "@depict-ai/types/api/SearchRequestV2";
14
- export const version = "4.10.15";
14
+ export const version = "4.10.17-0";
15
15
  /**
16
16
  * Use `javascript_media_query` but return a reactive accessor instead of taking a callback function
17
17
  */
package/ES10/index.js CHANGED
@@ -10,7 +10,7 @@ import $8tj43$babelruntimehelpersclassPrivateFieldGet from "@babel/runtime/helpe
10
10
  /*! Depict.ai search and product listing SDK */
11
11
  import { register_es_version } from "../multi_import_deduplicator.mjs";
12
12
  register_es_version("ui", "ES10");
13
- const $8db9a15135765c11$export$83d89fbfd8236492 = "4.10.15";
13
+ const $8db9a15135765c11$export$83d89fbfd8236492 = "4.10.17-0";
14
14
  function $00a5b94f0c11efbb$export$d9f36cf74e24d595(media_query) {
15
15
  const signal = $8tj43$createSignal(false);
16
16
  $8tj43$onCleanup($8tj43$javascript_media_query(media_query, _ref => {
@@ -2576,9 +2576,18 @@ $8tj43$delegateEvents(["click"]);
2576
2576
  async function $d5f1e6a0f9117286$export$79b1565500412d9b(search_modal, modal_fn_opts, parent) {
2577
2577
  const body = parent || document.body || (await (0, $8tj43$observer).wait_for_element("body"));
2578
2578
  let dispose_and_remove;
2579
+ let restoring_focus = false;
2579
2580
  const open_modal_ = $8tj43$catchify1(async function () {
2580
2581
  let extra_options_for_modal = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
2581
2582
  let on_dispose = arguments.length > 1 ? arguments[1] : undefined;
2583
+ if (restoring_focus)
2584
+ // Ignore opens triggered by our own focus restore below (a merchant can open the modal on the trigger's
2585
+ // `focus` event, and without this closing the modal would instantly reopen it, trapping keyboard users).
2586
+ // Only covers handlers that open within the same task as the restore — a handler that opens
2587
+ // asynchronously (debounce, framework effect) can still reopen the modal on close. Inversely, ANY open
2588
+ // in that task is dropped, including a programmatic close_modal_(); open_modal_() sequence when no
2589
+ // closing animation is registered — a focus-handler open is indistinguishable from one here.
2590
+ return;
2582
2591
  // Below await is very intentional,
2583
2592
  // The reason is that effects don't run in <Suspense> boundaries before they've resolved and this carries even through roots created, but if a modal is opened from a suspense boundary we still want the effects in it to run
2584
2593
  // This is because the suspense is for the content in the page and not the modal (the modal goes above it)
@@ -2587,6 +2596,11 @@ async function $d5f1e6a0f9117286$export$79b1565500412d9b(search_modal, modal_fn_
2587
2596
  if (await dispose_and_remove)
2588
2597
  // modal is already open
2589
2598
  return;
2599
+ // When the modal is mounted into a shadow root, document.activeElement is the shadow host, not the real
2600
+ // focused element — focus capture/restore (and the pre-existing contains()/blur() below) are best-effort
2601
+ // there and fail closed
2602
+ const opening_active_element = document.activeElement;
2603
+ const element_focused_before_open = opening_active_element instanceof HTMLElement && opening_active_element !== document.body && opening_active_element !== document.documentElement ? opening_active_element : undefined;
2590
2604
  const modal_els = $8tj43$createRoot(dispose => {
2591
2605
  let closing_animation;
2592
2606
  let animation_started;
@@ -2597,6 +2611,11 @@ async function $d5f1e6a0f9117286$export$79b1565500412d9b(search_modal, modal_fn_
2597
2611
  const {
2598
2612
  activeElement: activeElement
2599
2613
  } = document;
2614
+ // Don't steal focus if the user has already focused something outside the modal by the time it closes
2615
+ // (for example when the modal closes due to navigating away while they're typing elsewhere).
2616
+ // !dispose_and_remove: if the modal was reopened during our closing animation, don't steal focus from
2617
+ // the new instance — its input focuses asynchronously, so activeElement can still be body here
2618
+ const should_restore_focus = (!activeElement || activeElement === document.body || activeElement === document.documentElement || modal_els.some(el => el.contains(activeElement))) && !dispose_and_remove;
2600
2619
  modal_els.forEach(el => {
2601
2620
  if (el.contains(activeElement)) {
2602
2621
  var _blur, _ref;
@@ -2605,6 +2624,18 @@ async function $d5f1e6a0f9117286$export$79b1565500412d9b(search_modal, modal_fn_
2605
2624
  }
2606
2625
  el.remove();
2607
2626
  });
2627
+ if (should_restore_focus && element_focused_before_open !== null && element_focused_before_open !== void 0 && element_focused_before_open.isConnected) {
2628
+ restoring_focus = true;
2629
+ try {
2630
+ // preventScroll so this can't re-introduce the safari page-jump the blur() above works around
2631
+ element_focused_before_open.focus({
2632
+ preventScroll: true
2633
+ });
2634
+ } finally {
2635
+ // Cleared on a task instead of synchronously in case a browser delivers the focus event async
2636
+ setTimeout(() => restoring_focus = false, 0);
2637
+ }
2638
+ }
2608
2639
  };
2609
2640
  if (animation_started && +new Date() - animation_started > 1000)
2610
2641
  // Animation broke, close it this time
@@ -10154,6 +10185,9 @@ const $9a693c7e832d3f18$export$b05c0ae390cf3e06 = _ref => {
10154
10185
  get src_() {
10155
10186
  var _variant$image_urls2;
10156
10187
  return ((_variant$image_urls2 = variant.image_urls) === null || _variant$image_urls2 === void 0 ? void 0 : _variant$image_urls2[0]) || variant.image_url || variant.display_image_url;
10188
+ },
10189
+ get alt_() {
10190
+ return variant.title;
10157
10191
  }
10158
10192
  }), _el$7, _co$);
10159
10193
  $8tj43$insert(_el$5, $8tj43$createComponent((0, $8tj43$Show), {
@@ -10163,7 +10197,8 @@ const $9a693c7e832d3f18$export$b05c0ae390cf3e06 = _ref => {
10163
10197
  get children() {
10164
10198
  return $8tj43$createComponent(InstantCardImageComponent_, {
10165
10199
  src_: secondary_image_url,
10166
- class_: "secondary"
10200
+ class_: "secondary",
10201
+ alt_: ""
10167
10202
  });
10168
10203
  }
10169
10204
  }), _el$9, _co$2);
@@ -11744,17 +11779,20 @@ function $40411f2ec772c376$var$RawClassicSearchModal(_ref) {
11744
11779
  market_: market_,
11745
11780
  router_: router_,
11746
11781
  set_modal_search_results_: set_modal_search_results_,
11747
- InstantCardImageComponent_: props => $8tj43$createMemo(() => $8tj43$ContainedImage({
11748
- loading: "lazy",
11749
- aspect_ratio: 1,
11750
- alt: "",
11751
- rendering_options: {},
11752
- "src": props.src_,
11753
- srcset_opts: {
11754
- set_dataset: false
11755
- },
11756
- sizes: "0px"
11757
- })),
11782
+ InstantCardImageComponent_: props => $8tj43$createMemo(() => {
11783
+ var _props$alt_;
11784
+ return $8tj43$ContainedImage({
11785
+ loading: "lazy",
11786
+ aspect_ratio: 1,
11787
+ alt: (_props$alt_ = props.alt_) !== null && _props$alt_ !== void 0 ? _props$alt_ : "",
11788
+ rendering_options: {},
11789
+ "src": props.src_,
11790
+ srcset_opts: {
11791
+ set_dataset: false
11792
+ },
11793
+ sizes: "0px"
11794
+ });
11795
+ }),
11758
11796
  ...instant_results_options_
11759
11797
  });
11760
11798
  }
@@ -12636,8 +12674,11 @@ function $06fcd2a345efd24d$var$RawSearchModalV2(_ref) {
12636
12674
  get ["class"]() {
12637
12675
  return props.class_;
12638
12676
  },
12639
- imgProps: {
12640
- alt: "Product image"
12677
+ get imgProps() {
12678
+ var _props$alt_;
12679
+ return {
12680
+ alt: (_props$alt_ = props.alt_) !== null && _props$alt_ !== void 0 ? _props$alt_ : ""
12681
+ };
12641
12682
  },
12642
12683
  imageResizer_: imageResizer_
12643
12684
  }),
package/latest/index.cjs CHANGED
@@ -109,7 +109,7 @@ $parcel$export(module.exports, "BreadCrumbs", () => $1f4925833f07df29$export$5b7
109
109
  $parcel$export(module.exports, "QuickLinks", () => $1f4925833f07df29$export$de035477d6fef996);
110
110
  $parcel$export(module.exports, "CategoryPage", () => $14f289047713d67a$export$e2bf8ce7779e827e);
111
111
  /*! Depict.ai search and product listing SDK */
112
- const $b757f75811f3261f$export$83d89fbfd8236492 = "4.10.15";
112
+ const $b757f75811f3261f$export$83d89fbfd8236492 = "4.10.17-0";
113
113
  function $b54e6192d0caa293$export$d9f36cf74e24d595(media_query) {
114
114
  const signal = $gVhhF$solidjs.createSignal(false);
115
115
  $gVhhF$solidjs.onCleanup($gVhhF$depictaiutilishared.javascript_media_query(media_query, ({
@@ -2567,7 +2567,16 @@ $gVhhF$solidjsweb.delegateEvents(["click"]);
2567
2567
  async function $9abb0a35d4b60ed3$export$79b1565500412d9b(search_modal, modal_fn_opts, parent) {
2568
2568
  const body = parent || document.body || (await (0, $gVhhF$depictaiutilishared.observer).wait_for_element("body"));
2569
2569
  let dispose_and_remove;
2570
+ let restoring_focus = false;
2570
2571
  const open_modal_ = $gVhhF$depictaiutilishared.catchify(async (extra_options_for_modal = {}, on_dispose) => {
2572
+ if (restoring_focus)
2573
+ // Ignore opens triggered by our own focus restore below (a merchant can open the modal on the trigger's
2574
+ // `focus` event, and without this closing the modal would instantly reopen it, trapping keyboard users).
2575
+ // Only covers handlers that open within the same task as the restore — a handler that opens
2576
+ // asynchronously (debounce, framework effect) can still reopen the modal on close. Inversely, ANY open
2577
+ // in that task is dropped, including a programmatic close_modal_(); open_modal_() sequence when no
2578
+ // closing animation is registered — a focus-handler open is indistinguishable from one here.
2579
+ return;
2571
2580
  // Below await is very intentional,
2572
2581
  // The reason is that effects don't run in <Suspense> boundaries before they've resolved and this carries even through roots created, but if a modal is opened from a suspense boundary we still want the effects in it to run
2573
2582
  // This is because the suspense is for the content in the page and not the modal (the modal goes above it)
@@ -2576,6 +2585,11 @@ async function $9abb0a35d4b60ed3$export$79b1565500412d9b(search_modal, modal_fn_
2576
2585
  if (await dispose_and_remove)
2577
2586
  // modal is already open
2578
2587
  return;
2588
+ // When the modal is mounted into a shadow root, document.activeElement is the shadow host, not the real
2589
+ // focused element — focus capture/restore (and the pre-existing contains()/blur() below) are best-effort
2590
+ // there and fail closed
2591
+ const opening_active_element = document.activeElement;
2592
+ const element_focused_before_open = opening_active_element instanceof HTMLElement && opening_active_element !== document.body && opening_active_element !== document.documentElement ? opening_active_element : undefined;
2579
2593
  const modal_els = $gVhhF$solidjs.createRoot(dispose => {
2580
2594
  let closing_animation;
2581
2595
  let animation_started;
@@ -2586,12 +2600,29 @@ async function $9abb0a35d4b60ed3$export$79b1565500412d9b(search_modal, modal_fn_
2586
2600
  const {
2587
2601
  activeElement: activeElement
2588
2602
  } = document;
2603
+ // Don't steal focus if the user has already focused something outside the modal by the time it closes
2604
+ // (for example when the modal closes due to navigating away while they're typing elsewhere).
2605
+ // !dispose_and_remove: if the modal was reopened during our closing animation, don't steal focus from
2606
+ // the new instance — its input focuses asynchronously, so activeElement can still be body here
2607
+ const should_restore_focus = (!activeElement || activeElement === document.body || activeElement === document.documentElement || modal_els.some(el => el.contains(activeElement))) && !dispose_and_remove;
2589
2608
  modal_els.forEach(el => {
2590
2609
  if (el.contains(activeElement))
2591
2610
  // Fix page jumping to the bottom when closing modal with scape after animation in safari
2592
2611
  activeElement?.blur?.();
2593
2612
  el.remove();
2594
2613
  });
2614
+ if (should_restore_focus && element_focused_before_open?.isConnected) {
2615
+ restoring_focus = true;
2616
+ try {
2617
+ // preventScroll so this can't re-introduce the safari page-jump the blur() above works around
2618
+ element_focused_before_open.focus({
2619
+ preventScroll: true
2620
+ });
2621
+ } finally {
2622
+ // Cleared on a task instead of synchronously in case a browser delivers the focus event async
2623
+ setTimeout(() => restoring_focus = false, 0);
2624
+ }
2625
+ }
2595
2626
  };
2596
2627
  if (animation_started && +new Date() - animation_started > 1000)
2597
2628
  // Animation broke, close it this time
@@ -9697,6 +9728,9 @@ const $6ae613870d3380fc$export$b05c0ae390cf3e06 = ({
9697
9728
  $gVhhF$solidjsweb.insert(_el$5, $gVhhF$solidjsweb.createComponent(InstantCardImageComponent_, {
9698
9729
  get src_() {
9699
9730
  return variant.image_urls?.[0] || variant.image_url || variant.display_image_url;
9731
+ },
9732
+ get alt_() {
9733
+ return variant.title;
9700
9734
  }
9701
9735
  }), _el$7, _co$);
9702
9736
  $gVhhF$solidjsweb.insert(_el$5, $gVhhF$solidjsweb.createComponent((0, $gVhhF$solidjs.Show), {
@@ -9706,7 +9740,8 @@ const $6ae613870d3380fc$export$b05c0ae390cf3e06 = ({
9706
9740
  get children() {
9707
9741
  return $gVhhF$solidjsweb.createComponent(InstantCardImageComponent_, {
9708
9742
  src_: secondary_image_url,
9709
- class_: "secondary"
9743
+ class_: "secondary",
9744
+ alt_: ""
9710
9745
  });
9711
9746
  }
9712
9747
  }), _el$9, _co$2);
@@ -11224,7 +11259,7 @@ function $44f0b2e312a9b710$var$RawClassicSearchModal({
11224
11259
  InstantCardImageComponent_: props => $gVhhF$solidjs.createMemo(() => $gVhhF$depictaiutilishared.ContainedImage({
11225
11260
  loading: "lazy",
11226
11261
  aspect_ratio: 1,
11227
- alt: "",
11262
+ alt: props.alt_ ?? "",
11228
11263
  rendering_options: {},
11229
11264
  "src": props.src_,
11230
11265
  srcset_opts: {
@@ -12078,8 +12113,10 @@ function $11597ae03e703a47$var$RawSearchModalV2({
12078
12113
  get ["class"]() {
12079
12114
  return props.class_;
12080
12115
  },
12081
- imgProps: {
12082
- alt: "Product image"
12116
+ get imgProps() {
12117
+ return {
12118
+ alt: props.alt_ ?? ""
12119
+ };
12083
12120
  },
12084
12121
  imageResizer_: imageResizer_
12085
12122
  }),
package/latest/index.d.ts CHANGED
@@ -11,7 +11,7 @@ import { GetListingRequest } from "@depict-ai/types/api/GetListingRequest";
11
11
  import { BaseSearchRequestV3 } from "@depict-ai/types/api/BaseSearchRequestV3";
12
12
  import { FeaturedInDisplay } from "@depict-ai/types/api/FeaturedInResponseV3";
13
13
  import { SearchFilter as _SearchFilter3 } from "@depict-ai/types/api/SearchRequestV2";
14
- export const version = "4.10.15";
14
+ export const version = "4.10.17-0";
15
15
  /**
16
16
  * Use `javascript_media_query` but return a reactive accessor instead of taking a callback function
17
17
  */
package/latest/index.js CHANGED
@@ -7,7 +7,7 @@ import { createStore as $9HbP8$createStore, reconcile as $9HbP8$reconcile } from
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.10.15";
10
+ const $ec291e0010116877$export$83d89fbfd8236492 = "4.10.17-0";
11
11
  function $dc9262ef6f786812$export$d9f36cf74e24d595(media_query) {
12
12
  const signal = $9HbP8$createSignal(false);
13
13
  $9HbP8$onCleanup($9HbP8$javascript_media_query(media_query, ({
@@ -2465,7 +2465,16 @@ $9HbP8$delegateEvents(["click"]);
2465
2465
  async function $194bd6b8e4c59830$export$79b1565500412d9b(search_modal, modal_fn_opts, parent) {
2466
2466
  const body = parent || document.body || (await (0, $9HbP8$observer).wait_for_element("body"));
2467
2467
  let dispose_and_remove;
2468
+ let restoring_focus = false;
2468
2469
  const open_modal_ = $9HbP8$catchify1(async (extra_options_for_modal = {}, on_dispose) => {
2470
+ if (restoring_focus)
2471
+ // Ignore opens triggered by our own focus restore below (a merchant can open the modal on the trigger's
2472
+ // `focus` event, and without this closing the modal would instantly reopen it, trapping keyboard users).
2473
+ // Only covers handlers that open within the same task as the restore — a handler that opens
2474
+ // asynchronously (debounce, framework effect) can still reopen the modal on close. Inversely, ANY open
2475
+ // in that task is dropped, including a programmatic close_modal_(); open_modal_() sequence when no
2476
+ // closing animation is registered — a focus-handler open is indistinguishable from one here.
2477
+ return;
2469
2478
  // Below await is very intentional,
2470
2479
  // The reason is that effects don't run in <Suspense> boundaries before they've resolved and this carries even through roots created, but if a modal is opened from a suspense boundary we still want the effects in it to run
2471
2480
  // This is because the suspense is for the content in the page and not the modal (the modal goes above it)
@@ -2474,6 +2483,11 @@ async function $194bd6b8e4c59830$export$79b1565500412d9b(search_modal, modal_fn_
2474
2483
  if (await dispose_and_remove)
2475
2484
  // modal is already open
2476
2485
  return;
2486
+ // When the modal is mounted into a shadow root, document.activeElement is the shadow host, not the real
2487
+ // focused element — focus capture/restore (and the pre-existing contains()/blur() below) are best-effort
2488
+ // there and fail closed
2489
+ const opening_active_element = document.activeElement;
2490
+ const element_focused_before_open = opening_active_element instanceof HTMLElement && opening_active_element !== document.body && opening_active_element !== document.documentElement ? opening_active_element : undefined;
2477
2491
  const modal_els = $9HbP8$createRoot(dispose => {
2478
2492
  let closing_animation;
2479
2493
  let animation_started;
@@ -2484,12 +2498,29 @@ async function $194bd6b8e4c59830$export$79b1565500412d9b(search_modal, modal_fn_
2484
2498
  const {
2485
2499
  activeElement: activeElement
2486
2500
  } = document;
2501
+ // Don't steal focus if the user has already focused something outside the modal by the time it closes
2502
+ // (for example when the modal closes due to navigating away while they're typing elsewhere).
2503
+ // !dispose_and_remove: if the modal was reopened during our closing animation, don't steal focus from
2504
+ // the new instance — its input focuses asynchronously, so activeElement can still be body here
2505
+ const should_restore_focus = (!activeElement || activeElement === document.body || activeElement === document.documentElement || modal_els.some(el => el.contains(activeElement))) && !dispose_and_remove;
2487
2506
  modal_els.forEach(el => {
2488
2507
  if (el.contains(activeElement))
2489
2508
  // Fix page jumping to the bottom when closing modal with scape after animation in safari
2490
2509
  activeElement?.blur?.();
2491
2510
  el.remove();
2492
2511
  });
2512
+ if (should_restore_focus && element_focused_before_open?.isConnected) {
2513
+ restoring_focus = true;
2514
+ try {
2515
+ // preventScroll so this can't re-introduce the safari page-jump the blur() above works around
2516
+ element_focused_before_open.focus({
2517
+ preventScroll: true
2518
+ });
2519
+ } finally {
2520
+ // Cleared on a task instead of synchronously in case a browser delivers the focus event async
2521
+ setTimeout(() => restoring_focus = false, 0);
2522
+ }
2523
+ }
2493
2524
  };
2494
2525
  if (animation_started && +new Date() - animation_started > 1000)
2495
2526
  // Animation broke, close it this time
@@ -9595,6 +9626,9 @@ const $e003be2a50d9e5bc$export$b05c0ae390cf3e06 = ({
9595
9626
  $9HbP8$insert(_el$5, $9HbP8$createComponent(InstantCardImageComponent_, {
9596
9627
  get src_() {
9597
9628
  return variant.image_urls?.[0] || variant.image_url || variant.display_image_url;
9629
+ },
9630
+ get alt_() {
9631
+ return variant.title;
9598
9632
  }
9599
9633
  }), _el$7, _co$);
9600
9634
  $9HbP8$insert(_el$5, $9HbP8$createComponent((0, $9HbP8$Show), {
@@ -9604,7 +9638,8 @@ const $e003be2a50d9e5bc$export$b05c0ae390cf3e06 = ({
9604
9638
  get children() {
9605
9639
  return $9HbP8$createComponent(InstantCardImageComponent_, {
9606
9640
  src_: secondary_image_url,
9607
- class_: "secondary"
9641
+ class_: "secondary",
9642
+ alt_: ""
9608
9643
  });
9609
9644
  }
9610
9645
  }), _el$9, _co$2);
@@ -11122,7 +11157,7 @@ function $42912431795f188e$var$RawClassicSearchModal({
11122
11157
  InstantCardImageComponent_: props => $9HbP8$createMemo(() => $9HbP8$ContainedImage({
11123
11158
  loading: "lazy",
11124
11159
  aspect_ratio: 1,
11125
- alt: "",
11160
+ alt: props.alt_ ?? "",
11126
11161
  rendering_options: {},
11127
11162
  "src": props.src_,
11128
11163
  srcset_opts: {
@@ -11976,8 +12011,10 @@ function $500a16dd18ee5a71$var$RawSearchModalV2({
11976
12011
  get ["class"]() {
11977
12012
  return props.class_;
11978
12013
  },
11979
- imgProps: {
11980
- alt: "Product image"
12014
+ get imgProps() {
12015
+ return {
12016
+ alt: props.alt_ ?? ""
12017
+ };
11981
12018
  },
11982
12019
  imageResizer_: imageResizer_
11983
12020
  }),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@depict-ai/ui",
3
- "version": "4.10.15",
3
+ "version": "4.10.17-0",
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",
@@ -234,14 +234,15 @@
234
234
  },
235
235
  "dependencies": {
236
236
  "@babel/plugin-transform-typescript": "^7.22.9",
237
- "@babel/runtime": "latest",
237
+ "@babel/runtime": "^7.22.6",
238
238
  "@babel/runtime-corejs3": "^7.22.6",
239
- "@depict-ai/plp-styling": "^4.6.7",
240
- "@depict-ai/types": "^5.6.5",
241
- "@depict-ai/utilishared": "^2.6.5",
239
+ "@depict-ai/plp-styling": "^4.6.9-0",
240
+ "@depict-ai/types": "^5.6.7-0",
241
+ "@depict-ai/utilishared": "^2.6.7-0",
242
242
  "solid-js": "^1.9.10"
243
243
  },
244
244
  "type": "module",
245
+ "stableVersion": "4.10.16",
245
246
  "types": "ES10/index.d.ts",
246
247
  "main": "ES10/index.cjs",
247
248
  "module": "ES10/index.js"
@@ -115,7 +115,7 @@ $parcel$export(module.exports, "BreadCrumbs", () => $335c02c1241a9ca8$export$5b7
115
115
  $parcel$export(module.exports, "QuickLinks", () => $335c02c1241a9ca8$export$de035477d6fef996);
116
116
  $parcel$export(module.exports, "CategoryPage", () => $c10e33c4681f8c9b$export$e2bf8ce7779e827e);
117
117
  /*! Depict.ai search and product listing SDK */
118
- const $aa1938dec50d6c12$export$83d89fbfd8236492 = "4.10.15";
118
+ const $aa1938dec50d6c12$export$83d89fbfd8236492 = "4.10.17-0";
119
119
  function $3e17e871e9d0911d$export$d9f36cf74e24d595(media_query) {
120
120
  const signal = $3hooq$solidjs.createSignal(false);
121
121
  $3hooq$solidjs.onCleanup($3hooq$depictaiutilishared.javascript_media_query(media_query, _ref => {
@@ -2221,9 +2221,18 @@ function $c5842bdb10e57229$export$fecbb157621501bd(_ref) {
2221
2221
  async function $4cf7d9829d60d844$export$79b1565500412d9b(search_modal, modal_fn_opts, parent) {
2222
2222
  const body = parent || document.body || (await (0, $3hooq$depictaiutilishared.observer).wait_for_element("body"));
2223
2223
  let dispose_and_remove;
2224
+ let restoring_focus = false;
2224
2225
  const open_modal_ = $3hooq$depictaiutilishared.catchify(async function () {
2225
2226
  let extra_options_for_modal = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
2226
2227
  let on_dispose = arguments.length > 1 ? arguments[1] : undefined;
2228
+ if (restoring_focus)
2229
+ // Ignore opens triggered by our own focus restore below (a merchant can open the modal on the trigger's
2230
+ // `focus` event, and without this closing the modal would instantly reopen it, trapping keyboard users).
2231
+ // Only covers handlers that open within the same task as the restore — a handler that opens
2232
+ // asynchronously (debounce, framework effect) can still reopen the modal on close. Inversely, ANY open
2233
+ // in that task is dropped, including a programmatic close_modal_(); open_modal_() sequence when no
2234
+ // closing animation is registered — a focus-handler open is indistinguishable from one here.
2235
+ return;
2227
2236
  // Below await is very intentional,
2228
2237
  // The reason is that effects don't run in <Suspense> boundaries before they've resolved and this carries even through roots created, but if a modal is opened from a suspense boundary we still want the effects in it to run
2229
2238
  // This is because the suspense is for the content in the page and not the modal (the modal goes above it)
@@ -2232,6 +2241,11 @@ async function $4cf7d9829d60d844$export$79b1565500412d9b(search_modal, modal_fn_
2232
2241
  if (await dispose_and_remove)
2233
2242
  // modal is already open
2234
2243
  return;
2244
+ // When the modal is mounted into a shadow root, document.activeElement is the shadow host, not the real
2245
+ // focused element — focus capture/restore (and the pre-existing contains()/blur() below) are best-effort
2246
+ // there and fail closed
2247
+ const opening_active_element = document.activeElement;
2248
+ const element_focused_before_open = opening_active_element instanceof HTMLElement && opening_active_element !== document.body && opening_active_element !== document.documentElement ? opening_active_element : undefined;
2235
2249
  const modal_els = $3hooq$solidjs.createRoot(dispose => {
2236
2250
  let closing_animation;
2237
2251
  let animation_started;
@@ -2242,6 +2256,11 @@ async function $4cf7d9829d60d844$export$79b1565500412d9b(search_modal, modal_fn_
2242
2256
  const {
2243
2257
  activeElement: activeElement
2244
2258
  } = document;
2259
+ // Don't steal focus if the user has already focused something outside the modal by the time it closes
2260
+ // (for example when the modal closes due to navigating away while they're typing elsewhere).
2261
+ // !dispose_and_remove: if the modal was reopened during our closing animation, don't steal focus from
2262
+ // the new instance — its input focuses asynchronously, so activeElement can still be body here
2263
+ const should_restore_focus = (!activeElement || activeElement === document.body || activeElement === document.documentElement || modal_els.some(el => el.contains(activeElement))) && !dispose_and_remove;
2245
2264
  modal_els.forEach(el => {
2246
2265
  if (el.contains(activeElement)) {
2247
2266
  var _blur, _ref;
@@ -2250,6 +2269,18 @@ async function $4cf7d9829d60d844$export$79b1565500412d9b(search_modal, modal_fn_
2250
2269
  }
2251
2270
  el.remove();
2252
2271
  });
2272
+ if (should_restore_focus && element_focused_before_open !== null && element_focused_before_open !== void 0 && element_focused_before_open.isConnected) {
2273
+ restoring_focus = true;
2274
+ try {
2275
+ // preventScroll so this can't re-introduce the safari page-jump the blur() above works around
2276
+ element_focused_before_open.focus({
2277
+ preventScroll: true
2278
+ });
2279
+ } finally {
2280
+ // Cleared on a task instead of synchronously in case a browser delivers the focus event async
2281
+ setTimeout(() => restoring_focus = false, 0);
2282
+ }
2283
+ }
2253
2284
  };
2254
2285
  if (animation_started && +new Date() - animation_started > 1000)
2255
2286
  // Animation broke, close it this time
@@ -8469,6 +8500,9 @@ const $82d079b68975fbf7$export$b05c0ae390cf3e06 = _ref => {
8469
8500
  get src_() {
8470
8501
  var _variant$image_urls2;
8471
8502
  return ((_variant$image_urls2 = variant.image_urls) === null || _variant$image_urls2 === void 0 ? void 0 : _variant$image_urls2[0]) || variant.image_url || variant.display_image_url;
8503
+ },
8504
+ get alt_() {
8505
+ return variant.title;
8472
8506
  }
8473
8507
  })), $3hooq$solidjsweb.escape($3hooq$solidjsweb.createComponent((0, $3hooq$solidjs.Show), {
8474
8508
  get when() {
@@ -8477,7 +8511,8 @@ const $82d079b68975fbf7$export$b05c0ae390cf3e06 = _ref => {
8477
8511
  get children() {
8478
8512
  return $3hooq$solidjsweb.createComponent(InstantCardImageComponent_, {
8479
8513
  src_: secondary_image_url,
8480
- class_: "secondary"
8514
+ class_: "secondary",
8515
+ alt_: ""
8481
8516
  });
8482
8517
  }
8483
8518
  })), selected_index_ ? $3hooq$solidjsweb.escape($3hooq$solidjsweb.createComponent(TitleBrandTagline, {})) : $3hooq$solidjsweb.ssr($82d079b68975fbf7$var$_tmpl$7, $3hooq$solidjsweb.ssrHydrationKey(), $3hooq$solidjsweb.escape($3hooq$solidjsweb.createComponent(TitleBrandTagline, {}))), $3hooq$solidjsweb.escape($3hooq$solidjsweb.createComponent((0, $3hooq$solidjs.Show), {
@@ -9645,17 +9680,20 @@ function $9035b24c2dbd3f36$var$RawClassicSearchModal(_ref) {
9645
9680
  market_: market_,
9646
9681
  router_: router_,
9647
9682
  set_modal_search_results_: set_modal_search_results_,
9648
- InstantCardImageComponent_: props => $3hooq$solidjs.createMemo(() => $3hooq$depictaiutilishared.ContainedImage({
9649
- loading: "lazy",
9650
- aspect_ratio: 1,
9651
- alt: "",
9652
- rendering_options: {},
9653
- "src": props.src_,
9654
- srcset_opts: {
9655
- set_dataset: false
9656
- },
9657
- sizes: "0px"
9658
- })),
9683
+ InstantCardImageComponent_: props => $3hooq$solidjs.createMemo(() => {
9684
+ var _props$alt_;
9685
+ return $3hooq$depictaiutilishared.ContainedImage({
9686
+ loading: "lazy",
9687
+ aspect_ratio: 1,
9688
+ alt: (_props$alt_ = props.alt_) !== null && _props$alt_ !== void 0 ? _props$alt_ : "",
9689
+ rendering_options: {},
9690
+ "src": props.src_,
9691
+ srcset_opts: {
9692
+ set_dataset: false
9693
+ },
9694
+ sizes: "0px"
9695
+ });
9696
+ }),
9659
9697
  ...instant_results_options_
9660
9698
  });
9661
9699
  }
@@ -10369,8 +10407,11 @@ function $43600b30502feeda$var$RawSearchModalV2(_ref) {
10369
10407
  get ["class"]() {
10370
10408
  return props.class_;
10371
10409
  },
10372
- imgProps: {
10373
- alt: "Product image"
10410
+ get imgProps() {
10411
+ var _props$alt_;
10412
+ return {
10413
+ alt: (_props$alt_ = props.alt_) !== null && _props$alt_ !== void 0 ? _props$alt_ : ""
10414
+ };
10374
10415
  },
10375
10416
  imageResizer_: imageResizer_
10376
10417
  }),
@@ -11,7 +11,7 @@ import { GetListingRequest } from "@depict-ai/types/api/GetListingRequest";
11
11
  import { BaseSearchRequestV3 } from "@depict-ai/types/api/BaseSearchRequestV3";
12
12
  import { FeaturedInDisplay } from "@depict-ai/types/api/FeaturedInResponseV3";
13
13
  import { SearchFilter as _SearchFilter3 } from "@depict-ai/types/api/SearchRequestV2";
14
- export const version = "4.10.15";
14
+ export const version = "4.10.17-0";
15
15
  /**
16
16
  * Use `javascript_media_query` but return a reactive accessor instead of taking a callback function
17
17
  */
@@ -10,7 +10,7 @@ import $b8hua$babelruntimehelpersclassPrivateFieldGet from "@babel/runtime/helpe
10
10
  /*! Depict.ai search and product listing SDK */
11
11
  import { register_es_version } from "../../multi_import_deduplicator.mjs";
12
12
  register_es_version("ui", "ES10");
13
- const $8db9a15135765c11$export$83d89fbfd8236492 = "4.10.15";
13
+ const $8db9a15135765c11$export$83d89fbfd8236492 = "4.10.17-0";
14
14
  function $00a5b94f0c11efbb$export$d9f36cf74e24d595(media_query) {
15
15
  const signal = $b8hua$createSignal(false);
16
16
  $b8hua$onCleanup($b8hua$javascript_media_query(media_query, _ref => {
@@ -2116,9 +2116,18 @@ function $4711a82d08d2021b$export$fecbb157621501bd(_ref) {
2116
2116
  async function $d5f1e6a0f9117286$export$79b1565500412d9b(search_modal, modal_fn_opts, parent) {
2117
2117
  const body = parent || document.body || (await (0, $b8hua$observer).wait_for_element("body"));
2118
2118
  let dispose_and_remove;
2119
+ let restoring_focus = false;
2119
2120
  const open_modal_ = $b8hua$catchify1(async function () {
2120
2121
  let extra_options_for_modal = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
2121
2122
  let on_dispose = arguments.length > 1 ? arguments[1] : undefined;
2123
+ if (restoring_focus)
2124
+ // Ignore opens triggered by our own focus restore below (a merchant can open the modal on the trigger's
2125
+ // `focus` event, and without this closing the modal would instantly reopen it, trapping keyboard users).
2126
+ // Only covers handlers that open within the same task as the restore — a handler that opens
2127
+ // asynchronously (debounce, framework effect) can still reopen the modal on close. Inversely, ANY open
2128
+ // in that task is dropped, including a programmatic close_modal_(); open_modal_() sequence when no
2129
+ // closing animation is registered — a focus-handler open is indistinguishable from one here.
2130
+ return;
2122
2131
  // Below await is very intentional,
2123
2132
  // The reason is that effects don't run in <Suspense> boundaries before they've resolved and this carries even through roots created, but if a modal is opened from a suspense boundary we still want the effects in it to run
2124
2133
  // This is because the suspense is for the content in the page and not the modal (the modal goes above it)
@@ -2127,6 +2136,11 @@ async function $d5f1e6a0f9117286$export$79b1565500412d9b(search_modal, modal_fn_
2127
2136
  if (await dispose_and_remove)
2128
2137
  // modal is already open
2129
2138
  return;
2139
+ // When the modal is mounted into a shadow root, document.activeElement is the shadow host, not the real
2140
+ // focused element — focus capture/restore (and the pre-existing contains()/blur() below) are best-effort
2141
+ // there and fail closed
2142
+ const opening_active_element = document.activeElement;
2143
+ const element_focused_before_open = opening_active_element instanceof HTMLElement && opening_active_element !== document.body && opening_active_element !== document.documentElement ? opening_active_element : undefined;
2130
2144
  const modal_els = $b8hua$createRoot(dispose => {
2131
2145
  let closing_animation;
2132
2146
  let animation_started;
@@ -2137,6 +2151,11 @@ async function $d5f1e6a0f9117286$export$79b1565500412d9b(search_modal, modal_fn_
2137
2151
  const {
2138
2152
  activeElement: activeElement
2139
2153
  } = document;
2154
+ // Don't steal focus if the user has already focused something outside the modal by the time it closes
2155
+ // (for example when the modal closes due to navigating away while they're typing elsewhere).
2156
+ // !dispose_and_remove: if the modal was reopened during our closing animation, don't steal focus from
2157
+ // the new instance — its input focuses asynchronously, so activeElement can still be body here
2158
+ const should_restore_focus = (!activeElement || activeElement === document.body || activeElement === document.documentElement || modal_els.some(el => el.contains(activeElement))) && !dispose_and_remove;
2140
2159
  modal_els.forEach(el => {
2141
2160
  if (el.contains(activeElement)) {
2142
2161
  var _blur, _ref;
@@ -2145,6 +2164,18 @@ async function $d5f1e6a0f9117286$export$79b1565500412d9b(search_modal, modal_fn_
2145
2164
  }
2146
2165
  el.remove();
2147
2166
  });
2167
+ if (should_restore_focus && element_focused_before_open !== null && element_focused_before_open !== void 0 && element_focused_before_open.isConnected) {
2168
+ restoring_focus = true;
2169
+ try {
2170
+ // preventScroll so this can't re-introduce the safari page-jump the blur() above works around
2171
+ element_focused_before_open.focus({
2172
+ preventScroll: true
2173
+ });
2174
+ } finally {
2175
+ // Cleared on a task instead of synchronously in case a browser delivers the focus event async
2176
+ setTimeout(() => restoring_focus = false, 0);
2177
+ }
2178
+ }
2148
2179
  };
2149
2180
  if (animation_started && +new Date() - animation_started > 1000)
2150
2181
  // Animation broke, close it this time
@@ -8364,6 +8395,9 @@ const $9a693c7e832d3f18$export$b05c0ae390cf3e06 = _ref => {
8364
8395
  get src_() {
8365
8396
  var _variant$image_urls2;
8366
8397
  return ((_variant$image_urls2 = variant.image_urls) === null || _variant$image_urls2 === void 0 ? void 0 : _variant$image_urls2[0]) || variant.image_url || variant.display_image_url;
8398
+ },
8399
+ get alt_() {
8400
+ return variant.title;
8367
8401
  }
8368
8402
  })), $b8hua$escape($b8hua$createComponent((0, $b8hua$Show), {
8369
8403
  get when() {
@@ -8372,7 +8406,8 @@ const $9a693c7e832d3f18$export$b05c0ae390cf3e06 = _ref => {
8372
8406
  get children() {
8373
8407
  return $b8hua$createComponent(InstantCardImageComponent_, {
8374
8408
  src_: secondary_image_url,
8375
- class_: "secondary"
8409
+ class_: "secondary",
8410
+ alt_: ""
8376
8411
  });
8377
8412
  }
8378
8413
  })), selected_index_ ? $b8hua$escape($b8hua$createComponent(TitleBrandTagline, {})) : $b8hua$ssr($9a693c7e832d3f18$var$_tmpl$7, $b8hua$ssrHydrationKey(), $b8hua$escape($b8hua$createComponent(TitleBrandTagline, {}))), $b8hua$escape($b8hua$createComponent((0, $b8hua$Show), {
@@ -9540,17 +9575,20 @@ function $40411f2ec772c376$var$RawClassicSearchModal(_ref) {
9540
9575
  market_: market_,
9541
9576
  router_: router_,
9542
9577
  set_modal_search_results_: set_modal_search_results_,
9543
- InstantCardImageComponent_: props => $b8hua$createMemo(() => $b8hua$ContainedImage({
9544
- loading: "lazy",
9545
- aspect_ratio: 1,
9546
- alt: "",
9547
- rendering_options: {},
9548
- "src": props.src_,
9549
- srcset_opts: {
9550
- set_dataset: false
9551
- },
9552
- sizes: "0px"
9553
- })),
9578
+ InstantCardImageComponent_: props => $b8hua$createMemo(() => {
9579
+ var _props$alt_;
9580
+ return $b8hua$ContainedImage({
9581
+ loading: "lazy",
9582
+ aspect_ratio: 1,
9583
+ alt: (_props$alt_ = props.alt_) !== null && _props$alt_ !== void 0 ? _props$alt_ : "",
9584
+ rendering_options: {},
9585
+ "src": props.src_,
9586
+ srcset_opts: {
9587
+ set_dataset: false
9588
+ },
9589
+ sizes: "0px"
9590
+ });
9591
+ }),
9554
9592
  ...instant_results_options_
9555
9593
  });
9556
9594
  }
@@ -10264,8 +10302,11 @@ function $06fcd2a345efd24d$var$RawSearchModalV2(_ref) {
10264
10302
  get ["class"]() {
10265
10303
  return props.class_;
10266
10304
  },
10267
- imgProps: {
10268
- alt: "Product image"
10305
+ get imgProps() {
10306
+ var _props$alt_;
10307
+ return {
10308
+ alt: (_props$alt_ = props.alt_) !== null && _props$alt_ !== void 0 ? _props$alt_ : ""
10309
+ };
10269
10310
  },
10270
10311
  imageResizer_: imageResizer_
10271
10312
  }),
@@ -109,7 +109,7 @@ $parcel$export(module.exports, "BreadCrumbs", () => $1f4925833f07df29$export$5b7
109
109
  $parcel$export(module.exports, "QuickLinks", () => $1f4925833f07df29$export$de035477d6fef996);
110
110
  $parcel$export(module.exports, "CategoryPage", () => $14f289047713d67a$export$e2bf8ce7779e827e);
111
111
  /*! Depict.ai search and product listing SDK */
112
- const $b757f75811f3261f$export$83d89fbfd8236492 = "4.10.15";
112
+ const $b757f75811f3261f$export$83d89fbfd8236492 = "4.10.17-0";
113
113
  function $b54e6192d0caa293$export$d9f36cf74e24d595(media_query) {
114
114
  const signal = $5vyXB$solidjs.createSignal(false);
115
115
  $5vyXB$solidjs.onCleanup($5vyXB$depictaiutilishared.javascript_media_query(media_query, ({
@@ -2125,7 +2125,16 @@ function $235a2771a44e28e9$export$fecbb157621501bd({
2125
2125
  async function $9abb0a35d4b60ed3$export$79b1565500412d9b(search_modal, modal_fn_opts, parent) {
2126
2126
  const body = parent || document.body || (await (0, $5vyXB$depictaiutilishared.observer).wait_for_element("body"));
2127
2127
  let dispose_and_remove;
2128
+ let restoring_focus = false;
2128
2129
  const open_modal_ = $5vyXB$depictaiutilishared.catchify(async (extra_options_for_modal = {}, on_dispose) => {
2130
+ if (restoring_focus)
2131
+ // Ignore opens triggered by our own focus restore below (a merchant can open the modal on the trigger's
2132
+ // `focus` event, and without this closing the modal would instantly reopen it, trapping keyboard users).
2133
+ // Only covers handlers that open within the same task as the restore — a handler that opens
2134
+ // asynchronously (debounce, framework effect) can still reopen the modal on close. Inversely, ANY open
2135
+ // in that task is dropped, including a programmatic close_modal_(); open_modal_() sequence when no
2136
+ // closing animation is registered — a focus-handler open is indistinguishable from one here.
2137
+ return;
2129
2138
  // Below await is very intentional,
2130
2139
  // The reason is that effects don't run in <Suspense> boundaries before they've resolved and this carries even through roots created, but if a modal is opened from a suspense boundary we still want the effects in it to run
2131
2140
  // This is because the suspense is for the content in the page and not the modal (the modal goes above it)
@@ -2134,6 +2143,11 @@ async function $9abb0a35d4b60ed3$export$79b1565500412d9b(search_modal, modal_fn_
2134
2143
  if (await dispose_and_remove)
2135
2144
  // modal is already open
2136
2145
  return;
2146
+ // When the modal is mounted into a shadow root, document.activeElement is the shadow host, not the real
2147
+ // focused element — focus capture/restore (and the pre-existing contains()/blur() below) are best-effort
2148
+ // there and fail closed
2149
+ const opening_active_element = document.activeElement;
2150
+ const element_focused_before_open = opening_active_element instanceof HTMLElement && opening_active_element !== document.body && opening_active_element !== document.documentElement ? opening_active_element : undefined;
2137
2151
  const modal_els = $5vyXB$solidjs.createRoot(dispose => {
2138
2152
  let closing_animation;
2139
2153
  let animation_started;
@@ -2144,12 +2158,29 @@ async function $9abb0a35d4b60ed3$export$79b1565500412d9b(search_modal, modal_fn_
2144
2158
  const {
2145
2159
  activeElement: activeElement
2146
2160
  } = document;
2161
+ // Don't steal focus if the user has already focused something outside the modal by the time it closes
2162
+ // (for example when the modal closes due to navigating away while they're typing elsewhere).
2163
+ // !dispose_and_remove: if the modal was reopened during our closing animation, don't steal focus from
2164
+ // the new instance — its input focuses asynchronously, so activeElement can still be body here
2165
+ const should_restore_focus = (!activeElement || activeElement === document.body || activeElement === document.documentElement || modal_els.some(el => el.contains(activeElement))) && !dispose_and_remove;
2147
2166
  modal_els.forEach(el => {
2148
2167
  if (el.contains(activeElement))
2149
2168
  // Fix page jumping to the bottom when closing modal with scape after animation in safari
2150
2169
  activeElement?.blur?.();
2151
2170
  el.remove();
2152
2171
  });
2172
+ if (should_restore_focus && element_focused_before_open?.isConnected) {
2173
+ restoring_focus = true;
2174
+ try {
2175
+ // preventScroll so this can't re-introduce the safari page-jump the blur() above works around
2176
+ element_focused_before_open.focus({
2177
+ preventScroll: true
2178
+ });
2179
+ } finally {
2180
+ // Cleared on a task instead of synchronously in case a browser delivers the focus event async
2181
+ setTimeout(() => restoring_focus = false, 0);
2182
+ }
2183
+ }
2153
2184
  };
2154
2185
  if (animation_started && +new Date() - animation_started > 1000)
2155
2186
  // Animation broke, close it this time
@@ -7976,6 +8007,9 @@ const $6ae613870d3380fc$export$b05c0ae390cf3e06 = ({
7976
8007
  return $5vyXB$solidjsweb.ssr($6ae613870d3380fc$var$_tmpl$6, $5vyXB$solidjsweb.ssrHydrationKey(), `instant-card ${isSelected?.() ? "selected" : ""}`, $5vyXB$solidjsweb.ssrAttribute("href", $5vyXB$solidjsweb.escape(href(), true), false) + $5vyXB$solidjsweb.ssrAttribute("data-search-result-id", $5vyXB$solidjsweb.escape(display_.search_result_id, true), false) + $5vyXB$solidjsweb.ssrAttribute("aria-selected", $5vyXB$solidjsweb.escape(isSelected?.(), true), false), $5vyXB$solidjsweb.escape($5vyXB$solidjsweb.createComponent(InstantCardImageComponent_, {
7977
8008
  get src_() {
7978
8009
  return variant.image_urls?.[0] || variant.image_url || variant.display_image_url;
8010
+ },
8011
+ get alt_() {
8012
+ return variant.title;
7979
8013
  }
7980
8014
  })), $5vyXB$solidjsweb.escape($5vyXB$solidjsweb.createComponent((0, $5vyXB$solidjs.Show), {
7981
8015
  get when() {
@@ -7984,7 +8018,8 @@ const $6ae613870d3380fc$export$b05c0ae390cf3e06 = ({
7984
8018
  get children() {
7985
8019
  return $5vyXB$solidjsweb.createComponent(InstantCardImageComponent_, {
7986
8020
  src_: secondary_image_url,
7987
- class_: "secondary"
8021
+ class_: "secondary",
8022
+ alt_: ""
7988
8023
  });
7989
8024
  }
7990
8025
  })), selected_index_ ? $5vyXB$solidjsweb.escape($5vyXB$solidjsweb.createComponent(TitleBrandTagline, {})) : $5vyXB$solidjsweb.ssr($6ae613870d3380fc$var$_tmpl$7, $5vyXB$solidjsweb.ssrHydrationKey(), $5vyXB$solidjsweb.escape($5vyXB$solidjsweb.createComponent(TitleBrandTagline, {}))), $5vyXB$solidjsweb.escape($5vyXB$solidjsweb.createComponent((0, $5vyXB$solidjs.Show), {
@@ -9095,7 +9130,7 @@ function $44f0b2e312a9b710$var$RawClassicSearchModal({
9095
9130
  InstantCardImageComponent_: props => $5vyXB$solidjs.createMemo(() => $5vyXB$depictaiutilishared.ContainedImage({
9096
9131
  loading: "lazy",
9097
9132
  aspect_ratio: 1,
9098
- alt: "",
9133
+ alt: props.alt_ ?? "",
9099
9134
  rendering_options: {},
9100
9135
  "src": props.src_,
9101
9136
  srcset_opts: {
@@ -9782,8 +9817,10 @@ function $11597ae03e703a47$var$RawSearchModalV2({
9782
9817
  get ["class"]() {
9783
9818
  return props.class_;
9784
9819
  },
9785
- imgProps: {
9786
- alt: "Product image"
9820
+ get imgProps() {
9821
+ return {
9822
+ alt: props.alt_ ?? ""
9823
+ };
9787
9824
  },
9788
9825
  imageResizer_: imageResizer_
9789
9826
  }),
@@ -11,7 +11,7 @@ import { GetListingRequest } from "@depict-ai/types/api/GetListingRequest";
11
11
  import { BaseSearchRequestV3 } from "@depict-ai/types/api/BaseSearchRequestV3";
12
12
  import { FeaturedInDisplay } from "@depict-ai/types/api/FeaturedInResponseV3";
13
13
  import { SearchFilter as _SearchFilter3 } from "@depict-ai/types/api/SearchRequestV2";
14
- export const version = "4.10.15";
14
+ export const version = "4.10.17-0";
15
15
  /**
16
16
  * Use `javascript_media_query` but return a reactive accessor instead of taking a callback function
17
17
  */
@@ -7,7 +7,7 @@ import { createStore as $i7OB2$createStore, reconcile as $i7OB2$reconcile } from
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.10.15";
10
+ const $ec291e0010116877$export$83d89fbfd8236492 = "4.10.17-0";
11
11
  function $dc9262ef6f786812$export$d9f36cf74e24d595(media_query) {
12
12
  const signal = $i7OB2$createSignal(false);
13
13
  $i7OB2$onCleanup($i7OB2$javascript_media_query(media_query, ({
@@ -2023,7 +2023,16 @@ function $1adfe60f3a14a1d7$export$fecbb157621501bd({
2023
2023
  async function $194bd6b8e4c59830$export$79b1565500412d9b(search_modal, modal_fn_opts, parent) {
2024
2024
  const body = parent || document.body || (await (0, $i7OB2$observer).wait_for_element("body"));
2025
2025
  let dispose_and_remove;
2026
+ let restoring_focus = false;
2026
2027
  const open_modal_ = $i7OB2$catchify1(async (extra_options_for_modal = {}, on_dispose) => {
2028
+ if (restoring_focus)
2029
+ // Ignore opens triggered by our own focus restore below (a merchant can open the modal on the trigger's
2030
+ // `focus` event, and without this closing the modal would instantly reopen it, trapping keyboard users).
2031
+ // Only covers handlers that open within the same task as the restore — a handler that opens
2032
+ // asynchronously (debounce, framework effect) can still reopen the modal on close. Inversely, ANY open
2033
+ // in that task is dropped, including a programmatic close_modal_(); open_modal_() sequence when no
2034
+ // closing animation is registered — a focus-handler open is indistinguishable from one here.
2035
+ return;
2027
2036
  // Below await is very intentional,
2028
2037
  // The reason is that effects don't run in <Suspense> boundaries before they've resolved and this carries even through roots created, but if a modal is opened from a suspense boundary we still want the effects in it to run
2029
2038
  // This is because the suspense is for the content in the page and not the modal (the modal goes above it)
@@ -2032,6 +2041,11 @@ async function $194bd6b8e4c59830$export$79b1565500412d9b(search_modal, modal_fn_
2032
2041
  if (await dispose_and_remove)
2033
2042
  // modal is already open
2034
2043
  return;
2044
+ // When the modal is mounted into a shadow root, document.activeElement is the shadow host, not the real
2045
+ // focused element — focus capture/restore (and the pre-existing contains()/blur() below) are best-effort
2046
+ // there and fail closed
2047
+ const opening_active_element = document.activeElement;
2048
+ const element_focused_before_open = opening_active_element instanceof HTMLElement && opening_active_element !== document.body && opening_active_element !== document.documentElement ? opening_active_element : undefined;
2035
2049
  const modal_els = $i7OB2$createRoot(dispose => {
2036
2050
  let closing_animation;
2037
2051
  let animation_started;
@@ -2042,12 +2056,29 @@ async function $194bd6b8e4c59830$export$79b1565500412d9b(search_modal, modal_fn_
2042
2056
  const {
2043
2057
  activeElement: activeElement
2044
2058
  } = document;
2059
+ // Don't steal focus if the user has already focused something outside the modal by the time it closes
2060
+ // (for example when the modal closes due to navigating away while they're typing elsewhere).
2061
+ // !dispose_and_remove: if the modal was reopened during our closing animation, don't steal focus from
2062
+ // the new instance — its input focuses asynchronously, so activeElement can still be body here
2063
+ const should_restore_focus = (!activeElement || activeElement === document.body || activeElement === document.documentElement || modal_els.some(el => el.contains(activeElement))) && !dispose_and_remove;
2045
2064
  modal_els.forEach(el => {
2046
2065
  if (el.contains(activeElement))
2047
2066
  // Fix page jumping to the bottom when closing modal with scape after animation in safari
2048
2067
  activeElement?.blur?.();
2049
2068
  el.remove();
2050
2069
  });
2070
+ if (should_restore_focus && element_focused_before_open?.isConnected) {
2071
+ restoring_focus = true;
2072
+ try {
2073
+ // preventScroll so this can't re-introduce the safari page-jump the blur() above works around
2074
+ element_focused_before_open.focus({
2075
+ preventScroll: true
2076
+ });
2077
+ } finally {
2078
+ // Cleared on a task instead of synchronously in case a browser delivers the focus event async
2079
+ setTimeout(() => restoring_focus = false, 0);
2080
+ }
2081
+ }
2051
2082
  };
2052
2083
  if (animation_started && +new Date() - animation_started > 1000)
2053
2084
  // Animation broke, close it this time
@@ -7874,6 +7905,9 @@ const $e003be2a50d9e5bc$export$b05c0ae390cf3e06 = ({
7874
7905
  return $i7OB2$ssr($e003be2a50d9e5bc$var$_tmpl$6, $i7OB2$ssrHydrationKey(), `instant-card ${isSelected?.() ? "selected" : ""}`, $i7OB2$ssrAttribute("href", $i7OB2$escape(href(), true), false) + $i7OB2$ssrAttribute("data-search-result-id", $i7OB2$escape(display_.search_result_id, true), false) + $i7OB2$ssrAttribute("aria-selected", $i7OB2$escape(isSelected?.(), true), false), $i7OB2$escape($i7OB2$createComponent(InstantCardImageComponent_, {
7875
7906
  get src_() {
7876
7907
  return variant.image_urls?.[0] || variant.image_url || variant.display_image_url;
7908
+ },
7909
+ get alt_() {
7910
+ return variant.title;
7877
7911
  }
7878
7912
  })), $i7OB2$escape($i7OB2$createComponent((0, $i7OB2$Show), {
7879
7913
  get when() {
@@ -7882,7 +7916,8 @@ const $e003be2a50d9e5bc$export$b05c0ae390cf3e06 = ({
7882
7916
  get children() {
7883
7917
  return $i7OB2$createComponent(InstantCardImageComponent_, {
7884
7918
  src_: secondary_image_url,
7885
- class_: "secondary"
7919
+ class_: "secondary",
7920
+ alt_: ""
7886
7921
  });
7887
7922
  }
7888
7923
  })), selected_index_ ? $i7OB2$escape($i7OB2$createComponent(TitleBrandTagline, {})) : $i7OB2$ssr($e003be2a50d9e5bc$var$_tmpl$7, $i7OB2$ssrHydrationKey(), $i7OB2$escape($i7OB2$createComponent(TitleBrandTagline, {}))), $i7OB2$escape($i7OB2$createComponent((0, $i7OB2$Show), {
@@ -8993,7 +9028,7 @@ function $42912431795f188e$var$RawClassicSearchModal({
8993
9028
  InstantCardImageComponent_: props => $i7OB2$createMemo(() => $i7OB2$ContainedImage({
8994
9029
  loading: "lazy",
8995
9030
  aspect_ratio: 1,
8996
- alt: "",
9031
+ alt: props.alt_ ?? "",
8997
9032
  rendering_options: {},
8998
9033
  "src": props.src_,
8999
9034
  srcset_opts: {
@@ -9680,8 +9715,10 @@ function $500a16dd18ee5a71$var$RawSearchModalV2({
9680
9715
  get ["class"]() {
9681
9716
  return props.class_;
9682
9717
  },
9683
- imgProps: {
9684
- alt: "Product image"
9718
+ get imgProps() {
9719
+ return {
9720
+ alt: props.alt_ ?? ""
9721
+ };
9685
9722
  },
9686
9723
  imageResizer_: imageResizer_
9687
9724
  }),