@depict-ai/ui 4.10.20 → 4.10.21

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.20";
118
+ const $aa1938dec50d6c12$export$83d89fbfd8236492 = "4.10.21";
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 => {
@@ -2678,6 +2678,63 @@ function $c5842bdb10e57229$export$fecbb157621501bd(_ref) {
2678
2678
  return outmost_element;
2679
2679
  }
2680
2680
  $52FdA$solidjsweb.delegateEvents(["click"]);
2681
+
2682
+ /**
2683
+ * `HTMLElement.focus()` is a **silent no-op** on an element that is not being rendered
2684
+ * (`display: none`, `visibility: hidden`, a collapsed menu). Nothing throws, focus simply
2685
+ * stays where it was, so a caller that does not check believes it restored focus when it
2686
+ * left it on `<body>` — which puts the keyboard user back at the top of the document.
2687
+ *
2688
+ * That is reachable whenever the element which opened a modal stops being rendered while the
2689
+ * modal is open, and the common way for that to happen is the responsive-duplicate header:
2690
+ * a theme renders the same search trigger twice, once for desktop and once for mobile, and
2691
+ * hides one at the current breakpoint. Open the modal from the desktop trigger, cross the
2692
+ * breakpoint (rotate a tablet, resize a window), close the modal, and the remembered trigger
2693
+ * is still `isConnected` but no longer rendered. Verified against a live merchant storefront
2694
+ * on 4.10.19.
2695
+ *
2696
+ * So: verify that focus landed, and when it did not, look for a rendered twin of the same
2697
+ * control — the other half of the responsive pair — and use that instead.
2698
+ */ /**
2699
+ * An element can only take focus if it is actually being rendered.
2700
+ *
2701
+ * Injectable because it is the one part of this module that needs a layout engine: jsdom
2702
+ * reports no client rects for anything, so the specs pass their own predicate to exercise the
2703
+ * matching rules. The real predicate is only exercised in a browser.
2704
+ */
2705
+ const $1573c1a34371380a$export$e0652711c614c755 = element => !!element.getClientRects().length;
2706
+ /**
2707
+ * `document.activeElement` is the outermost shadow host when focus is inside a shadow tree,
2708
+ * so ask the element's own root rather than the document — matching how the rest of
2709
+ * modal_opener treats shadow-mounted modals.
2710
+ */
2711
+ const $1573c1a34371380a$var$has_focus = element => element.getRootNode().activeElement === element;
2712
+ /**
2713
+ * The accessible-name-ish identity we match twins on. Deliberately narrow: an explicit label
2714
+ * or the trimmed text. Two controls that share a tag and this string are the same control
2715
+ * rendered twice, which is exactly the responsive-duplicate case.
2716
+ */
2717
+ const $1573c1a34371380a$var$identity_of = element => (element.getAttribute("aria-label") || element.getAttribute("title") || element.textContent || "").trim().replace(/\s+/g, " ");
2718
+ function $1573c1a34371380a$export$5800d9d535a06e96(element) {
2719
+ let rendered = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : $1573c1a34371380a$export$e0652711c614c755;
2720
+ const identity = $1573c1a34371380a$var$identity_of(element);
2721
+ if (!identity) return; // nothing to match on — an icon-only button with no label
2722
+ const twins = [...element.ownerDocument.querySelectorAll(element.localName)].filter(candidate => candidate !== element && rendered(candidate) && $1573c1a34371380a$var$identity_of(candidate) === identity);
2723
+ return twins.length === 1 ? twins[0] : undefined;
2724
+ }
2725
+ function $1573c1a34371380a$export$51461d633c351649(element) {
2726
+ let rendered = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : $1573c1a34371380a$export$e0652711c614c755;
2727
+ element.focus({
2728
+ preventScroll: true
2729
+ });
2730
+ if ($1573c1a34371380a$var$has_focus(element)) return true;
2731
+ const twin = $1573c1a34371380a$export$5800d9d535a06e96(element, rendered);
2732
+ if (!twin) return false;
2733
+ twin.focus({
2734
+ preventScroll: true
2735
+ });
2736
+ return $1573c1a34371380a$var$has_focus(twin);
2737
+ }
2681
2738
  async function $4cf7d9829d60d844$export$79b1565500412d9b(search_modal, modal_fn_opts, parent) {
2682
2739
  const body = parent || document.body || (await (0, $52FdA$depictaiutilishared.observer).wait_for_element("body"));
2683
2740
  let dispose_and_remove;
@@ -2743,9 +2800,12 @@ async function $4cf7d9829d60d844$export$79b1565500412d9b(search_modal, modal_fn_
2743
2800
  });
2744
2801
  focused = requested_focus_target.getRootNode().activeElement === requested_focus_target;
2745
2802
  }
2746
- if (!focused && element_focused_before_open !== null && element_focused_before_open !== void 0 && element_focused_before_open.isConnected) element_focused_before_open.focus({
2747
- preventScroll: true
2748
- });
2803
+ if (!focused && element_focused_before_open !== null && element_focused_before_open !== void 0 && element_focused_before_open.isConnected)
2804
+ // restore_focus_to rather than a bare focus(): the remembered trigger can be
2805
+ // connected but no longer rendered (a responsive-duplicate header whose other
2806
+ // half is showing now), and focus() on an unrendered element silently does
2807
+ // nothing, leaving the keyboard user at the top of the page.
2808
+ $1573c1a34371380a$export$51461d633c351649(element_focused_before_open);
2749
2809
  } finally {
2750
2810
  // Cleared on a task instead of synchronously in case a browser delivers the focus event async
2751
2811
  setTimeout(() => restoring_focus = false, 0);
@@ -9788,20 +9848,28 @@ function $73014ff36186a4dd$var$_classPrivateMethodGet(receiver, privateSet, fn)
9788
9848
  }
9789
9849
  var $73014ff36186a4dd$var$_throwing_on_navigation = /*#__PURE__*/new WeakSet();
