@depict-ai/ui 4.10.13 → 4.10.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/latest/index.js CHANGED
@@ -7,7 +7,7 @@ import { createStore as $9HbP8$createStore, reconcile as $9HbP8$reconcile } from
7
7
  /*! Depict.ai search and product listing SDK */
8
8
  import { register_es_version } from "../multi_import_deduplicator.mjs";
9
9
  register_es_version("ui", "latest");
10
- const $ec291e0010116877$export$83d89fbfd8236492 = "4.10.13";
10
+ const $ec291e0010116877$export$83d89fbfd8236492 = "4.10.15";
11
11
  function $dc9262ef6f786812$export$d9f36cf74e24d595(media_query) {
12
12
  const signal = $9HbP8$createSignal(false);
13
13
  $9HbP8$onCleanup($9HbP8$javascript_media_query(media_query, ({
@@ -2596,17 +2596,49 @@ function $f8a56ba74eedd0c6$export$f21ab0717a89c77f(props) {
2596
2596
  return _el$;
2597
2597
  })();
2598
2598
  }
2599
+
2600
+ /**
2601
+ * Whether a click came from the keyboard (Enter/Space) rather than a pointer. Browsers report `detail === 0` for
2602
+ * keyboard-activated clicks and `>= 1` for real pointer clicks, so we use it to only move focus on keyboard opens.
2603
+ */
2604
+ function $5108d4f5c200e4e5$export$c5c485c196a19ec1(event) {
2605
+ return event.detail === 0;
2606
+ }
2599
2607
  var $67b173f9db427028$var$_tmpl$ = /*#__PURE__*/$9HbP8$template(`<button type=button class=expand-filter><!$><!/><span>`);
2600
2608
  function $67b173f9db427028$export$ca21a16d65b3dd8({
2601
2609
  show_extras_: [get_show_extras, set_show_extras],
2602
- i18n_: i18n_
2610
+ i18n_: i18n_,
2611
+ expanding_container_element_: expanding_container_element_
2603
2612
  }) {
2613
+ let button;
2604
2614
  return (() => {
2605
2615
  var _el$ = $9HbP8$getNextElement($67b173f9db427028$var$_tmpl$),
2606
2616
  _el$3 = _el$.firstChild,
2607
2617
  [_el$4, _co$] = $9HbP8$getNextMarker(_el$3.nextSibling),
2608
2618
  _el$2 = _el$4.nextSibling;
2609
- $9HbP8$addEventListener(_el$, "click", $9HbP8$catchify1(() => set_show_extras(p => !p)), true);
2619
+ $9HbP8$addEventListener(_el$, "click", $9HbP8$catchify1(event => {
2620
+ const container = expanding_container_element_;
2621
+ const will_expand = !get_show_extras();
2622
+ // Only move focus for keyboard activation; mouse users see the revealed options and don't need a focus jump.
2623
+ const keyboard_activated = $5108d4f5c200e4e5$export$c5c485c196a19ec1(event);
2624
+ const had_focus_inside = !!container && container.contains(document.activeElement);
2625
+ set_show_extras(will_expand);
2626
+ if (will_expand) {
2627
+ if (!keyboard_activated) return;
2628
+ // `expand()` (consumer's effect, runs after this) clears `inert`/`visibility`; focus the first revealed
2629
+ // option next frame so Tab continues through them. `preventScroll` so it doesn't yank the viewport down.
2630
+ requestAnimationFrame($9HbP8$catchify1(() => container?.querySelector("input:not(:disabled)")?.focus({
2631
+ preventScroll: true
2632
+ })));
2633
+ } else if (had_focus_inside)
2634
+ // Keep focus on the toggle when collapsing, otherwise making the container `inert` dumps focus to <body>
2635
+ // (Safari doesn't focus buttons on mouse click, so focus can still be inside the container here).
2636
+ button.focus({
2637
+ preventScroll: true
2638
+ });
2639
+ }), true);
2640
+ var _ref$ = button;
2641
+ typeof _ref$ === "function" ? $9HbP8$use(_ref$, _el$) : button = _el$;
2610
2642
  $9HbP8$insert(_el$, $9HbP8$createComponent((0, $f8a56ba74eedd0c6$export$f21ab0717a89c77f), {}), _el$4, _co$);
2611
2643
  $9HbP8$insert(_el$2, (() => {
2612
2644
  var _c$ = $9HbP8$memo(() => !!get_show_extras());
@@ -2796,7 +2828,8 @@ function $a4601e6bea3ed1e6$export$dd948f91abc3a1c4({
2796
2828
  $9HbP8$createRenderEffect(() => {
2797
2829
  set_view_more_button_below_group_(togglable_rest() ? $9HbP8$createComponent((0, $67b173f9db427028$export$ca21a16d65b3dd8), {
2798
2830
  show_extras_: show_extras_signal,
2799
- i18n_: i18n_
2831
+ i18n_: i18n_,
2832
+ expanding_container_element_: expanding_container_els
2800
2833
  }) : []);
2801
2834
  });
2802
2835
  $9HbP8$onCleanup(() => set_view_more_button_below_group_());
@@ -3725,7 +3758,8 @@ function $1c8cac76cb67e9af$export$a32086fa6616eafd({
3725
3758
  $9HbP8$createRenderEffect(() => {
3726
3759
  set_view_more_button_below_group(some_top_level_are_hidden() ? $9HbP8$createComponent((0, $67b173f9db427028$export$ca21a16d65b3dd8), {
3727
3760
  show_extras_: show_extras,
3728
- i18n_: i18n_
3761
+ i18n_: i18n_,
3762
+ expanding_container_element_: expanding_container_els
3729
3763
  }) : []);
3730
3764
  });
3731
3765
  $9HbP8$onCleanup(() => set_view_more_button_below_group());
@@ -4466,7 +4500,8 @@ function $e731d52d97444b7c$export$82ed4b28ecb67608({
4466
4500
  $9HbP8$createRenderEffect(() => {
4467
4501
  set_view_more_button_below_group_(togglable_rest() ? $9HbP8$createComponent((0, $67b173f9db427028$export$ca21a16d65b3dd8), {
4468
4502
  show_extras_: show_extras_signal,
4469
- i18n_: i18n_
4503
+ i18n_: i18n_,
4504
+ expanding_container_element_: expanding_container_els
4470
4505
  }) : []);
4471
4506
  });
4472
4507
  $9HbP8$onCleanup(() => set_view_more_button_below_group_());
@@ -5859,8 +5894,8 @@ var $11d594c749b76228$var$_tmpl$ = /*#__PURE__*/$9HbP8$template(`<svg xmlns=http
5859
5894
  function $11d594c749b76228$export$cb808a3c41736d8e() {
5860
5895
  return $9HbP8$getNextElement($11d594c749b76228$var$_tmpl$);
5861
5896
  }
5862
- var $cdea4cf8c5c071ef$var$_tmpl$ = /*#__PURE__*/$9HbP8$template(`<div class=filters><div class=body>`),
5863
- $cdea4cf8c5c071ef$var$_tmpl$2 = /*#__PURE__*/$9HbP8$template(`<div class=sorting><div class=body>`),
5897
+ var $cdea4cf8c5c071ef$var$_tmpl$ = /*#__PURE__*/$9HbP8$template(`<section class=filters tabindex=-1><div class=body>`),
5898
+ $cdea4cf8c5c071ef$var$_tmpl$2 = /*#__PURE__*/$9HbP8$template(`<section class=sorting tabindex=-1><div class=body>`),
5864
5899
  $cdea4cf8c5c071ef$var$_tmpl$3 = /*#__PURE__*/$9HbP8$template(`<div class=sort-and-filter-buttons><div class=outer><div class=inner><!$><!/><!$><!/><!$><!/>`),
5865
5900
  $cdea4cf8c5c071ef$var$_tmpl$4 = /*#__PURE__*/$9HbP8$template(`<span class=num-filters>`),
5866
5901
  $cdea4cf8c5c071ef$var$_tmpl$5 = /*#__PURE__*/$9HbP8$template(`<div class=open-filters-button-container><!$><!/><button type=button><!$><!/><span>`),
@@ -5929,6 +5964,39 @@ function $cdea4cf8c5c071ef$export$d7841e452f2e59a8({
5929
5964
  available_sortings_: available_sortings_,
5930
5965
  meta_of_currently_selected_sorting_: meta_of_currently_selected_sorting_
5931
5966
  };
5967
+ // Keyboard a11y for desktop panels (DOM order: after the grid). Keyboard opens move focus into the panel and
5968
+ // restore it to the toggle on close; mouse opens skip the focus move (already visible, and it'd jump the viewport).
5969
+ // `user_opened_panel` is set only on keyboard activation - `filters_open`/`sorting_open` also change
5970
+ // programmatically (undo toast, history restore, SDK consumers), which must not steal focus.
5971
+ let user_opened_panel = false;
5972
+ let filter_toggle_button;
5973
+ let sort_toggle_button;
5974
+ let current_panel;
5975
+ let current_panel_toggle_button;
5976
+ const focus_panel_when_in_dom = panel => $9HbP8$catchify1(async () => {
5977
+ // Bounded wait: if the panel never lands in the DOM (e.g. it's superseded by a rapid re-render before
5978
+ // insertion), the timeout lets this promise resolve instead of keeping `panel` - and the observer
5979
+ // listener it registers - referenced forever.
5980
+ await (0, $9HbP8$observer).wait_for_element(panel, 4000);
5981
+ if (panel.isConnected) {
5982
+ // Focus the first interactive control (often a group's <summary>), not the <section>: the section has no
5983
+ // visible focus ring and is flex-stretched to full grid height (scrolling it mis-aligns). The first
5984
+ // control keeps focus visible and lets Tab walk through the panel. `summary` is focusable but not matched
5985
+ // by the input/button/etc. selectors, so it must be listed explicitly.
5986
+ const focus_target = panel.querySelector("summary, input:not([type='hidden']):not(:disabled), button:not(:disabled), a[href], select:not(:disabled), [tabindex]:not([tabindex='-1'])") ?? panel;
5987
+ focus_target.focus({
5988
+ preventScroll: true
5989
+ });
5990
+ focus_target.scrollIntoView({
5991
+ block: "nearest"
5992
+ });
5993
+ }
5994
+ })();
5995
+ const register_desktop_panel = (el, toggle_button, should_focus) => {
5996
+ current_panel = el;
5997
+ current_panel_toggle_button = toggle_button;
5998
+ if (should_focus) focus_panel_when_in_dom(el);
5999
+ };
5932
6000
  const make_open_filters_button = (adhere_to_width = false, is_tabbable = false) => $9HbP8$createComponent($cdea4cf8c5c071ef$var$OpenFiltersButton, {
5933
6001
  tabindex_: is_tabbable ? 0 : -1,
5934
6002
  i18n_: i18n_,
@@ -5936,7 +6004,11 @@ function $cdea4cf8c5c071ef$export$d7841e452f2e59a8({
5936
6004
  search_sorting_open_: search_sorting_open_,
5937
6005
  number_of_rendered_selected_filters_items_: number_of_rendered_selected_filters_items_,
5938
6006
  filter_button_width_: adhere_to_width ? filter_button_width_ : undefined,
5939
- hide_button_: hide_filters
6007
+ hide_button_: hide_filters,
6008
+ on_user_open_: keyboard_activated => {
6009
+ if (keyboard_activated) user_opened_panel = true;
6010
+ },
6011
+ button_ref_: is_tabbable ? el => filter_toggle_button = el : undefined
5940
6012
  });
5941
6013
  const make_open_sorting_button = (adhere_to_width = false, is_tabbable = false) => $9HbP8$createComponent($cdea4cf8c5c071ef$var$OpenSortingButton, {
5942
6014
  tabindex_: is_tabbable ? 0 : -1,
@@ -5946,7 +6018,11 @@ function $cdea4cf8c5c071ef$export$d7841e452f2e59a8({
5946
6018
  search_sorting_open_: search_sorting_open_,
5947
6019
  available_sortings_: available_sortings_,
5948
6020
  sort_button_width_: adhere_to_width ? sort_button_width_ : undefined,
5949
- meta_of_currently_selected_sorting_: meta_of_currently_selected_sorting_
6021
+ meta_of_currently_selected_sorting_: meta_of_currently_selected_sorting_,
6022
+ on_user_open_: keyboard_activated => {
6023
+ if (keyboard_activated) user_opened_panel = true;
6024
+ },
6025
+ button_ref_: is_tabbable ? el => sort_toggle_button = el : undefined
5950
6026
  });
5951
6027
  let open_modal_;
5952
6028
  let close_modal_;
@@ -5965,9 +6041,48 @@ function $cdea4cf8c5c071ef$export$d7841e452f2e59a8({
5965
6041
  grid_is_in_original_state = true;
5966
6042
  }
5967
6043
  });
6044
+ // Restore focus to the relevant toggle button when a desktop panel is torn down while focus is still inside it.
6045
+ const restore_focus_to_toggle_on_close = () => {
6046
+ const button_to_restore = current_panel_toggle_button;
6047
+ const focus_was_in_panel = !!current_panel && current_panel.contains(document.activeElement);
6048
+ current_panel = undefined;
6049
+ current_panel_toggle_button = undefined;
6050
+ if (focus_was_in_panel && button_to_restore) button_to_restore.focus({
6051
+ preventScroll: true
6052
+ });
6053
+ };
6054
+ // `<section>` + `aria-label` so the panel announces itself ("Filters"/"Sort") when we move focus to it; the bare
6055
+ // tabindex=-1 container would otherwise give screen readers nothing to read on focus. A semantic <section> avoids
6056
+ // an ARIA role and the UA styling pitfalls of <fieldset>; the existing CSS targets the `.filters`/`.sorting` class.
6057
+ const FiltersPanel = should_focus => $9HbP8$createComponent((0, $9HbP8$Show), {
6058
+ get when() {
6059
+ return !hide_filters();
6060
+ },
6061
+ get children() {
6062
+ var _el$ = $9HbP8$getNextElement($cdea4cf8c5c071ef$var$_tmpl$),
6063
+ _el$2 = _el$.firstChild;
6064
+ $9HbP8$use(el => register_desktop_panel(el, filter_toggle_button, should_focus), _el$);
6065
+ $9HbP8$insert(_el$2, $9HbP8$createComponent((0, $fab07f52c9861732$export$3f1a388cc0d8fed7), filter_options));
6066
+ $9HbP8$effect(() => $9HbP8$setAttribute(_el$, "aria-label", i18n_.filters_()));
6067
+ return _el$;
6068
+ }
6069
+ });
6070
+ const SortingPanel = should_focus => (() => {
6071
+ var _el$3 = $9HbP8$getNextElement($cdea4cf8c5c071ef$var$_tmpl$2),
6072
+ _el$4 = _el$3.firstChild;
6073
+ $9HbP8$use(el => register_desktop_panel(el, sort_toggle_button, should_focus), _el$3);
6074
+ $9HbP8$insert(_el$4, $9HbP8$createComponent((0, $68a258a92b88f02f$export$1f1775a58da589b5), sorting_options));
6075
+ $9HbP8$effect(() => $9HbP8$setAttribute(_el$3, "aria-label", i18n_.sorting_text_()));
6076
+ return _el$3;
6077
+ })();
5968
6078
  $9HbP8$createEffect(() => {
6079
+ // Consume the user-intent flag once per run, in every branch, so a stale flag can't leak into a later
6080
+ // resize-triggered re-render and steal focus.
6081
+ const should_focus_panel = user_opened_panel;
6082
+ user_opened_panel = false;
5969
6083
  if (!filters_open() && !sorting_open()) {
5970
6084
  close_modal_?.();
6085
+ restore_focus_to_toggle_on_close();
5971
6086
  set_extra_els_in_results_container_("");
5972
6087
  } else if (isSmall()()) {
5973
6088
  if ($9HbP8$untrack(() => input_modal_open_?.())) {
@@ -5976,26 +6091,14 @@ function $cdea4cf8c5c071ef$export$d7841e452f2e59a8({
5976
6091
  set_sorting_open(false);
5977
6092
  return;
5978
6093
  }
6094
+ // The modal manages its own focus; just drop the desktop panel reference.
6095
+ current_panel = undefined;
6096
+ current_panel_toggle_button = undefined;
5979
6097
  open_modal_?.();
5980
6098
  set_extra_els_in_results_container_("");
5981
6099
  } else {
5982
6100
  close_modal_?.();
5983
- if (filters_open()) set_extra_els_in_results_container_(() => $9HbP8$createComponent((0, $9HbP8$Show), {
5984
- get when() {
5985
- return !hide_filters();
5986
- },
5987
- get children() {
5988
- var _el$ = $9HbP8$getNextElement($cdea4cf8c5c071ef$var$_tmpl$),
5989
- _el$2 = _el$.firstChild;
5990
- $9HbP8$insert(_el$2, $9HbP8$createComponent((0, $fab07f52c9861732$export$3f1a388cc0d8fed7), filter_options));
5991
- return _el$;
5992
- }
5993
- }));else set_extra_els_in_results_container_((() => {
5994
- var _el$3 = $9HbP8$getNextElement($cdea4cf8c5c071ef$var$_tmpl$2),
5995
- _el$4 = _el$3.firstChild;
5996
- $9HbP8$insert(_el$4, $9HbP8$createComponent((0, $68a258a92b88f02f$export$1f1775a58da589b5), sorting_options));
5997
- return _el$3;
5998
- })());
6101
+ if (filters_open()) set_extra_els_in_results_container_(() => FiltersPanel(should_focus_panel));else set_extra_els_in_results_container_(SortingPanel(should_focus_panel));
5999
6102
  }
6000
6103
  });
6001
6104
  $9HbP8$catchify1(async () => {
@@ -6059,7 +6162,9 @@ function $cdea4cf8c5c071ef$var$OpenFiltersButton({
6059
6162
  filter_button_width_: filter_button_width_,
6060
6163
  tabindex_: tabindex_,
6061
6164
  number_of_rendered_selected_filters_items_: [num_selected_filters],
6062
- hide_button_: hide_button_
6165
+ hide_button_: hide_button_,
6166
+ on_user_open_: on_user_open_,
6167
+ button_ref_: button_ref_
6063
6168
  }) {
6064
6169
  const filters_closed = $9HbP8$createMemo(() => !search_filters_open_[0]());
6065
6170
  // We assume in embedded_num_products that this is a single HTMLElement so pls change there if you change it here
@@ -6082,14 +6187,18 @@ function $cdea4cf8c5c071ef$var$OpenFiltersButton({
6082
6187
  return _el$15;
6083
6188
  }
6084
6189
  }), _el$21, _co$5);
6085
- $9HbP8$addEventListener(_el$16, "click", $9HbP8$catchify1(() => $9HbP8$batch(() => {
6190
+ $9HbP8$addEventListener(_el$16, "click", $9HbP8$catchify1(event => $9HbP8$batch(() => {
6086
6191
  if (search_filters_open_[0]()) {
6087
6192
  search_filters_open_[1](false);
6088
6193
  return;
6089
6194
  }
6195
+ // Only keyboard opens move focus into the panel; mouse opens leave the viewport put.
6196
+ on_user_open_?.($5108d4f5c200e4e5$export$c5c485c196a19ec1(event));
6090
6197
  search_filters_open_[1](true);
6091
6198
  search_sorting_open_[1](false);
6092
6199
  })), true);
6200
+ var _ref$2 = button_ref_;
6201
+ typeof _ref$2 === "function" ? $9HbP8$use(_ref$2, _el$16) : button_ref_ = _el$16;
6093
6202
  $9HbP8$setAttribute(_el$16, "tabindex", tabindex_);
6094
6203
  $9HbP8$className(_el$16, "filter toggle-button major-minor-transition" + ((0, $9HbP8$isServer) ? " minor" : ""));
6095
6204
  $9HbP8$insert(_el$16, $9HbP8$createComponent((0, $9HbP8$Show), {
@@ -6111,21 +6220,24 @@ function $cdea4cf8c5c071ef$var$OpenFiltersButton({
6111
6220
  _v$2 = !filters_closed(),
6112
6221
  _v$3 = !!filters_closed(),
6113
6222
  _v$4 = (filters_closed() ? i18n_.open_filters_ : i18n_.close_filters_)(),
6114
- _v$5 = filter_button_width_?.() ? {
6223
+ _v$5 = !filters_closed(),
6224
+ _v$6 = filter_button_width_?.() ? {
6115
6225
  width: filter_button_width_() + "px"
6116
6226
  } : {};
6117
6227
  _p$.e = $9HbP8$style(_el$14, _v$, _p$.e);
6118
6228
  _v$2 !== _p$.t && _el$16.classList.toggle("major", _p$.t = _v$2);
6119
6229
  _v$3 !== _p$.a && _el$16.classList.toggle("minor", _p$.a = _v$3);
6120
6230
  _v$4 !== _p$.o && $9HbP8$setAttribute(_el$16, "aria-label", _p$.o = _v$4);
6121
- _p$.i = $9HbP8$style(_el$16, _v$5, _p$.i);
6231
+ _v$5 !== _p$.i && $9HbP8$setAttribute(_el$16, "aria-expanded", _p$.i = _v$5);
6232
+ _p$.n = $9HbP8$style(_el$16, _v$6, _p$.n);
6122
6233
  return _p$;
6123
6234
  }, {
6124
6235
  e: undefined,
6125
6236
  t: undefined,
6126
6237
  a: undefined,
6127
6238
  o: undefined,
6128
- i: undefined
6239
+ i: undefined,
6240
+ n: undefined
6129
6241
  });
6130
6242
  $9HbP8$runHydrationEvents();
6131
6243
  return _el$14;
@@ -6139,7 +6251,9 @@ function $cdea4cf8c5c071ef$var$OpenSortingButton({
6139
6251
  search_sorting_open_: search_sorting_open_,
6140
6252
  sort_button_width_: sort_button_width_,
6141
6253
  tabindex_: tabindex_,
6142
- meta_of_currently_selected_sorting_: meta_of_currently_selected_sorting_
6254
+ meta_of_currently_selected_sorting_: meta_of_currently_selected_sorting_,
6255
+ on_user_open_: on_user_open_,
6256
+ button_ref_: button_ref_
6143
6257
  }) {
6144
6258
  const sorting_closed = $9HbP8$createMemo(() => !search_sorting_open_[0]());
6145
6259
  // We assume in embedded_num_products that this is a single HTMLElement so pls change there if you change it here
@@ -6148,14 +6262,18 @@ function $cdea4cf8c5c071ef$var$OpenSortingButton({
6148
6262
  _el$23 = _el$22.firstChild,
6149
6263
  _el$24 = _el$23.nextSibling,
6150
6264
  [_el$25, _co$6] = $9HbP8$getNextMarker(_el$24.nextSibling);
6151
- $9HbP8$addEventListener(_el$22, "click", $9HbP8$catchify1(() => $9HbP8$batch(() => {
6265
+ $9HbP8$addEventListener(_el$22, "click", $9HbP8$catchify1(event => $9HbP8$batch(() => {
6152
6266
  if (search_sorting_open_[0]()) {
6153
6267
  search_sorting_open_[1](false);
6154
6268
  return;
6155
6269
  }
6270
+ // Only keyboard opens move focus into the panel; mouse opens leave the viewport put.
6271
+ on_user_open_?.($5108d4f5c200e4e5$export$c5c485c196a19ec1(event));
6156
6272
  search_filters_open_[1](false);
6157
6273
  search_sorting_open_[1](true);
6158
6274
  })), true);
6275
+ var _ref$3 = button_ref_;
6276
+ typeof _ref$3 === "function" ? $9HbP8$use(_ref$3, _el$22) : button_ref_ = _el$22;
6159
6277
  $9HbP8$setAttribute(_el$22, "tabindex", tabindex_);
6160
6278
  $9HbP8$insert(_el$23, (() => {
6161
6279
  var _c$ = $9HbP8$memo(() => !!sorting_closed());
@@ -6177,22 +6295,25 @@ function $cdea4cf8c5c071ef$var$OpenSortingButton({
6177
6295
  }
6178
6296
  }), _el$25, _co$6);
6179
6297
  $9HbP8$effect(_p$ => {
6180
- var _v$6 = !sorting_closed(),
6181
- _v$7 = !!sorting_closed(),
6182
- _v$8 = (sorting_closed() ? i18n_.open_sorting_ : i18n_.close_sorting_)(),
6183
- _v$9 = sort_button_width_?.() ? {
6298
+ var _v$7 = !sorting_closed(),
6299
+ _v$8 = !!sorting_closed(),
6300
+ _v$9 = (sorting_closed() ? i18n_.open_sorting_ : i18n_.close_sorting_)(),
6301
+ _v$10 = !sorting_closed(),
6302
+ _v$11 = sort_button_width_?.() ? {
6184
6303
  width: sort_button_width_() + "px"
6185
6304
  } : {};
6186
- _v$6 !== _p$.e && _el$22.classList.toggle("major", _p$.e = _v$6);
6187
- _v$7 !== _p$.t && _el$22.classList.toggle("minor", _p$.t = _v$7);
6188
- _v$8 !== _p$.a && $9HbP8$setAttribute(_el$22, "aria-label", _p$.a = _v$8);
6189
- _p$.o = $9HbP8$style(_el$22, _v$9, _p$.o);
6305
+ _v$7 !== _p$.e && _el$22.classList.toggle("major", _p$.e = _v$7);
6306
+ _v$8 !== _p$.t && _el$22.classList.toggle("minor", _p$.t = _v$8);
6307
+ _v$9 !== _p$.a && $9HbP8$setAttribute(_el$22, "aria-label", _p$.a = _v$9);
6308
+ _v$10 !== _p$.o && $9HbP8$setAttribute(_el$22, "aria-expanded", _p$.o = _v$10);
6309
+ _p$.i = $9HbP8$style(_el$22, _v$11, _p$.i);
6190
6310
  return _p$;
6191
6311
  }, {
6192
6312
  e: undefined,
6193
6313
  t: undefined,
6194
6314
  a: undefined,
6195
- o: undefined
6315
+ o: undefined,
6316
+ i: undefined
6196
6317
  });
6197
6318
  $9HbP8$runHydrationEvents();
6198
6319
  return _el$22;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@depict-ai/ui",
3
- "version": "4.10.13",
3
+ "version": "4.10.15",
4
4
  "description": "Base package containing the actual UI of Depict UI along with translations, with a not-simplified API. We recommend using @depict-ai/js-ui or @depict-ai/react-ui instead.",
5
5
  "packageManager": "yarn@3.2.2",
6
6
  "sass": "styles.scss",
@@ -236,9 +236,9 @@
236
236
  "@babel/plugin-transform-typescript": "^7.22.9",
237
237
  "@babel/runtime": "latest",
238
238
  "@babel/runtime-corejs3": "^7.22.6",
239
- "@depict-ai/plp-styling": "^4.6.5",
240
- "@depict-ai/types": "^5.6.3",
241
- "@depict-ai/utilishared": "^2.6.3",
239
+ "@depict-ai/plp-styling": "^4.6.7",
240
+ "@depict-ai/types": "^5.6.5",
241
+ "@depict-ai/utilishared": "^2.6.5",
242
242
  "solid-js": "^1.9.10"
243
243
  },
244
244
  "type": "module",
@@ -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.13";
118
+ const $aa1938dec50d6c12$export$83d89fbfd8236492 = "4.10.15";
119
119
  function $3e17e871e9d0911d$export$d9f36cf74e24d595(media_query) {
120
120
  const signal = $3hooq$solidjs.createSignal(false);
121
121
  $3hooq$solidjs.onCleanup($3hooq$depictaiutilishared.javascript_media_query(media_query, _ref => {
@@ -2315,8 +2315,10 @@ var $b0477e4d832f090e$var$_tmpl$ = ["<button", ' type="button" class="', '"', ">
2315
2315
  function $b0477e4d832f090e$export$ca21a16d65b3dd8(_ref) {
2316
2316
  let {
2317
2317
  show_extras_: [get_show_extras, set_show_extras],
2318
- i18n_: i18n_
2318
+ i18n_: i18n_,
2319
+ expanding_container_element_: expanding_container_element_
2319
2320
  } = _ref;
2321
+ let button;
2320
2322
  return $3hooq$solidjsweb.ssr($b0477e4d832f090e$var$_tmpl$, $3hooq$solidjsweb.ssrHydrationKey(), "expand-filter ".concat(get_show_extras() ? "expanded" : ""), $3hooq$solidjsweb.ssrAttribute("aria-expanded", $3hooq$solidjsweb.escape(get_show_extras(), true), false), $3hooq$solidjsweb.escape($3hooq$solidjsweb.createComponent((0, $a904ccf883f60231$export$f21ab0717a89c77f), {})), get_show_extras() ? $3hooq$solidjsweb.escape(i18n_.view_less_()) : $3hooq$solidjsweb.escape(i18n_.view_more_()));
2321
2323
  }
2322
2324
  var $23c3f390dee33ff8$var$_tmpl$ = ["<span", ' class="highlighted-part">', "</span>"],
@@ -2478,7 +2480,8 @@ function $9b54b9c5381716eb$export$dd948f91abc3a1c4(_ref) {
2478
2480
  $3hooq$solidjs.createRenderEffect(() => {
2479
2481
  set_view_more_button_below_group_(togglable_rest() ? $3hooq$solidjsweb.createComponent((0, $b0477e4d832f090e$export$ca21a16d65b3dd8), {
2480
2482
  show_extras_: show_extras_signal,
2481
- i18n_: i18n_
2483
+ i18n_: i18n_,
2484
+ expanding_container_element_: expanding_container_els
2482
2485
  }) : []);
2483
2486
  });
2484
2487
  $3hooq$solidjs.onCleanup(() => set_view_more_button_below_group_());
@@ -3195,7 +3198,8 @@ function $9c1d49181fb3002a$export$a32086fa6616eafd(_ref) {
3195
3198
  $3hooq$solidjs.createRenderEffect(() => {
3196
3199
  set_view_more_button_below_group(some_top_level_are_hidden() ? $3hooq$solidjsweb.createComponent((0, $b0477e4d832f090e$export$ca21a16d65b3dd8), {
3197
3200
  show_extras_: show_extras,
3198
- i18n_: i18n_
3201
+ i18n_: i18n_,
3202
+ expanding_container_element_: expanding_container_els
3199
3203
  }) : []);
3200
3204
  });
3201
3205
  $3hooq$solidjs.onCleanup(() => set_view_more_button_below_group());
@@ -3921,7 +3925,8 @@ function $476a3dc36afc1938$export$82ed4b28ecb67608(_ref) {
3921
3925
  $3hooq$solidjs.createRenderEffect(() => {
3922
3926
  set_view_more_button_below_group_(togglable_rest() ? $3hooq$solidjsweb.createComponent((0, $b0477e4d832f090e$export$ca21a16d65b3dd8), {
3923
3927
  show_extras_: show_extras_signal,
3924
- i18n_: i18n_
3928
+ i18n_: i18n_,
3929
+ expanding_container_element_: expanding_container_els
3925
3930
  }) : []);
3926
3931
  });
3927
3932
  $3hooq$solidjs.onCleanup(() => set_view_more_button_below_group_());
@@ -5004,8 +5009,8 @@ var $ae261a5687fe84f4$var$_tmpl$ = ["<svg", ' xmlns="http://www.w3.org/2000/svg"
5004
5009
  function $ae261a5687fe84f4$export$cb808a3c41736d8e() {
5005
5010
  return $3hooq$solidjsweb.ssr($ae261a5687fe84f4$var$_tmpl$, $3hooq$solidjsweb.ssrHydrationKey());
5006
5011
  }
5007
- var $f70cc6dfcadf0fd5$var$_tmpl$ = ["<div", ' class="filters"><div class="body">', "</div></div>"],
5008
- $f70cc6dfcadf0fd5$var$_tmpl$2 = ["<div", ' class="sorting"><div class="body">', "</div></div>"],
5012
+ var $f70cc6dfcadf0fd5$var$_tmpl$ = ["<section", ' class="filters"', '><div class="body">', "</div></section>"],
5013
+ $f70cc6dfcadf0fd5$var$_tmpl$2 = ["<section", ' class="sorting"', '><div class="body">', "</div></section>"],
5009
5014
  $f70cc6dfcadf0fd5$var$_tmpl$3 = ["<div", ' class="sort-and-filter-buttons"><div class="outer"><div class="inner"><!--$-->', "<!--/--><!--$-->", "<!--/--><!--$-->", "<!--/--></div></div></div>"],
5010
5015
  $f70cc6dfcadf0fd5$var$_tmpl$4 = ["<span", ' class="num-filters" style="', '">', "</span>"],
5011
5016
  $f70cc6dfcadf0fd5$var$_tmpl$5 = ["<div", ' class="open-filters-button-container" style="', '"><!--$-->', "<!--/--><button", ' class="', '" type="button"', ' style="', '"><!--$-->', "<!--/--><span>", "</span></button></div>"],
@@ -5082,6 +5087,40 @@ function $f70cc6dfcadf0fd5$export$d7841e452f2e59a8(_ref) {
5082
5087
  available_sortings_: available_sortings_,
5083
5088
  meta_of_currently_selected_sorting_: meta_of_currently_selected_sorting_
5084
5089
  };
5090
+ // Keyboard a11y for desktop panels (DOM order: after the grid). Keyboard opens move focus into the panel and
5091
+ // restore it to the toggle on close; mouse opens skip the focus move (already visible, and it'd jump the viewport).
5092
+ // `user_opened_panel` is set only on keyboard activation - `filters_open`/`sorting_open` also change
5093
+ // programmatically (undo toast, history restore, SDK consumers), which must not steal focus.
5094
+ let user_opened_panel = false;
5095
+ let filter_toggle_button;
5096
+ let sort_toggle_button;
5097
+ let current_panel;
5098
+ let current_panel_toggle_button;
5099
+ const focus_panel_when_in_dom = panel => $3hooq$depictaiutilishared.catchify(async () => {
5100
+ // Bounded wait: if the panel never lands in the DOM (e.g. it's superseded by a rapid re-render before
5101
+ // insertion), the timeout lets this promise resolve instead of keeping `panel` - and the observer
5102
+ // listener it registers - referenced forever.
5103
+ await (0, $3hooq$depictaiutilishared.observer).wait_for_element(panel, 4000);
5104
+ if (panel.isConnected) {
5105
+ var _panel$querySelector;
5106
+ // Focus the first interactive control (often a group's <summary>), not the <section>: the section has no
5107
+ // visible focus ring and is flex-stretched to full grid height (scrolling it mis-aligns). The first
5108
+ // control keeps focus visible and lets Tab walk through the panel. `summary` is focusable but not matched
5109
+ // by the input/button/etc. selectors, so it must be listed explicitly.
5110
+ const focus_target = (_panel$querySelector = panel.querySelector("summary, input:not([type='hidden']):not(:disabled), button:not(:disabled), a[href], select:not(:disabled), [tabindex]:not([tabindex='-1'])")) !== null && _panel$querySelector !== void 0 ? _panel$querySelector : panel;
5111
+ focus_target.focus({
5112
+ preventScroll: true
5113
+ });
5114
+ focus_target.scrollIntoView({
5115
+ block: "nearest"
5116
+ });
5117
+ }
5118
+ })();
5119
+ const register_desktop_panel = (el, toggle_button, should_focus) => {
5120
+ current_panel = el;
5121
+ current_panel_toggle_button = toggle_button;
5122
+ if (should_focus) focus_panel_when_in_dom(el);
5123
+ };
5085
5124
  const make_open_filters_button = function () {
5086
5125
  let adhere_to_width = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
5087
5126
  let is_tabbable = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
@@ -5092,7 +5131,11 @@ function $f70cc6dfcadf0fd5$export$d7841e452f2e59a8(_ref) {
5092
5131
  search_sorting_open_: search_sorting_open_,
5093
5132
  number_of_rendered_selected_filters_items_: number_of_rendered_selected_filters_items_,
5094
5133
  filter_button_width_: adhere_to_width ? filter_button_width_ : undefined,
5095
- hide_button_: hide_filters
5134
+ hide_button_: hide_filters,
5135
+ on_user_open_: keyboard_activated => {
5136
+ if (keyboard_activated) user_opened_panel = true;
5137
+ },
5138
+ button_ref_: is_tabbable ? el => filter_toggle_button = el : undefined
5096
5139
  });
5097
5140
  };
5098
5141
  const make_open_sorting_button = function () {
@@ -5106,7 +5149,11 @@ function $f70cc6dfcadf0fd5$export$d7841e452f2e59a8(_ref) {
5106
5149
  search_sorting_open_: search_sorting_open_,
5107
5150
  available_sortings_: available_sortings_,
5108
5151
  sort_button_width_: adhere_to_width ? sort_button_width_ : undefined,
5109
- meta_of_currently_selected_sorting_: meta_of_currently_selected_sorting_
5152
+ meta_of_currently_selected_sorting_: meta_of_currently_selected_sorting_,
5153
+ on_user_open_: keyboard_activated => {
5154
+ if (keyboard_activated) user_opened_panel = true;
5155
+ },
5156
+ button_ref_: is_tabbable ? el => sort_toggle_button = el : undefined
5110
5157
  });
5111
5158
  };
5112
5159
  let open_modal_;
@@ -5126,10 +5173,37 @@ function $f70cc6dfcadf0fd5$export$d7841e452f2e59a8(_ref) {
5126
5173
  grid_is_in_original_state = true;
5127
5174
  }
5128
5175
  });
5176
+ // Restore focus to the relevant toggle button when a desktop panel is torn down while focus is still inside it.
5177
+ const restore_focus_to_toggle_on_close = () => {
5178
+ const button_to_restore = current_panel_toggle_button;
5179
+ const focus_was_in_panel = !!current_panel && current_panel.contains(document.activeElement);
5180
+ current_panel = undefined;
5181
+ current_panel_toggle_button = undefined;
5182
+ if (focus_was_in_panel && button_to_restore) button_to_restore.focus({
5183
+ preventScroll: true
5184
+ });
5185
+ };
5186
+ // `<section>` + `aria-label` so the panel announces itself ("Filters"/"Sort") when we move focus to it; the bare
5187
+ // tabindex=-1 container would otherwise give screen readers nothing to read on focus. A semantic <section> avoids
5188
+ // an ARIA role and the UA styling pitfalls of <fieldset>; the existing CSS targets the `.filters`/`.sorting` class.
5189
+ const FiltersPanel = should_focus => $3hooq$solidjsweb.createComponent((0, $3hooq$solidjs.Show), {
5190
+ get when() {
5191
+ return !hide_filters();
5192
+ },
5193
+ get children() {
5194
+ return $3hooq$solidjsweb.ssr($f70cc6dfcadf0fd5$var$_tmpl$, $3hooq$solidjsweb.ssrHydrationKey(), $3hooq$solidjsweb.ssrAttribute("tabindex", -1, false) + $3hooq$solidjsweb.ssrAttribute("aria-label", $3hooq$solidjsweb.escape(i18n_.filters_(), true), false), $3hooq$solidjsweb.escape($3hooq$solidjsweb.createComponent((0, $164d474fea221d77$export$3f1a388cc0d8fed7), filter_options)));
5195
+ }
5196
+ });
5197
+ const SortingPanel = should_focus => $3hooq$solidjsweb.ssr($f70cc6dfcadf0fd5$var$_tmpl$2, $3hooq$solidjsweb.ssrHydrationKey(), $3hooq$solidjsweb.ssrAttribute("tabindex", -1, false) + $3hooq$solidjsweb.ssrAttribute("aria-label", $3hooq$solidjsweb.escape(i18n_.sorting_text_(), true), false), $3hooq$solidjsweb.escape($3hooq$solidjsweb.createComponent((0, $3bd211c857f81dfd$export$1f1775a58da589b5), sorting_options)));
5129
5198
  $3hooq$solidjs.createEffect(() => {
5199
+ // Consume the user-intent flag once per run, in every branch, so a stale flag can't leak into a later
5200
+ // resize-triggered re-render and steal focus.
5201
+ const should_focus_panel = user_opened_panel;
5202
+ user_opened_panel = false;
5130
5203
  if (!filters_open() && !sorting_open()) {
5131
5204
  var _close_modal_;
5132
5205
  (_close_modal_ = close_modal_) === null || _close_modal_ === void 0 || _close_modal_();
5206
+ restore_focus_to_toggle_on_close();
5133
5207
  set_extra_els_in_results_container_("");
5134
5208
  } else if (isSmall()()) {
5135
5209
  var _open_modal_;
@@ -5139,19 +5213,15 @@ function $f70cc6dfcadf0fd5$export$d7841e452f2e59a8(_ref) {
5139
5213
  set_sorting_open(false);
5140
5214
  return;
5141
5215
  }
5216
+ // The modal manages its own focus; just drop the desktop panel reference.
5217
+ current_panel = undefined;
5218
+ current_panel_toggle_button = undefined;
5142
5219
  (_open_modal_ = open_modal_) === null || _open_modal_ === void 0 || _open_modal_();
5143
5220
  set_extra_els_in_results_container_("");
5144
5221
  } else {
5145
5222
  var _close_modal_2;
5146
5223
  (_close_modal_2 = close_modal_) === null || _close_modal_2 === void 0 || _close_modal_2();
5147
- if (filters_open()) set_extra_els_in_results_container_(() => $3hooq$solidjsweb.createComponent((0, $3hooq$solidjs.Show), {
5148
- get when() {
5149
- return !hide_filters();
5150
- },
5151
- get children() {
5152
- return $3hooq$solidjsweb.ssr($f70cc6dfcadf0fd5$var$_tmpl$, $3hooq$solidjsweb.ssrHydrationKey(), $3hooq$solidjsweb.escape($3hooq$solidjsweb.createComponent((0, $164d474fea221d77$export$3f1a388cc0d8fed7), filter_options)));
5153
- }
5154
- }));else set_extra_els_in_results_container_($3hooq$solidjsweb.ssr($f70cc6dfcadf0fd5$var$_tmpl$2, $3hooq$solidjsweb.ssrHydrationKey(), $3hooq$solidjsweb.escape($3hooq$solidjsweb.createComponent((0, $3bd211c857f81dfd$export$1f1775a58da589b5), sorting_options))));
5224
+ if (filters_open()) set_extra_els_in_results_container_(() => FiltersPanel(should_focus_panel));else set_extra_els_in_results_container_(SortingPanel(should_focus_panel));
5155
5225
  }
5156
5226
  });
5157
5227
  $3hooq$depictaiutilishared.catchify(async () => {
@@ -5202,7 +5272,9 @@ function $f70cc6dfcadf0fd5$var$OpenFiltersButton(_ref3) {
5202
5272
  filter_button_width_: filter_button_width_,
5203
5273
  tabindex_: tabindex_,
5204
5274
  number_of_rendered_selected_filters_items_: [num_selected_filters],
5205
- hide_button_: hide_button_
5275
+ hide_button_: hide_button_,
5276
+ on_user_open_: on_user_open_,
5277
+ button_ref_: button_ref_
5206
5278
  } = _ref3;
5207
5279
  const filters_closed = $3hooq$solidjs.createMemo(() => !search_filters_open_[0]());
5208
5280
  // We assume in embedded_num_products that this is a single HTMLElement so pls change there if you change it here
@@ -5215,7 +5287,7 @@ function $f70cc6dfcadf0fd5$var$OpenFiltersButton(_ref3) {
5215
5287
  get children() {
5216
5288
  return $3hooq$solidjsweb.ssr($f70cc6dfcadf0fd5$var$_tmpl$4, $3hooq$solidjsweb.ssrHydrationKey(), $3hooq$solidjsweb.ssrStyle(filters_closed() ? "" : "opacity:0"), $3hooq$solidjsweb.escape(num_selected_filters()));
5217
5289
  }
5218
- })), $3hooq$solidjsweb.ssrAttribute("tabindex", $3hooq$solidjsweb.escape(tabindex_, true), false), "".concat(!filters_closed() ? "major" : "", " ").concat(filters_closed() ? "minor" : "").concat("filter toggle-button major-minor-transition" + ((0, $3hooq$solidjsweb.isServer) ? " minor" : "")), $3hooq$solidjsweb.ssrAttribute("aria-label", $3hooq$solidjsweb.escape((filters_closed() ? i18n_.open_filters_ : i18n_.close_filters_)(), true), false), $3hooq$solidjsweb.ssrStyle(filter_button_width_ !== null && filter_button_width_ !== void 0 && filter_button_width_() ? {
5290
+ })), $3hooq$solidjsweb.ssrAttribute("tabindex", $3hooq$solidjsweb.escape(tabindex_, true), false), "".concat(!filters_closed() ? "major" : "", " ").concat(filters_closed() ? "minor" : "").concat("filter toggle-button major-minor-transition" + ((0, $3hooq$solidjsweb.isServer) ? " minor" : "")), $3hooq$solidjsweb.ssrAttribute("aria-label", $3hooq$solidjsweb.escape((filters_closed() ? i18n_.open_filters_ : i18n_.close_filters_)(), true), false) + $3hooq$solidjsweb.ssrAttribute("aria-expanded", !filters_closed(), false), $3hooq$solidjsweb.ssrStyle(filter_button_width_ !== null && filter_button_width_ !== void 0 && filter_button_width_() ? {
5219
5291
  width: filter_button_width_() + "px"
5220
5292
  } : {}), $3hooq$solidjsweb.escape($3hooq$solidjsweb.createComponent((0, $3hooq$solidjs.Show), {
5221
5293
  get when() {
@@ -5239,11 +5311,13 @@ function $f70cc6dfcadf0fd5$var$OpenSortingButton(_ref4) {
5239
5311
  search_sorting_open_: search_sorting_open_,
5240
5312
  sort_button_width_: sort_button_width_,
5241
5313
  tabindex_: tabindex_,
5242
- meta_of_currently_selected_sorting_: meta_of_currently_selected_sorting_
5314
+ meta_of_currently_selected_sorting_: meta_of_currently_selected_sorting_,
5315
+ on_user_open_: on_user_open_,
5316
+ button_ref_: button_ref_
5243
5317
  } = _ref4;
5244
5318
  const sorting_closed = $3hooq$solidjs.createMemo(() => !search_sorting_open_[0]());
5245
5319
  // We assume in embedded_num_products that this is a single HTMLElement so pls change there if you change it here
5246
- return $3hooq$solidjsweb.ssr($f70cc6dfcadf0fd5$var$_tmpl$6, $3hooq$solidjsweb.ssrHydrationKey(), "".concat(!sorting_closed() ? "major" : "", " ").concat(sorting_closed() ? "minor" : "", " toggle-button for-sorting major-minor-transition"), $3hooq$solidjsweb.ssrAttribute("tabindex", $3hooq$solidjsweb.escape(tabindex_, true), false) + $3hooq$solidjsweb.ssrAttribute("aria-label", $3hooq$solidjsweb.escape((sorting_closed() ? i18n_.open_sorting_ : i18n_.close_sorting_)(), true), false), $3hooq$solidjsweb.ssrStyle(sort_button_width_ !== null && sort_button_width_ !== void 0 && sort_button_width_() ? {
5320
+ return $3hooq$solidjsweb.ssr($f70cc6dfcadf0fd5$var$_tmpl$6, $3hooq$solidjsweb.ssrHydrationKey(), "".concat(!sorting_closed() ? "major" : "", " ").concat(sorting_closed() ? "minor" : "", " toggle-button for-sorting major-minor-transition"), $3hooq$solidjsweb.ssrAttribute("tabindex", $3hooq$solidjsweb.escape(tabindex_, true), false) + $3hooq$solidjsweb.ssrAttribute("aria-label", $3hooq$solidjsweb.escape((sorting_closed() ? i18n_.open_sorting_ : i18n_.close_sorting_)(), true), false) + $3hooq$solidjsweb.ssrAttribute("aria-expanded", !sorting_closed(), false), $3hooq$solidjsweb.ssrStyle(sort_button_width_ !== null && sort_button_width_ !== void 0 && sort_button_width_() ? {
5247
5321
  width: sort_button_width_() + "px"
5248
5322
  } : {}),
5249
5323
  // Show the title of the currently selected sorting when the sorting is closed, if there's currently a sorting selected
@@ -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.13";
14
+ export const version = "4.10.15";
15
15
  /**
16
16
  * Use `javascript_media_query` but return a reactive accessor instead of taking a callback function
17
17
  */