@depict-ai/ui 0.0.50 → 0.0.52

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/dist/index.d.ts CHANGED
@@ -9,7 +9,7 @@ import { SearchSuggestionsRequest } from "@depict-ai/types/api/SearchSuggestions
9
9
  import { ProductListingRequest } from "@depict-ai/types/api/ProductListingRequest";
10
10
  import { ProductListingResponse, BaseCategoryDisplay } from "@depict-ai/types/api/ProductListingResponse";
11
11
  import { JSX as _JSX1 } from "solid-js/types/jsx";
12
- export const version = "0.0.50";
12
+ export const version = "0.0.52";
13
13
  type ExclusiveId = {
14
14
  categoryId: string;
15
15
  productId?: undefined;
@@ -227,7 +227,7 @@ type DepictCategoryConstructorOptions = {
227
227
  market: string;
228
228
  merchant: string;
229
229
  localization: category_i18n;
230
- on_navigation?: (relative_url: string, ev: MouseEvent, display: BaseCategoryDisplay) => unknown;
230
+ on_navigation: (relative_url: string, ev: MouseEvent, display: BaseCategoryDisplay) => unknown;
231
231
  api?: DepictAPI;
232
232
  };
233
233
  export class DepictCategory {
@@ -349,6 +349,25 @@ interface SearchFieldParams {
349
349
  aligner_ref?: HTMLElement | null;
350
350
  }
351
351
  export function SearchField({ depict_search, modal_dispose_handler, click_handler, aligner_ref, }: SearchFieldParams): Node_Array;
352
+ type CenteredLocation = {
353
+ location?: "centered";
354
+ };
355
+ type AlignedLocation = {
356
+ location: "aligned";
357
+ element: HTMLElement;
358
+ };
359
+ /**
360
+ * How the modal should be opened.
361
+ */
362
+ type ModalLocation = AlignedLocation | CenteredLocation;
363
+ type OpenModalParams<T extends Display> = ModalLocation & {
364
+ search: DepictSearch<T>;
365
+ };
366
+ /**
367
+ * Open the modal with support for alignment.
368
+ * @returns {void}
369
+ */
370
+ export const open_modal_with_alignment: <T extends Display>(openModalParams: OpenModalParams<T>) => void;
352
371
  export function open_search_with_shift_slash(open_search: VoidFunction): () => void;
353
372
  export interface PageReplacer {
354
373
  readonly render_: (is_prerendering: boolean) => Node_Array;
@@ -361,7 +380,9 @@ export class PageReplacer extends EventTarget {
361
380
  #private;
362
381
  get open_(): boolean;
363
382
  destroy_(): void;
364
- constructor(options: Omit<PageReplacer, "destroy_" | "addEventListener" | "removeEventListener" | "dispatchEvent" | "open_">);
383
+ constructor({ is_spa_, ...options }: Omit<PageReplacer, "destroy_" | "addEventListener" | "removeEventListener" | "dispatchEvent" | "open_"> & {
384
+ is_spa_?: boolean;
385
+ });
365
386
  }
366
387
  /**
367
388
  * makes so that links to the previous page close the search instead of closing the search and reloading the page (to be used with non-spas)
package/dist/main.js CHANGED
@@ -18,6 +18,7 @@ $parcel$export(module.exports, "DepictSearch", () => $79fbf145b15af988$export$ac
18
18
  $parcel$export(module.exports, "get_search_query_updating_blocked_signal", () => $79fbf145b15af988$export$bde3eb976dfded9);
19
19
  $parcel$export(module.exports, "SearchPage", () => $79fbf145b15af988$export$f2dc7e4e4aa11abb);
20
20
  $parcel$export(module.exports, "SearchField", () => $79fbf145b15af988$export$316f6f283ae64765);
21
+ $parcel$export(module.exports, "open_modal_with_alignment", () => $9c5a1d57540c06e5$export$d86a33413558014e);
21
22
  $parcel$export(module.exports, "DefaultInstantCardPlaceholder", () => $17c49340edfdbb31$export$ee1733d9a8495996);
22
23
  $parcel$export(module.exports, "align_field", () => $67b60b572b7b7059$export$b3ad617e9036868);
23
24
  $parcel$export(module.exports, "styles_to_valid_css_string", () => $d3c94b3b96557917$export$1233ca167bf71f9a);
@@ -32,7 +33,7 @@ $parcel$export(module.exports, "solid_format_price", () => $5fc9fc23df1591db$exp
32
33
  $parcel$export(module.exports, "connect_search_field_height_to_aligner_height", () => $246e3e89a097f014$export$828dcdd425fd3231);
33
34
  $parcel$export(module.exports, "PageReplacer", () => $37e96a5ebc768cc7$export$3ccaa27d81dd8556);
34
35
  $parcel$export(module.exports, "version", () => $ae93561d5649a847$export$83d89fbfd8236492);
35
- /*! Depict.ai search and product listing SDK */ const $ae93561d5649a847$export$83d89fbfd8236492 = "0.0.50";
36
+ /*! Depict.ai search and product listing SDK */ const $ae93561d5649a847$export$83d89fbfd8236492 = "0.0.52";
36
37
 
37
38
 
38
39
 
@@ -4122,7 +4123,7 @@ function $495e9998757c5d32$export$5646050d516e06bc({ crumb_data_: get_api_crumb_
4122
4123
  },
4123
4124
  get children () {
4124
4125
  const _el$2 = $495e9998757c5d32$var$_tmpl$2.cloneNode(true);
4125
- (0, $46MJV$solidjsweb.addEventListener)(_el$2, "click", (0, $46MJV$depictaiutilishared.catchify)((ev)=>on_crumb_click_?.(ev, display_accessor())), true);
4126
+ (0, $46MJV$solidjsweb.addEventListener)(_el$2, "click", (0, $46MJV$depictaiutilishared.catchify)((ev)=>on_crumb_click_(ev, display_accessor())), true);
4126
4127
  (0, $46MJV$solidjsweb.insert)(_el$2, ()=>display_accessor().title);
4127
4128
  (0, $46MJV$solidjsweb.effect)(()=>(0, $46MJV$solidjsweb.setAttribute)(_el$2, "href", display_accessor().page_url));
4128
4129
  return _el$2;
@@ -4289,7 +4290,7 @@ function $cdc2c00c0a7da86e$export$466cdc317043f6cb({ quicklinks_data_: quicklink
4289
4290
  const _el$3 = $cdc2c00c0a7da86e$var$_tmpl$3.cloneNode(true);
4290
4291
  (0, $46MJV$solidjsweb.addEventListener)(_el$3, "click", (0, $46MJV$depictaiutilishared.catchify)((ev)=>{
4291
4292
  if (is_selected() && !ev.metaKey && !ev.ctrlKey && !ev.button) ev.preventDefault();
4292
- else if (display_accessor()) on_link_click_?.(ev, display_accessor());
4293
+ else if (display_accessor()) on_link_click_(ev, display_accessor());
4293
4294
  }), true);
4294
4295
  (0, $46MJV$solidjsweb.insert)(_el$3, ()=>display_accessor()?.title || (0, $46MJV$solidjsweb.createComponent)((0, $46MJV$depictaiutilishared.Placeholder), {
4295
4296
  height: "1em",
@@ -4957,7 +4958,7 @@ class $068a627cc18f7bb6$export$f34314aa9505732e {
4957
4958
  category_scroll_restoration_data: []
4958
4959
  }));
4959
4960
  const i18n_ = (0, $623202ae203e9e59$export$f091cbac892643a4)(this.#locale_signal[0]);
4960
- const on_category_link_click_ = on_navigation && ((e, d)=>{
4961
+ const on_category_link_click_ = (e, d)=>{
4961
4962
  if (e.button || e.metaKey || e.ctrlKey) // Not single page navigating because button is not 0 or ctrl/command click
4962
4963
  return;
4963
4964
  try {
@@ -4971,7 +4972,7 @@ class $068a627cc18f7bb6$export$f34314aa9505732e {
4971
4972
  throw e;
4972
4973
  });
4973
4974
  }
4974
- });
4975
+ };
4975
4976
  $068a627cc18f7bb6$var$privately_shared_properties.set(this, {
4976
4977
  state_: state_,
4977
4978
  breadcrumb_signal_: (0, $46MJV$solidjs.createSignal)(),
@@ -7721,6 +7722,47 @@ function $79fbf145b15af988$var$align_field_modal(style_props_, input, aligner_re
7721
7722
 
7722
7723
 
7723
7724
 
7725
+ const $9c5a1d57540c06e5$export$d86a33413558014e = (openModalParams)=>{
7726
+ const { search: search } = openModalParams;
7727
+ openModalParams.location ||= "centered";
7728
+ if (openModalParams.location === "aligned" && !openModalParams.element) throw new Error("No active search header aligner on the page!");
7729
+ if (openModalParams.location !== "aligned") {
7730
+ // Open centered modal
7731
+ search.modal_open = true;
7732
+ return;
7733
+ }
7734
+ const alignment_height = openModalParams.element?.clientHeight;
7735
+ if (isNaN(alignment_height)) {
7736
+ // Open centered modal
7737
+ search.modal_open = true;
7738
+ return;
7739
+ }
7740
+ const { cleanup: cleanup } = (0, $246e3e89a097f014$export$828dcdd425fd3231)(openModalParams.element);
7741
+ let dispose_aligner;
7742
+ /**
7743
+ * Setting position to fixed is important to make the modal work with align_field.
7744
+ */ const style_props_ = (0, $46MJV$solidjs.createSignal)({
7745
+ position: "fixed"
7746
+ });
7747
+ (0, $46MJV$solidjs.createRoot)((dispose)=>{
7748
+ dispose_aligner = ()=>{
7749
+ cleanup();
7750
+ dispose();
7751
+ };
7752
+ (0, $67b60b572b7b7059$export$b3ad617e9036868)(style_props_, openModalParams.element, undefined, false, "top");
7753
+ });
7754
+ search.modal_open = [
7755
+ {
7756
+ style_props_: style_props_
7757
+ },
7758
+ dispose_aligner
7759
+ ];
7760
+ };
7761
+
7762
+
7763
+
7764
+
7765
+
7724
7766
 
7725
7767
 
7726
7768
  function $308e64f91bb9f839$export$d0596b5cae35ad4(open_search) {
@@ -7776,16 +7818,28 @@ function $d589470b68ce5b67$export$689e204eadb33baf(pr) {
7776
7818
 
7777
7819
 
7778
7820
 
7821
+
7779
7822
  class $37e96a5ebc768cc7$export$3ccaa27d81dd8556 extends EventTarget {
7780
7823
  #stop_listening;
7781
7824
  #disconnect_observer;
7782
7825
  #revert_fns = [];
7783
7826
  #is_open = false;
7827
+ static #reverting_permitted = true;
7828
+ static #visited_urls = (()=>{
7829
+ // Keep track of all URLs that have been visited since the document that was initially loaded (loaded means the process where the browser creates a new `document` and `window` object).
7830
+ (0, $46MJV$depictaiutilishared.catchify)(async ()=>{
7831
+ // Static initialization blocks aren't supported in MobileSafari yet, otherwise this would be one.
7832
+ // I don't think we want to ship a library that users are *forced* to transpile, it should run in the newest version of all major browsers. So we'll just use a function wrapper for now (adding "last 1 ios version" to the build target does a bunch of other unneeded transpilations).
7833
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Static_initialization_blocks
7834
+ for await (const href of (0, $46MJV$depictaiutilishared.href_change_ipns))$37e96a5ebc768cc7$export$3ccaa27d81dd8556.#visited_urls.add($37e96a5ebc768cc7$var$clean_url(href));
7835
+ })();
7836
+ return new Set();
7837
+ })();
7784
7838
  get open_() {
7785
7839
  // make this "readonly" from the outside, the only way to open is by making the url change so that is_page_ returns true
7786
7840
  return this.#is_open;
7787
7841
  }
7788
- #listen = (0, $46MJV$depictaiutilishared.deparallelize)(async ()=>{
7842
+ async #listen() {
7789
7843
  const [exit_ipns, exitable_ipns] = (0, $46MJV$depictaiutilishared.make_asyncIterable_exiter)((0, $46MJV$depictaiutilishared.href_change_ipns));
7790
7844
  this.#stop_listening = exit_ipns;
7791
7845
  const caught_page_check_fn = (0, $46MJV$depictaiutilishared.catchify)(this.is_page_, "page check failed");
@@ -7800,7 +7854,7 @@ class $37e96a5ebc768cc7$export$3ccaa27d81dd8556 extends EventTarget {
7800
7854
  if (!els?.length) continue;
7801
7855
  this.#load(els);
7802
7856
  }
7803
- });
7857
+ }
7804
7858
  #get_rendered_recs() {
7805
7859
  return (0, $46MJV$solidjs.createRoot)((dispose)=>{
7806
7860
  this.addEventListener("close", dispose, {
@@ -7812,7 +7866,7 @@ class $37e96a5ebc768cc7$export$3ccaa27d81dd8556 extends EventTarget {
7812
7866
  #unload = (0, $46MJV$depictaiutilishared.catchify)(()=>{
7813
7867
  if (!this.#is_open) return;
7814
7868
  this.#disconnect_observer?.();
7815
- while(this.#revert_fns.length)this.#revert_fns.pop()?.();
7869
+ if ($37e96a5ebc768cc7$export$3ccaa27d81dd8556.#reverting_permitted) while(this.#revert_fns.length)this.#revert_fns.pop()?.();
7816
7870
  this.#is_open = false;
7817
7871
  this.dispatchEvent(new CustomEvent("close"));
7818
7872
  }, "failed to close page");
@@ -7856,21 +7910,65 @@ class $37e96a5ebc768cc7$export$3ccaa27d81dd8556 extends EventTarget {
7856
7910
  document.title = this.title_;
7857
7911
  this.#revert_fns.push(()=>document.title = title);
7858
7912
  }
7913
+ this.#empty_page_bugfix();
7859
7914
  this.dispatchEvent(new CustomEvent("open")); // we dispatch this once it's actually open
7860
7915
  }, "failed to open page");
7916
+ #empty_page_bugfix() {
7917
+ // Called when a "page" is "opened" using PageReplacer
7918
+ const on_popstate = ()=>{
7919
+ const cleaned_current_url = $37e96a5ebc768cc7$var$clean_url(location.href);
7920
+ if (!$37e96a5ebc768cc7$export$3ccaa27d81dd8556.#visited_urls.has(cleaned_current_url)) {
7921
+ /*
7922
+ What does this check do? Well, there's a bug without it where people end up on a page that they shouldn't see (i.e. a somewhat blank page).
7923
+ The culprit of that bug is: the browser remembers where to make "hard"-navigations (i.e. a full document rebuild with a new `document` and `window` object) when going back and forwards in the history and where to just change the url. What it doesn't always correctly remember (observed in at least chrome and firefox), is *what document to hard-navigate to*. Therefore, we have to jump in and take care of that.
7924
+ The reproduction is this:
7925
+ Spendrups has set up a search page for us under /start/search. We don't want to hard-reload the page though to get there.
7926
+ 1. Go to a product page like https://ehandel.spendrups.se/Product/1104201
7927
+ 2. Make a search
7928
+ We use PageReplacer to just replace the current page (and the url in the address bar) with the search page. The thing is though, that when going back now the URL just updates but the browser doesn't actually navigate - that's fine, PageReplacer will just restore the old page contents
7929
+ 3. Try going back just for fun
7930
+ Everything works as expected
7931
+ 4. Go forwards again (just for fun, skip this if you skipped 3)
7932
+ Now the trouble comes when we introduce "hard" navigation.
7933
+ 5. Click on a search result. The browser will now "hard" navigate to a new page.
7934
+ 6. Click "back"
7935
+ What happens now, is that the browser hard-navigates to /start/search.
7936
+ 7. Click "back" again
7937
+ What happens now, is that the browser "soft-navigates" (just changes the URL) to https://ehandel.spendrups.se/Product/1104201
7938
+ PageReplacer unload itself and reveals the original document - it is expected to be the one under https://ehandel.spendrups.se/Product/1104201, but actually it is the one under /start/search - a white page.
7939
+ The fix: is to remember all pages that we have visited since the creation of `window` (we do that by having a static property on PageReplacer since we assume the PageReplacer source code will only exist once on the page and will be loaded at page load, the latter assumption might be wrong, but I hope not). If we get a "popstate" event (meaning back or forwards button, not new page being pushed) to an unknown page we reload.
7940
+ */ (0, $46MJV$depictaiutilishared.dlog)("PageReplacer: ", cleaned_current_url, " hasn't been visited since document was created, reloading since we won't be able to show this page otherwise");
7941
+ location.reload();
7942
+ $37e96a5ebc768cc7$export$3ccaa27d81dd8556.#reverting_permitted = false; // Disallow any reverting since we can't - it would flash a white page until the reload has finished.
7943
+ this.#unload(); // Call unload to "kill" the solid-js stuff quicker so that it stops updating - the user is expecting the next thing that happens to be a navigation to the actual page the URL says they're on.
7944
+ }
7945
+ };
7946
+ (0, $46MJV$depictaiutilishared.instant_exec_on_popstate_before_ipns_update).add(on_popstate);
7947
+ this.#revert_fns.push(()=>(0, $46MJV$depictaiutilishared.instant_exec_on_popstate_before_ipns_update).delete(on_popstate)); // Don't run the bugfixer if there isn't a PageReplacer page open since I assume the bug only happens when going back from a PageReplace'd page
7948
+ }
7861
7949
  destroy_() {
7862
7950
  this.#unload();
7863
7951
  this.#stop_listening?.();
7864
7952
  }
7865
- constructor(options){
7953
+ constructor({ is_spa_: is_spa_ , ...options }){
7866
7954
  super();
7867
7955
  Object.assign(this, options);
7868
7956
  this.#listen().catch((e)=>(0, $46MJV$depictaiutilishared.report)([
7869
7957
  e,
7870
7958
  "Error listening to URL change"
7871
7959
  ], "error"));
7960
+ if (!is_spa_) (0, $d589470b68ce5b67$export$689e204eadb33baf)(this); // Make so that clicking on links to the previous page work instantly
7872
7961
  }
7873
7962
  }
7963
+ /**
7964
+ * Remove hash from URL
7965
+ */ function $37e96a5ebc768cc7$var$clean_url(url) {
7966
+ // Would want to have this a static private function on PageReplacer, but that's not supported in MobileSafari yet
7967
+ // remove hash, all other params *could* trigger a re-request
7968
+ const url_object = new URL(url);
7969
+ url_object.hash = "";
7970
+ return url_object.href;
7971
+ }
7874
7972
 
7875
7973
 
7876
7974
  (0, $46MJV$depictaiutilishared.add_to_version_info)("ui", (0, $ae93561d5649a847$export$83d89fbfd8236492));
package/dist/module.js CHANGED
@@ -1,8 +1,8 @@
1
- import {add_to_version_info as $g9l4l$add_to_version_info, fetch_recs as $g9l4l$fetch_recs, snakeCasifyObject as $g9l4l$snakeCasifyObject, GenericSliderArrowButton as $g9l4l$GenericSliderArrowButton, catchify as $g9l4l$catchify, base_url as $g9l4l$base_url, get_depict_id as $g9l4l$get_depict_id, get_user_id as $g9l4l$get_user_id, get_session_id as $g9l4l$get_session_id, dlog as $g9l4l$dlog, make_url_with_query_params as $g9l4l$make_url_with_query_params, fetch_retry as $g9l4l$fetch_retry, add_version_query_params as $g9l4l$add_version_query_params, sort_obj as $g9l4l$sort_obj, Tenant as $g9l4l$Tenant, Placeholder as $g9l4l$Placeholder, report as $g9l4l$report, javascript_media_query as $g9l4l$javascript_media_query, standard_price_format as $g9l4l$standard_price_format, deparallelize_no_drop as $g9l4l$deparallelize_no_drop, default_rr as $g9l4l$default_rr, make_random_classname as $g9l4l$make_random_classname, n_col_styling_with_dist as $g9l4l$n_col_styling_with_dist, observer as $g9l4l$observer, Lazyfier as $g9l4l$Lazyfier, queueMacroTask as $g9l4l$queueMacroTask, deparallelize as $g9l4l$deparallelize, Rec_Renderer as $g9l4l$Rec_Renderer, CrossIcon as $g9l4l$CrossIcon, useExpandingContainer as $g9l4l$useExpandingContainer, Amazing_Slider as $g9l4l$Amazing_Slider, slider_snap_css as $g9l4l$slider_snap_css, slider_fading_factory as $g9l4l$slider_fading_factory, MinusIcon as $g9l4l$MinusIcon, FilterIcon as $g9l4l$FilterIcon, SortIconAsc as $g9l4l$SortIconAsc, SortIconDesc as $g9l4l$SortIconDesc, PlusIcon as $g9l4l$PlusIcon, TickIcon as $g9l4l$TickIcon, ArrowUp as $g9l4l$ArrowUp, normal_get_product_id as $g9l4l$normal_get_product_id, ContainedPlaceholderImage as $g9l4l$ContainedPlaceholderImage, BackIcon as $g9l4l$BackIcon, SearchIcon as $g9l4l$SearchIcon, Layout as $g9l4l$Layout, make_placeholder_rr as $g9l4l$make_placeholder_rr, async_iterable_ipns as $g9l4l$async_iterable_ipns, make_asyncIterable_exiter as $g9l4l$make_asyncIterable_exiter, href_change_ipns as $g9l4l$href_change_ipns, depict_click as $g9l4l$depict_click, impression_track_element as $g9l4l$impression_track_element, add_new_tab_listener as $g9l4l$add_new_tab_listener, ContainedImage as $g9l4l$ContainedImage, buildThresholdList as $g9l4l$buildThresholdList, PreviousSearchIcon as $g9l4l$PreviousSearchIcon, insert_surfaces as $g9l4l$insert_surfaces} from "@depict-ai/utilishared";
1
+ import {add_to_version_info as $g9l4l$add_to_version_info, fetch_recs as $g9l4l$fetch_recs, snakeCasifyObject as $g9l4l$snakeCasifyObject, GenericSliderArrowButton as $g9l4l$GenericSliderArrowButton, catchify as $g9l4l$catchify, base_url as $g9l4l$base_url, get_depict_id as $g9l4l$get_depict_id, get_user_id as $g9l4l$get_user_id, get_session_id as $g9l4l$get_session_id, dlog as $g9l4l$dlog, make_url_with_query_params as $g9l4l$make_url_with_query_params, fetch_retry as $g9l4l$fetch_retry, add_version_query_params as $g9l4l$add_version_query_params, sort_obj as $g9l4l$sort_obj, Tenant as $g9l4l$Tenant, Placeholder as $g9l4l$Placeholder, report as $g9l4l$report, javascript_media_query as $g9l4l$javascript_media_query, standard_price_format as $g9l4l$standard_price_format, deparallelize_no_drop as $g9l4l$deparallelize_no_drop, default_rr as $g9l4l$default_rr, make_random_classname as $g9l4l$make_random_classname, n_col_styling_with_dist as $g9l4l$n_col_styling_with_dist, observer as $g9l4l$observer, Lazyfier as $g9l4l$Lazyfier, queueMacroTask as $g9l4l$queueMacroTask, deparallelize as $g9l4l$deparallelize, Rec_Renderer as $g9l4l$Rec_Renderer, CrossIcon as $g9l4l$CrossIcon, useExpandingContainer as $g9l4l$useExpandingContainer, Amazing_Slider as $g9l4l$Amazing_Slider, slider_snap_css as $g9l4l$slider_snap_css, slider_fading_factory as $g9l4l$slider_fading_factory, MinusIcon as $g9l4l$MinusIcon, FilterIcon as $g9l4l$FilterIcon, SortIconAsc as $g9l4l$SortIconAsc, SortIconDesc as $g9l4l$SortIconDesc, PlusIcon as $g9l4l$PlusIcon, TickIcon as $g9l4l$TickIcon, ArrowUp as $g9l4l$ArrowUp, normal_get_product_id as $g9l4l$normal_get_product_id, ContainedPlaceholderImage as $g9l4l$ContainedPlaceholderImage, BackIcon as $g9l4l$BackIcon, SearchIcon as $g9l4l$SearchIcon, Layout as $g9l4l$Layout, make_placeholder_rr as $g9l4l$make_placeholder_rr, async_iterable_ipns as $g9l4l$async_iterable_ipns, make_asyncIterable_exiter as $g9l4l$make_asyncIterable_exiter, href_change_ipns as $g9l4l$href_change_ipns, depict_click as $g9l4l$depict_click, impression_track_element as $g9l4l$impression_track_element, add_new_tab_listener as $g9l4l$add_new_tab_listener, ContainedImage as $g9l4l$ContainedImage, buildThresholdList as $g9l4l$buildThresholdList, PreviousSearchIcon as $g9l4l$PreviousSearchIcon, insert_surfaces as $g9l4l$insert_surfaces, instant_exec_on_popstate_before_ipns_update as $g9l4l$instant_exec_on_popstate_before_ipns_update} from "@depict-ai/utilishared";
2
2
  import {template as $g9l4l$template, insert as $g9l4l$insert, effect as $g9l4l$effect, setAttribute as $g9l4l$setAttribute, createComponent as $g9l4l$createComponent, use as $g9l4l$use, addEventListener as $g9l4l$addEventListener, delegateEvents as $g9l4l$delegateEvents, spread as $g9l4l$spread, memo as $g9l4l$memo, style as $g9l4l$style, mergeProps as $g9l4l$mergeProps, classList as $g9l4l$classList, className as $g9l4l$className} from "solid-js/web";
3
3
  import {createSignal as $g9l4l$createSignal, onCleanup as $g9l4l$onCleanup, createComputed as $g9l4l$createComputed, createEffect as $g9l4l$createEffect, untrack as $g9l4l$untrack, batch as $g9l4l$batch, createResource as $g9l4l$createResource, createMemo as $g9l4l$createMemo, Show as $g9l4l$Show, createRenderEffect as $g9l4l$createRenderEffect, runWithOwner as $g9l4l$runWithOwner, createRoot as $g9l4l$createRoot, getOwner as $g9l4l$getOwner, on as $g9l4l$on, Index as $g9l4l$Index, For as $g9l4l$For, ErrorBoundary as $g9l4l$ErrorBoundary} from "solid-js";
4
4
 
5
- /*! Depict.ai search and product listing SDK */ const $18bf1d29fb889cca$export$83d89fbfd8236492 = "0.0.50";
5
+ /*! Depict.ai search and product listing SDK */ const $18bf1d29fb889cca$export$83d89fbfd8236492 = "0.0.52";
6
6
 
7
7
 
8
8
 
@@ -4092,7 +4092,7 @@ function $1b5a927e48d892a1$export$5646050d516e06bc({ crumb_data_: get_api_crumb_
4092
4092
  },
4093
4093
  get children () {
4094
4094
  const _el$2 = $1b5a927e48d892a1$var$_tmpl$2.cloneNode(true);
4095
- (0, $g9l4l$addEventListener)(_el$2, "click", (0, $g9l4l$catchify)((ev)=>on_crumb_click_?.(ev, display_accessor())), true);
4095
+ (0, $g9l4l$addEventListener)(_el$2, "click", (0, $g9l4l$catchify)((ev)=>on_crumb_click_(ev, display_accessor())), true);
4096
4096
  (0, $g9l4l$insert)(_el$2, ()=>display_accessor().title);
4097
4097
  (0, $g9l4l$effect)(()=>(0, $g9l4l$setAttribute)(_el$2, "href", display_accessor().page_url));
4098
4098
  return _el$2;
@@ -4259,7 +4259,7 @@ function $6af87e1502f04aa3$export$466cdc317043f6cb({ quicklinks_data_: quicklink
4259
4259
  const _el$3 = $6af87e1502f04aa3$var$_tmpl$3.cloneNode(true);
4260
4260
  (0, $g9l4l$addEventListener)(_el$3, "click", (0, $g9l4l$catchify)((ev)=>{
4261
4261
  if (is_selected() && !ev.metaKey && !ev.ctrlKey && !ev.button) ev.preventDefault();
4262
- else if (display_accessor()) on_link_click_?.(ev, display_accessor());
4262
+ else if (display_accessor()) on_link_click_(ev, display_accessor());
4263
4263
  }), true);
4264
4264
  (0, $g9l4l$insert)(_el$3, ()=>display_accessor()?.title || (0, $g9l4l$createComponent)((0, $g9l4l$Placeholder), {
4265
4265
  height: "1em",
@@ -4927,7 +4927,7 @@ class $ec52ef1e6c117813$export$f34314aa9505732e {
4927
4927
  category_scroll_restoration_data: []
4928
4928
  }));
4929
4929
  const i18n_ = (0, $0466953901b38f18$export$f091cbac892643a4)(this.#locale_signal[0]);
4930
- const on_category_link_click_ = on_navigation && ((e, d)=>{
4930
+ const on_category_link_click_ = (e, d)=>{
4931
4931
  if (e.button || e.metaKey || e.ctrlKey) // Not single page navigating because button is not 0 or ctrl/command click
4932
4932
  return;
4933
4933
  try {
@@ -4941,7 +4941,7 @@ class $ec52ef1e6c117813$export$f34314aa9505732e {
4941
4941
  throw e;
4942
4942
  });
4943
4943
  }
4944
- });
4944
+ };
4945
4945
  $ec52ef1e6c117813$var$privately_shared_properties.set(this, {
4946
4946
  state_: state_,
4947
4947
  breadcrumb_signal_: (0, $g9l4l$createSignal)(),
@@ -7691,6 +7691,47 @@ function $540e930c58500cbe$var$align_field_modal(style_props_, input, aligner_re
7691
7691
 
7692
7692
 
7693
7693
 
7694
+ const $7cbfd6122dbaf087$export$d86a33413558014e = (openModalParams)=>{
7695
+ const { search: search } = openModalParams;
7696
+ openModalParams.location ||= "centered";
7697
+ if (openModalParams.location === "aligned" && !openModalParams.element) throw new Error("No active search header aligner on the page!");
7698
+ if (openModalParams.location !== "aligned") {
7699
+ // Open centered modal
7700
+ search.modal_open = true;
7701
+ return;
7702
+ }
7703
+ const alignment_height = openModalParams.element?.clientHeight;
7704
+ if (isNaN(alignment_height)) {
7705
+ // Open centered modal
7706
+ search.modal_open = true;
7707
+ return;
7708
+ }
7709
+ const { cleanup: cleanup } = (0, $a79ffb1cbfbd2a09$export$828dcdd425fd3231)(openModalParams.element);
7710
+ let dispose_aligner;
7711
+ /**
7712
+ * Setting position to fixed is important to make the modal work with align_field.
7713
+ */ const style_props_ = (0, $g9l4l$createSignal)({
7714
+ position: "fixed"
7715
+ });
7716
+ (0, $g9l4l$createRoot)((dispose)=>{
7717
+ dispose_aligner = ()=>{
7718
+ cleanup();
7719
+ dispose();
7720
+ };
7721
+ (0, $079defea521d5e2a$export$b3ad617e9036868)(style_props_, openModalParams.element, undefined, false, "top");
7722
+ });
7723
+ search.modal_open = [
7724
+ {
7725
+ style_props_: style_props_
7726
+ },
7727
+ dispose_aligner
7728
+ ];
7729
+ };
7730
+
7731
+
7732
+
7733
+
7734
+
7694
7735
 
7695
7736
 
7696
7737
  function $a3e81b85527bc866$export$d0596b5cae35ad4(open_search) {
@@ -7746,16 +7787,28 @@ function $36316d7b4c897a03$export$689e204eadb33baf(pr) {
7746
7787
 
7747
7788
 
7748
7789
 
7790
+
7749
7791
  class $ba6c9609228edf6b$export$3ccaa27d81dd8556 extends EventTarget {
7750
7792
  #stop_listening;
7751
7793
  #disconnect_observer;
7752
7794
  #revert_fns = [];
7753
7795
  #is_open = false;
7796
+ static #reverting_permitted = true;
7797
+ static #visited_urls = (()=>{
7798
+ // Keep track of all URLs that have been visited since the document that was initially loaded (loaded means the process where the browser creates a new `document` and `window` object).
7799
+ (0, $g9l4l$catchify)(async ()=>{
7800
+ // Static initialization blocks aren't supported in MobileSafari yet, otherwise this would be one.
7801
+ // I don't think we want to ship a library that users are *forced* to transpile, it should run in the newest version of all major browsers. So we'll just use a function wrapper for now (adding "last 1 ios version" to the build target does a bunch of other unneeded transpilations).
7802
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Static_initialization_blocks
7803
+ for await (const href of (0, $g9l4l$href_change_ipns))$ba6c9609228edf6b$export$3ccaa27d81dd8556.#visited_urls.add($ba6c9609228edf6b$var$clean_url(href));
7804
+ })();
7805
+ return new Set();
7806
+ })();
7754
7807
  get open_() {
7755
7808
  // make this "readonly" from the outside, the only way to open is by making the url change so that is_page_ returns true
7756
7809
  return this.#is_open;
7757
7810
  }
7758
- #listen = (0, $g9l4l$deparallelize)(async ()=>{
7811
+ async #listen() {
7759
7812
  const [exit_ipns, exitable_ipns] = (0, $g9l4l$make_asyncIterable_exiter)((0, $g9l4l$href_change_ipns));
7760
7813
  this.#stop_listening = exit_ipns;
7761
7814
  const caught_page_check_fn = (0, $g9l4l$catchify)(this.is_page_, "page check failed");
@@ -7770,7 +7823,7 @@ class $ba6c9609228edf6b$export$3ccaa27d81dd8556 extends EventTarget {
7770
7823
  if (!els?.length) continue;
7771
7824
  this.#load(els);
7772
7825
  }
7773
- });
7826
+ }
7774
7827
  #get_rendered_recs() {
7775
7828
  return (0, $g9l4l$createRoot)((dispose)=>{
7776
7829
  this.addEventListener("close", dispose, {
@@ -7782,7 +7835,7 @@ class $ba6c9609228edf6b$export$3ccaa27d81dd8556 extends EventTarget {
7782
7835
  #unload = (0, $g9l4l$catchify)(()=>{
7783
7836
  if (!this.#is_open) return;
7784
7837
  this.#disconnect_observer?.();
7785
- while(this.#revert_fns.length)this.#revert_fns.pop()?.();
7838
+ if ($ba6c9609228edf6b$export$3ccaa27d81dd8556.#reverting_permitted) while(this.#revert_fns.length)this.#revert_fns.pop()?.();
7786
7839
  this.#is_open = false;
7787
7840
  this.dispatchEvent(new CustomEvent("close"));
7788
7841
  }, "failed to close page");
@@ -7826,24 +7879,68 @@ class $ba6c9609228edf6b$export$3ccaa27d81dd8556 extends EventTarget {
7826
7879
  document.title = this.title_;
7827
7880
  this.#revert_fns.push(()=>document.title = title);
7828
7881
  }
7882
+ this.#empty_page_bugfix();
7829
7883
  this.dispatchEvent(new CustomEvent("open")); // we dispatch this once it's actually open
7830
7884
  }, "failed to open page");
7885
+ #empty_page_bugfix() {
7886
+ // Called when a "page" is "opened" using PageReplacer
7887
+ const on_popstate = ()=>{
7888
+ const cleaned_current_url = $ba6c9609228edf6b$var$clean_url(location.href);
7889
+ if (!$ba6c9609228edf6b$export$3ccaa27d81dd8556.#visited_urls.has(cleaned_current_url)) {
7890
+ /*
7891
+ What does this check do? Well, there's a bug without it where people end up on a page that they shouldn't see (i.e. a somewhat blank page).
7892
+ The culprit of that bug is: the browser remembers where to make "hard"-navigations (i.e. a full document rebuild with a new `document` and `window` object) when going back and forwards in the history and where to just change the url. What it doesn't always correctly remember (observed in at least chrome and firefox), is *what document to hard-navigate to*. Therefore, we have to jump in and take care of that.
7893
+ The reproduction is this:
7894
+ Spendrups has set up a search page for us under /start/search. We don't want to hard-reload the page though to get there.
7895
+ 1. Go to a product page like https://ehandel.spendrups.se/Product/1104201
7896
+ 2. Make a search
7897
+ We use PageReplacer to just replace the current page (and the url in the address bar) with the search page. The thing is though, that when going back now the URL just updates but the browser doesn't actually navigate - that's fine, PageReplacer will just restore the old page contents
7898
+ 3. Try going back just for fun
7899
+ Everything works as expected
7900
+ 4. Go forwards again (just for fun, skip this if you skipped 3)
7901
+ Now the trouble comes when we introduce "hard" navigation.
7902
+ 5. Click on a search result. The browser will now "hard" navigate to a new page.
7903
+ 6. Click "back"
7904
+ What happens now, is that the browser hard-navigates to /start/search.
7905
+ 7. Click "back" again
7906
+ What happens now, is that the browser "soft-navigates" (just changes the URL) to https://ehandel.spendrups.se/Product/1104201
7907
+ PageReplacer unload itself and reveals the original document - it is expected to be the one under https://ehandel.spendrups.se/Product/1104201, but actually it is the one under /start/search - a white page.
7908
+ The fix: is to remember all pages that we have visited since the creation of `window` (we do that by having a static property on PageReplacer since we assume the PageReplacer source code will only exist once on the page and will be loaded at page load, the latter assumption might be wrong, but I hope not). If we get a "popstate" event (meaning back or forwards button, not new page being pushed) to an unknown page we reload.
7909
+ */ (0, $g9l4l$dlog)("PageReplacer: ", cleaned_current_url, " hasn't been visited since document was created, reloading since we won't be able to show this page otherwise");
7910
+ location.reload();
7911
+ $ba6c9609228edf6b$export$3ccaa27d81dd8556.#reverting_permitted = false; // Disallow any reverting since we can't - it would flash a white page until the reload has finished.
7912
+ this.#unload(); // Call unload to "kill" the solid-js stuff quicker so that it stops updating - the user is expecting the next thing that happens to be a navigation to the actual page the URL says they're on.
7913
+ }
7914
+ };
7915
+ (0, $g9l4l$instant_exec_on_popstate_before_ipns_update).add(on_popstate);
7916
+ this.#revert_fns.push(()=>(0, $g9l4l$instant_exec_on_popstate_before_ipns_update).delete(on_popstate)); // Don't run the bugfixer if there isn't a PageReplacer page open since I assume the bug only happens when going back from a PageReplace'd page
7917
+ }
7831
7918
  destroy_() {
7832
7919
  this.#unload();
7833
7920
  this.#stop_listening?.();
7834
7921
  }
7835
- constructor(options){
7922
+ constructor({ is_spa_: is_spa_ , ...options }){
7836
7923
  super();
7837
7924
  Object.assign(this, options);
7838
7925
  this.#listen().catch((e)=>(0, $g9l4l$report)([
7839
7926
  e,
7840
7927
  "Error listening to URL change"
7841
7928
  ], "error"));
7929
+ if (!is_spa_) (0, $36316d7b4c897a03$export$689e204eadb33baf)(this); // Make so that clicking on links to the previous page work instantly
7842
7930
  }
7843
7931
  }
7932
+ /**
7933
+ * Remove hash from URL
7934
+ */ function $ba6c9609228edf6b$var$clean_url(url) {
7935
+ // Would want to have this a static private function on PageReplacer, but that's not supported in MobileSafari yet
7936
+ // remove hash, all other params *could* trigger a re-request
7937
+ const url_object = new URL(url);
7938
+ url_object.hash = "";
7939
+ return url_object.href;
7940
+ }
7844
7941
 
7845
7942
 
7846
7943
  (0, $g9l4l$add_to_version_info)("ui", (0, $18bf1d29fb889cca$export$83d89fbfd8236492));
7847
7944
 
7848
7945
 
7849
- export {$e95f1855a5e3864f$export$58e808b1db15d3bc as fetchDepictRecommendations, $ec52ef1e6c117813$export$de035477d6fef996 as QuickLinks, $ec52ef1e6c117813$export$5b7c3ce0ebe51f86 as BreadCrumbs, $6340962f8bafd049$export$b91cec6bdc6b32a1 as CategoryTitle, $9b693ea3ff420de6$export$824dde41348efed1 as embedded_num_products, $80a49d3809ecb080$export$643598a327b5833e as get_categories, $ec52ef1e6c117813$export$f34314aa9505732e as DepictCategory, $ec52ef1e6c117813$export$e2bf8ce7779e827e as CategoryPage, $540e930c58500cbe$export$ac6237957a15b3f0 as DepictSearch, $540e930c58500cbe$export$bde3eb976dfded9 as get_search_query_updating_blocked_signal, $540e930c58500cbe$export$f2dc7e4e4aa11abb as SearchPage, $540e930c58500cbe$export$316f6f283ae64765 as SearchField, $aaacfac0064e97d9$export$ee1733d9a8495996 as DefaultInstantCardPlaceholder, $079defea521d5e2a$export$b3ad617e9036868 as align_field, $246aa49a8f85c7f3$export$1233ca167bf71f9a as styles_to_valid_css_string, $a1123c5ed9b0ee29$export$fe334901e8bebd57 as disable_scrolling, $a3e81b85527bc866$export$d0596b5cae35ad4 as open_search_with_shift_slash, $972ebf2641490830$export$26df08e242fe83a3 as push_state_with_query_param_value, $3846bb34098c738f$export$e015ed2f2950f2e3 as DepictAPI, $fefe7683325039b3$export$d9f36cf74e24d595 as media_query_to_accessor, $36316d7b4c897a03$export$689e204eadb33baf as setup_fast_leave, $886fa987db4f3805$export$5196e4c01fa56fcb as show_toast, $5178263ed1e394ca$export$469c7af753d08925 as solid_format_price, $a79ffb1cbfbd2a09$export$828dcdd425fd3231 as connect_search_field_height_to_aligner_height, $ba6c9609228edf6b$export$3ccaa27d81dd8556 as PageReplacer, $18bf1d29fb889cca$export$83d89fbfd8236492 as version};
7946
+ export {$e95f1855a5e3864f$export$58e808b1db15d3bc as fetchDepictRecommendations, $ec52ef1e6c117813$export$de035477d6fef996 as QuickLinks, $ec52ef1e6c117813$export$5b7c3ce0ebe51f86 as BreadCrumbs, $6340962f8bafd049$export$b91cec6bdc6b32a1 as CategoryTitle, $9b693ea3ff420de6$export$824dde41348efed1 as embedded_num_products, $80a49d3809ecb080$export$643598a327b5833e as get_categories, $ec52ef1e6c117813$export$f34314aa9505732e as DepictCategory, $ec52ef1e6c117813$export$e2bf8ce7779e827e as CategoryPage, $540e930c58500cbe$export$ac6237957a15b3f0 as DepictSearch, $540e930c58500cbe$export$bde3eb976dfded9 as get_search_query_updating_blocked_signal, $540e930c58500cbe$export$f2dc7e4e4aa11abb as SearchPage, $540e930c58500cbe$export$316f6f283ae64765 as SearchField, $7cbfd6122dbaf087$export$d86a33413558014e as open_modal_with_alignment, $aaacfac0064e97d9$export$ee1733d9a8495996 as DefaultInstantCardPlaceholder, $079defea521d5e2a$export$b3ad617e9036868 as align_field, $246aa49a8f85c7f3$export$1233ca167bf71f9a as styles_to_valid_css_string, $a1123c5ed9b0ee29$export$fe334901e8bebd57 as disable_scrolling, $a3e81b85527bc866$export$d0596b5cae35ad4 as open_search_with_shift_slash, $972ebf2641490830$export$26df08e242fe83a3 as push_state_with_query_param_value, $3846bb34098c738f$export$e015ed2f2950f2e3 as DepictAPI, $fefe7683325039b3$export$d9f36cf74e24d595 as media_query_to_accessor, $36316d7b4c897a03$export$689e204eadb33baf as setup_fast_leave, $886fa987db4f3805$export$5196e4c01fa56fcb as show_toast, $5178263ed1e394ca$export$469c7af753d08925 as solid_format_price, $a79ffb1cbfbd2a09$export$828dcdd425fd3231 as connect_search_field_height_to_aligner_height, $ba6c9609228edf6b$export$3ccaa27d81dd8556 as PageReplacer, $18bf1d29fb889cca$export$83d89fbfd8236492 as version};