9790
9850
  var $73014ff36186a4dd$var$_get_navigation_happened_promise = /*#__PURE__*/new WeakSet();
9851
+ var $73014ff36186a4dd$var$_watch_for_navigation_to_same_href = /*#__PURE__*/new WeakSet();
9791
9852
  class $73014ff36186a4dd$export$7e1297a3a19f10d8 {
9792
9853
  /**
9793
9854
  * Function that will be called when we want to navigate, or a string saying it's not an SPA. You can change this OTG
9794
9855
  */ /**
9795
9856
  * We store listing_id in history.state and do operate on this both here and in js-ui, this is the key to be used which js-ui provides
9796
9857
  */ /**
9797
- * Vue has async router that needs to be awaited (it navigates after a microtask)
9798
- * This standardises waiting for all kinds of routers
9799
- */
9858
+ * Called when a go_to_ didn't change location.href, for example when following a link to the page one is already on. href_change_ipns stays silent then, so whoever waits for a href change to react to a navigation has to listen here too
9859
+ */ /**
9860
+ * Vue has async router that needs to be awaited (it navigates after a microtask)
9861
+ * This standardises waiting for all kinds of routers
9862
+ */ /**
9863
+ * Calls navigation_to_same_href_listeners_ if the navigation we're about to request ends on the href we're already on
9864
+ * @returns function to stop watching, call it when the navigation failed
9865
+ */
9800
9866
  constructor(on_navigation, listing_query_state_key) {
9867
+ $73014ff36186a4dd$var$_classPrivateMethodInitSpec(this, $73014ff36186a4dd$var$_watch_for_navigation_to_same_href);
9801
9868
  $73014ff36186a4dd$var$_classPrivateMethodInitSpec(this, $73014ff36186a4dd$var$_get_navigation_happened_promise);
9802
9869
  $73014ff36186a4dd$var$_classPrivateMethodInitSpec(this, $73014ff36186a4dd$var$_throwing_on_navigation);
9803
9870
  $parcel$interopDefault($52FdA$babelruntimehelpersdefineProperty)(this, "on_navigation_", void 0);
9804
9871
  $parcel$interopDefault($52FdA$babelruntimehelpersdefineProperty)(this, "listing_query_state_key", void 0);
9872
+ $parcel$interopDefault($52FdA$babelruntimehelpersdefineProperty)(this, "navigation_to_same_href_listeners_", new Set());
9805
9873
  $parcel$interopDefault($52FdA$babelruntimehelpersdefineProperty)(this, "navigate_", {
9806
9874
  /**
9807
9875
  * Call this if you *just* want to replace the state object in history.state
@@ -9843,6 +9911,7 @@ class $73014ff36186a4dd$export$7e1297a3a19f10d8 {
9843
9911
  }
9844
9912
  event_ === null || event_ === void 0 || event_.preventDefault();
9845
9913
  const promise = $73014ff36186a4dd$var$_classPrivateMethodGet(this, $73014ff36186a4dd$var$_get_navigation_happened_promise, $73014ff36186a4dd$var$_get_navigation_happened_promise2).call(this, true);
9914
+ const stop_watching_for_same_href = $73014ff36186a4dd$var$_classPrivateMethodGet(this, $73014ff36186a4dd$var$_watch_for_navigation_to_same_href, $73014ff36186a4dd$var$_watch_for_navigation_to_same_href2).call(this, new_url_);
9846
9915
  if (force_spa_navigation_ && wants_hard_navigation) {
9847
9916
  // Don't allow hard navigation if we're forcing SPA navigation, which we do for things where PageReplacer can be used for performance
9848
9917
  const new_state = history.state;
@@ -9856,7 +9925,7 @@ class $73014ff36186a4dd$export$7e1297a3a19f10d8 {
9856
9925
  is_replace: is_replace_,
9857
9926
  new_url: new_url_ instanceof URL ? new_url_ : new URL(new_url_, location.href),
9858
9927
  scroll: scroll
9859
- });
9928
+ }, stop_watching_for_same_href);
9860
9929
  await promise;
9861
9930
  })
9862
9931
  });
@@ -9864,15 +9933,16 @@ class $73014ff36186a4dd$export$7e1297a3a19f10d8 {
9864
9933
  this.on_navigation_ = on_navigation;
9865
9934
  }
9866
9935
  }
9867
- function $73014ff36186a4dd$var$_throwing_on_navigation2() {
9936
+ function $73014ff36186a4dd$var$_throwing_on_navigation2(options, on_throw_) {
9868
9937
  const {
9869
9938
  on_navigation_: on_navigation_
9870
9939
  } = this;
9871
9940
  if (typeof on_navigation_ !== "function") throw new Error("on_navigation_ is not a function");
9872
9941
  // make sure that we don't accidentally catchify if the SDK consumer has fucked up
9873
9942
  try {
9874
- return on_navigation_(...arguments);
9943
+ return on_navigation_(options);
9875
9944
  } catch (e) {
9945
+ on_throw_ === null || on_throw_ === void 0 || on_throw_();
9876
9946
  queueMicrotask(() => {
9877
9947
  throw e;
9878
9948
  });
@@ -9889,6 +9959,24 @@ function $73014ff36186a4dd$var$_get_navigation_happened_promise2(is_replace) {
9889
9959
  (0, $52FdA$depictaiutilishared.instant_exec_on_suspect_history_change).add(handler);
9890
9960
  });
9891
9961
  }
9962
+ function $73014ff36186a4dd$var$_watch_for_navigation_to_same_href2(target_url) {
9963
+ const href_before = location.href;
9964
+ const stop_watching = () => {
9965
+ (0, $52FdA$depictaiutilishared.instant_exec_on_suspect_history_change).delete(on_history_change);
9966
+ clearTimeout(timeout);
9967
+ };
9968
+ const notify_if_href_unchanged = $52FdA$depictaiutilishared.catchify(() => {
9969
+ stop_watching();
9970
+ if (location.href !== href_before) return;
9971
+ this.navigation_to_same_href_listeners_.forEach(listener => $52FdA$depictaiutilishared.catchify(listener)());
9972
+ });
9973
+ // The router has acted once it touches history, if the href is still the same then it took us to the page we're on
9974
+ const on_history_change = () => notify_if_href_unchanged();
9975
+ // A router asked to go to the exact href we're on might not touch history at all, so don't wait longer than 500ms for it. For any other target we can't tell a slow navigation from one that goes nowhere, so only stop listening after a while
9976
+ const timeout = target_url.href === href_before ? setTimeout(notify_if_href_unchanged, 500) : setTimeout(stop_watching, 10000);
9977
+ (0, $52FdA$depictaiutilishared.instant_exec_on_suspect_history_change).add(on_history_change);
9978
+ return stop_watching;
9979
+ }
9892
9980
  var $2c920813c92d6987$var$_tmpl$ = /*#__PURE__*/$52FdA$solidjsweb.template(`<button class="show-more major"type=button>`),
9893
9981
  $2c920813c92d6987$var$_tmpl$2 = /*#__PURE__*/$52FdA$solidjsweb.template(`<h2 class=recs-title>`),
9894
9982
  $2c920813c92d6987$var$_tmpl$3 = /*#__PURE__*/$52FdA$solidjsweb.template(`<div class=recommendation-grid><!$><!/><!$><!/>`);
@@ -11698,11 +11786,17 @@ var $820f2920cc576f7e$var$_tmpl$ = /*#__PURE__*/$52FdA$solidjsweb.template(`<svg
11698
11786
  function $820f2920cc576f7e$export$8d8893ffd8aca39a() {
11699
11787
  return $52FdA$solidjsweb.getNextElement($820f2920cc576f7e$var$_tmpl$);
11700
11788
  }
11701
- function $4b71b4fabdee5753$export$93d9bfd0c9c1a5b8(close_modal, search_query_url_param_name_) {
11789
+ function $4b71b4fabdee5753$export$93d9bfd0c9c1a5b8(close_modal, search_query_url_param_name_, router_) {
11702
11790
  const [exit_iterable, exitable_iterable] = $52FdA$depictaiutilishared.make_asyncIterable_exiter((0, $52FdA$depictaiutilishared.href_change_ipns));
11791
+ const {
11792
+ navigation_to_same_href_listeners_: navigation_to_same_href_listeners_
11793
+ } = router_;
11703
11794
  let set_initial_values = false;
11704
11795
  let old_pathname;
11705
11796
  let old_param_value;
11797
+ // A card linking to the page one is already on doesn't change the href, close the modal anyway so the click does something
11798
+ navigation_to_same_href_listeners_.add(close_modal);
11799
+ $52FdA$solidjs.onCleanup(() => navigation_to_same_href_listeners_.delete(close_modal));
11706
11800
  $52FdA$solidjs.onCleanup($52FdA$depictaiutilishared.catchify(exit_iterable));
11707
11801
  $52FdA$depictaiutilishared.catchify(async () => {
11708
11802
  for await (const href of exitable_iterable) {
@@ -11863,7 +11957,7 @@ function $9035b24c2dbd3f36$var$RawClassicSearchModal(_ref) {
11863
11957
  return _el$;
11864
11958
  })();
11865
11959
  if (disable_scrolling_) $8c2e68c6d458169b$export$fe334901e8bebd57();
11866
- $4b71b4fabdee5753$export$93d9bfd0c9c1a5b8(close_modal_, search_query_url_param_name_);
11960
+ $4b71b4fabdee5753$export$93d9bfd0c9c1a5b8(close_modal_, search_query_url_param_name_, router_);
11867
11961
  window.addEventListener("keydown", close_on_escape_handler);
11868
11962
  $52FdA$solidjs.onCleanup($52FdA$depictaiutilishared.catchify(() => window.removeEventListener("keydown", close_on_escape_handler)));
11869
11963
  if (closing_animation_) register_closing_animation_(closing_animation_);
@@ -12784,7 +12878,7 @@ function $43600b30502feeda$var$RawSearchModalV2(_ref) {
12784
12878
  return (((_get_modal_search_res5 = get_modal_search_results_()) === null || _get_modal_search_res5 === void 0 ? void 0 : _get_modal_search_res5.loading) === false && !suggestions_response_.loading || !!prev) && totalNumberResults() === 0 && previous_searches_matching_query().length === 0;
12785
12879
  });
12786
12880
  if (disable_scrolling_) $8c2e68c6d458169b$export$fe334901e8bebd57();
12787
- $4b71b4fabdee5753$export$93d9bfd0c9c1a5b8(close_modal_, search_query_url_param_name_);
12881
+ $4b71b4fabdee5753$export$93d9bfd0c9c1a5b8(close_modal_, search_query_url_param_name_, router_);
12788
12882
  addEventListener("keydown", close_on_escape_handler);
12789
12883
  $52FdA$solidjs.onCleanup(() => removeEventListener("keydown", close_on_escape_handler));
12790
12884
  $52FdA$solidjs.createEffect($52FdA$solidjs.on(get_search_field_value_, () => {
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.20";
14
+ export const version = "4.10.21";
15
15
  /**
16
16
  * Use `javascript_media_query` but return a reactive accessor instead of taking a callback function
17
17
  */
@@ -485,6 +485,10 @@ export class PseudoRouter {
485
485
  * We store listing_id in history.state and do operate on this both here and in js-ui, this is the key to be used which js-ui provides
486
486
  */
487
487
  listing_query_state_key: string | undefined;
488
+ /**
489
+ * Called when a go_to_ didn't change location.href, for example when following a link to the page one is already on. href_change_ipns stays silent then, so whoever waits for a href change to react to a navigation has to listen here too
490
+ */
491
+ navigation_to_same_href_listeners_: Set<VoidFunction>;
488
492
  navigate_: {
489
493
  /**
490
494
  * Call this if you *just* want to replace the state object in history.state
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.20";
13
+ const $8db9a15135765c11$export$83d89fbfd8236492 = "4.10.21";
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 => {
@@ -2573,6 +2573,63 @@ function $4711a82d08d2021b$export$fecbb157621501bd(_ref) {
2573
2573
  return outmost_element;
2574
2574
  }
2575
2575
  $8tj43$delegateEvents(["click"]);
2576
+
2577
+ /**
2578
+ * `HTMLElement.focus()` is a **silent no-op** on an element that is not being rendered
2579
+ * (`display: none`, `visibility: hidden`, a collapsed menu). Nothing throws, focus simply
2580
+ * stays where it was, so a caller that does not check believes it restored focus when it
2581
+ * left it on `<body>` — which puts the keyboard user back at the top of the document.
2582
+ *
2583
+ * That is reachable whenever the element which opened a modal stops being rendered while the
2584
+ * modal is open, and the common way for that to happen is the responsive-duplicate header:
2585
+ * a theme renders the same search trigger twice, once for desktop and once for mobile, and
2586
+ * hides one at the current breakpoint. Open the modal from the desktop trigger, cross the
2587
+ * breakpoint (rotate a tablet, resize a window), close the modal, and the remembered trigger
2588
+ * is still `isConnected` but no longer rendered. Verified against a live merchant storefront
2589
+ * on 4.10.19.
2590
+ *
2591
+ * So: verify that focus landed, and when it did not, look for a rendered twin of the same
2592
+ * control — the other half of the responsive pair — and use that instead.
2593
+ */ /**
2594
+ * An element can only take focus if it is actually being rendered.
2595
+ *
2596
+ * Injectable because it is the one part of this module that needs a layout engine: jsdom
2597
+ * reports no client rects for anything, so the specs pass their own predicate to exercise the
2598
+ * matching rules. The real predicate is only exercised in a browser.
2599
+ */
2600
+ const $1c5106dfe72d554c$export$e0652711c614c755 = element => !!element.getClientRects().length;
2601
+ /**
2602
+ * `document.activeElement` is the outermost shadow host when focus is inside a shadow tree,
2603
+ * so ask the element's own root rather than the document — matching how the rest of
2604
+ * modal_opener treats shadow-mounted modals.
2605
+ */
2606
+ const $1c5106dfe72d554c$var$has_focus = element => element.getRootNode().activeElement === element;
2607
+ /**
2608
+ * The accessible-name-ish identity we match twins on. Deliberately narrow: an explicit label
2609
+ * or the trimmed text. Two controls that share a tag and this string are the same control
2610
+ * rendered twice, which is exactly the responsive-duplicate case.
2611
+ */
2612
+ const $1c5106dfe72d554c$var$identity_of = element => (element.getAttribute("aria-label") || element.getAttribute("title") || element.textContent || "").trim().replace(/\s+/g, " ");
2613
+ function $1c5106dfe72d554c$export$5800d9d535a06e96(element) {
2614
+ let rendered = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : $1c5106dfe72d554c$export$e0652711c614c755;
2615
+ const identity = $1c5106dfe72d554c$var$identity_of(element);
2616
+ if (!identity) return; // nothing to match on — an icon-only button with no label
2617
+ const twins = [...element.ownerDocument.querySelectorAll(element.localName)].filter(candidate => candidate !== element && rendered(candidate) && $1c5106dfe72d554c$var$identity_of(candidate) === identity);
2618
+ return twins.length === 1 ? twins[0] : undefined;
2619
+ }
2620
+ function $1c5106dfe72d554c$export$51461d633c351649(element) {
2621
+ let rendered = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : $1c5106dfe72d554c$export$e0652711c614c755;
2622
+ element.focus({
2623
+ preventScroll: true
2624
+ });
2625
+ if ($1c5106dfe72d554c$var$has_focus(element)) return true;
2626
+ const twin = $1c5106dfe72d554c$export$5800d9d535a06e96(element, rendered);
2627
+ if (!twin) return false;
2628
+ twin.focus({
2629
+ preventScroll: true
2630
+ });
2631
+ return $1c5106dfe72d554c$var$has_focus(twin);
2632
+ }
2576
2633
  async function $d5f1e6a0f9117286$export$79b1565500412d9b(search_modal, modal_fn_opts, parent) {
2577
2634
  const body = parent || document.body || (await (0, $8tj43$observer).wait_for_element("body"));
2578
2635
  let dispose_and_remove;
@@ -2638,9 +2695,12 @@ async function $d5f1e6a0f9117286$export$79b1565500412d9b(search_modal, modal_fn_
2638
2695
  });
2639
2696
  focused = requested_focus_target.getRootNode().activeElement === requested_focus_target;
2640
2697
  }
2641
- if (!focused && element_focused_before_open !== null && element_focused_before_open !== void 0 && element_focused_before_open.isConnected) element_focused_before_open.focus({
2642
- preventScroll: true
2643
- });
2698
+ if (!focused && element_focused_before_open !== null && element_focused_before_open !== void 0 && element_focused_before_open.isConnected)
2699
+ // restore_focus_to rather than a bare focus(): the remembered trigger can be
2700
+ // connected but no longer rendered (a responsive-duplicate header whose other
2701
+ // half is showing now), and focus() on an unrendered element silently does
2702
+ // nothing, leaving the keyboard user at the top of the page.
2703
+ $1c5106dfe72d554c$export$51461d633c351649(element_focused_before_open);
2644
2704
  } finally {
2645
2705
  // Cleared on a task instead of synchronously in case a browser delivers the focus event async
2646
2706
  setTimeout(() => restoring_focus = false, 0);
@@ -9683,20 +9743,28 @@ function $ac571292d606f30b$var$_classPrivateMethodGet(receiver, privateSet, fn)
9683
9743
  }
9684
9744
  var $ac571292d606f30b$var$_throwing_on_navigation = /*#__PURE__*/new WeakSet();
9685
9745
  var $ac571292d606f30b$var$_get_navigation_happened_promise = /*#__PURE__*/new WeakSet();
9746
+ var $ac571292d606f30b$var$_watch_for_navigation_to_same_href = /*#__PURE__*/new WeakSet();
9686
9747
  class $ac571292d606f30b$export$7e1297a3a19f10d8 {
9687
9748
  /**
9688
9749
  * Function that will be called when we want to navigate, or a string saying it's not an SPA. You can change this OTG
9689
9750
  */ /**
9690
9751
  * We store listing_id in history.state and do operate on this both here and in js-ui, this is the key to be used which js-ui provides
9691
9752
  */ /**
9692
- * Vue has async router that needs to be awaited (it navigates after a microtask)
9693
- * This standardises waiting for all kinds of routers
9694
- */
9753
+ * Called when a go_to_ didn't change location.href, for example when following a link to the page one is already on. href_change_ipns stays silent then, so whoever waits for a href change to react to a navigation has to listen here too
9754
+ */ /**
9755
+ * Vue has async router that needs to be awaited (it navigates after a microtask)
9756
+ * This standardises waiting for all kinds of routers
9757
+ */ /**
9758
+ * Calls navigation_to_same_href_listeners_ if the navigation we're about to request ends on the href we're already on
9759
+ * @returns function to stop watching, call it when the navigation failed
9760
+ */
9695
9761
  constructor(on_navigation, listing_query_state_key) {
9762
+ $ac571292d606f30b$var$_classPrivateMethodInitSpec(this, $ac571292d606f30b$var$_watch_for_navigation_to_same_href);
9696
9763
  $ac571292d606f30b$var$_classPrivateMethodInitSpec(this, $ac571292d606f30b$var$_get_navigation_happened_promise);
9697
9764
  $ac571292d606f30b$var$_classPrivateMethodInitSpec(this, $ac571292d606f30b$var$_throwing_on_navigation);
9698
9765
  $8tj43$babelruntimehelpersdefineProperty(this, "on_navigation_", void 0);
9699
9766
  $8tj43$babelruntimehelpersdefineProperty(this, "listing_query_state_key", void 0);
9767
+ $8tj43$babelruntimehelpersdefineProperty(this, "navigation_to_same_href_listeners_", new Set());
9700
9768
  $8tj43$babelruntimehelpersdefineProperty(this, "navigate_", {
9701
9769
  /**
9702
9770
  * Call this if you *just* want to replace the state object in history.state
@@ -9738,6 +9806,7 @@ class $ac571292d606f30b$export$7e1297a3a19f10d8 {
9738
9806
  }
9739
9807
  event_ === null || event_ === void 0 || event_.preventDefault();
9740
9808
  const promise = $ac571292d606f30b$var$_classPrivateMethodGet(this, $ac571292d606f30b$var$_get_navigation_happened_promise, $ac571292d606f30b$var$_get_navigation_happened_promise2).call(this, true);
9809
+ const stop_watching_for_same_href = $ac571292d606f30b$var$_classPrivateMethodGet(this, $ac571292d606f30b$var$_watch_for_navigation_to_same_href, $ac571292d606f30b$var$_watch_for_navigation_to_same_href2).call(this, new_url_);
9741
9810
  if (force_spa_navigation_ && wants_hard_navigation) {
9742
9811
  // Don't allow hard navigation if we're forcing SPA navigation, which we do for things where PageReplacer can be used for performance
9743
9812
  const new_state = history.state;
@@ -9751,7 +9820,7 @@ class $ac571292d606f30b$export$7e1297a3a19f10d8 {
9751
9820
  is_replace: is_replace_,
9752
9821
  new_url: new_url_ instanceof URL ? new_url_ : new URL(new_url_, location.href),
9753
9822
  scroll: scroll
9754
- });
9823
+ }, stop_watching_for_same_href);
9755
9824
  await promise;
9756
9825
  })
9757
9826
  });
@@ -9759,15 +9828,16 @@ class $ac571292d606f30b$export$7e1297a3a19f10d8 {
9759
9828
  this.on_navigation_ = on_navigation;
9760
9829
  }
9761
9830
  }
9762
- function $ac571292d606f30b$var$_throwing_on_navigation2() {
9831
+ function $ac571292d606f30b$var$_throwing_on_navigation2(options, on_throw_) {
9763
9832
  const {
9764
9833
  on_navigation_: on_navigation_
9765
9834
  } = this;
9766
9835
  if (typeof on_navigation_ !== "function") throw new Error("on_navigation_ is not a function");
9767
9836
  // make sure that we don't accidentally catchify if the SDK consumer has fucked up
9768
9837
  try {
9769
- return on_navigation_(...arguments);
9838
+ return on_navigation_(options);
9770
9839
  } catch (e) {
9840
+ on_throw_ === null || on_throw_ === void 0 || on_throw_();
9771
9841
  queueMicrotask(() => {
9772
9842
  throw e;
9773
9843
  });
@@ -9784,6 +9854,24 @@ function $ac571292d606f30b$var$_get_navigation_happened_promise2(is_replace) {
9784
9854
  (0, $8tj43$instant_exec_on_suspect_history_change).add(handler);
9785
9855
  });
9786
9856
  }
9857
+ function $ac571292d606f30b$var$_watch_for_navigation_to_same_href2(target_url) {
9858
+ const href_before = location.href;
9859
+ const stop_watching = () => {
9860
+ (0, $8tj43$instant_exec_on_suspect_history_change).delete(on_history_change);
9861
+ clearTimeout(timeout);
9862
+ };
9863
+ const notify_if_href_unchanged = $8tj43$catchify1(() => {
9864
+ stop_watching();
9865
+ if (location.href !== href_before) return;
9866
+ this.navigation_to_same_href_listeners_.forEach(listener => $8tj43$catchify1(listener)());
9867
+ });
9868
+ // The router has acted once it touches history, if the href is still the same then it took us to the page we're on
9869
+ const on_history_change = () => notify_if_href_unchanged();
9870
+ // A router asked to go to the exact href we're on might not touch history at all, so don't wait longer than 500ms for it. For any other target we can't tell a slow navigation from one that goes nowhere, so only stop listening after a while
9871
+ const timeout = target_url.href === href_before ? setTimeout(notify_if_href_unchanged, 500) : setTimeout(stop_watching, 10000);
9872
+ (0, $8tj43$instant_exec_on_suspect_history_change).add(on_history_change);
9873
+ return stop_watching;
9874
+ }
9787
9875
  var $e3efeb558d952696$var$_tmpl$ = /*#__PURE__*/$8tj43$template(`<button class="show-more major"type=button>`),
9788
9876
  $e3efeb558d952696$var$_tmpl$2 = /*#__PURE__*/$8tj43$template(`<h2 class=recs-title>`),
9789
9877
  $e3efeb558d952696$var$_tmpl$3 = /*#__PURE__*/$8tj43$template(`<div class=recommendation-grid><!$><!/><!$><!/>`);
@@ -11593,11 +11681,17 @@ var $fac58b5610eec6a9$var$_tmpl$ = /*#__PURE__*/$8tj43$template(`<svg xmlns=http
11593
11681
  function $fac58b5610eec6a9$export$8d8893ffd8aca39a() {
11594
11682
  return $8tj43$getNextElement($fac58b5610eec6a9$var$_tmpl$);
11595
11683
  }
11596
- function $a82a41ab7188eda2$export$93d9bfd0c9c1a5b8(close_modal, search_query_url_param_name_) {
11684
+ function $a82a41ab7188eda2$export$93d9bfd0c9c1a5b8(close_modal, search_query_url_param_name_, router_) {
11597
11685
  const [exit_iterable, exitable_iterable] = $8tj43$make_asyncIterable_exiter((0, $8tj43$href_change_ipns));
11686
+ const {
11687
+ navigation_to_same_href_listeners_: navigation_to_same_href_listeners_
11688
+ } = router_;
11598
11689
  let set_initial_values = false;
11599
11690
  let old_pathname;
11600
11691
  let old_param_value;
11692
+ // A card linking to the page one is already on doesn't change the href, close the modal anyway so the click does something
11693
+ navigation_to_same_href_listeners_.add(close_modal);
11694
+ $8tj43$onCleanup(() => navigation_to_same_href_listeners_.delete(close_modal));
11601
11695
  $8tj43$onCleanup($8tj43$catchify1(exit_iterable));
11602
11696
  $8tj43$catchify1(async () => {
11603
11697
  for await (const href of exitable_iterable) {
@@ -11758,7 +11852,7 @@ function $40411f2ec772c376$var$RawClassicSearchModal(_ref) {
11758
11852
  return _el$;
11759
11853
  })();
11760
11854
  if (disable_scrolling_) $e56d241d0f33d459$export$fe334901e8bebd57();
11761
- $a82a41ab7188eda2$export$93d9bfd0c9c1a5b8(close_modal_, search_query_url_param_name_);
11855
+ $a82a41ab7188eda2$export$93d9bfd0c9c1a5b8(close_modal_, search_query_url_param_name_, router_);
11762
11856
  window.addEventListener("keydown", close_on_escape_handler);
11763
11857
  $8tj43$onCleanup($8tj43$catchify1(() => window.removeEventListener("keydown", close_on_escape_handler)));
11764
11858
  if (closing_animation_) register_closing_animation_(closing_animation_);
@@ -12679,7 +12773,7 @@ function $06fcd2a345efd24d$var$RawSearchModalV2(_ref) {
12679
12773
  return (((_get_modal_search_res5 = get_modal_search_results_()) === null || _get_modal_search_res5 === void 0 ? void 0 : _get_modal_search_res5.loading) === false && !suggestions_response_.loading || !!prev) && totalNumberResults() === 0 && previous_searches_matching_query().length === 0;
12680
12774
  });
12681
12775
  if (disable_scrolling_) $e56d241d0f33d459$export$fe334901e8bebd57();
12682
- $a82a41ab7188eda2$export$93d9bfd0c9c1a5b8(close_modal_, search_query_url_param_name_);
12776
+ $a82a41ab7188eda2$export$93d9bfd0c9c1a5b8(close_modal_, search_query_url_param_name_, router_);
12683
12777
  addEventListener("keydown", close_on_escape_handler);
12684
12778
  $8tj43$onCleanup(() => removeEventListener("keydown", close_on_escape_handler));
12685
12779
  $8tj43$createEffect($8tj43$on(get_search_field_value_, () => {
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.20";
112
+ const $b757f75811f3261f$export$83d89fbfd8236492 = "4.10.21";
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, ({
@@ -2564,6 +2564,61 @@ function $235a2771a44e28e9$export$fecbb157621501bd({
2564
2564
  return outmost_element;
2565
2565
  }
2566
2566
  $gVhhF$solidjsweb.delegateEvents(["click"]);
2567
+
2568
+ /**
2569
+ * `HTMLElement.focus()` is a **silent no-op** on an element that is not being rendered
2570
+ * (`display: none`, `visibility: hidden`, a collapsed menu). Nothing throws, focus simply
2571
+ * stays where it was, so a caller that does not check believes it restored focus when it
2572
+ * left it on `<body>` — which puts the keyboard user back at the top of the document.
2573
+ *
2574
+ * That is reachable whenever the element which opened a modal stops being rendered while the
2575
+ * modal is open, and the common way for that to happen is the responsive-duplicate header:
2576
+ * a theme renders the same search trigger twice, once for desktop and once for mobile, and
2577
+ * hides one at the current breakpoint. Open the modal from the desktop trigger, cross the
2578
+ * breakpoint (rotate a tablet, resize a window), close the modal, and the remembered trigger
2579
+ * is still `isConnected` but no longer rendered. Verified against a live merchant storefront
2580
+ * on 4.10.19.
2581
+ *
2582
+ * So: verify that focus landed, and when it did not, look for a rendered twin of the same
2583
+ * control — the other half of the responsive pair — and use that instead.
2584
+ */ /**
2585
+ * An element can only take focus if it is actually being rendered.
2586
+ *
2587
+ * Injectable because it is the one part of this module that needs a layout engine: jsdom
2588
+ * reports no client rects for anything, so the specs pass their own predicate to exercise the
2589
+ * matching rules. The real predicate is only exercised in a browser.
2590
+ */
2591
+ const $7f80d1bc95bbbc60$export$e0652711c614c755 = element => !!element.getClientRects().length;
2592
+ /**
2593
+ * `document.activeElement` is the outermost shadow host when focus is inside a shadow tree,
2594
+ * so ask the element's own root rather than the document — matching how the rest of
2595
+ * modal_opener treats shadow-mounted modals.
2596
+ */
2597
+ const $7f80d1bc95bbbc60$var$has_focus = element => element.getRootNode().activeElement === element;
2598
+ /**
2599
+ * The accessible-name-ish identity we match twins on. Deliberately narrow: an explicit label
2600
+ * or the trimmed text. Two controls that share a tag and this string are the same control
2601
+ * rendered twice, which is exactly the responsive-duplicate case.
2602
+ */
2603
+ const $7f80d1bc95bbbc60$var$identity_of = element => (element.getAttribute("aria-label") || element.getAttribute("title") || element.textContent || "").trim().replace(/\s+/g, " ");
2604
+ function $7f80d1bc95bbbc60$export$5800d9d535a06e96(element, rendered = $7f80d1bc95bbbc60$export$e0652711c614c755) {
2605
+ const identity = $7f80d1bc95bbbc60$var$identity_of(element);
2606
+ if (!identity) return; // nothing to match on — an icon-only button with no label
2607
+ const twins = [...element.ownerDocument.querySelectorAll(element.localName)].filter(candidate => candidate !== element && rendered(candidate) && $7f80d1bc95bbbc60$var$identity_of(candidate) === identity);
2608
+ return twins.length === 1 ? twins[0] : undefined;
2609
+ }
2610
+ function $7f80d1bc95bbbc60$export$51461d633c351649(element, rendered = $7f80d1bc95bbbc60$export$e0652711c614c755) {
2611
+ element.focus({
2612
+ preventScroll: true
2613
+ });
2614
+ if ($7f80d1bc95bbbc60$var$has_focus(element)) return true;
2615
+ const twin = $7f80d1bc95bbbc60$export$5800d9d535a06e96(element, rendered);
2616
+ if (!twin) return false;
2617
+ twin.focus({
2618
+ preventScroll: true
2619
+ });
2620
+ return $7f80d1bc95bbbc60$var$has_focus(twin);
2621
+ }
2567
2622
  async function $9abb0a35d4b60ed3$export$79b1565500412d9b(search_modal, modal_fn_opts, parent) {
2568
2623
  const body = parent || document.body || (await (0, $gVhhF$depictaiutilishared.observer).wait_for_element("body"));
2569
2624
  let dispose_and_remove;
@@ -2624,9 +2679,12 @@ async function $9abb0a35d4b60ed3$export$79b1565500412d9b(search_modal, modal_fn_
2624
2679
  });
2625
2680
  focused = requested_focus_target.getRootNode().activeElement === requested_focus_target;
2626
2681
  }
2627
- if (!focused && element_focused_before_open?.isConnected) element_focused_before_open.focus({
2628
- preventScroll: true
2629
- });
2682
+ if (!focused && element_focused_before_open?.isConnected)
2683
+ // restore_focus_to rather than a bare focus(): the remembered trigger can be
2684
+ // connected but no longer rendered (a responsive-duplicate header whose other
2685
+ // half is showing now), and focus() on an unrendered element silently does
2686
+ // nothing, leaving the keyboard user at the top of the page.
2687
+ $7f80d1bc95bbbc60$export$51461d633c351649(element_focused_before_open);
2630
2688
  } finally {
2631
2689
  // Cleared on a task instead of synchronously in case a browser delivers the focus event async
2632
2690
  setTimeout(() => restoring_focus = false, 0);
@@ -9267,16 +9325,20 @@ class $e96dd13889871fc0$export$7e1297a3a19f10d8 {
9267
9325
  * Function that will be called when we want to navigate, or a string saying it's not an SPA. You can change this OTG
9268
9326
  */ /**
9269
9327
  * We store listing_id in history.state and do operate on this both here and in js-ui, this is the key to be used which js-ui provides
9270
- */
9271
- #throwing_on_navigation(...args) {
9328
+ */ /**
9329
+ * Called when a go_to_ didn't change location.href, for example when following a link to the page one is already on. href_change_ipns stays silent then, so whoever waits for a href change to react to a navigation has to listen here too
9330
+ */
9331
+ navigation_to_same_href_listeners_ = new Set();
9332
+ #throwing_on_navigation(options, on_throw_) {
9272
9333
  const {
9273
9334
  on_navigation_: on_navigation_
9274
9335
  } = this;
9275
9336
  if (typeof on_navigation_ !== "function") throw new Error("on_navigation_ is not a function");
9276
9337
  // make sure that we don't accidentally catchify if the SDK consumer has fucked up
9277
9338
  try {
9278
- return on_navigation_(...args);
9339
+ return on_navigation_(options);
9279
9340
  } catch (e) {
9341
+ on_throw_?.();
9280
9342
  queueMicrotask(() => {
9281
9343
  throw e;
9282
9344
  });
@@ -9297,6 +9359,28 @@ class $e96dd13889871fc0$export$7e1297a3a19f10d8 {
9297
9359
  (0, $gVhhF$depictaiutilishared.instant_exec_on_suspect_history_change).add(handler);
9298
9360
  });
9299
9361
  }
9362
+ /**
9363
+ * Calls navigation_to_same_href_listeners_ if the navigation we're about to request ends on the href we're already on
9364
+ * @returns function to stop watching, call it when the navigation failed
9365
+ */
9366
+ #watch_for_navigation_to_same_href(target_url) {
9367
+ const href_before = location.href;
9368
+ const stop_watching = () => {
9369
+ (0, $gVhhF$depictaiutilishared.instant_exec_on_suspect_history_change).delete(on_history_change);
9370
+ clearTimeout(timeout);
9371
+ };
9372
+ const notify_if_href_unchanged = $gVhhF$depictaiutilishared.catchify(() => {
9373
+ stop_watching();
9374
+ if (location.href !== href_before) return;
9375
+ this.navigation_to_same_href_listeners_.forEach(listener => $gVhhF$depictaiutilishared.catchify(listener)());
9376
+ });
9377
+ // The router has acted once it touches history, if the href is still the same then it took us to the page we're on
9378
+ const on_history_change = () => notify_if_href_unchanged();
9379
+ // A router asked to go to the exact href we're on might not touch history at all, so don't wait longer than 500ms for it. For any other target we can't tell a slow navigation from one that goes nowhere, so only stop listening after a while
9380
+ const timeout = target_url.href === href_before ? setTimeout(notify_if_href_unchanged, 500) : setTimeout(stop_watching, 10000);
9381
+ (0, $gVhhF$depictaiutilishared.instant_exec_on_suspect_history_change).add(on_history_change);
9382
+ return stop_watching;
9383
+ }
9300
9384
  navigate_ = {
9301
9385
  /**
9302
9386
  * Call this if you *just* want to replace the state object in history.state
@@ -9336,6 +9420,7 @@ class $e96dd13889871fc0$export$7e1297a3a19f10d8 {
9336
9420
  }
9337
9421
  event_?.preventDefault();
9338
9422
  const promise = this.#get_navigation_happened_promise(true);
9423
+ const stop_watching_for_same_href = this.#watch_for_navigation_to_same_href(new_url_);
9339
9424
  if (force_spa_navigation_ && wants_hard_navigation) {
9340
9425
  // Don't allow hard navigation if we're forcing SPA navigation, which we do for things where PageReplacer can be used for performance
9341
9426
  const new_state = history.state;
@@ -9349,7 +9434,7 @@ class $e96dd13889871fc0$export$7e1297a3a19f10d8 {
9349
9434
  is_replace: is_replace_,
9350
9435
  new_url: new_url_ instanceof URL ? new_url_ : new URL(new_url_, location.href),
9351
9436
  scroll: scroll
9352
- });
9437
+ }, stop_watching_for_same_href);
9353
9438
  await promise;
9354
9439
  })
9355
9440
  };
@@ -11076,11 +11161,17 @@ var $610b584d4f54699e$var$_tmpl$ = /*#__PURE__*/$gVhhF$solidjsweb.template(`<svg
11076
11161
  function $610b584d4f54699e$export$8d8893ffd8aca39a() {
11077
11162
  return $gVhhF$solidjsweb.getNextElement($610b584d4f54699e$var$_tmpl$);
11078
11163
  }
11079
- function $eb3039a27db40508$export$93d9bfd0c9c1a5b8(close_modal, search_query_url_param_name_) {
11164
+ function $eb3039a27db40508$export$93d9bfd0c9c1a5b8(close_modal, search_query_url_param_name_, router_) {
11080
11165
  const [exit_iterable, exitable_iterable] = $gVhhF$depictaiutilishared.make_asyncIterable_exiter((0, $gVhhF$depictaiutilishared.href_change_ipns));
11166
+ const {
11167
+ navigation_to_same_href_listeners_: navigation_to_same_href_listeners_
11168
+ } = router_;
11081
11169
  let set_initial_values = false;
11082
11170
  let old_pathname;
11083
11171
  let old_param_value;
11172
+ // A card linking to the page one is already on doesn't change the href, close the modal anyway so the click does something
11173
+ navigation_to_same_href_listeners_.add(close_modal);
11174
+ $gVhhF$solidjs.onCleanup(() => navigation_to_same_href_listeners_.delete(close_modal));
11084
11175
  $gVhhF$solidjs.onCleanup($gVhhF$depictaiutilishared.catchify(exit_iterable));
11085
11176
  $gVhhF$depictaiutilishared.catchify(async () => {
11086
11177
  for await (const href of exitable_iterable) {
@@ -11236,7 +11327,7 @@ function $44f0b2e312a9b710$var$RawClassicSearchModal({
11236
11327
  return _el$;
11237
11328
  })();
11238
11329
  if (disable_scrolling_) $ca1176b694a6ee2e$export$fe334901e8bebd57();
11239
- $eb3039a27db40508$export$93d9bfd0c9c1a5b8(close_modal_, search_query_url_param_name_);
11330
+ $eb3039a27db40508$export$93d9bfd0c9c1a5b8(close_modal_, search_query_url_param_name_, router_);
11240
11331
  window.addEventListener("keydown", close_on_escape_handler);
11241
11332
  $gVhhF$solidjs.onCleanup($gVhhF$depictaiutilishared.catchify(() => window.removeEventListener("keydown", close_on_escape_handler)));
11242
11333
  if (closing_animation_) register_closing_animation_(closing_animation_);
@@ -12115,7 +12206,7 @@ function $11597ae03e703a47$var$RawSearchModalV2({
12115
12206
  });
12116
12207
  const noResults = $gVhhF$solidjs.createMemo(prev => (get_modal_search_results_()?.loading === false && !suggestions_response_.loading || !!prev) && totalNumberResults() === 0 && previous_searches_matching_query().length === 0);
12117
12208
  if (disable_scrolling_) $ca1176b694a6ee2e$export$fe334901e8bebd57();
12118
- $eb3039a27db40508$export$93d9bfd0c9c1a5b8(close_modal_, search_query_url_param_name_);
12209
+ $eb3039a27db40508$export$93d9bfd0c9c1a5b8(close_modal_, search_query_url_param_name_, router_);
12119
12210
  addEventListener("keydown", close_on_escape_handler);
12120
12211
  $gVhhF$solidjs.onCleanup(() => removeEventListener("keydown", close_on_escape_handler));
12121
12212
  $gVhhF$solidjs.createEffect($gVhhF$solidjs.on(get_search_field_value_, () => {