@depict-ai/ui 4.10.19 → 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.
@@ -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.19";
112
+ const $b757f75811f3261f$export$83d89fbfd8236492 = "4.10.21";
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, ({
@@ -2122,6 +2122,61 @@ function $235a2771a44e28e9$export$fecbb157621501bd({
2122
2122
  }));
2123
2123
  return outmost_element;
2124
2124
  }
2125
+
2126
+ /**
2127
+ * `HTMLElement.focus()` is a **silent no-op** on an element that is not being rendered
2128
+ * (`display: none`, `visibility: hidden`, a collapsed menu). Nothing throws, focus simply
2129
+ * stays where it was, so a caller that does not check believes it restored focus when it
2130
+ * left it on `<body>` — which puts the keyboard user back at the top of the document.
2131
+ *
2132
+ * That is reachable whenever the element which opened a modal stops being rendered while the
2133
+ * modal is open, and the common way for that to happen is the responsive-duplicate header:
2134
+ * a theme renders the same search trigger twice, once for desktop and once for mobile, and
2135
+ * hides one at the current breakpoint. Open the modal from the desktop trigger, cross the
2136
+ * breakpoint (rotate a tablet, resize a window), close the modal, and the remembered trigger
2137
+ * is still `isConnected` but no longer rendered. Verified against a live merchant storefront
2138
+ * on 4.10.19.
2139
+ *
2140
+ * So: verify that focus landed, and when it did not, look for a rendered twin of the same
2141
+ * control — the other half of the responsive pair — and use that instead.
2142
+ */ /**
2143
+ * An element can only take focus if it is actually being rendered.
2144
+ *
2145
+ * Injectable because it is the one part of this module that needs a layout engine: jsdom
2146
+ * reports no client rects for anything, so the specs pass their own predicate to exercise the
2147
+ * matching rules. The real predicate is only exercised in a browser.
2148
+ */
2149
+ const $7f80d1bc95bbbc60$export$e0652711c614c755 = element => !!element.getClientRects().length;
2150
+ /**
2151
+ * `document.activeElement` is the outermost shadow host when focus is inside a shadow tree,
2152
+ * so ask the element's own root rather than the document — matching how the rest of
2153
+ * modal_opener treats shadow-mounted modals.
2154
+ */
2155
+ const $7f80d1bc95bbbc60$var$has_focus = element => element.getRootNode().activeElement === element;
2156
+ /**
2157
+ * The accessible-name-ish identity we match twins on. Deliberately narrow: an explicit label
2158
+ * or the trimmed text. Two controls that share a tag and this string are the same control
2159
+ * rendered twice, which is exactly the responsive-duplicate case.
2160
+ */
2161
+ const $7f80d1bc95bbbc60$var$identity_of = element => (element.getAttribute("aria-label") || element.getAttribute("title") || element.textContent || "").trim().replace(/\s+/g, " ");
2162
+ function $7f80d1bc95bbbc60$export$5800d9d535a06e96(element, rendered = $7f80d1bc95bbbc60$export$e0652711c614c755) {
2163
+ const identity = $7f80d1bc95bbbc60$var$identity_of(element);
2164
+ if (!identity) return; // nothing to match on — an icon-only button with no label
2165
+ const twins = [...element.ownerDocument.querySelectorAll(element.localName)].filter(candidate => candidate !== element && rendered(candidate) && $7f80d1bc95bbbc60$var$identity_of(candidate) === identity);
2166
+ return twins.length === 1 ? twins[0] : undefined;
2167
+ }
2168
+ function $7f80d1bc95bbbc60$export$51461d633c351649(element, rendered = $7f80d1bc95bbbc60$export$e0652711c614c755) {
2169
+ element.focus({
2170
+ preventScroll: true
2171
+ });
2172
+ if ($7f80d1bc95bbbc60$var$has_focus(element)) return true;
2173
+ const twin = $7f80d1bc95bbbc60$export$5800d9d535a06e96(element, rendered);
2174
+ if (!twin) return false;
2175
+ twin.focus({
2176
+ preventScroll: true
2177
+ });
2178
+ return $7f80d1bc95bbbc60$var$has_focus(twin);
2179
+ }
2125
2180
  async function $9abb0a35d4b60ed3$export$79b1565500412d9b(search_modal, modal_fn_opts, parent) {
2126
2181
  const body = parent || document.body || (await (0, $5vyXB$depictaiutilishared.observer).wait_for_element("body"));
2127
2182
  let dispose_and_remove;
@@ -2151,6 +2206,7 @@ async function $9abb0a35d4b60ed3$export$79b1565500412d9b(search_modal, modal_fn_
2151
2206
  const modal_els = $5vyXB$solidjs.createRoot(dispose => {
2152
2207
  let closing_animation;
2153
2208
  let animation_started;
2209
+ let focus_target_after_close;
2154
2210
  dispose_and_remove = () => {
2155
2211
  const actually_close = () => {
2156
2212
  dispose();
@@ -2169,13 +2225,24 @@ async function $9abb0a35d4b60ed3$export$79b1565500412d9b(search_modal, modal_fn_
2169
2225
  activeElement?.blur?.();
2170
2226
  el.remove();
2171
2227
  });
2172
- if (should_restore_focus && element_focused_before_open?.isConnected) {
2228
+ const requested_focus_target = focus_target_after_close?.();
2229
+ if (should_restore_focus && (requested_focus_target?.isConnected || element_focused_before_open?.isConnected)) {
2173
2230
  restoring_focus = true;
2174
2231
  try {
2175
2232
  // 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
- });
2233
+ let focused = false;
2234
+ if (requested_focus_target?.isConnected) {
2235
+ requested_focus_target.focus({
2236
+ preventScroll: true
2237
+ });
2238
+ focused = requested_focus_target.getRootNode().activeElement === requested_focus_target;
2239
+ }
2240
+ if (!focused && element_focused_before_open?.isConnected)
2241
+ // restore_focus_to rather than a bare focus(): the remembered trigger can be
2242
+ // connected but no longer rendered (a responsive-duplicate header whose other
2243
+ // half is showing now), and focus() on an unrendered element silently does
2244
+ // nothing, leaving the keyboard user at the top of the page.
2245
+ $7f80d1bc95bbbc60$export$51461d633c351649(element_focused_before_open);
2179
2246
  } finally {
2180
2247
  // Cleared on a task instead of synchronously in case a browser delivers the focus event async
2181
2248
  setTimeout(() => restoring_focus = false, 0);
@@ -2194,7 +2261,8 @@ async function $9abb0a35d4b60ed3$export$79b1565500412d9b(search_modal, modal_fn_
2194
2261
  ...extra_options_for_modal,
2195
2262
  close_modal_: close_modal_,
2196
2263
  open_modal_: open_modal_,
2197
- register_closing_animation_: animation => closing_animation = animation
2264
+ register_closing_animation_: animation => closing_animation = animation,
2265
+ set_focus_target_after_close_: get_target => focus_target_after_close = get_target
2198
2266
  });
2199
2267
  });
2200
2268
  body.append(...modal_els);
@@ -7579,16 +7647,20 @@ class $e96dd13889871fc0$export$7e1297a3a19f10d8 {
7579
7647
  * Function that will be called when we want to navigate, or a string saying it's not an SPA. You can change this OTG
7580
7648
  */ /**
7581
7649
  * 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
7582
- */
7583
- #throwing_on_navigation(...args) {
7650
+ */ /**
7651
+ * 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
7652
+ */
7653
+ navigation_to_same_href_listeners_ = new Set();
7654
+ #throwing_on_navigation(options, on_throw_) {
7584
7655
  const {
7585
7656
  on_navigation_: on_navigation_
7586
7657
  } = this;
7587
7658
  if (typeof on_navigation_ !== "function") throw new Error("on_navigation_ is not a function");
7588
7659
  // make sure that we don't accidentally catchify if the SDK consumer has fucked up
7589
7660
  try {
7590
- return on_navigation_(...args);
7661
+ return on_navigation_(options);
7591
7662
  } catch (e) {
7663
+ on_throw_?.();
7592
7664
  queueMicrotask(() => {
7593
7665
  throw e;
7594
7666
  });
@@ -7609,6 +7681,28 @@ class $e96dd13889871fc0$export$7e1297a3a19f10d8 {
7609
7681
  (0, $5vyXB$depictaiutilishared.instant_exec_on_suspect_history_change).add(handler);
7610
7682
  });
7611
7683
  }
7684
+ /**
7685
+ * Calls navigation_to_same_href_listeners_ if the navigation we're about to request ends on the href we're already on
7686
+ * @returns function to stop watching, call it when the navigation failed
7687
+ */
7688
+ #watch_for_navigation_to_same_href(target_url) {
7689
+ const href_before = location.href;
7690
+ const stop_watching = () => {
7691
+ (0, $5vyXB$depictaiutilishared.instant_exec_on_suspect_history_change).delete(on_history_change);
7692
+ clearTimeout(timeout);
7693
+ };
7694
+ const notify_if_href_unchanged = $5vyXB$depictaiutilishared.catchify(() => {
7695
+ stop_watching();
7696
+ if (location.href !== href_before) return;
7697
+ this.navigation_to_same_href_listeners_.forEach(listener => $5vyXB$depictaiutilishared.catchify(listener)());
7698
+ });
7699
+ // 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
7700
+ const on_history_change = () => notify_if_href_unchanged();
7701
+ // 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
7702
+ const timeout = target_url.href === href_before ? setTimeout(notify_if_href_unchanged, 500) : setTimeout(stop_watching, 10000);
7703
+ (0, $5vyXB$depictaiutilishared.instant_exec_on_suspect_history_change).add(on_history_change);
7704
+ return stop_watching;
7705
+ }
7612
7706
  navigate_ = {
7613
7707
  /**
7614
7708
  * Call this if you *just* want to replace the state object in history.state
@@ -7648,6 +7742,7 @@ class $e96dd13889871fc0$export$7e1297a3a19f10d8 {
7648
7742
  }
7649
7743
  event_?.preventDefault();
7650
7744
  const promise = this.#get_navigation_happened_promise(true);
7745
+ const stop_watching_for_same_href = this.#watch_for_navigation_to_same_href(new_url_);
7651
7746
  if (force_spa_navigation_ && wants_hard_navigation) {
7652
7747
  // Don't allow hard navigation if we're forcing SPA navigation, which we do for things where PageReplacer can be used for performance
7653
7748
  const new_state = history.state;
@@ -7661,7 +7756,7 @@ class $e96dd13889871fc0$export$7e1297a3a19f10d8 {
7661
7756
  is_replace: is_replace_,
7662
7757
  new_url: new_url_ instanceof URL ? new_url_ : new URL(new_url_, location.href),
7663
7758
  scroll: scroll
7664
- });
7759
+ }, stop_watching_for_same_href);
7665
7760
  await promise;
7666
7761
  })
7667
7762
  };
@@ -8840,6 +8935,7 @@ function $0175183387c8923a$export$f7658c347b037c3f(props = {}) {
8840
8935
  var $66b8b9a9613c141d$var$_tmpl$ = ["<div", ' style="', '" class="spacer"></div>'],
8841
8936
  $66b8b9a9613c141d$var$_tmpl$2 = ['<div class="field"><!--$-->', "<!--/-->", "<!--$-->", "<!--/--></div>"],
8842
8937
  $66b8b9a9613c141d$var$_tmpl$3 = ["<button", ' class="submit major" type="button"', ">", "</button>"];
8938
+ const $66b8b9a9613c141d$var$keys_not_opening_modal = new Set(["Tab", "Shift", "Control", "Alt", "AltGraph", "Meta", "CapsLock", "Escape", "ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight", "PageUp", "PageDown", "Home", "End", "Insert", "ContextMenu", "NumLock", "ScrollLock", "Pause", "PrintScreen"]);
8843
8939
  function $66b8b9a9613c141d$export$b94867ecbd698f21({
8844
8940
  search_field_value_: [searchFieldValue, setSearchFieldValue],
8845
8941
  input_field_ref_: input_field_ref_,
@@ -8958,11 +9054,17 @@ var $610b584d4f54699e$var$_tmpl$ = ["<svg", ' xmlns="http://www.w3.org/2000/svg"
8958
9054
  function $610b584d4f54699e$export$8d8893ffd8aca39a() {
8959
9055
  return $5vyXB$solidjsweb.ssr($610b584d4f54699e$var$_tmpl$, $5vyXB$solidjsweb.ssrHydrationKey());
8960
9056
  }
8961
- function $eb3039a27db40508$export$93d9bfd0c9c1a5b8(close_modal, search_query_url_param_name_) {
9057
+ function $eb3039a27db40508$export$93d9bfd0c9c1a5b8(close_modal, search_query_url_param_name_, router_) {
8962
9058
  const [exit_iterable, exitable_iterable] = $5vyXB$depictaiutilishared.make_asyncIterable_exiter((0, $5vyXB$depictaiutilishared.href_change_ipns));
9059
+ const {
9060
+ navigation_to_same_href_listeners_: navigation_to_same_href_listeners_
9061
+ } = router_;
8963
9062
  let set_initial_values = false;
8964
9063
  let old_pathname;
8965
9064
  let old_param_value;
9065
+ // A card linking to the page one is already on doesn't change the href, close the modal anyway so the click does something
9066
+ navigation_to_same_href_listeners_.add(close_modal);
9067
+ $5vyXB$solidjs.onCleanup(() => navigation_to_same_href_listeners_.delete(close_modal));
8966
9068
  $5vyXB$solidjs.onCleanup($5vyXB$depictaiutilishared.catchify(exit_iterable));
8967
9069
  $5vyXB$depictaiutilishared.catchify(async () => {
8968
9070
  for await (const href of exitable_iterable) {
@@ -8981,6 +9083,31 @@ function $eb3039a27db40508$export$93d9bfd0c9c1a5b8(close_modal, search_query_url
8981
9083
  }
8982
9084
  })();
8983
9085
  }
9086
+ function $5f9dc76a99175920$export$9e9e2ae069cf2fe1({
9087
+ props_search_field_value_: props_search_field_value_,
9088
+ props_submit_query_: props_submit_query_,
9089
+ dont_sync_search_field_value_except_on_submit_: dont_sync_search_field_value_except_on_submit_,
9090
+ set_focus_target_after_close_: set_focus_target_after_close_,
9091
+ get_search_page_input_element_: get_search_page_input_element_
9092
+ }) {
9093
+ const search_field_value_ = dont_sync_search_field_value_except_on_submit_ ? $5vyXB$solidjs.createSignal("") : props_search_field_value_;
9094
+ let submits_in_flight = 0;
9095
+ const submit_query_ = async (new_query = $5vyXB$solidjs.untrack(search_field_value_[0])) => {
9096
+ submits_in_flight++;
9097
+ try {
9098
+ return await props_submit_query_(new_query, () => set_focus_target_after_close_(get_search_page_input_element_));
9099
+ } finally {
9100
+ submits_in_flight--;
9101
+ }
9102
+ };
9103
+ if (dont_sync_search_field_value_except_on_submit_) $5vyXB$solidjs.createEffect($5vyXB$solidjs.on(search_field_value_[0], value => submits_in_flight > 0 && props_search_field_value_[1](value), {
9104
+ defer: true
9105
+ }));
9106
+ return {
9107
+ search_field_value_: search_field_value_,
9108
+ submit_query_: submit_query_
9109
+ };
9110
+ }
8984
9111
  var $44f0b2e312a9b710$var$_tmpl$ = ["<div", ' class="discover-more"><button class="major" type="button">', "</button></div>"],
8985
9112
  $44f0b2e312a9b710$var$_tmpl$2 = ["<b", ">", "</b>"],
8986
9113
  $44f0b2e312a9b710$var$_tmpl$3 = ["<div", ' class="', '"><div class="', '" role="search" aria-label="Search modal with search field, query suggestions and result preview"><!--$-->', '<!--/--><div id="depict-suggestions-and-result-preview" class="padded"><!--$-->', "<!--/--><!--$-->", "<!--/--><!--$-->", "<!--/--></div><!--$-->", "<!--/--></div><!--$-->", "<!--/--></div>"],
@@ -8994,6 +9121,8 @@ function $44f0b2e312a9b710$var$RawClassicSearchModal({
8994
9121
  search_query_url_param_name_: search_query_url_param_name_,
8995
9122
  previous_searches_: previous_searches_,
8996
9123
  close_modal_: close_modal_,
9124
+ set_focus_target_after_close_: set_focus_target_after_close_,
9125
+ get_search_page_input_element_: get_search_page_input_element_,
8997
9126
  depict_api_: depict_api_,
8998
9127
  merchant_: merchant_,
8999
9128
  market_: market_,
@@ -9018,15 +9147,16 @@ function $44f0b2e312a9b710$var$RawClassicSearchModal({
9018
9147
  // As per feedback from stronger we sometimes want a modal with the search field value disconnected from the one in the rest of the UI, see https://depictaiworkspace.slack.com/archives/C02J16R9XEH/p1679065878487709.
9019
9148
  // This is desired for when the modal is opened from a UI element that visually does not look connected with the search page or current query
9020
9149
  // Basically the UI signals that the button that opens the modal starts a new search.
9021
- const search_field_value_ = dont_sync_search_field_value_except_on_submit_ ? $5vyXB$solidjs.createSignal("") : props_search_field_value;
9022
- const propagate_search_field_value_back = dont_sync_search_field_value_except_on_submit_ ? () => props_search_field_value[1]($5vyXB$solidjs.untrack(search_field_value_[0])) // Set actual search field value to the throwaway internal one before submitting
9023
- : undefined;
9024
- const submit_query_ = (...args) => {
9025
- // submit_query expects to read the current search field value which is why we have to write the current one to the signal before calling it
9026
- // It will write the old one back and then write the new one after submit
9027
- propagate_search_field_value_back?.();
9028
- return props_submit_query(...args);
9029
- };
9150
+ const {
9151
+ search_field_value_: search_field_value_,
9152
+ submit_query_: submit_query_
9153
+ } = $5f9dc76a99175920$export$9e9e2ae069cf2fe1({
9154
+ props_search_field_value_: props_search_field_value,
9155
+ props_submit_query_: props_submit_query,
9156
+ dont_sync_search_field_value_except_on_submit_: dont_sync_search_field_value_except_on_submit_,
9157
+ set_focus_target_after_close_: set_focus_target_after_close_,
9158
+ get_search_page_input_element_: get_search_page_input_element_
9159
+ });
9030
9160
  const [input_field, set_input_field] = $5vyXB$solidjs.createSignal();
9031
9161
  const [get_search_field_value_] = search_field_value_;
9032
9162
  // Search field alignment unused in ClassicSearchModal since the search field spans the whole modal
@@ -9079,7 +9209,7 @@ function $44f0b2e312a9b710$var$RawClassicSearchModal({
9079
9209
  }
9080
9210
  })));
9081
9211
  if (disable_scrolling_) $ca1176b694a6ee2e$export$fe334901e8bebd57();
9082
- $eb3039a27db40508$export$93d9bfd0c9c1a5b8(close_modal_, search_query_url_param_name_);
9212
+ $eb3039a27db40508$export$93d9bfd0c9c1a5b8(close_modal_, search_query_url_param_name_, router_);
9083
9213
  window.addEventListener("keydown", close_on_escape_handler);
9084
9214
  $5vyXB$solidjs.onCleanup($5vyXB$depictaiutilishared.catchify(() => window.removeEventListener("keydown", close_on_escape_handler)));
9085
9215
  if (closing_animation_) register_closing_animation_(closing_animation_);
@@ -9624,6 +9754,8 @@ function $11597ae03e703a47$var$RawSearchModalV2({
9624
9754
  search_query_url_param_name_: search_query_url_param_name_,
9625
9755
  previous_searches_: previous_searches_,
9626
9756
  close_modal_: close_modal_,
9757
+ set_focus_target_after_close_: set_focus_target_after_close_,
9758
+ get_search_page_input_element_: get_search_page_input_element_,
9627
9759
  alignmentSignals_: alignmentSignals_,
9628
9760
  depict_api_: depict_api_,
9629
9761
  merchant_: merchant_,
@@ -9654,15 +9786,16 @@ function $11597ae03e703a47$var$RawSearchModalV2({
9654
9786
  // As per feedback from stronger we sometimes want a modal with the search field value disconnected from the one in the rest of the UI, see https://depictaiworkspace.slack.com/archives/C02J16R9XEH/p1679065878487709.
9655
9787
  // This is desired for when the modal is opened from a UI element that visually does not look connected with the search page or current query
9656
9788
  // Basically the UI signals that the button that opens the modal starts a new search.
9657
- const search_field_value_ = dont_sync_search_field_value_except_on_submit_ ? $5vyXB$solidjs.createSignal("") : props_search_field_value;
9658
- const propagate_search_field_value_back = dont_sync_search_field_value_except_on_submit_ ? () => props_search_field_value[1]($5vyXB$solidjs.untrack(search_field_value_[0])) // Set actual search field value to the throwaway internal one before submitting
9659
- : undefined;
9660
- const submit_query_ = (...args) => {
9661
- // submit_query expects to read the current search field value which is why we have to write the current one to the signal before calling it
9662
- // It will write the old one back and then write the new one after submit
9663
- propagate_search_field_value_back?.();
9664
- return props_submit_query(...args);
9665
- };
9789
+ const {
9790
+ search_field_value_: search_field_value_,
9791
+ submit_query_: submit_query_
9792
+ } = $5f9dc76a99175920$export$9e9e2ae069cf2fe1({
9793
+ props_search_field_value_: props_search_field_value,
9794
+ props_submit_query_: props_submit_query,
9795
+ dont_sync_search_field_value_except_on_submit_: dont_sync_search_field_value_except_on_submit_,
9796
+ set_focus_target_after_close_: set_focus_target_after_close_,
9797
+ get_search_page_input_element_: get_search_page_input_element_
9798
+ });
9666
9799
  const [input_field, set_input_field] = $5vyXB$solidjs.createSignal();
9667
9800
  const [searching_for_value_, set_searching_for_value_] = $5vyXB$solidjs.createSignal();
9668
9801
  const [show_stale_content_results_, set_show_stale_content_results_] = $5vyXB$solidjs.createSignal(false);
@@ -9769,7 +9902,7 @@ function $11597ae03e703a47$var$RawSearchModalV2({
9769
9902
  });
9770
9903
  const noResults = $5vyXB$solidjs.createMemo(prev => (get_modal_search_results_()?.loading === false && !suggestions_response_.loading || !!prev) && totalNumberResults() === 0 && previous_searches_matching_query().length === 0);
9771
9904
  if (disable_scrolling_) $ca1176b694a6ee2e$export$fe334901e8bebd57();
9772
- $eb3039a27db40508$export$93d9bfd0c9c1a5b8(close_modal_, search_query_url_param_name_);
9905
+ $eb3039a27db40508$export$93d9bfd0c9c1a5b8(close_modal_, search_query_url_param_name_, router_);
9773
9906
  addEventListener("keydown", close_on_escape_handler);
9774
9907
  $5vyXB$solidjs.onCleanup(() => removeEventListener("keydown", close_on_escape_handler));
9775
9908
  $5vyXB$solidjs.createEffect($5vyXB$solidjs.on(get_search_field_value_, () => {
@@ -11490,11 +11623,12 @@ function $2b7a005a65221045$export$84bba84a7b837187(read_search_query) {
11490
11623
  async function $f328bd45f8ede33c$export$e16630ecb9644a1c({
11491
11624
  on_submit_with_unchanged_value_: on_submit_with_unchanged_value_,
11492
11625
  search_param_name_: search_param_name_,
11493
- set_search_field_value_: set_search_field_value_,
11626
+ search_field_value_: search_field_value_,
11494
11627
  get_search_query_: get_search_query_,
11495
11628
  url_transformer_: url_transformer_,
11496
11629
  router_: router_,
11497
11630
  after_submit_: after_submit_,
11631
+ before_navigate_: before_navigate_,
11498
11632
  get_new_query: get_new_query
11499
11633
  }) {
11500
11634
  const new_search_query = get_new_query();
@@ -11502,21 +11636,89 @@ async function $f328bd45f8ede33c$export$e16630ecb9644a1c({
11502
11636
  if (previous_query === new_search_query && new URLSearchParams(location.search).has(search_param_name_)) {
11503
11637
  // don't push a history entry / re-search if someone submits a search again
11504
11638
  // do close the modal though
11639
+ search_field_value_.settle_(new_search_query);
11505
11640
  on_submit_with_unchanged_value_?.();
11506
- return;
11641
+ return false;
11507
11642
  }
11508
- set_search_field_value_(previous_query); // restore the value to the previous query since this one goes to the new history entry
11509
11643
  const target_url = new URL(location.href);
11510
11644
  url_transformer_?.(target_url);
11511
11645
  target_url.searchParams.set(search_param_name_, new_search_query);
11512
- await router_.navigate_.go_to_({
11513
- new_url_: target_url,
11514
- is_replace_: false,
11515
- force_spa_navigation_: true
11516
- });
11517
- set_search_field_value_(new_search_query);
11646
+ const end_submit = search_field_value_.begin_submit_(new_search_query, previous_query);
11647
+ before_navigate_?.();
11648
+ let landed;
11649
+ try {
11650
+ await router_.navigate_.go_to_({
11651
+ new_url_: target_url,
11652
+ is_replace_: false,
11653
+ force_spa_navigation_: true
11654
+ });
11655
+ } finally {
11656
+ landed = end_submit();
11657
+ }
11658
+ if (!landed) return false;
11518
11659
  scrollTo(0, 0);
11519
11660
  after_submit_?.();
11661
+ return true;
11662
+ }
11663
+ function $380b434363eee5f6$export$d13a185c53aa5edf(persisted_, get_search_query_) {
11664
+ const [get_persisted, set_persisted] = persisted_;
11665
+ const [get_submitting, set_submitting] = $5vyXB$solidjs.createSignal();
11666
+ const get_value = $5vyXB$solidjs.createMemo(() => get_submitting() ?? get_persisted());
11667
+ const set_value = value => {
11668
+ const new_value = typeof value === "function" ? value($5vyXB$solidjs.untrack(get_value)) : value;
11669
+ if ($5vyXB$solidjs.untrack(get_submitting) === undefined) set_persisted(new_value);else set_submitting(new_value);
11670
+ return new_value;
11671
+ };
11672
+ let current_submit;
11673
+ const end_submit = landed => {
11674
+ current_submit.landed_ = landed;
11675
+ current_submit = undefined;
11676
+ $5vyXB$solidjs.batch(() => {
11677
+ const submitting = $5vyXB$solidjs.untrack(get_submitting);
11678
+ set_submitting(undefined);
11679
+ if (landed && submitting !== undefined) set_persisted(submitting);
11680
+ });
11681
+ };
11682
+ const abort_on_popstate = what_happened => {
11683
+ if (what_happened === "popstate" && current_submit) end_submit(false);
11684
+ };
11685
+ (0, $5vyXB$depictaiutilishared.instant_exec_on_suspect_history_change).add(abort_on_popstate);
11686
+ $5vyXB$solidjs.onCleanup(() => (0, $5vyXB$depictaiutilishared.instant_exec_on_suspect_history_change).delete(abort_on_popstate));
11687
+ $5vyXB$solidjs.createEffect($5vyXB$solidjs.on(get_search_query_, query => {
11688
+ const token = current_submit;
11689
+ if (!token || query !== token.query_) return;
11690
+ queueMicrotask(() => {
11691
+ if (current_submit === token) end_submit(true);
11692
+ });
11693
+ }, {
11694
+ defer: true
11695
+ }));
11696
+ const begin_submit_ = (submitted_query, query_of_current_history_entry) => {
11697
+ const token = current_submit = {
11698
+ query_: submitted_query,
11699
+ landed_: false
11700
+ };
11701
+ $5vyXB$solidjs.batch(() => {
11702
+ set_submitting(submitted_query);
11703
+ set_persisted(query_of_current_history_entry);
11704
+ });
11705
+ return () => {
11706
+ if (current_submit === token) end_submit(true);
11707
+ return token.landed_;
11708
+ };
11709
+ };
11710
+ const settle_ = query => {
11711
+ if (current_submit) return;
11712
+ $5vyXB$solidjs.batch(() => {
11713
+ set_submitting(undefined);
11714
+ set_persisted(query);
11715
+ });
11716
+ };
11717
+ return {
11718
+ value_: [get_value, set_value],
11719
+ begin_submit_: begin_submit_,
11720
+ settle_: settle_
11721
+ };
11520
11722
  }
11521
11723
  function $331c7a65de274e6c$export$181cceed5c91919e(callback, alsoRaF, passiveListener) {
11522
11724
  let aligned_this_microtask = false;
@@ -12152,6 +12354,7 @@ function $f1d7cb69f839a526$export$a54ec244c8863715({
12152
12354
  scroll_restoration_data_: scroll_restoration_data_,
12153
12355
  product_card_template_: product_card_template_,
12154
12356
  modalAlignmentSignalsRef_: modalAlignmentSignalsRef_,
12357
+ inputFieldRef_: inputFieldRef_,
12155
12358
  router_: router_,
12156
12359
  content_results_rows_: content_results_rows_,
12157
12360
  content_layout_options_: content_layout_options_,
@@ -12331,6 +12534,8 @@ function $f1d7cb69f839a526$export$a54ec244c8863715({
12331
12534
  if (!search_field) break add_input_field;
12332
12535
  setModalBodyStyle({});
12333
12536
  modalAlignmentSignalsRef_?.(alignmentSignals_);
12537
+ inputFieldRef_?.(input_element);
12538
+ $5vyXB$solidjs.onCleanup(() => inputFieldRef_?.(undefined));
12334
12539
  const [pollAlignment, setPollAlignment_] = $5vyXB$solidjs.createSignal(false);
12335
12540
  const align_position = $5vyXB$depictaiutilishared.catchify((0, $fd463ff0edb29915$export$b3ad617e9036868))(alignmentSignals_, search_field, (0, $fd463ff0edb29915$export$3f238c96c45e6cc6) | (0, $fd463ff0edb29915$export$7895aadd1956b1c7) | (modalVersionUsed_ === 1 ? (0, $fd463ff0edb29915$export$902f8b4a7dae5d78) : (0, $fd463ff0edb29915$export$6060bc6bee850eb6) | (0, $fd463ff0edb29915$export$5097308c5200105a)), true, pollAlignment);
12336
12541
  div_with_field_class.addEventListener("click", $5vyXB$depictaiutilishared.catchify(e => {
@@ -12632,14 +12837,16 @@ class $2c61e4cd8229c49a$export$ac6237957a15b3f0 {
12632
12837
  set_search_content_results_rows((0, $3716b8caf7f959e2$export$70ceae36059a1036));
12633
12838
  }; // do this every time after we have submitted a new query - otherwise there might already be a saved number of rows in the state
12634
12839
  const on_submit_with_unchanged_value_ = () => this.modal_open = false;
12635
- const [get_search_field_value_, set_search_field_value_] = state_.field_value;
12636
- const configured_submit_query_ = new_query => $f328bd45f8ede33c$export$e16630ecb9644a1c({
12840
+ const search_field_value_ = $380b434363eee5f6$export$d13a185c53aa5edf(state_.field_value, get_search_query_);
12841
+ const [get_search_field_value_] = search_field_value_.value_;
12842
+ const configured_submit_query_ = (new_query, before_navigate_) => $f328bd45f8ede33c$export$e16630ecb9644a1c({
12637
12843
  on_submit_with_unchanged_value_: on_submit_with_unchanged_value_,
12638
- set_search_field_value_: set_search_field_value_,
12844
+ search_field_value_: search_field_value_,
12639
12845
  get_search_query_: get_search_query_,
12640
12846
  search_param_name_: search_query_url_param_name_,
12641
12847
  after_submit_: after_submit_,
12642
- get_new_query: () => new_query || $5vyXB$solidjs.untrack(get_search_field_value_),
12848
+ before_navigate_: before_navigate_,
12849
+ get_new_query: () => new_query ?? $5vyXB$solidjs.untrack(get_search_field_value_),
12643
12850
  url_transformer_: url_transformer,
12644
12851
  router_: this.#router
12645
12852
  });
@@ -12658,6 +12865,7 @@ class $2c61e4cd8229c49a$export$ac6237957a15b3f0 {
12658
12865
  search_query_url_param_name_: search_query_url_param_name_,
12659
12866
  reset_history_state_: reset_history_state_,
12660
12867
  state_: state_,
12868
+ search_field_value_: search_field_value_.value_,
12661
12869
  router_: this.#router,
12662
12870
  url_transformer_: url_transformer,
12663
12871
  depict_api_: depict_api_,
@@ -12681,7 +12889,8 @@ class $2c61e4cd8229c49a$export$ac6237957a15b3f0 {
12681
12889
  $5vyXB$solidjs.onCleanup(() => this.modal_open = false);
12682
12890
  return searchModalComponent(...args);
12683
12891
  }, {
12684
- search_field_value_: state_.field_value,
12892
+ search_field_value_: search_field_value_.value_,
12893
+ get_search_page_input_element_: () => $2c61e4cd8229c49a$export$dde08b2dca8cc1ce.get(this)?.search_page_input_element_,
12685
12894
  get_search_query_: get_search_query_,
12686
12895
  search_query_url_param_name_: search_query_url_param_name_,
12687
12896
  previous_searches_: previous_searches,
@@ -12829,6 +13038,7 @@ function $2c61e4cd8229c49a$export$f2dc7e4e4aa11abb(props) {
12829
13038
  const {
12830
13039
  state_: state_
12831
13040
  } = shared_properties;
13041
+ let registered_input_element;
12832
13042
  const grid_spacing_override = $5vyXB$solidjs.createMemo(() => {
12833
13043
  const {
12834
13044
  grid_spacing: grid_spacing
@@ -12857,7 +13067,7 @@ function $2c61e4cd8229c49a$export$f2dc7e4e4aa11abb(props) {
12857
13067
  min_products_to_fetch_: $2c61e4cd8229c49a$var$min_products_to_fetch_,
12858
13068
  local_filter_cache_: state_.local_filter_cache,
12859
13069
  content_search_enabled_: () => depict_search.enable_content_search ?? $2c61e4cd8229c49a$var$content_search_default,
12860
- search_field_value_: state_.field_value,
13070
+ search_field_value_: shared_properties.search_field_value_,
12861
13071
  get_search_query_: shared_properties.get_search_query_,
12862
13072
  clear_filters_on_next_submit_: shared_properties.clear_filters_on_next_submit_,
12863
13073
  open_modal_: (...args) => depict_search.modal_open = args,
@@ -12865,6 +13075,16 @@ function $2c61e4cd8229c49a$export$f2dc7e4e4aa11abb(props) {
12865
13075
  modalVersionUsed_: shared_properties.modalVersionUsed_,
12866
13076
  showSliderArrow_: () => props.showSliderArrow_,
12867
13077
  i18n_: shared_properties.i18n_,
13078
+ inputFieldRef_: input_element => {
13079
+ const shared_info_right_now = $2c61e4cd8229c49a$export$dde08b2dca8cc1ce.get(depict_search);
13080
+ if (!shared_info_right_now) return;
13081
+ if (input_element === undefined && shared_info_right_now.search_page_input_element_ !== registered_input_element) return;
13082
+ registered_input_element = input_element;
13083
+ $2c61e4cd8229c49a$export$dde08b2dca8cc1ce.set(depict_search, {
13084
+ ...shared_info_right_now,
13085
+ search_page_input_element_: input_element
13086
+ });
13087
+ },
12868
13088
  modalAlignmentSignalsRef_: style => {
12869
13089
  const shared_info_right_now = $2c61e4cd8229c49a$export$dde08b2dca8cc1ce.get(depict_search);
12870
13090
  if (!shared_info_right_now) return;
@@ -13215,7 +13435,7 @@ function $c0a0659cf97e8eb2$export$316f6f283ae64765(props) {
13215
13435
  return shared_properties.BackIcon_;
13216
13436
  },
13217
13437
  get search_field_value_() {
13218
- return shared_properties.state_.field_value;
13438
+ return shared_properties.search_field_value_;
13219
13439
  },
13220
13440
  get submit_query_() {
13221
13441
  return shared_properties.configured_submit_query_;
@@ -13258,7 +13478,7 @@ function $c0a0659cf97e8eb2$export$316f6f283ae64765(props) {
13258
13478
  return shared_properties.BackIcon_;
13259
13479
  },
13260
13480
  get search_field_value_() {
13261
- return shared_properties.state_.field_value;
13481
+ return shared_properties.search_field_value_;
13262
13482
  },
13263
13483
  get submit_query_() {
13264
13484
  return shared_properties.configured_submit_query_;
@@ -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.19";
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
@@ -713,6 +717,8 @@ declare function RawSearchModalV2<InputDisplay extends Display, OutputDisplay ex
713
717
  search_query_url_param_name_,
714
718
  previous_searches_,
715
719
  close_modal_,
720
+ set_focus_target_after_close_,
721
+ get_search_page_input_element_,
716
722
  alignmentSignals_,
717
723
  depict_api_,
718
724
  merchant_,
@@ -743,12 +749,14 @@ declare function RawSearchModalV2<InputDisplay extends Display, OutputDisplay ex
743
749
  search_query_url_param_name_: string;
744
750
  previous_searches_: Signal<string[]>;
745
751
  close_modal_: VoidFunction;
752
+ set_focus_target_after_close_: (get_target: (() => HTMLElement | undefined) | undefined) => void;
753
+ get_search_page_input_element_: () => HTMLInputElement | undefined;
746
754
  alignmentSignals_?: ModalAlignmentSignals;
747
755
  depict_api_: DepictAPI<InputDisplay, OutputDisplay>;
748
756
  clear_filters_: (user_triggered: boolean) => void;
749
757
  merchant_: Accessor<string>;
750
758
  market_: Accessor<string>;
751
- submit_query_: (new_query?: string) => void;
759
+ submit_query_: (new_query?: string, before_navigate_?: VoidFunction) => Promise<boolean>;
752
760
  class_list_?: Accessor<Record<string, boolean>>;
753
761
  url_transformer_?: (url_object: URL) => unknown;
754
762
  disable_scrolling_?: boolean;
@@ -785,6 +793,8 @@ declare function RawClassicSearchModal({
785
793
  search_query_url_param_name_,
786
794
  previous_searches_,
787
795
  close_modal_,
796
+ set_focus_target_after_close_,
797
+ get_search_page_input_element_,
788
798
  depict_api_,
789
799
  merchant_,
790
800
  market_,
@@ -810,11 +820,13 @@ declare function RawClassicSearchModal({
810
820
  search_query_url_param_name_: string;
811
821
  previous_searches_: Signal<string[]>;
812
822
  close_modal_: VoidFunction;
823
+ set_focus_target_after_close_: (get_target: (() => HTMLElement | undefined) | undefined) => void;
824
+ get_search_page_input_element_: () => HTMLInputElement | undefined;
813
825
  depict_api_: DepictAPI<any>;
814
826
  clear_filters_: (user_triggered: boolean) => void;
815
827
  merchant_: Accessor<string>;
816
828
  market_: Accessor<string>;
817
- submit_query_: (new_query?: string) => void;
829
+ submit_query_: (new_query?: string, before_navigate_?: VoidFunction) => Promise<boolean>;
818
830
  class_list_?: Accessor<Record<string, boolean>>;
819
831
  url_transformer_?: (url_object: URL) => unknown;
820
832
  disable_scrolling_?: boolean;
@@ -1038,6 +1050,7 @@ export function general_modal_abstraction<StaticOptions extends {
1038
1050
  close_modal_: VoidFunction;
1039
1051
  open_modal_: (extra_options_for_modal?: ExtraOptionsProvidedOnOpen, on_dispose?: VoidFunction) => void;
1040
1052
  register_closing_animation_: (animation: () => Promise<any>) => void;
1053
+ set_focus_target_after_close_: (get_target: (() => HTMLElement | undefined) | undefined) => void;
1041
1054
  }) => Node_Array, modal_fn_opts: StaticOptions, parent?: HTMLElement | ShadowRoot): Promise<{
1042
1055
  open_modal_: (extra_options_for_modal?: any, on_dispose?: VoidFunction | undefined) => Promise<void | undefined>;
1043
1056
  close_modal_: () => void | undefined;