@empathyco/x-components 6.0.0-alpha.215 → 6.0.0-alpha.217

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.
Files changed (31) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/docs/API-reference/components/ai/x-components.ai-carousel.md +1 -1
  3. package/js/components/animations/change-height.vue.js +2 -1
  4. package/js/components/animations/change-height.vue.js.map +1 -1
  5. package/js/components/animations/change-height.vue2.js.map +1 -1
  6. package/js/components/animations/change-height.vue3.js +7 -0
  7. package/js/components/animations/change-height.vue3.js.map +1 -0
  8. package/js/components/base-dropdown.vue2.js +1 -0
  9. package/js/components/base-dropdown.vue2.js.map +1 -1
  10. package/js/components/modals/base-modal.vue2.js +1 -0
  11. package/js/components/modals/base-modal.vue2.js.map +1 -1
  12. package/js/components/panels/base-header-toggle-panel.vue2.js +1 -0
  13. package/js/components/panels/base-header-toggle-panel.vue2.js.map +1 -1
  14. package/js/components/panels/base-id-toggle-panel.vue2.js +1 -0
  15. package/js/components/panels/base-id-toggle-panel.vue2.js.map +1 -1
  16. package/js/components/panels/base-tabs-panel.vue2.js +1 -0
  17. package/js/components/panels/base-tabs-panel.vue2.js.map +1 -1
  18. package/js/components/result/base-result-image.vue2.js +1 -0
  19. package/js/components/result/base-result-image.vue2.js.map +1 -1
  20. package/js/x-modules/ai/components/ai-carousel.vue.js +4 -1
  21. package/js/x-modules/ai/components/ai-carousel.vue.js.map +1 -1
  22. package/js/x-modules/ai/components/ai-carousel.vue2.js.map +1 -1
  23. package/js/x-modules/empathize/components/empathize.vue2.js +1 -0
  24. package/js/x-modules/empathize/components/empathize.vue2.js.map +1 -1
  25. package/js/x-modules/queries-preview/components/query-preview-button.vue2.js +1 -0
  26. package/js/x-modules/queries-preview/components/query-preview-button.vue2.js.map +1 -1
  27. package/js/x-modules/queries-preview/components/query-preview.vue2.js +1 -0
  28. package/js/x-modules/queries-preview/components/query-preview.vue2.js.map +1 -1
  29. package/js/x-modules/scroll/components/scroll-to-top.vue2.js +1 -0
  30. package/js/x-modules/scroll/components/scroll-to-top.vue2.js.map +1 -1
  31. package/package.json +7 -7
package/CHANGELOG.md CHANGED
@@ -3,6 +3,14 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## 6.0.0-alpha.217 (2026-03-05)
7
+
8
+ - feat(x-utils): recover cjs bundler ([5f9dd59](https://github.com/empathyco/x/commit/5f9dd59))
9
+
10
+ ## 6.0.0-alpha.216 (2026-03-04)
11
+
12
+ - fix: update slot props in AI carousel and improve height transition styles (#2041) ([e025504](https://github.com/empathyco/x/commit/e025504)), closes [#2041](https://github.com/empathyco/x/issues/2041)
13
+
6
14
  ## 6.0.0-alpha.215 (2026-03-04)
7
15
 
8
16
  - feat: add AI carousel component with related integration and tests ([8fe744e](https://github.com/empathyco/x/commit/8fe744e))
@@ -19,7 +19,7 @@ title: AiCarousel
19
19
 
20
20
  | Name | Description | Bindings<br />(name - type - description) |
21
21
  | ---------------------------------------- | ---------------------- | ----------------------------------------- |
22
- | <code>sliding-panel</code> | | |
22
+ | <code>sliding-panel</code> | | <br /> |
23
23
  | <code>sliding-panels-addons</code> | | |
24
24
  | <code>sliding-panels-left-button</code> | | None |
25
25
  | <code>sliding-panels-right-button</code> | | None |
@@ -1,8 +1,9 @@
1
1
  import _sfc_main from './change-height.vue2.js';
2
2
  import { openBlock, createElementBlock, createElementVNode, renderSlot } from 'vue';
3
+ import './change-height.vue3.js';
3
4
  import _export_sfc from '../../_virtual/_plugin-vue_export-helper.js';
4
5
 
5
- const _hoisted_1 = { class: "x-overflow-hidden x-transition-all x-duration-300" };
6
+ const _hoisted_1 = { class: "x-change-height" };
6
7
  const _hoisted_2 = { ref: "wrapper" };
7
8
  function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
8
9
  return openBlock(), createElementBlock("div", _hoisted_1, [
@@ -1 +1 @@
1
- {"version":3,"file":"change-height.vue.js","sources":["../../../../src/components/animations/change-height.vue"],"sourcesContent":["<template>\n <div class=\"x-overflow-hidden x-transition-all x-duration-300\">\n <div ref=\"wrapper\">\n <slot />\n </div>\n </div>\n</template>\n\n<script lang=\"ts\">\nimport { defineComponent, onMounted, onUnmounted, ref } from 'vue'\n\nexport default defineComponent({\n setup() {\n const wrapper = ref<HTMLElement>()\n const observer = new ResizeObserver(entries => {\n for (const entry of entries) {\n if (entry.target.parentElement) {\n entry.target.parentElement.style.height = `${entry.contentRect.height}px`\n }\n }\n })\n\n onMounted(() => {\n if (wrapper.value?.parentElement) {\n const height = wrapper.value.getBoundingClientRect().height\n wrapper.value.parentElement.style.height = `${height}px`\n observer.observe(wrapper.value)\n }\n })\n\n onUnmounted(() => {\n if (wrapper.value) {\n observer.unobserve(wrapper.value)\n }\n })\n\n return {\n wrapper,\n }\n },\n})\n</script>\n\n<docs lang=\"mdx\">\n## Examples\n\nThe ChangeHeight component automatically animates the height of its content when it changes size.\n\n### Basic usage:\n\n```vue\n<template>\n <ChangeHeight>\n <div>Content whose height will be animated</div>\n </ChangeHeight>\n</template>\n\n<script setup>\nimport ChangeHeight from '@empathyco/x-components/js/components/animations/change-height.vue'\n</script>\n```\n\n### Example with dynamic content:\n\n```vue\n<template>\n <div>\n <button @click=\"expanded = !expanded\">Toggle</button>\n <ChangeHeight>\n <div v-if=\"expanded\" style=\"height: 200px; background: #eee;\">Expanded content</div>\n <div v-else style=\"height: 50px; background: #ccc;\">Collapsed content</div>\n </ChangeHeight>\n </div>\n</template>\n\n<script setup>\nimport { ref } from 'vue'\nimport ChangeHeight from '@empathyco/x-components/js/components/animations/change-height.vue'\n\nconst expanded = ref(false)\n</script>\n```\n</docs>\n"],"names":["_openBlock","_createElementBlock","_createElementVNode","_renderSlot"],"mappings":";;;;AACO,MAAA,UAAA,GAAA,EAAA,KAAA,EAAM,mDAAA,EAAmD;AACvD,MAAA,UAAA,GAAA,EAAA,GAAA,EAAI,SAAA,EAAS;;AADpB,EAAA,OAAAA,SAAA,EAAA,EAAAC,kBAAA,CAIM,OAJN,UAAA,EAIM;AAAA,IAHJC,kBAAA;AAAA,MAEM,KAAA;AAAA,MAFN,UAAA;AAAA,MAEM;AAAA,QADJC,UAAA,CAAQ,IAAA,CAAA,MAAA,EAAA,SAAA;AAAA,OAAA;;;;;;;;;;"}
1
+ {"version":3,"file":"change-height.vue.js","sources":["../../../../src/components/animations/change-height.vue"],"sourcesContent":["<template>\n <div class=\"x-change-height\">\n <div ref=\"wrapper\">\n <slot />\n </div>\n </div>\n</template>\n\n<script lang=\"ts\">\nimport { defineComponent, onMounted, onUnmounted, ref } from 'vue'\n\nexport default defineComponent({\n setup() {\n const wrapper = ref<HTMLElement>()\n const observer = new ResizeObserver(entries => {\n for (const entry of entries) {\n if (entry.target.parentElement) {\n entry.target.parentElement.style.height = `${entry.contentRect.height}px`\n }\n }\n })\n\n onMounted(() => {\n if (wrapper.value?.parentElement) {\n const height = wrapper.value.getBoundingClientRect().height\n wrapper.value.parentElement.style.height = `${height}px`\n observer.observe(wrapper.value)\n }\n })\n\n onUnmounted(() => {\n if (wrapper.value) {\n observer.unobserve(wrapper.value)\n }\n })\n\n return {\n wrapper,\n }\n },\n})\n</script>\n<style lang=\"css\">\n.x-change-height {\n --x-change-height-duration: 0.3s;\n overflow: hidden;\n transition: all var(--x-change-height-duration) ease-out;\n}\n</style>\n\n<docs lang=\"mdx\">\n## Examples\n\nThe ChangeHeight component automatically animates the height of its content when it changes size.\n\n### Basic usage:\n\n```vue\n<template>\n <ChangeHeight>\n <div>Content whose height will be animated</div>\n </ChangeHeight>\n</template>\n\n<script setup>\nimport ChangeHeight from '@empathyco/x-components/js/components/animations/change-height.vue'\n</script>\n```\n\n### Example with dynamic content:\n\n```vue\n<template>\n <div>\n <button @click=\"expanded = !expanded\">Toggle</button>\n <ChangeHeight>\n <div v-if=\"expanded\" style=\"height: 200px; background: #eee;\">Expanded content</div>\n <div v-else style=\"height: 50px; background: #ccc;\">Collapsed content</div>\n </ChangeHeight>\n </div>\n</template>\n\n<script setup>\nimport { ref } from 'vue'\nimport ChangeHeight from '@empathyco/x-components/js/components/animations/change-height.vue'\n\nconst expanded = ref(false)\n</script>\n```\n</docs>\n"],"names":["_openBlock","_createElementBlock","_createElementVNode","_renderSlot"],"mappings":";;;;;AACO,MAAA,UAAA,GAAA,EAAA,KAAA,EAAM,iBAAA,EAAiB;AACrB,MAAA,UAAA,GAAA,EAAA,GAAA,EAAI,SAAA,EAAS;;AADpB,EAAA,OAAAA,SAAA,EAAA,EAAAC,kBAAA,CAIM,OAJN,UAAA,EAIM;AAAA,IAHJC,kBAAA;AAAA,MAEM,KAAA;AAAA,MAFN,UAAA;AAAA,MAEM;AAAA,QADJC,UAAA,CAAQ,IAAA,CAAA,MAAA,EAAA,SAAA;AAAA,OAAA;;;;;;;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"change-height.vue2.js","sources":["../../../../src/components/animations/change-height.vue"],"sourcesContent":["<template>\n <div class=\"x-overflow-hidden x-transition-all x-duration-300\">\n <div ref=\"wrapper\">\n <slot />\n </div>\n </div>\n</template>\n\n<script lang=\"ts\">\nimport { defineComponent, onMounted, onUnmounted, ref } from 'vue'\n\nexport default defineComponent({\n setup() {\n const wrapper = ref<HTMLElement>()\n const observer = new ResizeObserver(entries => {\n for (const entry of entries) {\n if (entry.target.parentElement) {\n entry.target.parentElement.style.height = `${entry.contentRect.height}px`\n }\n }\n })\n\n onMounted(() => {\n if (wrapper.value?.parentElement) {\n const height = wrapper.value.getBoundingClientRect().height\n wrapper.value.parentElement.style.height = `${height}px`\n observer.observe(wrapper.value)\n }\n })\n\n onUnmounted(() => {\n if (wrapper.value) {\n observer.unobserve(wrapper.value)\n }\n })\n\n return {\n wrapper,\n }\n },\n})\n</script>\n\n<docs lang=\"mdx\">\n## Examples\n\nThe ChangeHeight component automatically animates the height of its content when it changes size.\n\n### Basic usage:\n\n```vue\n<template>\n <ChangeHeight>\n <div>Content whose height will be animated</div>\n </ChangeHeight>\n</template>\n\n<script setup>\nimport ChangeHeight from '@empathyco/x-components/js/components/animations/change-height.vue'\n</script>\n```\n\n### Example with dynamic content:\n\n```vue\n<template>\n <div>\n <button @click=\"expanded = !expanded\">Toggle</button>\n <ChangeHeight>\n <div v-if=\"expanded\" style=\"height: 200px; background: #eee;\">Expanded content</div>\n <div v-else style=\"height: 50px; background: #ccc;\">Collapsed content</div>\n </ChangeHeight>\n </div>\n</template>\n\n<script setup>\nimport { ref } from 'vue'\nimport ChangeHeight from '@empathyco/x-components/js/components/animations/change-height.vue'\n\nconst expanded = ref(false)\n</script>\n```\n</docs>\n"],"names":[],"mappings":";;AAWA,gBAAe,eAAe,CAAC;IAC7B,KAAK,GAAA;AACH,QAAA,MAAM,OAAM,GAAI,GAAG,EAAc;AACjC,QAAA,MAAM,QAAO,GAAI,IAAI,cAAc,CAAC,OAAM,IAAG;AAC3C,YAAA,KAAK,MAAM,KAAI,IAAK,OAAO,EAAE;AAC3B,gBAAA,IAAI,KAAK,CAAC,MAAM,CAAC,aAAa,EAAE;AAC9B,oBAAA,KAAK,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,MAAK,GAAI,CAAA,EAAG,KAAK,CAAC,WAAW,CAAC,MAAM,IAAG;gBAC1E;YACF;AACF,QAAA,CAAC,CAAA;QAED,SAAS,CAAC,MAAI;AACZ,YAAA,IAAI,OAAO,CAAC,KAAK,EAAE,aAAa,EAAE;gBAChC,MAAM,MAAK,GAAI,OAAO,CAAC,KAAK,CAAC,qBAAqB,EAAE,CAAC,MAAK;AAC1D,gBAAA,OAAO,CAAC,KAAK,CAAC,aAAa,CAAC,KAAK,CAAC,MAAK,GAAI,CAAA,EAAG,MAAM,CAAA,EAAA,CAAG;AACvD,gBAAA,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAA;YAChC;AACF,QAAA,CAAC,CAAA;QAED,WAAW,CAAC,MAAI;AACd,YAAA,IAAI,OAAO,CAAC,KAAK,EAAE;AACjB,gBAAA,QAAQ,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,CAAA;YAClC;AACF,QAAA,CAAC,CAAA;QAED,OAAO;YACL,OAAO;SACT;IACF,CAAC;AACF,CAAA,CAAA;;;;"}
1
+ {"version":3,"file":"change-height.vue2.js","sources":["../../../../src/components/animations/change-height.vue"],"sourcesContent":["<template>\n <div class=\"x-change-height\">\n <div ref=\"wrapper\">\n <slot />\n </div>\n </div>\n</template>\n\n<script lang=\"ts\">\nimport { defineComponent, onMounted, onUnmounted, ref } from 'vue'\n\nexport default defineComponent({\n setup() {\n const wrapper = ref<HTMLElement>()\n const observer = new ResizeObserver(entries => {\n for (const entry of entries) {\n if (entry.target.parentElement) {\n entry.target.parentElement.style.height = `${entry.contentRect.height}px`\n }\n }\n })\n\n onMounted(() => {\n if (wrapper.value?.parentElement) {\n const height = wrapper.value.getBoundingClientRect().height\n wrapper.value.parentElement.style.height = `${height}px`\n observer.observe(wrapper.value)\n }\n })\n\n onUnmounted(() => {\n if (wrapper.value) {\n observer.unobserve(wrapper.value)\n }\n })\n\n return {\n wrapper,\n }\n },\n})\n</script>\n<style lang=\"css\">\n.x-change-height {\n --x-change-height-duration: 0.3s;\n overflow: hidden;\n transition: all var(--x-change-height-duration) ease-out;\n}\n</style>\n\n<docs lang=\"mdx\">\n## Examples\n\nThe ChangeHeight component automatically animates the height of its content when it changes size.\n\n### Basic usage:\n\n```vue\n<template>\n <ChangeHeight>\n <div>Content whose height will be animated</div>\n </ChangeHeight>\n</template>\n\n<script setup>\nimport ChangeHeight from '@empathyco/x-components/js/components/animations/change-height.vue'\n</script>\n```\n\n### Example with dynamic content:\n\n```vue\n<template>\n <div>\n <button @click=\"expanded = !expanded\">Toggle</button>\n <ChangeHeight>\n <div v-if=\"expanded\" style=\"height: 200px; background: #eee;\">Expanded content</div>\n <div v-else style=\"height: 50px; background: #ccc;\">Collapsed content</div>\n </ChangeHeight>\n </div>\n</template>\n\n<script setup>\nimport { ref } from 'vue'\nimport ChangeHeight from '@empathyco/x-components/js/components/animations/change-height.vue'\n\nconst expanded = ref(false)\n</script>\n```\n</docs>\n"],"names":[],"mappings":";;AAWA,gBAAe,eAAe,CAAC;IAC7B,KAAK,GAAA;AACH,QAAA,MAAM,OAAM,GAAI,GAAG,EAAc;AACjC,QAAA,MAAM,QAAO,GAAI,IAAI,cAAc,CAAC,OAAM,IAAG;AAC3C,YAAA,KAAK,MAAM,KAAI,IAAK,OAAO,EAAE;AAC3B,gBAAA,IAAI,KAAK,CAAC,MAAM,CAAC,aAAa,EAAE;AAC9B,oBAAA,KAAK,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,MAAK,GAAI,CAAA,EAAG,KAAK,CAAC,WAAW,CAAC,MAAM,IAAG;gBAC1E;YACF;AACF,QAAA,CAAC,CAAA;QAED,SAAS,CAAC,MAAI;AACZ,YAAA,IAAI,OAAO,CAAC,KAAK,EAAE,aAAa,EAAE;gBAChC,MAAM,MAAK,GAAI,OAAO,CAAC,KAAK,CAAC,qBAAqB,EAAE,CAAC,MAAK;AAC1D,gBAAA,OAAO,CAAC,KAAK,CAAC,aAAa,CAAC,KAAK,CAAC,MAAK,GAAI,CAAA,EAAG,MAAM,CAAA,EAAA,CAAG;AACvD,gBAAA,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAA;YAChC;AACF,QAAA,CAAC,CAAA;QAED,WAAW,CAAC,MAAI;AACd,YAAA,IAAI,OAAO,CAAC,KAAK,EAAE;AACjB,gBAAA,QAAQ,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,CAAA;YAClC;AACF,QAAA,CAAC,CAAA;QAED,OAAO;YACL,OAAO;SACT;IACF,CAAC;AACF,CAAA,CAAA;;;;"}
@@ -0,0 +1,7 @@
1
+ import injectCss from '../../../tools/inject-css.js';
2
+
3
+ var css = ".x-change-height{--x-change-height-duration:0.3s;overflow:hidden;transition:all var(--x-change-height-duration) ease-out}";
4
+ injectCss(css);
5
+
6
+ export { css };
7
+ //# sourceMappingURL=change-height.vue3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"change-height.vue3.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;"}
@@ -10,6 +10,7 @@ import './animations/animate-translate/animate-translate.style.css.js';
10
10
  import './animations/animate-width.vue2.js';
11
11
  import './animations/animate-width.vue3.js';
12
12
  import './animations/change-height.vue2.js';
13
+ import './animations/change-height.vue3.js';
13
14
  import './animations/collapse-height.vue2.js';
14
15
  import './animations/collapse-height.vue3.js';
15
16
  import './animations/collapse-width.vue2.js';
@@ -1 +1 @@
1
- {"version":3,"file":"base-dropdown.vue2.js","sources":["../../../src/components/base-dropdown.vue"],"sourcesContent":["<template>\n <div\n ref=\"rootRef\"\n :class=\"dropdownCSSClasses\"\n class=\"x-dropdown\"\n @keydown=\"updateSearchBuffer\"\n @keydown.down.prevent=\"highlightNextItem\"\n @keydown.up.prevent=\"highlightPreviousItem\"\n >\n <button\n ref=\"toggleButtonRef\"\n class=\"x-dropdown__toggle\"\n data-test=\"dropdown-toggle\"\n role=\"combobox\"\n aria-haspopup=\"listbox\"\n :aria-expanded=\"isOpen\"\n :aria-controls=\"listId\"\n :aria-label=\"ariaLabel\"\n aria-autocomplete=\"none\"\n @click=\"toggle\"\n @keydown.up.down.prevent.stop=\"open\"\n >\n <!--\n @slot Used to render the contents of the dropdown toggle button. If not provided, it uses\n the item slot as fallback.\n @binding {string|number|Identifiable} item - The item data to render.\n @binding {boolean} isOpen - True if the dropdown is opened, and false if it is closed.\n -->\n <slot v-if=\"hasToggleSlot\" :is-open=\"isOpen\" :item=\"modelValue\" name=\"toggle\">\n {{ modelValue }}\n </slot>\n <slot v-else :item=\"modelValue\" name=\"item\">{{ modelValue }}</slot>\n </button>\n\n <component :is=\"animation\">\n <ul\n v-show=\"isOpen\"\n :id=\"listId\"\n class=\"x-dropdown__items-list\"\n data-test=\"dropdown-list\"\n role=\"listbox\"\n tabindex=\"-1\"\n @keydown.end=\"highlightLastItem\"\n @keydown.esc=\"closeAndFocusToggleButton\"\n @keydown.home=\"highlightFirstItem\"\n >\n <li\n v-for=\"(item, index) in items\"\n :key=\"(item as Identifiable).id ?? item\"\n class=\"x-dropdown__list-item\"\n >\n <button\n :ref=\"el => (itemsButtonRefs[index] = el as HTMLButtonElement | null)\"\n :aria-selected=\"item === modelValue\"\n :class=\"itemsCSSClasses[index]\"\n class=\"x-dropdown__item\"\n data-test=\"dropdown-item\"\n role=\"option\"\n @click=\"emitSelectedItemChanged(item)\"\n >\n <!--\n @slot (required) Used to render each one of the items content, and as fallback\n for the toggle button content slot if it is not provided.\n @binding {string|number|Identifiable} item - Item to render\n @binding {boolean} isHighlighted - True when the item has the focus.\n @binding {boolean} isSelected - True when the item is selected.\n -->\n <slot\n :is-highlighted=\"index === highlightedItemIndex\"\n :is-selected=\"item === modelValue\"\n :item=\"item\"\n name=\"item\"\n >\n {{ item }}\n </slot>\n </button>\n </li>\n </ul>\n </component>\n </div>\n</template>\n\n<script lang=\"ts\">\nimport type { Identifiable } from '@empathyco/x-types'\nimport type { PropType } from 'vue'\nimport { computed, defineComponent, nextTick, onBeforeUnmount, ref, watch } from 'vue'\nimport { AnimationProp } from '../types'\nimport { debounceFunction, getTargetElement, normalizeString } from '../utils'\nimport { NoAnimation } from './animations'\n\ntype DropdownItem = string | number | Identifiable\nlet dropdownCount = 0\n\n/**\n * Dropdown component that mimics a Select element behavior, but with the option\n * to customize the toggle button and each item contents.\n */\nexport default defineComponent({\n name: 'BaseDropdown',\n props: {\n /** List of items to display.*/\n items: {\n type: Array as PropType<DropdownItem[]>,\n required: true,\n },\n /** The selected item. */\n modelValue: {\n type: null as unknown as PropType<DropdownItem | null>,\n validator: (v: any) =>\n typeof v === 'string' || typeof v === 'number' || typeof v === 'object' || v === null,\n required: true,\n },\n /** Description of what the dropdown is used for. */\n ariaLabel: String,\n /**\n * Animation component to use for expanding the dropdown. This is a single element animation,\n * so only `<transition>` components are allowed.\n */\n animation: {\n type: AnimationProp,\n default: () => NoAnimation,\n },\n /** Time to wait without receiving any keystroke before resetting the items search query. */\n searchTimeoutMs: {\n type: Number,\n default: 1000,\n },\n },\n emits: ['update:modelValue'],\n setup(props, { emit, slots }) {\n const rootRef = ref<HTMLElement>()\n /** The button that opens and closes the list of options. */\n const toggleButtonRef = ref<HTMLButtonElement>()\n /** Array containing the dropdown list buttons HTMLElements. */\n const itemsButtonRefs = ref<(HTMLButtonElement | null)[]>([])\n\n /** Property to check whether the dropdown is expanded or closed. */\n const isOpen = ref(false)\n /** Index of the element that has the focus in the list. -1 means no element has focus. */\n const highlightedItemIndex = ref(-1)\n /** String to search for the first element that starts with it. */\n const searchBuffer = ref('')\n\n /** Resets the search buffer after a certain time has passed. */\n let restartResetSearchTimeout: () => void\n /* Unique ID to identify the dropdown. */\n const listId = `x-dropdown-${dropdownCount++}`\n\n /**\n * Dynamic CSS classes to add to the dropdown root element.\n *\n * @returns An object containing the CSS classes to add to the dropdown root element.\n */\n const dropdownCSSClasses = computed(() => ({ 'x-dropdown--is-open': isOpen }))\n\n /**\n * Dynamic CSS classes to add to each one of the items.\n *\n * @returns An object containing the CSS classes to add to each item.\n */\n const itemsCSSClasses = computed(() =>\n props.items.map((item, index) => ({\n 'x-dropdown__item--is-selected': props.modelValue === item,\n 'x-dropdown__item--is-highlighted': highlightedItemIndex.value === index,\n })),\n )\n\n /* Opens the dropdown. */\n const open = () => (isOpen.value = true)\n /* Closes the dropdown. */\n const close = () => (isOpen.value = false)\n /* Toggles the dropdown. */\n const toggle = () => (isOpen.value = !isOpen.value)\n\n /**\n * Closes the modal and focuses the toggle button.\n */\n function closeAndFocusToggleButton() {\n close()\n toggleButtonRef.value?.focus()\n }\n\n /**\n * Emits the event that the selected item has changed.\n *\n * @param item - The new selected item.\n */\n function emitSelectedItemChanged(item: DropdownItem) {\n emit('update:modelValue', item)\n closeAndFocusToggleButton()\n }\n\n /**\n * Highlights the item after the one that is currently highlighted.\n */\n function highlightNextItem() {\n open()\n highlightedItemIndex.value = (highlightedItemIndex.value + 1) % props.items.length\n }\n\n /**\n * Highlights the item before the one that is currently highlighted.\n */\n function highlightPreviousItem() {\n const currentIndex = highlightedItemIndex.value\n open()\n highlightedItemIndex.value = currentIndex > 0 ? currentIndex - 1 : props.items.length - 1\n }\n\n /**\n * Highlights the first of the provided items.\n */\n function highlightFirstItem() {\n highlightedItemIndex.value = 0\n }\n\n /**\n * Highlights the last of the provided items.\n */\n function highlightLastItem() {\n highlightedItemIndex.value = props.items.length - 1\n }\n\n /**\n * Updates the variable that is used to search in the filters.\n *\n * @param event - The event coming from the user typing.\n */\n function updateSearchBuffer(event: KeyboardEvent) {\n if (/^\\w$/.test(event.key)) {\n const key = event.key\n searchBuffer.value += key\n restartResetSearchTimeout()\n }\n }\n\n /**\n * Resets the search buffer.\n */\n function resetSearchBuffer() {\n searchBuffer.value = ''\n }\n\n /**\n * Closes the dropdown if the passed event has happened on an element out of the dropdown.\n *\n * @param event - The event to check if it has happened out of the dropdown component.\n */\n function closeIfEventIsOutOfDropdown(event: MouseEvent | TouchEvent | FocusEvent) {\n if (!rootRef.value?.contains(getTargetElement(event))) {\n close()\n }\n }\n\n /**\n * Adds listeners to the document element to detect if the focus has moved out from the\n * dropdown.\n */\n function addDocumentCloseListeners() {\n document.addEventListener('mousedown', closeIfEventIsOutOfDropdown)\n document.addEventListener('touchstart', closeIfEventIsOutOfDropdown)\n document.addEventListener('focusin', closeIfEventIsOutOfDropdown)\n }\n\n /**\n * Removes the listeners of the document element to detect if the focus has moved out from the\n * dropdown.\n */\n function removeDocumentCloseListeners() {\n document.removeEventListener('mousedown', closeIfEventIsOutOfDropdown)\n document.removeEventListener('touchstart', closeIfEventIsOutOfDropdown)\n document.removeEventListener('focusin', closeIfEventIsOutOfDropdown)\n }\n\n /**\n * Highlights the item that matches the search buffer. To do so it checks the list buttons\n * text content. It highlights items following this priority:\n * - If an element is already highlighted, it starts searching from that element.\n * - If no element is found starting from the previously highlighted, it returns the first one.\n * - If no element is found matching the search query it highlights the first element.\n *\n * @param search - The search string to find in the HTML.\n */\n watch(searchBuffer, search => {\n if (search) {\n const normalizedSearch = normalizeString(search)\n const matchingIndices = itemsButtonRefs?.value?.reduce<number[]>(\n (matchingIndices, button, index) => {\n if (button) {\n const safeButtonWordCharacters = button.textContent!.replace(/\\W/g, '')\n const normalizedButtonText = normalizeString(safeButtonWordCharacters)\n if (normalizedButtonText.startsWith(normalizedSearch)) {\n matchingIndices.push(index)\n }\n }\n return matchingIndices\n },\n [],\n )\n highlightedItemIndex.value =\n // First matching item starting to search from the current highlighted element\n matchingIndices?.find(index => index >= highlightedItemIndex.value) ??\n // First matching item\n matchingIndices?.[0] ??\n // First item\n 0\n }\n })\n\n /**\n * Updates the debounced function to reset the search.\n *\n * @param searchTimeoutMs - The new milliseconds that have to pass without typing before\n * resetting the search.\n */\n watch(\n () => props.searchTimeoutMs,\n searchTimeoutMs => {\n restartResetSearchTimeout = debounceFunction(resetSearchBuffer, searchTimeoutMs)\n },\n { immediate: true },\n )\n\n /**\n * Focuses the DOM element which matches the `highlightedItemIndex`.\n *\n * @param highlightedItemIndex - The index of the HTML element to focus.\n */\n watch(\n highlightedItemIndex,\n highlightedItemIndex => {\n nextTick(() => itemsButtonRefs.value[highlightedItemIndex]?.focus())\n },\n { immediate: true },\n )\n\n /**\n * When the dropdown is open it sets the focused element to the one that is selected.\n *\n * @param isOpen - True if the dropdown is open, false otherwise.\n */\n watch(isOpen, isOpen => {\n highlightedItemIndex.value = isOpen\n ? props.modelValue === null\n ? 0\n : props.items.indexOf(props.modelValue)\n : -1\n })\n\n /**\n * Adds and removes listeners to close the dropdown when it loses the focus.\n *\n * @param isOpen - True if the dropdown is open, false otherwise.\n */\n watch(isOpen, isOpen => {\n /*\n * Because there is an issue with Firefox in macOS and Safari that doesn't focus the target\n * element of the `mousedown` events, the `focusout` event `relatedTarget` property can't be\n * used to detect whether the user has blurred the dropdown. The hack here is to use\n * document listeners that have the side effect of losing the focus.\n */\n if (isOpen) {\n addDocumentCloseListeners()\n } else {\n removeDocumentCloseListeners()\n }\n })\n\n /**\n * If the dropdown is destroyed before removing the document listeners, it ensures that they\n * are removed too.\n */\n onBeforeUnmount(() => {\n removeDocumentCloseListeners()\n })\n\n return {\n hasToggleSlot: !!slots.toggle,\n closeAndFocusToggleButton,\n dropdownCSSClasses,\n emitSelectedItemChanged,\n highlightFirstItem,\n highlightLastItem,\n highlightNextItem,\n highlightPreviousItem,\n highlightedItemIndex,\n isOpen,\n itemsButtonRefs,\n itemsCSSClasses,\n listId,\n open,\n rootRef,\n toggle,\n toggleButtonRef,\n updateSearchBuffer,\n }\n },\n})\n</script>\n\n<style lang=\"css\" scoped>\n.x-dropdown {\n position: relative;\n}\n\n.x-dropdown__items-list {\n z-index: 1;\n list-style: none;\n position: absolute;\n padding: 0;\n margin: 0;\n top: calc(100% + var(--x-size-gap-dropdown-default, 0));\n}\n</style>\n\n<docs lang=\"mdx\">\n## Example\n\nThe `Dropdown` component is a simple yet customizable select component. The component needs to work\nwith the list of items available to select, which are passed using the `items` prop, and the selected\nitem, which is passed in using the `value` prop.\n\nThe supported items must be an array that can contain unique strings, unique numbers, or objects\nwith a unique `id` property.\n\nThe content of each item can be customized using the `item` slot, which apart from the data of the\nitem, it also receives via prop if the element is selected or highlighted.\n\nThe `toggle` slot can be used to customize the button that opens the dropdown. If this is not\nprovided, the `item` slot will be used for that.\n\n```vue\n<template>\n <BaseDropdown v-model=\"value\" :items=\"items\">\n <template #toggle=\"{ item, isOpen }\">{{ item }} {{ isOpen ? '🔼' : '🔽' }}️</template>\n <template #item=\"{ item, isSelected, isHighlighted }\">\n <span v-if=\"isHighlighted\">🟢</span>\n <span v-if=\"isSelected\">✅</span>\n <span>{{ item }}</span>\n </template>\n </BaseDropdown>\n</template>\n\n<script setup>\nimport { BaseDropdown } from '@empathyco/x-components'\nimport { ref } from 'vue'\nconst items = ['a', 2, { id: '3' }]\nconst value = ref('a')\n</script>\n```\n</docs>\n"],"names":["NoAnimation","debounceFunction"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AA2FA,IAAI,gBAAgB,CAAA;AAEpB;;;AAGE;AACF,gBAAe,eAAe,CAAC;AAC7B,IAAA,IAAI,EAAE,cAAc;AACpB,IAAA,KAAK,EAAE;;AAEL,QAAA,KAAK,EAAE;AACL,YAAA,IAAI,EAAE,KAAiC;AACvC,YAAA,QAAQ,EAAE,IAAI;AACf,SAAA;;AAED,QAAA,UAAU,EAAE;AACV,YAAA,IAAI,EAAE,IAAgD;YACtD,SAAS,EAAE,CAAC,CAAM,KAChB,OAAO,CAAA,KAAM,QAAO,IAAK,OAAO,CAAA,KAAM,QAAO,IAAK,OAAO,CAAA,KAAM,YAAY,CAAA,KAAM,IAAI;AACvF,YAAA,QAAQ,EAAE,IAAI;AACf,SAAA;;AAED,QAAA,SAAS,EAAE,MAAM;AACjB;;;AAGE;AACF,QAAA,SAAS,EAAE;AACT,YAAA,IAAI,EAAE,aAAa;AACnB,YAAA,OAAO,EAAE,MAAMA,WAAW;AAC3B,SAAA;;AAED,QAAA,eAAe,EAAE;AACf,YAAA,IAAI,EAAE,MAAM;AACZ,YAAA,OAAO,EAAE,IAAI;AACd,SAAA;AACF,KAAA;IACD,KAAK,EAAE,CAAC,mBAAmB,CAAC;AAC5B,IAAA,KAAK,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,KAAI,EAAG,EAAA;AAC1B,QAAA,MAAM,OAAM,GAAI,GAAG,EAAc;;AAEjC,QAAA,MAAM,kBAAkB,GAAG,EAAoB;;AAE/C,QAAA,MAAM,eAAc,GAAI,GAAG,CAA+B,EAAE,CAAA;;AAG5D,QAAA,MAAM,SAAS,GAAG,CAAC,KAAK,CAAA;;AAExB,QAAA,MAAM,oBAAmB,GAAI,GAAG,CAAC,EAAE,CAAA;;AAEnC,QAAA,MAAM,YAAW,GAAI,GAAG,CAAC,EAAE,CAAA;;AAG3B,QAAA,IAAI,yBAAoC;;AAExC,QAAA,MAAM,MAAK,GAAI,CAAA,WAAA,EAAc,aAAa,EAAE,EAAC;AAE7C;;;;AAIE;AACF,QAAA,MAAM,qBAAqB,QAAQ,CAAC,OAAO,EAAE,qBAAqB,EAAE,MAAK,EAAG,CAAC,CAAA;AAE7E;;;;AAIE;QACF,MAAM,eAAc,GAAI,QAAQ,CAAC,MAC/B,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,MAAM;AAChC,YAAA,+BAA+B,EAAE,KAAK,CAAC,UAAS,KAAM,IAAI;AAC1D,YAAA,kCAAkC,EAAE,oBAAoB,CAAC,KAAI,KAAM,KAAK;SACzE,CAAC,CAAC,CACL;;AAGA,QAAA,MAAM,IAAG,GAAI,OAAO,MAAM,CAAC,KAAI,GAAI,IAAI,CAAA;;AAEvC,QAAA,MAAM,KAAI,GAAI,OAAO,MAAM,CAAC,KAAI,GAAI,KAAK,CAAA;;AAEzC,QAAA,MAAM,MAAK,GAAI,OAAO,MAAM,CAAC,KAAI,GAAI,CAAC,MAAM,CAAC,KAAK,CAAA;AAElD;;AAEE;AACF,QAAA,SAAS,yBAAyB,GAAA;AAChC,YAAA,KAAK,EAAC;AACN,YAAA,eAAe,CAAC,KAAK,EAAE,KAAK,EAAC;QAC/B;AAEA;;;;AAIE;QACF,SAAS,uBAAuB,CAAC,IAAkB,EAAA;AACjD,YAAA,IAAI,CAAC,mBAAmB,EAAE,IAAI,CAAA;AAC9B,YAAA,yBAAyB,EAAC;QAC5B;AAEA;;AAEE;AACF,QAAA,SAAS,iBAAiB,GAAA;AACxB,YAAA,IAAI,EAAC;AACL,YAAA,oBAAoB,CAAC,KAAI,GAAI,CAAC,oBAAoB,CAAC,QAAQ,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,MAAK;QACnF;AAEA;;AAEE;AACF,QAAA,SAAS,qBAAqB,GAAA;AAC5B,YAAA,MAAM,YAAW,GAAI,oBAAoB,CAAC,KAAI;AAC9C,YAAA,IAAI,EAAC;YACL,oBAAoB,CAAC,QAAQ,YAAW,GAAI,CAAA,GAAI,YAAW,GAAI,CAAA,GAAI,KAAK,CAAC,KAAK,CAAC,SAAS,CAAA;QAC1F;AAEA;;AAEE;AACF,QAAA,SAAS,kBAAkB,GAAA;AACzB,YAAA,oBAAoB,CAAC,KAAI,GAAI,CAAA;QAC/B;AAEA;;AAEE;AACF,QAAA,SAAS,iBAAiB,GAAA;YACxB,oBAAoB,CAAC,KAAI,GAAI,KAAK,CAAC,KAAK,CAAC,SAAS,CAAA;QACpD;AAEA;;;;AAIE;QACF,SAAS,kBAAkB,CAAC,KAAoB,EAAA;YAC9C,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE;AAC1B,gBAAA,MAAM,GAAE,GAAI,KAAK,CAAC,GAAE;AACpB,gBAAA,YAAY,CAAC,SAAS,GAAE;AACxB,gBAAA,yBAAyB,EAAC;YAC5B;QACF;AAEA;;AAEE;AACF,QAAA,SAAS,iBAAiB,GAAA;AACxB,YAAA,YAAY,CAAC,KAAI,GAAI,EAAC;QACxB;AAEA;;;;AAIE;QACF,SAAS,2BAA2B,CAAC,KAA2C,EAAA;AAC9E,YAAA,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,QAAQ,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC,EAAE;AACrD,gBAAA,KAAK,EAAC;YACR;QACF;AAEA;;;AAGE;AACF,QAAA,SAAS,yBAAyB,GAAA;AAChC,YAAA,QAAQ,CAAC,gBAAgB,CAAC,WAAW,EAAE,2BAA2B,CAAA;AAClE,YAAA,QAAQ,CAAC,gBAAgB,CAAC,YAAY,EAAE,2BAA2B,CAAA;AACnE,YAAA,QAAQ,CAAC,gBAAgB,CAAC,SAAS,EAAE,2BAA2B,CAAA;QAClE;AAEA;;;AAGE;AACF,QAAA,SAAS,4BAA4B,GAAA;AACnC,YAAA,QAAQ,CAAC,mBAAmB,CAAC,WAAW,EAAE,2BAA2B,CAAA;AACrE,YAAA,QAAQ,CAAC,mBAAmB,CAAC,YAAY,EAAE,2BAA2B,CAAA;AACtE,YAAA,QAAQ,CAAC,mBAAmB,CAAC,SAAS,EAAE,2BAA2B,CAAA;QACrE;AAEA;;;;;;;;AAQE;AACF,QAAA,KAAK,CAAC,YAAY,EAAE,MAAK,IAAG;YAC1B,IAAI,MAAM,EAAE;AACV,gBAAA,MAAM,gBAAe,GAAI,eAAe,CAAC,MAAM,CAAA;AAC/C,gBAAA,MAAM,eAAc,GAAI,eAAe,EAAE,KAAK,EAAE,MAAM,CACpD,CAAC,eAAe,EAAE,MAAM,EAAE,KAAK,KAAG;oBAChC,IAAI,MAAM,EAAE;AACV,wBAAA,MAAM,wBAAuB,GAAI,MAAM,CAAC,WAAY,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAA;AACtE,wBAAA,MAAM,oBAAmB,GAAI,eAAe,CAAC,wBAAwB,CAAA;AACrE,wBAAA,IAAI,oBAAoB,CAAC,UAAU,CAAC,gBAAgB,CAAC,EAAE;AACrD,4BAAA,eAAe,CAAC,IAAI,CAAC,KAAK,CAAA;wBAC5B;oBACF;AACA,oBAAA,OAAO,eAAc;gBACvB,CAAC,EACD,EAAE,CACJ;AACA,gBAAA,oBAAoB,CAAC,KAAI;;AAEvB,oBAAA,eAAe,EAAE,IAAI,CAAC,KAAI,IAAK,KAAI,IAAK,oBAAoB,CAAC,KAAK;;wBAElE,eAAe,GAAG,CAAC;;AAEnB,wBAAA,CAAA;YACJ;AACF,QAAA,CAAC,CAAA;AAED;;;;;AAKE;QACF,KAAK,CACH,MAAM,KAAK,CAAC,eAAe,EAC3B,eAAc,IAAG;AACf,YAAA,yBAAwB,GAAIC,QAAgB,CAAC,iBAAiB,EAAE,eAAe,CAAA;AACjF,QAAA,CAAC,EACD,EAAE,SAAS,EAAE,MAAM,CACrB;AAEA;;;;AAIE;AACF,QAAA,KAAK,CACH,oBAAoB,EACpB,oBAAmB,IAAG;AACpB,YAAA,QAAQ,CAAC,MAAM,eAAe,CAAC,KAAK,CAAC,oBAAoB,CAAC,EAAE,KAAK,EAAE,CAAA;AACrE,QAAA,CAAC,EACD,EAAE,SAAS,EAAE,MAAM,CACrB;AAEA;;;;AAIE;AACF,QAAA,KAAK,CAAC,MAAM,EAAE,MAAK,IAAG;YACpB,oBAAoB,CAAC,KAAI,GAAI;AAC3B,kBAAE,KAAK,CAAC,UAAS,KAAM;AACrB,sBAAE;sBACA,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,UAAU;kBACtC,EAAC;AACP,QAAA,CAAC,CAAA;AAED;;;;AAIE;AACF,QAAA,KAAK,CAAC,MAAM,EAAE,MAAK,IAAG;AACpB;;;;;AAKE;YACF,IAAI,MAAM,EAAE;AACV,gBAAA,yBAAyB,EAAC;YAC5B;iBAAO;AACL,gBAAA,4BAA4B,EAAC;YAC/B;AACF,QAAA,CAAC,CAAA;AAED;;;AAGE;QACF,eAAe,CAAC,MAAI;AAClB,YAAA,4BAA4B,EAAC;AAC/B,QAAA,CAAC,CAAA;QAED,OAAO;AACL,YAAA,aAAa,EAAE,CAAC,CAAC,KAAK,CAAC,MAAM;YAC7B,yBAAyB;YACzB,kBAAkB;YAClB,uBAAuB;YACvB,kBAAkB;YAClB,iBAAiB;YACjB,iBAAiB;YACjB,qBAAqB;YACrB,oBAAoB;YACpB,MAAM;YACN,eAAe;YACf,eAAe;YACf,MAAM;YACN,IAAI;YACJ,OAAO;YACP,MAAM;YACN,eAAe;YACf,kBAAkB;SACpB;IACF,CAAC;AACF,CAAA,CAAA;;;;"}
1
+ {"version":3,"file":"base-dropdown.vue2.js","sources":["../../../src/components/base-dropdown.vue"],"sourcesContent":["<template>\n <div\n ref=\"rootRef\"\n :class=\"dropdownCSSClasses\"\n class=\"x-dropdown\"\n @keydown=\"updateSearchBuffer\"\n @keydown.down.prevent=\"highlightNextItem\"\n @keydown.up.prevent=\"highlightPreviousItem\"\n >\n <button\n ref=\"toggleButtonRef\"\n class=\"x-dropdown__toggle\"\n data-test=\"dropdown-toggle\"\n role=\"combobox\"\n aria-haspopup=\"listbox\"\n :aria-expanded=\"isOpen\"\n :aria-controls=\"listId\"\n :aria-label=\"ariaLabel\"\n aria-autocomplete=\"none\"\n @click=\"toggle\"\n @keydown.up.down.prevent.stop=\"open\"\n >\n <!--\n @slot Used to render the contents of the dropdown toggle button. If not provided, it uses\n the item slot as fallback.\n @binding {string|number|Identifiable} item - The item data to render.\n @binding {boolean} isOpen - True if the dropdown is opened, and false if it is closed.\n -->\n <slot v-if=\"hasToggleSlot\" :is-open=\"isOpen\" :item=\"modelValue\" name=\"toggle\">\n {{ modelValue }}\n </slot>\n <slot v-else :item=\"modelValue\" name=\"item\">{{ modelValue }}</slot>\n </button>\n\n <component :is=\"animation\">\n <ul\n v-show=\"isOpen\"\n :id=\"listId\"\n class=\"x-dropdown__items-list\"\n data-test=\"dropdown-list\"\n role=\"listbox\"\n tabindex=\"-1\"\n @keydown.end=\"highlightLastItem\"\n @keydown.esc=\"closeAndFocusToggleButton\"\n @keydown.home=\"highlightFirstItem\"\n >\n <li\n v-for=\"(item, index) in items\"\n :key=\"(item as Identifiable).id ?? item\"\n class=\"x-dropdown__list-item\"\n >\n <button\n :ref=\"el => (itemsButtonRefs[index] = el as HTMLButtonElement | null)\"\n :aria-selected=\"item === modelValue\"\n :class=\"itemsCSSClasses[index]\"\n class=\"x-dropdown__item\"\n data-test=\"dropdown-item\"\n role=\"option\"\n @click=\"emitSelectedItemChanged(item)\"\n >\n <!--\n @slot (required) Used to render each one of the items content, and as fallback\n for the toggle button content slot if it is not provided.\n @binding {string|number|Identifiable} item - Item to render\n @binding {boolean} isHighlighted - True when the item has the focus.\n @binding {boolean} isSelected - True when the item is selected.\n -->\n <slot\n :is-highlighted=\"index === highlightedItemIndex\"\n :is-selected=\"item === modelValue\"\n :item=\"item\"\n name=\"item\"\n >\n {{ item }}\n </slot>\n </button>\n </li>\n </ul>\n </component>\n </div>\n</template>\n\n<script lang=\"ts\">\nimport type { Identifiable } from '@empathyco/x-types'\nimport type { PropType } from 'vue'\nimport { computed, defineComponent, nextTick, onBeforeUnmount, ref, watch } from 'vue'\nimport { AnimationProp } from '../types'\nimport { debounceFunction, getTargetElement, normalizeString } from '../utils'\nimport { NoAnimation } from './animations'\n\ntype DropdownItem = string | number | Identifiable\nlet dropdownCount = 0\n\n/**\n * Dropdown component that mimics a Select element behavior, but with the option\n * to customize the toggle button and each item contents.\n */\nexport default defineComponent({\n name: 'BaseDropdown',\n props: {\n /** List of items to display.*/\n items: {\n type: Array as PropType<DropdownItem[]>,\n required: true,\n },\n /** The selected item. */\n modelValue: {\n type: null as unknown as PropType<DropdownItem | null>,\n validator: (v: any) =>\n typeof v === 'string' || typeof v === 'number' || typeof v === 'object' || v === null,\n required: true,\n },\n /** Description of what the dropdown is used for. */\n ariaLabel: String,\n /**\n * Animation component to use for expanding the dropdown. This is a single element animation,\n * so only `<transition>` components are allowed.\n */\n animation: {\n type: AnimationProp,\n default: () => NoAnimation,\n },\n /** Time to wait without receiving any keystroke before resetting the items search query. */\n searchTimeoutMs: {\n type: Number,\n default: 1000,\n },\n },\n emits: ['update:modelValue'],\n setup(props, { emit, slots }) {\n const rootRef = ref<HTMLElement>()\n /** The button that opens and closes the list of options. */\n const toggleButtonRef = ref<HTMLButtonElement>()\n /** Array containing the dropdown list buttons HTMLElements. */\n const itemsButtonRefs = ref<(HTMLButtonElement | null)[]>([])\n\n /** Property to check whether the dropdown is expanded or closed. */\n const isOpen = ref(false)\n /** Index of the element that has the focus in the list. -1 means no element has focus. */\n const highlightedItemIndex = ref(-1)\n /** String to search for the first element that starts with it. */\n const searchBuffer = ref('')\n\n /** Resets the search buffer after a certain time has passed. */\n let restartResetSearchTimeout: () => void\n /* Unique ID to identify the dropdown. */\n const listId = `x-dropdown-${dropdownCount++}`\n\n /**\n * Dynamic CSS classes to add to the dropdown root element.\n *\n * @returns An object containing the CSS classes to add to the dropdown root element.\n */\n const dropdownCSSClasses = computed(() => ({ 'x-dropdown--is-open': isOpen }))\n\n /**\n * Dynamic CSS classes to add to each one of the items.\n *\n * @returns An object containing the CSS classes to add to each item.\n */\n const itemsCSSClasses = computed(() =>\n props.items.map((item, index) => ({\n 'x-dropdown__item--is-selected': props.modelValue === item,\n 'x-dropdown__item--is-highlighted': highlightedItemIndex.value === index,\n })),\n )\n\n /* Opens the dropdown. */\n const open = () => (isOpen.value = true)\n /* Closes the dropdown. */\n const close = () => (isOpen.value = false)\n /* Toggles the dropdown. */\n const toggle = () => (isOpen.value = !isOpen.value)\n\n /**\n * Closes the modal and focuses the toggle button.\n */\n function closeAndFocusToggleButton() {\n close()\n toggleButtonRef.value?.focus()\n }\n\n /**\n * Emits the event that the selected item has changed.\n *\n * @param item - The new selected item.\n */\n function emitSelectedItemChanged(item: DropdownItem) {\n emit('update:modelValue', item)\n closeAndFocusToggleButton()\n }\n\n /**\n * Highlights the item after the one that is currently highlighted.\n */\n function highlightNextItem() {\n open()\n highlightedItemIndex.value = (highlightedItemIndex.value + 1) % props.items.length\n }\n\n /**\n * Highlights the item before the one that is currently highlighted.\n */\n function highlightPreviousItem() {\n const currentIndex = highlightedItemIndex.value\n open()\n highlightedItemIndex.value = currentIndex > 0 ? currentIndex - 1 : props.items.length - 1\n }\n\n /**\n * Highlights the first of the provided items.\n */\n function highlightFirstItem() {\n highlightedItemIndex.value = 0\n }\n\n /**\n * Highlights the last of the provided items.\n */\n function highlightLastItem() {\n highlightedItemIndex.value = props.items.length - 1\n }\n\n /**\n * Updates the variable that is used to search in the filters.\n *\n * @param event - The event coming from the user typing.\n */\n function updateSearchBuffer(event: KeyboardEvent) {\n if (/^\\w$/.test(event.key)) {\n const key = event.key\n searchBuffer.value += key\n restartResetSearchTimeout()\n }\n }\n\n /**\n * Resets the search buffer.\n */\n function resetSearchBuffer() {\n searchBuffer.value = ''\n }\n\n /**\n * Closes the dropdown if the passed event has happened on an element out of the dropdown.\n *\n * @param event - The event to check if it has happened out of the dropdown component.\n */\n function closeIfEventIsOutOfDropdown(event: MouseEvent | TouchEvent | FocusEvent) {\n if (!rootRef.value?.contains(getTargetElement(event))) {\n close()\n }\n }\n\n /**\n * Adds listeners to the document element to detect if the focus has moved out from the\n * dropdown.\n */\n function addDocumentCloseListeners() {\n document.addEventListener('mousedown', closeIfEventIsOutOfDropdown)\n document.addEventListener('touchstart', closeIfEventIsOutOfDropdown)\n document.addEventListener('focusin', closeIfEventIsOutOfDropdown)\n }\n\n /**\n * Removes the listeners of the document element to detect if the focus has moved out from the\n * dropdown.\n */\n function removeDocumentCloseListeners() {\n document.removeEventListener('mousedown', closeIfEventIsOutOfDropdown)\n document.removeEventListener('touchstart', closeIfEventIsOutOfDropdown)\n document.removeEventListener('focusin', closeIfEventIsOutOfDropdown)\n }\n\n /**\n * Highlights the item that matches the search buffer. To do so it checks the list buttons\n * text content. It highlights items following this priority:\n * - If an element is already highlighted, it starts searching from that element.\n * - If no element is found starting from the previously highlighted, it returns the first one.\n * - If no element is found matching the search query it highlights the first element.\n *\n * @param search - The search string to find in the HTML.\n */\n watch(searchBuffer, search => {\n if (search) {\n const normalizedSearch = normalizeString(search)\n const matchingIndices = itemsButtonRefs?.value?.reduce<number[]>(\n (matchingIndices, button, index) => {\n if (button) {\n const safeButtonWordCharacters = button.textContent!.replace(/\\W/g, '')\n const normalizedButtonText = normalizeString(safeButtonWordCharacters)\n if (normalizedButtonText.startsWith(normalizedSearch)) {\n matchingIndices.push(index)\n }\n }\n return matchingIndices\n },\n [],\n )\n highlightedItemIndex.value =\n // First matching item starting to search from the current highlighted element\n matchingIndices?.find(index => index >= highlightedItemIndex.value) ??\n // First matching item\n matchingIndices?.[0] ??\n // First item\n 0\n }\n })\n\n /**\n * Updates the debounced function to reset the search.\n *\n * @param searchTimeoutMs - The new milliseconds that have to pass without typing before\n * resetting the search.\n */\n watch(\n () => props.searchTimeoutMs,\n searchTimeoutMs => {\n restartResetSearchTimeout = debounceFunction(resetSearchBuffer, searchTimeoutMs)\n },\n { immediate: true },\n )\n\n /**\n * Focuses the DOM element which matches the `highlightedItemIndex`.\n *\n * @param highlightedItemIndex - The index of the HTML element to focus.\n */\n watch(\n highlightedItemIndex,\n highlightedItemIndex => {\n nextTick(() => itemsButtonRefs.value[highlightedItemIndex]?.focus())\n },\n { immediate: true },\n )\n\n /**\n * When the dropdown is open it sets the focused element to the one that is selected.\n *\n * @param isOpen - True if the dropdown is open, false otherwise.\n */\n watch(isOpen, isOpen => {\n highlightedItemIndex.value = isOpen\n ? props.modelValue === null\n ? 0\n : props.items.indexOf(props.modelValue)\n : -1\n })\n\n /**\n * Adds and removes listeners to close the dropdown when it loses the focus.\n *\n * @param isOpen - True if the dropdown is open, false otherwise.\n */\n watch(isOpen, isOpen => {\n /*\n * Because there is an issue with Firefox in macOS and Safari that doesn't focus the target\n * element of the `mousedown` events, the `focusout` event `relatedTarget` property can't be\n * used to detect whether the user has blurred the dropdown. The hack here is to use\n * document listeners that have the side effect of losing the focus.\n */\n if (isOpen) {\n addDocumentCloseListeners()\n } else {\n removeDocumentCloseListeners()\n }\n })\n\n /**\n * If the dropdown is destroyed before removing the document listeners, it ensures that they\n * are removed too.\n */\n onBeforeUnmount(() => {\n removeDocumentCloseListeners()\n })\n\n return {\n hasToggleSlot: !!slots.toggle,\n closeAndFocusToggleButton,\n dropdownCSSClasses,\n emitSelectedItemChanged,\n highlightFirstItem,\n highlightLastItem,\n highlightNextItem,\n highlightPreviousItem,\n highlightedItemIndex,\n isOpen,\n itemsButtonRefs,\n itemsCSSClasses,\n listId,\n open,\n rootRef,\n toggle,\n toggleButtonRef,\n updateSearchBuffer,\n }\n },\n})\n</script>\n\n<style lang=\"css\" scoped>\n.x-dropdown {\n position: relative;\n}\n\n.x-dropdown__items-list {\n z-index: 1;\n list-style: none;\n position: absolute;\n padding: 0;\n margin: 0;\n top: calc(100% + var(--x-size-gap-dropdown-default, 0));\n}\n</style>\n\n<docs lang=\"mdx\">\n## Example\n\nThe `Dropdown` component is a simple yet customizable select component. The component needs to work\nwith the list of items available to select, which are passed using the `items` prop, and the selected\nitem, which is passed in using the `value` prop.\n\nThe supported items must be an array that can contain unique strings, unique numbers, or objects\nwith a unique `id` property.\n\nThe content of each item can be customized using the `item` slot, which apart from the data of the\nitem, it also receives via prop if the element is selected or highlighted.\n\nThe `toggle` slot can be used to customize the button that opens the dropdown. If this is not\nprovided, the `item` slot will be used for that.\n\n```vue\n<template>\n <BaseDropdown v-model=\"value\" :items=\"items\">\n <template #toggle=\"{ item, isOpen }\">{{ item }} {{ isOpen ? '🔼' : '🔽' }}️</template>\n <template #item=\"{ item, isSelected, isHighlighted }\">\n <span v-if=\"isHighlighted\">🟢</span>\n <span v-if=\"isSelected\">✅</span>\n <span>{{ item }}</span>\n </template>\n </BaseDropdown>\n</template>\n\n<script setup>\nimport { BaseDropdown } from '@empathyco/x-components'\nimport { ref } from 'vue'\nconst items = ['a', 2, { id: '3' }]\nconst value = ref('a')\n</script>\n```\n</docs>\n"],"names":["NoAnimation","debounceFunction"],"mappings":"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"}
@@ -21,6 +21,7 @@ import '../animations/animate-translate/animate-translate.style.css.js';
21
21
  import '../animations/animate-width.vue2.js';
22
22
  import '../animations/animate-width.vue3.js';
23
23
  import '../animations/change-height.vue2.js';
24
+ import '../animations/change-height.vue3.js';
24
25
  import '../animations/collapse-height.vue2.js';
25
26
  import '../animations/collapse-height.vue3.js';
26
27
  import '../animations/collapse-width.vue2.js';
@@ -1 +1 @@
1
- {"version":3,"file":"base-modal.vue2.js","sources":["../../../../src/components/modals/base-modal.vue"],"sourcesContent":["<template>\n <div v-show=\"isWaitingForLeave || open\" ref=\"modalRef\" class=\"x-modal\" data-test=\"modal\">\n <component\n :is=\"animation\"\n @before-leave=\"isWaitingForLeave = true\"\n @after-leave=\"isWaitingForLeave = false\"\n >\n <div\n v-if=\"open\"\n ref=\"modalContentRef\"\n class=\"x-modal__content\"\n data-test=\"modal-content\"\n role=\"dialog\"\n :class=\"contentClass\"\n aria-label=\"Base modal content\"\n >\n <!-- @slot (Required) Modal container content -->\n <slot />\n </div>\n </component>\n <component :is=\"overlayAnimation\">\n <div\n v-if=\"open\"\n class=\"x-modal__overlay\"\n :class=\"overlayClass\"\n data-test=\"modal-overlay\"\n @click=\"emitOverlayClicked\"\n @keydown=\"emitOverlayClicked\"\n />\n </component>\n </div>\n</template>\n\n<script lang=\"ts\">\nimport { defineComponent, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'\nimport { useDebounce } from '../../composables'\nimport { AnimationProp } from '../../types'\nimport { FOCUSABLE_SELECTORS, getTargetElement } from '../../utils'\nimport { Fade, NoAnimation } from '../animations'\n\n/**\n * Base component with no XPlugin dependencies that serves as a utility for constructing more\n * complex modals.\n *\n * @public\n */\nexport default defineComponent({\n name: 'BaseModal',\n props: {\n /** Determines if the modal is open or not. */\n open: {\n type: Boolean,\n required: true,\n },\n /**\n * Determines if the focused element changes to one inside the modal when it opens. Either the\n * first element with a positive tabindex or just the first focusable element.\n */\n focusOnOpen: {\n type: Boolean,\n default: true,\n },\n /**\n * The reference selector of a DOM element to use as reference to position the modal.\n * This selector can be an ID or a class, if it is a class, it will use the first\n * element that matches.\n */\n referenceSelector: String,\n /** Animation to use for opening/closing the modal.This animation only affects the content. */\n animation: {\n type: AnimationProp,\n default: () => NoAnimation,\n },\n /**\n * Animation to use for the overlay (backdrop) part of the modal. By default, it uses\n * a fade transition.\n */\n overlayAnimation: {\n type: AnimationProp,\n default: () => Fade,\n },\n /** Class inherited by content element. */\n contentClass: String,\n /** Class inherited by overlay element. */\n overlayClass: String,\n },\n emits: ['click:overlay', 'focusin:body'],\n setup(props, { emit }) {\n /** Reference to the modal element in the DOM. */\n const modalRef = ref<HTMLDivElement>()\n /** Reference to the modal content element in the DOM. */\n const modalContentRef = ref<HTMLDivElement>()\n\n /** The previous value of the body overflow style. */\n const previousBodyOverflow = ref('')\n /** The previous value of the HTML element overflow style. */\n const previousHTMLOverflow = ref('')\n /** Boolean to delay the leave animation until it has completed. */\n const isWaitingForLeave = ref(false)\n /** The reference element to use to find the modal's position. */\n let referenceElement: HTMLElement | undefined\n\n /** Disables the scroll of both the body and the window. */\n function disableScroll() {\n previousBodyOverflow.value = document.body.style.overflow\n previousHTMLOverflow.value = document.documentElement.style.overflow\n document.body.style.overflow = document.documentElement.style.overflow = 'hidden'\n }\n\n /** Restores the scroll of both the body and the window. */\n function enableScroll() {\n document.body.style.overflow = previousBodyOverflow.value\n document.documentElement.style.overflow = previousHTMLOverflow.value\n }\n\n /**\n * Emits the `click:overlay` event if the click has been triggered in the overlay layer.\n *\n * @param event - The click event.\n */\n function emitOverlayClicked(event: Event) {\n // eslint-disable-next-line vue/custom-event-name-casing\n emit('click:overlay', event)\n }\n\n /**\n * Emits the `focusin:body` event if a focus event has been triggered outside the modal.\n *\n * @param event - The focusin event.\n */\n function emitFocusInBody(event: FocusEvent) {\n if (!modalContentRef.value?.contains(getTargetElement(event))) {\n // eslint-disable-next-line vue/custom-event-name-casing\n emit('focusin:body', event)\n }\n }\n\n /**\n * Adds listeners to the body element ot detect if the modal should be closed.\n *\n * @remarks TODO find a better solution and remove the timeout\n * To avoid emit the focusin on opening X that provokes closing it immediately.\n * This is because this event was emitted after the open of main modal when the user clicks\n * on the customer website search box (focus event). This way we avoid add the listener before\n * the open and the avoid the event that provokes the close.\n */\n function addBodyListeners() {\n setTimeout(() => {\n document.body.addEventListener('focusin', emitFocusInBody)\n })\n }\n\n /** Removes the body listeners. */\n function removeBodyListeners() {\n document.body.removeEventListener('focusin', emitFocusInBody)\n }\n\n /**\n * Sets the focused element to the first element either the first element with a positive\n * tabindex or, if there isn't any, the first focusable element inside the modal.\n */\n function setFocus() {\n const candidates: HTMLElement[] = Array.from(\n modalContentRef.value?.querySelectorAll(FOCUSABLE_SELECTORS) ?? [],\n )\n const element = candidates.find(element => element.tabIndex) ?? candidates[0]\n element?.focus()\n }\n\n /**\n * Syncs the body to the open state of the modal, adding or removing styles and listeners.\n *\n * @remarks nextTick() to wait for `modalContentRef` to be updated to look for focusable\n * candidates inside.\n *\n * @param isOpen - True when the modal is opened.\n */\n async function syncBody(isOpen: boolean) {\n if (isOpen) {\n disableScroll()\n addBodyListeners()\n if (props.focusOnOpen) {\n await nextTick()\n setFocus()\n }\n } else {\n enableScroll()\n removeBodyListeners()\n }\n }\n\n /**\n * Updates the position of the modal setting the top of the element depending\n * on the selector. The modal will be placed under this selector.\n */\n const debouncedUpdatePosition = useDebounce(\n () => {\n const { height, y } = referenceElement?.getBoundingClientRect() ?? { height: 0, y: 0 }\n modalRef.value!.style.top = `${height + y}px`\n modalRef.value!.style.bottom = '0'\n modalRef.value!.style.height = 'auto'\n },\n 100,\n { leading: true },\n )\n\n let resizeObserver: ResizeObserver\n\n onMounted(() => {\n watch(() => props.open, syncBody)\n if (props.open) {\n syncBody(true)\n }\n\n resizeObserver = new ResizeObserver(debouncedUpdatePosition)\n\n watch(\n () => props.referenceSelector,\n () => {\n resizeObserver.disconnect()\n\n if (props.referenceSelector) {\n const element = document.querySelector(props.referenceSelector) as HTMLElement\n if (element) {\n referenceElement = element\n resizeObserver.observe(element)\n }\n } else {\n referenceElement = undefined\n debouncedUpdatePosition()\n }\n },\n { immediate: true },\n )\n })\n\n onBeforeUnmount(() => {\n if (props.open) {\n removeBodyListeners()\n enableScroll()\n }\n resizeObserver.disconnect()\n })\n\n return {\n emitOverlayClicked,\n isWaitingForLeave,\n modalContentRef,\n modalRef,\n }\n },\n})\n</script>\n\n<style lang=\"css\" scoped>\n.x-modal {\n position: fixed;\n top: 0;\n left: 0;\n display: flex;\n align-items: flex-start;\n justify-content: flex-start;\n width: 100%;\n height: 100%;\n z-index: 1;\n}\n\n.x-modal__content {\n display: flex;\n flex-flow: column nowrap;\n z-index: 1;\n}\n\n.x-modal__overlay {\n width: 100%;\n height: 100%;\n position: absolute;\n background-color: rgb(0, 0, 0);\n opacity: 0.3;\n}\n</style>\n\n<docs lang=\"mdx\">\n## Examples\n\nThe `BaseModal` is a simple component that serves to create complex modals. Its open state has to be\npassed via prop. There is a prop, `referenceSelector`, used to place the modal under some element\ninstead of set the top of the element directly. It also accepts an animation to use for opening &\nclosing.\n\nIt emits a `click:overlay` event when any part out of the content is clicked, but only if the modal\nis open.\n\n```vue\n<template>\n <div>\n <button @click=\"open = true\">Open modal</button>\n <BaseModal\n :animation=\"fadeAndSlide\"\n :open=\"open\"\n @click:overlay=\"open = false\"\n referenceSelector=\".header\"\n >\n <h1>Hello</h1>\n <p>The modal is working</p>\n <button @click=\"open = false\">Close modal</button>\n </BaseModal>\n </div>\n</template>\n\n<script setup>\nimport { ref } from 'vue'\nimport { BaseModal, FadeAndSlide } from '@empathyco/x-components'\nconst open = ref(false)\nconst fadeAndSlide = FadeAndSlide\n</script>\n```\n\n### Customized usage\n\n#### Customizing the content with classes\n\nThe `contentClass` prop can be used to add classes to the modal content.\n\n```vue\n<template>\n <div>\n <button @click=\"open = true\">Open modal</button>\n <BaseModal\n :animation=\"fadeAndSlide\"\n :open=\"open\"\n @click:overlay=\"open = false\"\n referenceSelector=\".header\"\n contentClass=\"x-bg-neutral-75\"\n >\n <h1>Hello</h1>\n <p>The modal is working</p>\n <button @click=\"open = false\">Close modal</button>\n </BaseModal>\n </div>\n</template>\n\n<script setup>\nimport { ref } from 'vue'\nimport { BaseModal, FadeAndSlide } from '@empathyco/x-components'\nconst open = ref(false)\nconst fadeAndSlide = FadeAndSlide\n</script>\n```\n\n#### Customizing the overlay with classes\n\nThe `overlayClass` prop can be used to add classes to the modal overlay.\n\n```vue\n<template>\n <div>\n <button @click=\"open = true\">Open modal</button>\n <BaseModal\n :animation=\"fadeAndSlide\"\n :open=\"open\"\n @click:overlay=\"open = false\"\n referenceSelector=\".header\"\n overlayClass=\"x-bg-neutral-75\"\n >\n <h1>Hello</h1>\n <p>The modal is working</p>\n <button @click=\"open = false\">Close modal</button>\n </BaseModal>\n </div>\n</template>\n\n<script setup>\nimport { ref } from 'vue'\nimport { BaseModal, FadeAndSlide } from '@empathyco/x-components'\nconst open = ref(false)\nconst fadeAndSlide = FadeAndSlide\n</script>\n```\n\n## Vue Events\n\nA list of events that the component will emit:\n\n- `click:overlay`: the event is emitted after the user clicks any part out of the content but only\n if the modal is open. The event payload is the mouse event that triggers it.\n- `focusin:body`: the event is emitted after the user focus in any part out of the content but only\n if the modal is open. The event payload is the focus event that triggers it.\n</docs>\n"],"names":["NoAnimation"],"mappings":"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"}
1
+ {"version":3,"file":"base-modal.vue2.js","sources":["../../../../src/components/modals/base-modal.vue"],"sourcesContent":["<template>\n <div v-show=\"isWaitingForLeave || open\" ref=\"modalRef\" class=\"x-modal\" data-test=\"modal\">\n <component\n :is=\"animation\"\n @before-leave=\"isWaitingForLeave = true\"\n @after-leave=\"isWaitingForLeave = false\"\n >\n <div\n v-if=\"open\"\n ref=\"modalContentRef\"\n class=\"x-modal__content\"\n data-test=\"modal-content\"\n role=\"dialog\"\n :class=\"contentClass\"\n aria-label=\"Base modal content\"\n >\n <!-- @slot (Required) Modal container content -->\n <slot />\n </div>\n </component>\n <component :is=\"overlayAnimation\">\n <div\n v-if=\"open\"\n class=\"x-modal__overlay\"\n :class=\"overlayClass\"\n data-test=\"modal-overlay\"\n @click=\"emitOverlayClicked\"\n @keydown=\"emitOverlayClicked\"\n />\n </component>\n </div>\n</template>\n\n<script lang=\"ts\">\nimport { defineComponent, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'\nimport { useDebounce } from '../../composables'\nimport { AnimationProp } from '../../types'\nimport { FOCUSABLE_SELECTORS, getTargetElement } from '../../utils'\nimport { Fade, NoAnimation } from '../animations'\n\n/**\n * Base component with no XPlugin dependencies that serves as a utility for constructing more\n * complex modals.\n *\n * @public\n */\nexport default defineComponent({\n name: 'BaseModal',\n props: {\n /** Determines if the modal is open or not. */\n open: {\n type: Boolean,\n required: true,\n },\n /**\n * Determines if the focused element changes to one inside the modal when it opens. Either the\n * first element with a positive tabindex or just the first focusable element.\n */\n focusOnOpen: {\n type: Boolean,\n default: true,\n },\n /**\n * The reference selector of a DOM element to use as reference to position the modal.\n * This selector can be an ID or a class, if it is a class, it will use the first\n * element that matches.\n */\n referenceSelector: String,\n /** Animation to use for opening/closing the modal.This animation only affects the content. */\n animation: {\n type: AnimationProp,\n default: () => NoAnimation,\n },\n /**\n * Animation to use for the overlay (backdrop) part of the modal. By default, it uses\n * a fade transition.\n */\n overlayAnimation: {\n type: AnimationProp,\n default: () => Fade,\n },\n /** Class inherited by content element. */\n contentClass: String,\n /** Class inherited by overlay element. */\n overlayClass: String,\n },\n emits: ['click:overlay', 'focusin:body'],\n setup(props, { emit }) {\n /** Reference to the modal element in the DOM. */\n const modalRef = ref<HTMLDivElement>()\n /** Reference to the modal content element in the DOM. */\n const modalContentRef = ref<HTMLDivElement>()\n\n /** The previous value of the body overflow style. */\n const previousBodyOverflow = ref('')\n /** The previous value of the HTML element overflow style. */\n const previousHTMLOverflow = ref('')\n /** Boolean to delay the leave animation until it has completed. */\n const isWaitingForLeave = ref(false)\n /** The reference element to use to find the modal's position. */\n let referenceElement: HTMLElement | undefined\n\n /** Disables the scroll of both the body and the window. */\n function disableScroll() {\n previousBodyOverflow.value = document.body.style.overflow\n previousHTMLOverflow.value = document.documentElement.style.overflow\n document.body.style.overflow = document.documentElement.style.overflow = 'hidden'\n }\n\n /** Restores the scroll of both the body and the window. */\n function enableScroll() {\n document.body.style.overflow = previousBodyOverflow.value\n document.documentElement.style.overflow = previousHTMLOverflow.value\n }\n\n /**\n * Emits the `click:overlay` event if the click has been triggered in the overlay layer.\n *\n * @param event - The click event.\n */\n function emitOverlayClicked(event: Event) {\n // eslint-disable-next-line vue/custom-event-name-casing\n emit('click:overlay', event)\n }\n\n /**\n * Emits the `focusin:body` event if a focus event has been triggered outside the modal.\n *\n * @param event - The focusin event.\n */\n function emitFocusInBody(event: FocusEvent) {\n if (!modalContentRef.value?.contains(getTargetElement(event))) {\n // eslint-disable-next-line vue/custom-event-name-casing\n emit('focusin:body', event)\n }\n }\n\n /**\n * Adds listeners to the body element ot detect if the modal should be closed.\n *\n * @remarks TODO find a better solution and remove the timeout\n * To avoid emit the focusin on opening X that provokes closing it immediately.\n * This is because this event was emitted after the open of main modal when the user clicks\n * on the customer website search box (focus event). This way we avoid add the listener before\n * the open and the avoid the event that provokes the close.\n */\n function addBodyListeners() {\n setTimeout(() => {\n document.body.addEventListener('focusin', emitFocusInBody)\n })\n }\n\n /** Removes the body listeners. */\n function removeBodyListeners() {\n document.body.removeEventListener('focusin', emitFocusInBody)\n }\n\n /**\n * Sets the focused element to the first element either the first element with a positive\n * tabindex or, if there isn't any, the first focusable element inside the modal.\n */\n function setFocus() {\n const candidates: HTMLElement[] = Array.from(\n modalContentRef.value?.querySelectorAll(FOCUSABLE_SELECTORS) ?? [],\n )\n const element = candidates.find(element => element.tabIndex) ?? candidates[0]\n element?.focus()\n }\n\n /**\n * Syncs the body to the open state of the modal, adding or removing styles and listeners.\n *\n * @remarks nextTick() to wait for `modalContentRef` to be updated to look for focusable\n * candidates inside.\n *\n * @param isOpen - True when the modal is opened.\n */\n async function syncBody(isOpen: boolean) {\n if (isOpen) {\n disableScroll()\n addBodyListeners()\n if (props.focusOnOpen) {\n await nextTick()\n setFocus()\n }\n } else {\n enableScroll()\n removeBodyListeners()\n }\n }\n\n /**\n * Updates the position of the modal setting the top of the element depending\n * on the selector. The modal will be placed under this selector.\n */\n const debouncedUpdatePosition = useDebounce(\n () => {\n const { height, y } = referenceElement?.getBoundingClientRect() ?? { height: 0, y: 0 }\n modalRef.value!.style.top = `${height + y}px`\n modalRef.value!.style.bottom = '0'\n modalRef.value!.style.height = 'auto'\n },\n 100,\n { leading: true },\n )\n\n let resizeObserver: ResizeObserver\n\n onMounted(() => {\n watch(() => props.open, syncBody)\n if (props.open) {\n syncBody(true)\n }\n\n resizeObserver = new ResizeObserver(debouncedUpdatePosition)\n\n watch(\n () => props.referenceSelector,\n () => {\n resizeObserver.disconnect()\n\n if (props.referenceSelector) {\n const element = document.querySelector(props.referenceSelector) as HTMLElement\n if (element) {\n referenceElement = element\n resizeObserver.observe(element)\n }\n } else {\n referenceElement = undefined\n debouncedUpdatePosition()\n }\n },\n { immediate: true },\n )\n })\n\n onBeforeUnmount(() => {\n if (props.open) {\n removeBodyListeners()\n enableScroll()\n }\n resizeObserver.disconnect()\n })\n\n return {\n emitOverlayClicked,\n isWaitingForLeave,\n modalContentRef,\n modalRef,\n }\n },\n})\n</script>\n\n<style lang=\"css\" scoped>\n.x-modal {\n position: fixed;\n top: 0;\n left: 0;\n display: flex;\n align-items: flex-start;\n justify-content: flex-start;\n width: 100%;\n height: 100%;\n z-index: 1;\n}\n\n.x-modal__content {\n display: flex;\n flex-flow: column nowrap;\n z-index: 1;\n}\n\n.x-modal__overlay {\n width: 100%;\n height: 100%;\n position: absolute;\n background-color: rgb(0, 0, 0);\n opacity: 0.3;\n}\n</style>\n\n<docs lang=\"mdx\">\n## Examples\n\nThe `BaseModal` is a simple component that serves to create complex modals. Its open state has to be\npassed via prop. There is a prop, `referenceSelector`, used to place the modal under some element\ninstead of set the top of the element directly. It also accepts an animation to use for opening &\nclosing.\n\nIt emits a `click:overlay` event when any part out of the content is clicked, but only if the modal\nis open.\n\n```vue\n<template>\n <div>\n <button @click=\"open = true\">Open modal</button>\n <BaseModal\n :animation=\"fadeAndSlide\"\n :open=\"open\"\n @click:overlay=\"open = false\"\n referenceSelector=\".header\"\n >\n <h1>Hello</h1>\n <p>The modal is working</p>\n <button @click=\"open = false\">Close modal</button>\n </BaseModal>\n </div>\n</template>\n\n<script setup>\nimport { ref } from 'vue'\nimport { BaseModal, FadeAndSlide } from '@empathyco/x-components'\nconst open = ref(false)\nconst fadeAndSlide = FadeAndSlide\n</script>\n```\n\n### Customized usage\n\n#### Customizing the content with classes\n\nThe `contentClass` prop can be used to add classes to the modal content.\n\n```vue\n<template>\n <div>\n <button @click=\"open = true\">Open modal</button>\n <BaseModal\n :animation=\"fadeAndSlide\"\n :open=\"open\"\n @click:overlay=\"open = false\"\n referenceSelector=\".header\"\n contentClass=\"x-bg-neutral-75\"\n >\n <h1>Hello</h1>\n <p>The modal is working</p>\n <button @click=\"open = false\">Close modal</button>\n </BaseModal>\n </div>\n</template>\n\n<script setup>\nimport { ref } from 'vue'\nimport { BaseModal, FadeAndSlide } from '@empathyco/x-components'\nconst open = ref(false)\nconst fadeAndSlide = FadeAndSlide\n</script>\n```\n\n#### Customizing the overlay with classes\n\nThe `overlayClass` prop can be used to add classes to the modal overlay.\n\n```vue\n<template>\n <div>\n <button @click=\"open = true\">Open modal</button>\n <BaseModal\n :animation=\"fadeAndSlide\"\n :open=\"open\"\n @click:overlay=\"open = false\"\n referenceSelector=\".header\"\n overlayClass=\"x-bg-neutral-75\"\n >\n <h1>Hello</h1>\n <p>The modal is working</p>\n <button @click=\"open = false\">Close modal</button>\n </BaseModal>\n </div>\n</template>\n\n<script setup>\nimport { ref } from 'vue'\nimport { BaseModal, FadeAndSlide } from '@empathyco/x-components'\nconst open = ref(false)\nconst fadeAndSlide = FadeAndSlide\n</script>\n```\n\n## Vue Events\n\nA list of events that the component will emit:\n\n- `click:overlay`: the event is emitted after the user clicks any part out of the content but only\n if the modal is open. The event payload is the mouse event that triggers it.\n- `focusin:body`: the event is emitted after the user focus in any part out of the content but only\n if the modal is open. The event payload is the focus event that triggers it.\n</docs>\n"],"names":["NoAnimation"],"mappings":"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"}
@@ -6,6 +6,7 @@ import '../animations/animate-translate/animate-translate.style.css.js';
6
6
  import '../animations/animate-width.vue2.js';
7
7
  import '../animations/animate-width.vue3.js';
8
8
  import '../animations/change-height.vue2.js';
9
+ import '../animations/change-height.vue3.js';
9
10
  import '../animations/collapse-height.vue2.js';
10
11
  import '../animations/collapse-height.vue3.js';
11
12
  import '../animations/collapse-width.vue2.js';
@@ -1 +1 @@
1
- {"version":3,"file":"base-header-toggle-panel.vue2.js","sources":["../../../../src/components/panels/base-header-toggle-panel.vue"],"sourcesContent":["<template>\n <div class=\"x-header-toggle-panel\">\n <!-- @slot (Required) Slot that is be used for replacing the whole header. -->\n <slot name=\"header\" v-bind=\"{ toggleOpen, open }\">\n <!-- header-toggle-panel__header -->\n <button\n class=\"x-header-toggle-panel__header\"\n :class=\"headerClass\"\n data-test=\"toggle-panel-header\"\n @click=\"toggleOpen\"\n >\n <!-- @slot (Required) Slot used to just pass the content. -->\n <slot name=\"header-content\" v-bind=\"{ open }\"></slot>\n </button>\n </slot>\n\n <BaseTogglePanel :open=\"open\" :animation=\"animation\">\n <!-- @slot (Required) Default content. -->\n <slot />\n </BaseTogglePanel>\n </div>\n</template>\n\n<script lang=\"ts\">\nimport { defineComponent, ref } from 'vue'\nimport { AnimationProp } from '../../types'\nimport { NoAnimation } from '../animations'\nimport BaseTogglePanel from './base-toggle-panel.vue'\n\n/**\n * Toggle panel which uses the base toggle panel, adds a header and manage the\n * open / close state of the panel.\n *\n * @public\n */\nexport default defineComponent({\n name: 'BaseHeaderTogglePanel',\n components: { BaseTogglePanel },\n props: {\n /** Animation component that will be used to animate the base-toggle-panel. */\n animation: {\n type: AnimationProp,\n default: () => NoAnimation,\n },\n /** Handles if the panel is open by default. */\n startCollapsed: Boolean,\n /** Class inherited by content element. */\n headerClass: String,\n },\n setup(props, { emit }) {\n /**\n * Handles if the base panel is open or closed.\n *\n * @internal\n */\n const open = ref(!props.startCollapsed)\n\n /**\n * Emits open status event.\n *\n * @internal\n */\n const emitOpenStatusEvent = () => {\n emit(open.value ? 'open' : 'close')\n }\n\n /**\n * Toggles the open property.\n *\n * @internal\n */\n const toggleOpen = () => {\n open.value = !open.value\n emitOpenStatusEvent()\n }\n\n return {\n open,\n toggleOpen,\n }\n },\n})\n</script>\n\n<style lang=\"css\" scoped>\n.x-header-toggle-panel__header {\n cursor: pointer;\n}\n</style>\n\n<docs lang=\"mdx\">\n## Events\n\nA list of events that the component will emit:\n\n- `open`: emitted after the user clicks the element and opens it.\n- `close`: emitted after the user clicks the element and closes it.\n\n## Examples\n\nToggle panel which uses the base toggle panel, adds a header and manages the open/close state of the panel.\n\n### Basic usage\n\n```vue\n<template>\n <BaseHeaderTogglePanel :animation=\"animation\" :start-collapsed=\"false\">\n <template #header-content=\"{ open }\">\n <p>Header, open: {{ open ? 'close' : 'open' }}</p>\n </template>\n <template #default>\n <p>Default content</p>\n </template>\n </BaseHeaderTogglePanel>\n</template>\n\n<script setup>\nimport { BaseHeaderTogglePanel } from '@empathyco/x-components'\nimport { CollapseHeight } from '@empathyco/x-components/animations'\nconst animation = CollapseHeight\n</script>\n```\n\n### Custom header\n\n```vue\n<template>\n <BaseHeaderTogglePanel :animation=\"animation\" :start-collapsed=\"true\">\n <template #header=\"{ toggleOpen, open }\">\n <p>Header, open: {{ open ? 'close' : 'open' }}</p>\n <button @click=\"toggleOpen\">Toggle</button>\n </template>\n <template #default>\n <p>Default content</p>\n </template>\n </BaseHeaderTogglePanel>\n</template>\n\n<script setup>\nimport { BaseHeaderTogglePanel } from '@empathyco/x-components'\nimport { CollapseHeight } from '@empathyco/x-components/animations'\nconst animation = CollapseHeight\n</script>\n```\n\n### Customizing default header with classes\n\nThe `headerClass` prop can be used to add classes to the header of the toggle panel.\n\n```vue\n<template>\n <BaseHeaderTogglePanel headerClass=\"custom-class\" :animation=\"animation\" :start-collapsed=\"false\">\n <template #header-content=\"{ open }\">\n <p>Header, open: {{ open ? 'close' : 'open' }}</p>\n </template>\n <template #default>\n <p>Default content</p>\n </template>\n </BaseHeaderTogglePanel>\n</template>\n\n<script setup>\nimport { BaseHeaderTogglePanel } from '@empathyco/x-components'\nimport { CollapseHeight } from '@empathyco/x-components/animations'\nconst animation = CollapseHeight\n</script>\n```\n</docs>\n"],"names":["NoAnimation"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AA6BA;;;;;AAKE;AACF,gBAAe,eAAe,CAAC;AAC7B,IAAA,IAAI,EAAE,uBAAuB;IAC7B,UAAU,EAAE,EAAE,eAAc,EAAG;AAC/B,IAAA,KAAK,EAAE;;AAEL,QAAA,SAAS,EAAE;AACT,YAAA,IAAI,EAAE,aAAa;AACnB,YAAA,OAAO,EAAE,MAAMA,WAAW;AAC3B,SAAA;;AAED,QAAA,cAAc,EAAE,OAAO;;AAEvB,QAAA,WAAW,EAAE,MAAM;AACpB,KAAA;AACD,IAAA,KAAK,CAAC,KAAK,EAAE,EAAE,IAAG,EAAG,EAAA;AACnB;;;;AAIE;QACF,MAAM,IAAG,GAAI,GAAG,CAAC,CAAC,KAAK,CAAC,cAAc,CAAA;AAEtC;;;;AAIE;QACF,MAAM,mBAAkB,GAAI,MAAI;AAC9B,YAAA,IAAI,CAAC,IAAI,CAAC,KAAI,GAAI,MAAK,GAAI,OAAO,CAAA;AACpC,QAAA,CAAA;AAEA;;;;AAIE;QACF,MAAM,UAAS,GAAI,MAAI;AACrB,YAAA,IAAI,CAAC,KAAI,GAAI,CAAC,IAAI,CAAC,KAAI;AACvB,YAAA,mBAAmB,EAAC;AACtB,QAAA,CAAA;QAEA,OAAO;YACL,IAAI;YACJ,UAAU;SACZ;IACF,CAAC;AACF,CAAA,CAAA;;;;"}
1
+ {"version":3,"file":"base-header-toggle-panel.vue2.js","sources":["../../../../src/components/panels/base-header-toggle-panel.vue"],"sourcesContent":["<template>\n <div class=\"x-header-toggle-panel\">\n <!-- @slot (Required) Slot that is be used for replacing the whole header. -->\n <slot name=\"header\" v-bind=\"{ toggleOpen, open }\">\n <!-- header-toggle-panel__header -->\n <button\n class=\"x-header-toggle-panel__header\"\n :class=\"headerClass\"\n data-test=\"toggle-panel-header\"\n @click=\"toggleOpen\"\n >\n <!-- @slot (Required) Slot used to just pass the content. -->\n <slot name=\"header-content\" v-bind=\"{ open }\"></slot>\n </button>\n </slot>\n\n <BaseTogglePanel :open=\"open\" :animation=\"animation\">\n <!-- @slot (Required) Default content. -->\n <slot />\n </BaseTogglePanel>\n </div>\n</template>\n\n<script lang=\"ts\">\nimport { defineComponent, ref } from 'vue'\nimport { AnimationProp } from '../../types'\nimport { NoAnimation } from '../animations'\nimport BaseTogglePanel from './base-toggle-panel.vue'\n\n/**\n * Toggle panel which uses the base toggle panel, adds a header and manage the\n * open / close state of the panel.\n *\n * @public\n */\nexport default defineComponent({\n name: 'BaseHeaderTogglePanel',\n components: { BaseTogglePanel },\n props: {\n /** Animation component that will be used to animate the base-toggle-panel. */\n animation: {\n type: AnimationProp,\n default: () => NoAnimation,\n },\n /** Handles if the panel is open by default. */\n startCollapsed: Boolean,\n /** Class inherited by content element. */\n headerClass: String,\n },\n setup(props, { emit }) {\n /**\n * Handles if the base panel is open or closed.\n *\n * @internal\n */\n const open = ref(!props.startCollapsed)\n\n /**\n * Emits open status event.\n *\n * @internal\n */\n const emitOpenStatusEvent = () => {\n emit(open.value ? 'open' : 'close')\n }\n\n /**\n * Toggles the open property.\n *\n * @internal\n */\n const toggleOpen = () => {\n open.value = !open.value\n emitOpenStatusEvent()\n }\n\n return {\n open,\n toggleOpen,\n }\n },\n})\n</script>\n\n<style lang=\"css\" scoped>\n.x-header-toggle-panel__header {\n cursor: pointer;\n}\n</style>\n\n<docs lang=\"mdx\">\n## Events\n\nA list of events that the component will emit:\n\n- `open`: emitted after the user clicks the element and opens it.\n- `close`: emitted after the user clicks the element and closes it.\n\n## Examples\n\nToggle panel which uses the base toggle panel, adds a header and manages the open/close state of the panel.\n\n### Basic usage\n\n```vue\n<template>\n <BaseHeaderTogglePanel :animation=\"animation\" :start-collapsed=\"false\">\n <template #header-content=\"{ open }\">\n <p>Header, open: {{ open ? 'close' : 'open' }}</p>\n </template>\n <template #default>\n <p>Default content</p>\n </template>\n </BaseHeaderTogglePanel>\n</template>\n\n<script setup>\nimport { BaseHeaderTogglePanel } from '@empathyco/x-components'\nimport { CollapseHeight } from '@empathyco/x-components/animations'\nconst animation = CollapseHeight\n</script>\n```\n\n### Custom header\n\n```vue\n<template>\n <BaseHeaderTogglePanel :animation=\"animation\" :start-collapsed=\"true\">\n <template #header=\"{ toggleOpen, open }\">\n <p>Header, open: {{ open ? 'close' : 'open' }}</p>\n <button @click=\"toggleOpen\">Toggle</button>\n </template>\n <template #default>\n <p>Default content</p>\n </template>\n </BaseHeaderTogglePanel>\n</template>\n\n<script setup>\nimport { BaseHeaderTogglePanel } from '@empathyco/x-components'\nimport { CollapseHeight } from '@empathyco/x-components/animations'\nconst animation = CollapseHeight\n</script>\n```\n\n### Customizing default header with classes\n\nThe `headerClass` prop can be used to add classes to the header of the toggle panel.\n\n```vue\n<template>\n <BaseHeaderTogglePanel headerClass=\"custom-class\" :animation=\"animation\" :start-collapsed=\"false\">\n <template #header-content=\"{ open }\">\n <p>Header, open: {{ open ? 'close' : 'open' }}</p>\n </template>\n <template #default>\n <p>Default content</p>\n </template>\n </BaseHeaderTogglePanel>\n</template>\n\n<script setup>\nimport { BaseHeaderTogglePanel } from '@empathyco/x-components'\nimport { CollapseHeight } from '@empathyco/x-components/animations'\nconst animation = CollapseHeight\n</script>\n```\n</docs>\n"],"names":["NoAnimation"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AA6BA;;;;;AAKE;AACF,gBAAe,eAAe,CAAC;AAC7B,IAAA,IAAI,EAAE,uBAAuB;IAC7B,UAAU,EAAE,EAAE,eAAc,EAAG;AAC/B,IAAA,KAAK,EAAE;;AAEL,QAAA,SAAS,EAAE;AACT,YAAA,IAAI,EAAE,aAAa;AACnB,YAAA,OAAO,EAAE,MAAMA,WAAW;AAC3B,SAAA;;AAED,QAAA,cAAc,EAAE,OAAO;;AAEvB,QAAA,WAAW,EAAE,MAAM;AACpB,KAAA;AACD,IAAA,KAAK,CAAC,KAAK,EAAE,EAAE,IAAG,EAAG,EAAA;AACnB;;;;AAIE;QACF,MAAM,IAAG,GAAI,GAAG,CAAC,CAAC,KAAK,CAAC,cAAc,CAAA;AAEtC;;;;AAIE;QACF,MAAM,mBAAkB,GAAI,MAAI;AAC9B,YAAA,IAAI,CAAC,IAAI,CAAC,KAAI,GAAI,MAAK,GAAI,OAAO,CAAA;AACpC,QAAA,CAAA;AAEA;;;;AAIE;QACF,MAAM,UAAS,GAAI,MAAI;AACrB,YAAA,IAAI,CAAC,KAAI,GAAI,CAAC,IAAI,CAAC,KAAI;AACvB,YAAA,mBAAmB,EAAC;AACtB,QAAA,CAAA;QAEA,OAAO;YACL,IAAI;YACJ,UAAU;SACZ;IACF,CAAC;AACF,CAAA,CAAA;;;;"}
@@ -18,6 +18,7 @@ import '../animations/animate-translate/animate-translate.style.css.js';
18
18
  import '../animations/animate-width.vue2.js';
19
19
  import '../animations/animate-width.vue3.js';
20
20
  import '../animations/change-height.vue2.js';
21
+ import '../animations/change-height.vue3.js';
21
22
  import '../animations/collapse-height.vue2.js';
22
23
  import '../animations/collapse-height.vue3.js';
23
24
  import '../animations/collapse-width.vue2.js';
@@ -1 +1 @@
1
- {"version":3,"file":"base-id-toggle-panel.vue2.js","sources":["../../../../src/components/panels/base-id-toggle-panel.vue"],"sourcesContent":["<template>\n <BaseTogglePanel :open=\"isOpen\" :animation=\"animation\">\n <!-- @slot (Required) Default content -->\n <slot />\n </BaseTogglePanel>\n</template>\n\n<script lang=\"ts\">\nimport { defineComponent, ref, watch } from 'vue'\nimport { use$x } from '../../composables'\nimport { AnimationProp } from '../../types'\nimport { NoAnimation } from '../animations'\nimport BaseTogglePanel from './base-toggle-panel.vue'\n\n/**\n * Simple panel that could receives its initial open state via prop, if not the default is opens\n * and a required prop, named `panelId`, which are responsible of rendering default slot\n * inside a configurable transition.\n *\n * It reacts to `UserClickedPanelToggleButton` event, when their payload matches the component's\n * 'panelId' prop, to handle its open/close state.\n *\n * The default slot offers the possibility to customise the modal content.\n *\n * @public\n */\nexport default defineComponent({\n name: 'BaseIdTogglePanel',\n components: { BaseTogglePanel },\n props: {\n /** Shows the panel open at the beginning or not, depending on its state. */\n startOpen: {\n type: Boolean,\n default: true,\n },\n /** Animation component that will be used to animate the panel content. */\n animation: {\n type: AnimationProp,\n default: () => NoAnimation,\n },\n /**\n * The id to link with the BaseIdTogglePanelButton.\n * Both components must use the same Id to make them interact.\n */\n panelId: {\n type: String,\n required: true,\n },\n },\n setup(props) {\n const $x = use$x()\n\n /** Whether the toggle panel is open or not. */\n const isOpen = ref(props.startOpen)\n\n /**\n * Method to subscribe to the {@link XEventsTypes.UserClickedPanelToggleButton} event.\n *\n * @param panelId - The payload of the {@link XEventsTypes.UserClickedPanelToggleButton} event.\n *\n * @public\n */\n const togglePanel = (panelId: string) => {\n if (props.panelId === panelId) {\n isOpen.value = !isOpen.value\n }\n }\n\n $x.on('UserClickedPanelToggleButton', false).subscribe(togglePanel)\n\n /**\n * Emits the {@link XEventsTypes.TogglePanelStateChanged} event, when the internal state\n * changes.\n *\n * @remarks This event is necessary to link the state with the {@link BaseIdTogglePanelButton}\n * component.\n * @public\n */\n watch(\n () => isOpen.value,\n () => {\n $x.emit('TogglePanelStateChanged', isOpen.value, {\n id: props.panelId,\n target: document.getElementById(props.panelId) as HTMLElement,\n })\n },\n { immediate: true },\n )\n\n return { isOpen }\n },\n})\n</script>\n\n<docs lang=\"mdx\">\n## Events\n\nA list of events that the component will watch and emit:\n\n- [`UserClickedPanelToggleButton`](https://github.com/empathyco/x/blob/main/packages/x-components/src/wiring/events.types.ts):\n watched to toggle the panel when the payload matches the `panelId` prop.\n- [`TogglePanelStateChanged`](https://github.com/empathyco/x/blob/main/packages/x-components/src/wiring/events.types.ts):\n emitted when the internal open state changes, with the new state and panel id.\n\n## Examples\n\n### Basic usage\n\n```vue\n<template>\n <div>\n <BaseIdTogglePanelButton panelId=\"myToggle\">\n <img src=\"./open-button-icon.svg\" alt=\"Toggle Aside\" />\n <span>Toggle Aside</span>\n </BaseIdTogglePanelButton>\n <BaseIdTogglePanel :startOpen=\"true\" :animation=\"animation\" panelId=\"myToggle\">\n <div class=\"x-text1\">My toggle</div>\n </BaseIdTogglePanel>\n </div>\n</template>\n\n<script setup>\nimport { BaseIdTogglePanel, BaseIdTogglePanelButton } from '@empathyco/x-components'\nimport { CollapseFromTop } from '@empathyco/x-components/animations'\nconst animation = CollapseFromTop\n</script>\n```\n\n### Listening to state changes\n\nYou can listen to the `TogglePanelStateChanged` event to react to panel open/close state changes:\n\n```vue\n<template>\n <div>\n <span>Panel is {{ isPanelOpen ? 'open' : 'closed' }}</span>\n </div>\n</template>\n\n<script setup>\nimport { ref } from 'vue'\nimport { use$x } from '@empathyco/x-components'\n\nconst x = use$x()\nconst isPanelOpen = ref(false)\nconst panelId = 'myToggle'\n\nx.on('TogglePanelStateChanged').subscribe((isOpen, { id }) => {\n if (id === panelId) {\n isPanelOpen.value = isOpen\n }\n})\n</script>\n```\n</docs>\n"],"names":["NoAnimation"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAcA;;;;;;;;;;;AAWE;AACF,gBAAe,eAAe,CAAC;AAC7B,IAAA,IAAI,EAAE,mBAAmB;IACzB,UAAU,EAAE,EAAE,eAAc,EAAG;AAC/B,IAAA,KAAK,EAAE;;AAEL,QAAA,SAAS,EAAE;AACT,YAAA,IAAI,EAAE,OAAO;AACb,YAAA,OAAO,EAAE,IAAI;AACd,SAAA;;AAED,QAAA,SAAS,EAAE;AACT,YAAA,IAAI,EAAE,aAAa;AACnB,YAAA,OAAO,EAAE,MAAMA,WAAW;AAC3B,SAAA;AACD;;;AAGE;AACF,QAAA,OAAO,EAAE;AACP,YAAA,IAAI,EAAE,MAAM;AACZ,YAAA,QAAQ,EAAE,IAAI;AACf,SAAA;AACF,KAAA;AACD,IAAA,KAAK,CAAC,KAAK,EAAA;AACT,QAAA,MAAM,EAAC,GAAI,KAAK,EAAC;;QAGjB,MAAM,MAAK,GAAI,GAAG,CAAC,KAAK,CAAC,SAAS,CAAA;AAElC;;;;;;AAME;AACF,QAAA,MAAM,WAAU,GAAI,CAAC,OAAe,KAAG;AACrC,YAAA,IAAI,KAAK,CAAC,OAAM,KAAM,OAAO,EAAE;AAC7B,gBAAA,MAAM,CAAC,KAAI,GAAI,CAAC,MAAM,CAAC,KAAI;YAC7B;AACF,QAAA,CAAA;AAEA,QAAA,EAAE,CAAC,EAAE,CAAC,8BAA8B,EAAE,KAAK,CAAC,CAAC,SAAS,CAAC,WAAW,CAAA;AAElE;;;;;;;AAOE;QACF,KAAK,CACH,MAAM,MAAM,CAAC,KAAK,EAClB,MAAI;YACF,EAAE,CAAC,IAAI,CAAC,yBAAyB,EAAE,MAAM,CAAC,KAAK,EAAE;gBAC/C,EAAE,EAAE,KAAK,CAAC,OAAO;gBACjB,MAAM,EAAE,QAAQ,CAAC,cAAc,CAAC,KAAK,CAAC,OAAO,CAAgB;AAC9D,aAAA,CAAA;AACH,QAAA,CAAC,EACD,EAAE,SAAS,EAAE,MAAM,CACrB;QAEA,OAAO,EAAE,MAAK,EAAE;IAClB,CAAC;AACF,CAAA,CAAA;;;;"}
1
+ {"version":3,"file":"base-id-toggle-panel.vue2.js","sources":["../../../../src/components/panels/base-id-toggle-panel.vue"],"sourcesContent":["<template>\n <BaseTogglePanel :open=\"isOpen\" :animation=\"animation\">\n <!-- @slot (Required) Default content -->\n <slot />\n </BaseTogglePanel>\n</template>\n\n<script lang=\"ts\">\nimport { defineComponent, ref, watch } from 'vue'\nimport { use$x } from '../../composables'\nimport { AnimationProp } from '../../types'\nimport { NoAnimation } from '../animations'\nimport BaseTogglePanel from './base-toggle-panel.vue'\n\n/**\n * Simple panel that could receives its initial open state via prop, if not the default is opens\n * and a required prop, named `panelId`, which are responsible of rendering default slot\n * inside a configurable transition.\n *\n * It reacts to `UserClickedPanelToggleButton` event, when their payload matches the component's\n * 'panelId' prop, to handle its open/close state.\n *\n * The default slot offers the possibility to customise the modal content.\n *\n * @public\n */\nexport default defineComponent({\n name: 'BaseIdTogglePanel',\n components: { BaseTogglePanel },\n props: {\n /** Shows the panel open at the beginning or not, depending on its state. */\n startOpen: {\n type: Boolean,\n default: true,\n },\n /** Animation component that will be used to animate the panel content. */\n animation: {\n type: AnimationProp,\n default: () => NoAnimation,\n },\n /**\n * The id to link with the BaseIdTogglePanelButton.\n * Both components must use the same Id to make them interact.\n */\n panelId: {\n type: String,\n required: true,\n },\n },\n setup(props) {\n const $x = use$x()\n\n /** Whether the toggle panel is open or not. */\n const isOpen = ref(props.startOpen)\n\n /**\n * Method to subscribe to the {@link XEventsTypes.UserClickedPanelToggleButton} event.\n *\n * @param panelId - The payload of the {@link XEventsTypes.UserClickedPanelToggleButton} event.\n *\n * @public\n */\n const togglePanel = (panelId: string) => {\n if (props.panelId === panelId) {\n isOpen.value = !isOpen.value\n }\n }\n\n $x.on('UserClickedPanelToggleButton', false).subscribe(togglePanel)\n\n /**\n * Emits the {@link XEventsTypes.TogglePanelStateChanged} event, when the internal state\n * changes.\n *\n * @remarks This event is necessary to link the state with the {@link BaseIdTogglePanelButton}\n * component.\n * @public\n */\n watch(\n () => isOpen.value,\n () => {\n $x.emit('TogglePanelStateChanged', isOpen.value, {\n id: props.panelId,\n target: document.getElementById(props.panelId) as HTMLElement,\n })\n },\n { immediate: true },\n )\n\n return { isOpen }\n },\n})\n</script>\n\n<docs lang=\"mdx\">\n## Events\n\nA list of events that the component will watch and emit:\n\n- [`UserClickedPanelToggleButton`](https://github.com/empathyco/x/blob/main/packages/x-components/src/wiring/events.types.ts):\n watched to toggle the panel when the payload matches the `panelId` prop.\n- [`TogglePanelStateChanged`](https://github.com/empathyco/x/blob/main/packages/x-components/src/wiring/events.types.ts):\n emitted when the internal open state changes, with the new state and panel id.\n\n## Examples\n\n### Basic usage\n\n```vue\n<template>\n <div>\n <BaseIdTogglePanelButton panelId=\"myToggle\">\n <img src=\"./open-button-icon.svg\" alt=\"Toggle Aside\" />\n <span>Toggle Aside</span>\n </BaseIdTogglePanelButton>\n <BaseIdTogglePanel :startOpen=\"true\" :animation=\"animation\" panelId=\"myToggle\">\n <div class=\"x-text1\">My toggle</div>\n </BaseIdTogglePanel>\n </div>\n</template>\n\n<script setup>\nimport { BaseIdTogglePanel, BaseIdTogglePanelButton } from '@empathyco/x-components'\nimport { CollapseFromTop } from '@empathyco/x-components/animations'\nconst animation = CollapseFromTop\n</script>\n```\n\n### Listening to state changes\n\nYou can listen to the `TogglePanelStateChanged` event to react to panel open/close state changes:\n\n```vue\n<template>\n <div>\n <span>Panel is {{ isPanelOpen ? 'open' : 'closed' }}</span>\n </div>\n</template>\n\n<script setup>\nimport { ref } from 'vue'\nimport { use$x } from '@empathyco/x-components'\n\nconst x = use$x()\nconst isPanelOpen = ref(false)\nconst panelId = 'myToggle'\n\nx.on('TogglePanelStateChanged').subscribe((isOpen, { id }) => {\n if (id === panelId) {\n isPanelOpen.value = isOpen\n }\n})\n</script>\n```\n</docs>\n"],"names":["NoAnimation"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAcA;;;;;;;;;;;AAWE;AACF,gBAAe,eAAe,CAAC;AAC7B,IAAA,IAAI,EAAE,mBAAmB;IACzB,UAAU,EAAE,EAAE,eAAc,EAAG;AAC/B,IAAA,KAAK,EAAE;;AAEL,QAAA,SAAS,EAAE;AACT,YAAA,IAAI,EAAE,OAAO;AACb,YAAA,OAAO,EAAE,IAAI;AACd,SAAA;;AAED,QAAA,SAAS,EAAE;AACT,YAAA,IAAI,EAAE,aAAa;AACnB,YAAA,OAAO,EAAE,MAAMA,WAAW;AAC3B,SAAA;AACD;;;AAGE;AACF,QAAA,OAAO,EAAE;AACP,YAAA,IAAI,EAAE,MAAM;AACZ,YAAA,QAAQ,EAAE,IAAI;AACf,SAAA;AACF,KAAA;AACD,IAAA,KAAK,CAAC,KAAK,EAAA;AACT,QAAA,MAAM,EAAC,GAAI,KAAK,EAAC;;QAGjB,MAAM,MAAK,GAAI,GAAG,CAAC,KAAK,CAAC,SAAS,CAAA;AAElC;;;;;;AAME;AACF,QAAA,MAAM,WAAU,GAAI,CAAC,OAAe,KAAG;AACrC,YAAA,IAAI,KAAK,CAAC,OAAM,KAAM,OAAO,EAAE;AAC7B,gBAAA,MAAM,CAAC,KAAI,GAAI,CAAC,MAAM,CAAC,KAAI;YAC7B;AACF,QAAA,CAAA;AAEA,QAAA,EAAE,CAAC,EAAE,CAAC,8BAA8B,EAAE,KAAK,CAAC,CAAC,SAAS,CAAC,WAAW,CAAA;AAElE;;;;;;;AAOE;QACF,KAAK,CACH,MAAM,MAAM,CAAC,KAAK,EAClB,MAAI;YACF,EAAE,CAAC,IAAI,CAAC,yBAAyB,EAAE,MAAM,CAAC,KAAK,EAAE;gBAC/C,EAAE,EAAE,KAAK,CAAC,OAAO;gBACjB,MAAM,EAAE,QAAQ,CAAC,cAAc,CAAC,KAAK,CAAC,OAAO,CAAgB;AAC9D,aAAA,CAAA;AACH,QAAA,CAAC,EACD,EAAE,SAAS,EAAE,MAAM,CACrB;QAEA,OAAO,EAAE,MAAK,EAAE;IAClB,CAAC;AACF,CAAA,CAAA;;;;"}
@@ -6,6 +6,7 @@ import '../animations/animate-translate/animate-translate.style.css.js';
6
6
  import '../animations/animate-width.vue2.js';
7
7
  import '../animations/animate-width.vue3.js';
8
8
  import '../animations/change-height.vue2.js';
9
+ import '../animations/change-height.vue3.js';
9
10
  import '../animations/collapse-height.vue2.js';
10
11
  import '../animations/collapse-height.vue3.js';
11
12
  import '../animations/collapse-width.vue2.js';
@@ -1 +1 @@
1
- {"version":3,"file":"base-tabs-panel.vue2.js","sources":["../../../../src/components/panels/base-tabs-panel.vue"],"sourcesContent":["<template>\n <section v-if=\"getTabs().length > 0\" class=\"x-tabs-panel\" data-test=\"base-tabs-panel\">\n <component\n :is=\"tabsAnimation\"\n class=\"x-tabs-panel__items-list\"\n :class=\"tabsListClass\"\n data-test=\"base-tabs-panel-list\"\n role=\"tablist\"\n >\n <!--\n @slot Slot used to replace the whole tab.\n @binding {string} tab - The tab name.\n @binding {boolean} isSelected - Indicates if the tab is selected.\n @binding {function} select - Function to select the tab.\n -->\n <slot\n v-for=\"tab in getTabs()\"\n name=\"tab\"\n v-bind=\"{ tab, isSelected: tabIsSelected(tab), select: () => selectTab(tab) }\"\n >\n <button\n :id=\"`base-tabs-panel-${tab}`\"\n :key=\"tab\"\n class=\"x-tabs-panel__list-item x-tabs-panel__button x-button\"\n :class=\"tabIsSelected(tab) ? activeTabClass : tabClass\"\n :aria-selected=\"tabIsSelected(tab)\"\n data-test=\"base-tabs-panel-button\"\n role=\"tab\"\n @click=\"selectTab(tab)\"\n >\n <!--\n @slot Slot used to just pass the content.\n @binding {string} tab - The tab name.\n @binding {boolean} isSelected - Indicates if the tab is selected.\n -->\n <slot name=\"tab-content\" v-bind=\"{ tab, isSelected: tabIsSelected(tab) }\">\n {{ tab }}\n </slot>\n </button>\n </slot>\n </component>\n\n <component :is=\"contentAnimation\">\n <div\n v-if=\"selectedTab && slots[selectedTab]\"\n :key=\"selectedTab\"\n :class=\"contentClass\"\n :aria-labelledby=\"`base-tabs-panel-${selectedTab}`\"\n data-test=\"base-tabs-panel-content\"\n role=\"tabpanel\"\n >\n <!--\n @slot Slot used to display the selected tab content.\n @binding {string} tab - This content's tab name.\n @binding {function} selectTab - Function to select a tab.\n @binding {function} deselectTab - Function to deselect the tab.\n -->\n <slot\n :name=\"selectedTab\"\n v-bind=\"{ tab: selectedTab, selectTab, deselectTab: () => selectTab(selectedTab) }\"\n />\n </div>\n </component>\n </section>\n</template>\n\n<script lang=\"ts\">\nimport { defineComponent, ref } from 'vue'\nimport { AnimationProp } from '../../types'\nimport { NoAnimation } from '../animations'\n\n/**\n * Base Tabs Panel.\n *\n * @public\n */\nexport default defineComponent({\n name: 'BaseTabsPanel',\n props: {\n /**\n * Animation component that will be used to animate the tabs list.\n *\n * @public\n */\n tabsAnimation: {\n type: AnimationProp,\n default: 'header',\n },\n /**\n * Animation component that will be used to animate the selected tab content.\n *\n * @public\n */\n contentAnimation: {\n type: AnimationProp,\n default: () => NoAnimation,\n },\n /**\n * The tab to be initially selected.\n *\n * @public\n */\n initialTab: {\n type: String,\n default: '',\n },\n /**\n * Allows the tabs to be unselected.\n *\n * @public\n */\n allowTabDeselect: {\n type: Boolean,\n default: false,\n },\n /** Class inherited by content element. */\n activeTabClass: String,\n /** Class inherited by content element. */\n contentClass: String,\n /** Class inherited by content element. */\n tabClass: String,\n /** Class inherited by content element. */\n tabsListClass: String,\n },\n setup(props, { slots }) {\n /**\n * The currently selected tab.\n *\n * @internal\n */\n const selectedTab = ref(props.initialTab)\n\n /**\n * Extracts the tab from the slots.\n *\n * @returns The list of tabs.\n *\n * @internal\n */\n const getTabs = () =>\n Object.keys(slots).filter(slotName => !['tab', 'tab-content'].includes(slotName))\n\n /**\n * Changes the current selected tab. If the tab is already selected\n * and `allowTabDeselect` is `true`, the tab will be unselected.\n *\n * @param tab - The tab to be selected.\n *\n * @internal\n */\n const selectTab = (tab: string) => {\n if (props.allowTabDeselect && selectedTab.value === tab) {\n selectedTab.value = ''\n } else {\n selectedTab.value = tab\n }\n }\n\n /**\n * Checks if a tab is selected.\n *\n * @param tab - Tab to check.\n * @returns True if the tab is selected, false otherwise.\n *\n * @internal\n */\n const tabIsSelected = (tab: string) => selectedTab.value === tab\n\n return {\n selectedTab,\n slots,\n getTabs,\n selectTab,\n tabIsSelected,\n }\n },\n})\n</script>\n\n<style lang=\"css\" scoped>\n.x-tabs-panel__items-list {\n display: flex;\n}\n</style>\n\n<docs lang=\"mdx\">\n## Events\n\nThis component emits no events.\n\n## Examples\n\nThis component renders a list of tabs based on the name of the slots passed on its template. By\ndefault, the name of each slot will be used as tab label. If an initial tab is passed by prop, the\ncontent of its correspondent slot will be displayed in a panel. Otherwise, no content will be displayed\nuntil a tab is selected.\n\n### Basic example\n\nIt renders a list of tabs and, when a tab is clicked, a panel with its content will be displayed.\n\n```vue\n<template>\n <BaseTabsPanel>\n <template #summer>\n <div>Summer Top Sales</div>\n </template>\n <template #fall>\n <div>Fall Top Sales</div>\n </template>\n <template #outlet>\n <div>Outlet Top Sales</div>\n </template>\n </BaseTabsPanel>\n</template>\n\n<script setup>\nimport { BaseTabsPanel } from '@empathyco/x-components'\n</script>\n```\n\n### Play with props\n\n#### Define the tab to be initially opened\n\nBy default, no tab is selected so any panel is displayed. The `initialTab` prop allows to indicate\nwhich tab should be opened at first.\n\n```vue\n<template>\n <BaseTabsPanel initialTab=\"summer\">\n <template #summer>\n <div>Summer Top Sales</div>\n </template>\n <template #fall>\n <div>Fall Top Sales</div>\n </template>\n <template #outlet>\n <div>Outlet Top Sales</div>\n </template>\n </BaseTabsPanel>\n</template>\n\n<script setup>\nimport { BaseTabsPanel } from '@empathyco/x-components'\n</script>\n```\n\n#### Allowing tabs deselection\n\nThe prop `allowTabDeselect` allows the tabs to be deselected. When a tab that is already selected is\nclicked again, the tab will be deselected and no panel content will be displayed. By default, this\nbehavior is deactivated.\n\n```vue\n<template>\n <BaseTabsPanel initialTab=\"summer\" allowTabDeselect>\n <template #summer>\n <div>Summer Top Sales</div>\n </template>\n <template #fall>\n <div>Fall Top Sales</div>\n </template>\n <template #outlet>\n <div>Outlet Top Sales</div>\n </template>\n </BaseTabsPanel>\n</template>\n\n<script setup>\nimport { BaseTabsPanel } from '@empathyco/x-components'\n</script>\n```\n\n#### Customizing the content with classes\n\n- The `activeTabClass` prop can be used to add classes to the active tab button.\n- The `contentClass` prop can be used to add classes to the content.\n- The `tabClass` prop can be used to add classes to the tab buttons.\n- The `tabsListClass` prop can be used to add classes to the tabs list.\n\n```vue\n<template>\n <BaseTabsPanel\n activeTabClass=\"x-button-auxiliary\"\n contentClass=\"x-p-12 x-bg-auxiliary-25\"\n tabClass=\"x-button-ghost\"\n tabsListClass=\"x-flex-col\"\n >\n <template #summer>\n <div>Summer Top Sales</div>\n </template>\n <template #fall>\n <div>Fall Top Sales</div>\n </template>\n <template #outlet>\n <div>Outlet Top Sales</div>\n </template>\n </BaseTabsPanel>\n</template>\n\n<script setup>\nimport { BaseTabsPanel } from '@empathyco/x-components'\n</script>\n```\n\n#### Play with the animations\n\n- The `tabsAnimation` prop can be used to animate the tabs list.\n- The `contentAnimation` prop can be used to animate the selected tab content.\n\n```vue\n<template>\n <BaseTabsPanel :tabsAnimation=\"staggeredFadeAndSlide\" :contentAnimation=\"staggeredFadeAndSlide\">\n <template #summer>\n <div>Summer Top Sales</div>\n </template>\n <template #fall>\n <div>Fall Top Sales</div>\n </template>\n </BaseTabsPanel>\n</template>\n\n<script setup>\nimport { BaseTabsPanel, StaggeredFadeAndSlide } from '@empathyco/x-components'\nconst staggeredFadeAndSlide = StaggeredFadeAndSlide\n</script>\n```\n\n### Overriding the slots\n\n#### Customizing the tab button\n\nBy default, the component is rendering a button for each tab to be displayed. This button can be\nreplaced entirely through the `tab` slot.\n\n```vue\n<template>\n <BaseTabsPanel>\n <template #tab=\"{ tab, isSelected, select }\">\n <CheckIcon v-if=\"isSelected\" />\n This is the {{ tab }} tab.\n <button @click=\"select\">Open tab</button>\n </template>\n <template #summer>\n <div>Summer Top Sales</div>\n </template>\n <template #fall>\n <div>Fall Top Sales</div>\n </template>\n <template #outlet>\n <div>Outlet Top Sales</div>\n </template>\n </BaseTabsPanel>\n</template>\n\n<script setup>\nimport { BaseTabsPanel, CheckIcon } from '@empathyco/x-components'\n</script>\n```\n\n#### Customizing the tab button content\n\nAlternatively to the previous example, instead of changing the whole tab button, the slot\n`tab-content` offers the possibility of changing just its contents.\n\n```vue\n<template>\n <BaseTabsPanel>\n <template #tab-content=\"{ tab, isSelected }\">\n <CheckIcon v-if=\"isSelected\" />\n {{ tab }}\n </template>\n <template #summer>\n <div>Summer Top Sales</div>\n </template>\n <template #fall>\n <div>Fall Top Sales</div>\n </template>\n <template #outlet>\n <div>Outlet Top Sales</div>\n </template>\n </BaseTabsPanel>\n</template>\n\n<script setup>\nimport { BaseTabsPanel, CheckIcon } from '@empathyco/x-components'\n</script>\n```\n\n#### Customizing the tab panel content\n\nThe displayed tab name and a method to select a tab are exposed to the tab panel content slot.\n\n```vue\n<template>\n <BaseTabsPanel>\n <template #summer=\"{ tab, selectTab, deselectTab }\">\n <h1>{{ tab }}</h1>\n <button @click=\"() => selectTab('fall')\">Open Fall</button>\n <button @click=\"deselectTab\">Close tab</button>\n </template>\n <template #fall>\n <div>Fall Top Sales</div>\n </template>\n </BaseTabsPanel>\n</template>\n\n<script setup>\nimport { BaseTabsPanel } from '@empathyco/x-components'\n</script>\n```\n</docs>\n"],"names":["NoAnimation"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAuEA;;;;AAIE;AACF,gBAAe,eAAe,CAAC;AAC7B,IAAA,IAAI,EAAE,eAAe;AACrB,IAAA,KAAK,EAAE;AACL;;;;AAIE;AACF,QAAA,aAAa,EAAE;AACb,YAAA,IAAI,EAAE,aAAa;AACnB,YAAA,OAAO,EAAE,QAAQ;AAClB,SAAA;AACD;;;;AAIE;AACF,QAAA,gBAAgB,EAAE;AAChB,YAAA,IAAI,EAAE,aAAa;AACnB,YAAA,OAAO,EAAE,MAAMA,WAAW;AAC3B,SAAA;AACD;;;;AAIE;AACF,QAAA,UAAU,EAAE;AACV,YAAA,IAAI,EAAE,MAAM;AACZ,YAAA,OAAO,EAAE,EAAE;AACZ,SAAA;AACD;;;;AAIE;AACF,QAAA,gBAAgB,EAAE;AAChB,YAAA,IAAI,EAAE,OAAO;AACb,YAAA,OAAO,EAAE,KAAK;AACf,SAAA;;AAED,QAAA,cAAc,EAAE,MAAM;;AAEtB,QAAA,YAAY,EAAE,MAAM;;AAEpB,QAAA,QAAQ,EAAE,MAAM;;AAEhB,QAAA,aAAa,EAAE,MAAM;AACtB,KAAA;AACD,IAAA,KAAK,CAAC,KAAK,EAAE,EAAE,KAAI,EAAG,EAAA;AACpB;;;;AAIE;QACF,MAAM,cAAc,GAAG,CAAC,KAAK,CAAC,UAAU,CAAA;AAExC;;;;;;AAME;AACF,QAAA,MAAM,OAAM,GAAI,MACd,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,QAAO,IAAK,CAAC,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAA;AAElF;;;;;;;AAOE;AACF,QAAA,MAAM,SAAQ,GAAI,CAAC,GAAW,KAAG;YAC/B,IAAI,KAAK,CAAC,gBAAe,IAAK,WAAW,CAAC,KAAI,KAAM,GAAG,EAAE;AACvD,gBAAA,WAAW,CAAC,KAAI,GAAI,EAAC;YACvB;iBAAO;AACL,gBAAA,WAAW,CAAC,KAAI,GAAI,GAAE;YACxB;AACF,QAAA,CAAA;AAEA;;;;;;;AAOE;AACF,QAAA,MAAM,aAAY,GAAI,CAAC,GAAW,KAAK,WAAW,CAAC,KAAI,KAAM,GAAE;QAE/D,OAAO;YACL,WAAW;YACX,KAAK;YACL,OAAO;YACP,SAAS;YACT,aAAa;SACf;IACF,CAAC;AACF,CAAA,CAAA;;;;"}
1
+ {"version":3,"file":"base-tabs-panel.vue2.js","sources":["../../../../src/components/panels/base-tabs-panel.vue"],"sourcesContent":["<template>\n <section v-if=\"getTabs().length > 0\" class=\"x-tabs-panel\" data-test=\"base-tabs-panel\">\n <component\n :is=\"tabsAnimation\"\n class=\"x-tabs-panel__items-list\"\n :class=\"tabsListClass\"\n data-test=\"base-tabs-panel-list\"\n role=\"tablist\"\n >\n <!--\n @slot Slot used to replace the whole tab.\n @binding {string} tab - The tab name.\n @binding {boolean} isSelected - Indicates if the tab is selected.\n @binding {function} select - Function to select the tab.\n -->\n <slot\n v-for=\"tab in getTabs()\"\n name=\"tab\"\n v-bind=\"{ tab, isSelected: tabIsSelected(tab), select: () => selectTab(tab) }\"\n >\n <button\n :id=\"`base-tabs-panel-${tab}`\"\n :key=\"tab\"\n class=\"x-tabs-panel__list-item x-tabs-panel__button x-button\"\n :class=\"tabIsSelected(tab) ? activeTabClass : tabClass\"\n :aria-selected=\"tabIsSelected(tab)\"\n data-test=\"base-tabs-panel-button\"\n role=\"tab\"\n @click=\"selectTab(tab)\"\n >\n <!--\n @slot Slot used to just pass the content.\n @binding {string} tab - The tab name.\n @binding {boolean} isSelected - Indicates if the tab is selected.\n -->\n <slot name=\"tab-content\" v-bind=\"{ tab, isSelected: tabIsSelected(tab) }\">\n {{ tab }}\n </slot>\n </button>\n </slot>\n </component>\n\n <component :is=\"contentAnimation\">\n <div\n v-if=\"selectedTab && slots[selectedTab]\"\n :key=\"selectedTab\"\n :class=\"contentClass\"\n :aria-labelledby=\"`base-tabs-panel-${selectedTab}`\"\n data-test=\"base-tabs-panel-content\"\n role=\"tabpanel\"\n >\n <!--\n @slot Slot used to display the selected tab content.\n @binding {string} tab - This content's tab name.\n @binding {function} selectTab - Function to select a tab.\n @binding {function} deselectTab - Function to deselect the tab.\n -->\n <slot\n :name=\"selectedTab\"\n v-bind=\"{ tab: selectedTab, selectTab, deselectTab: () => selectTab(selectedTab) }\"\n />\n </div>\n </component>\n </section>\n</template>\n\n<script lang=\"ts\">\nimport { defineComponent, ref } from 'vue'\nimport { AnimationProp } from '../../types'\nimport { NoAnimation } from '../animations'\n\n/**\n * Base Tabs Panel.\n *\n * @public\n */\nexport default defineComponent({\n name: 'BaseTabsPanel',\n props: {\n /**\n * Animation component that will be used to animate the tabs list.\n *\n * @public\n */\n tabsAnimation: {\n type: AnimationProp,\n default: 'header',\n },\n /**\n * Animation component that will be used to animate the selected tab content.\n *\n * @public\n */\n contentAnimation: {\n type: AnimationProp,\n default: () => NoAnimation,\n },\n /**\n * The tab to be initially selected.\n *\n * @public\n */\n initialTab: {\n type: String,\n default: '',\n },\n /**\n * Allows the tabs to be unselected.\n *\n * @public\n */\n allowTabDeselect: {\n type: Boolean,\n default: false,\n },\n /** Class inherited by content element. */\n activeTabClass: String,\n /** Class inherited by content element. */\n contentClass: String,\n /** Class inherited by content element. */\n tabClass: String,\n /** Class inherited by content element. */\n tabsListClass: String,\n },\n setup(props, { slots }) {\n /**\n * The currently selected tab.\n *\n * @internal\n */\n const selectedTab = ref(props.initialTab)\n\n /**\n * Extracts the tab from the slots.\n *\n * @returns The list of tabs.\n *\n * @internal\n */\n const getTabs = () =>\n Object.keys(slots).filter(slotName => !['tab', 'tab-content'].includes(slotName))\n\n /**\n * Changes the current selected tab. If the tab is already selected\n * and `allowTabDeselect` is `true`, the tab will be unselected.\n *\n * @param tab - The tab to be selected.\n *\n * @internal\n */\n const selectTab = (tab: string) => {\n if (props.allowTabDeselect && selectedTab.value === tab) {\n selectedTab.value = ''\n } else {\n selectedTab.value = tab\n }\n }\n\n /**\n * Checks if a tab is selected.\n *\n * @param tab - Tab to check.\n * @returns True if the tab is selected, false otherwise.\n *\n * @internal\n */\n const tabIsSelected = (tab: string) => selectedTab.value === tab\n\n return {\n selectedTab,\n slots,\n getTabs,\n selectTab,\n tabIsSelected,\n }\n },\n})\n</script>\n\n<style lang=\"css\" scoped>\n.x-tabs-panel__items-list {\n display: flex;\n}\n</style>\n\n<docs lang=\"mdx\">\n## Events\n\nThis component emits no events.\n\n## Examples\n\nThis component renders a list of tabs based on the name of the slots passed on its template. By\ndefault, the name of each slot will be used as tab label. If an initial tab is passed by prop, the\ncontent of its correspondent slot will be displayed in a panel. Otherwise, no content will be displayed\nuntil a tab is selected.\n\n### Basic example\n\nIt renders a list of tabs and, when a tab is clicked, a panel with its content will be displayed.\n\n```vue\n<template>\n <BaseTabsPanel>\n <template #summer>\n <div>Summer Top Sales</div>\n </template>\n <template #fall>\n <div>Fall Top Sales</div>\n </template>\n <template #outlet>\n <div>Outlet Top Sales</div>\n </template>\n </BaseTabsPanel>\n</template>\n\n<script setup>\nimport { BaseTabsPanel } from '@empathyco/x-components'\n</script>\n```\n\n### Play with props\n\n#### Define the tab to be initially opened\n\nBy default, no tab is selected so any panel is displayed. The `initialTab` prop allows to indicate\nwhich tab should be opened at first.\n\n```vue\n<template>\n <BaseTabsPanel initialTab=\"summer\">\n <template #summer>\n <div>Summer Top Sales</div>\n </template>\n <template #fall>\n <div>Fall Top Sales</div>\n </template>\n <template #outlet>\n <div>Outlet Top Sales</div>\n </template>\n </BaseTabsPanel>\n</template>\n\n<script setup>\nimport { BaseTabsPanel } from '@empathyco/x-components'\n</script>\n```\n\n#### Allowing tabs deselection\n\nThe prop `allowTabDeselect` allows the tabs to be deselected. When a tab that is already selected is\nclicked again, the tab will be deselected and no panel content will be displayed. By default, this\nbehavior is deactivated.\n\n```vue\n<template>\n <BaseTabsPanel initialTab=\"summer\" allowTabDeselect>\n <template #summer>\n <div>Summer Top Sales</div>\n </template>\n <template #fall>\n <div>Fall Top Sales</div>\n </template>\n <template #outlet>\n <div>Outlet Top Sales</div>\n </template>\n </BaseTabsPanel>\n</template>\n\n<script setup>\nimport { BaseTabsPanel } from '@empathyco/x-components'\n</script>\n```\n\n#### Customizing the content with classes\n\n- The `activeTabClass` prop can be used to add classes to the active tab button.\n- The `contentClass` prop can be used to add classes to the content.\n- The `tabClass` prop can be used to add classes to the tab buttons.\n- The `tabsListClass` prop can be used to add classes to the tabs list.\n\n```vue\n<template>\n <BaseTabsPanel\n activeTabClass=\"x-button-auxiliary\"\n contentClass=\"x-p-12 x-bg-auxiliary-25\"\n tabClass=\"x-button-ghost\"\n tabsListClass=\"x-flex-col\"\n >\n <template #summer>\n <div>Summer Top Sales</div>\n </template>\n <template #fall>\n <div>Fall Top Sales</div>\n </template>\n <template #outlet>\n <div>Outlet Top Sales</div>\n </template>\n </BaseTabsPanel>\n</template>\n\n<script setup>\nimport { BaseTabsPanel } from '@empathyco/x-components'\n</script>\n```\n\n#### Play with the animations\n\n- The `tabsAnimation` prop can be used to animate the tabs list.\n- The `contentAnimation` prop can be used to animate the selected tab content.\n\n```vue\n<template>\n <BaseTabsPanel :tabsAnimation=\"staggeredFadeAndSlide\" :contentAnimation=\"staggeredFadeAndSlide\">\n <template #summer>\n <div>Summer Top Sales</div>\n </template>\n <template #fall>\n <div>Fall Top Sales</div>\n </template>\n </BaseTabsPanel>\n</template>\n\n<script setup>\nimport { BaseTabsPanel, StaggeredFadeAndSlide } from '@empathyco/x-components'\nconst staggeredFadeAndSlide = StaggeredFadeAndSlide\n</script>\n```\n\n### Overriding the slots\n\n#### Customizing the tab button\n\nBy default, the component is rendering a button for each tab to be displayed. This button can be\nreplaced entirely through the `tab` slot.\n\n```vue\n<template>\n <BaseTabsPanel>\n <template #tab=\"{ tab, isSelected, select }\">\n <CheckIcon v-if=\"isSelected\" />\n This is the {{ tab }} tab.\n <button @click=\"select\">Open tab</button>\n </template>\n <template #summer>\n <div>Summer Top Sales</div>\n </template>\n <template #fall>\n <div>Fall Top Sales</div>\n </template>\n <template #outlet>\n <div>Outlet Top Sales</div>\n </template>\n </BaseTabsPanel>\n</template>\n\n<script setup>\nimport { BaseTabsPanel, CheckIcon } from '@empathyco/x-components'\n</script>\n```\n\n#### Customizing the tab button content\n\nAlternatively to the previous example, instead of changing the whole tab button, the slot\n`tab-content` offers the possibility of changing just its contents.\n\n```vue\n<template>\n <BaseTabsPanel>\n <template #tab-content=\"{ tab, isSelected }\">\n <CheckIcon v-if=\"isSelected\" />\n {{ tab }}\n </template>\n <template #summer>\n <div>Summer Top Sales</div>\n </template>\n <template #fall>\n <div>Fall Top Sales</div>\n </template>\n <template #outlet>\n <div>Outlet Top Sales</div>\n </template>\n </BaseTabsPanel>\n</template>\n\n<script setup>\nimport { BaseTabsPanel, CheckIcon } from '@empathyco/x-components'\n</script>\n```\n\n#### Customizing the tab panel content\n\nThe displayed tab name and a method to select a tab are exposed to the tab panel content slot.\n\n```vue\n<template>\n <BaseTabsPanel>\n <template #summer=\"{ tab, selectTab, deselectTab }\">\n <h1>{{ tab }}</h1>\n <button @click=\"() => selectTab('fall')\">Open Fall</button>\n <button @click=\"deselectTab\">Close tab</button>\n </template>\n <template #fall>\n <div>Fall Top Sales</div>\n </template>\n </BaseTabsPanel>\n</template>\n\n<script setup>\nimport { BaseTabsPanel } from '@empathyco/x-components'\n</script>\n```\n</docs>\n"],"names":["NoAnimation"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAuEA;;;;AAIE;AACF,gBAAe,eAAe,CAAC;AAC7B,IAAA,IAAI,EAAE,eAAe;AACrB,IAAA,KAAK,EAAE;AACL;;;;AAIE;AACF,QAAA,aAAa,EAAE;AACb,YAAA,IAAI,EAAE,aAAa;AACnB,YAAA,OAAO,EAAE,QAAQ;AAClB,SAAA;AACD;;;;AAIE;AACF,QAAA,gBAAgB,EAAE;AAChB,YAAA,IAAI,EAAE,aAAa;AACnB,YAAA,OAAO,EAAE,MAAMA,WAAW;AAC3B,SAAA;AACD;;;;AAIE;AACF,QAAA,UAAU,EAAE;AACV,YAAA,IAAI,EAAE,MAAM;AACZ,YAAA,OAAO,EAAE,EAAE;AACZ,SAAA;AACD;;;;AAIE;AACF,QAAA,gBAAgB,EAAE;AAChB,YAAA,IAAI,EAAE,OAAO;AACb,YAAA,OAAO,EAAE,KAAK;AACf,SAAA;;AAED,QAAA,cAAc,EAAE,MAAM;;AAEtB,QAAA,YAAY,EAAE,MAAM;;AAEpB,QAAA,QAAQ,EAAE,MAAM;;AAEhB,QAAA,aAAa,EAAE,MAAM;AACtB,KAAA;AACD,IAAA,KAAK,CAAC,KAAK,EAAE,EAAE,KAAI,EAAG,EAAA;AACpB;;;;AAIE;QACF,MAAM,cAAc,GAAG,CAAC,KAAK,CAAC,UAAU,CAAA;AAExC;;;;;;AAME;AACF,QAAA,MAAM,OAAM,GAAI,MACd,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,QAAO,IAAK,CAAC,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAA;AAElF;;;;;;;AAOE;AACF,QAAA,MAAM,SAAQ,GAAI,CAAC,GAAW,KAAG;YAC/B,IAAI,KAAK,CAAC,gBAAe,IAAK,WAAW,CAAC,KAAI,KAAM,GAAG,EAAE;AACvD,gBAAA,WAAW,CAAC,KAAI,GAAI,EAAC;YACvB;iBAAO;AACL,gBAAA,WAAW,CAAC,KAAI,GAAI,GAAE;YACxB;AACF,QAAA,CAAA;AAEA;;;;;;;AAOE;AACF,QAAA,MAAM,aAAY,GAAI,CAAC,GAAW,KAAK,WAAW,CAAC,KAAI,KAAM,GAAE;QAE/D,OAAO;YACL,WAAW;YACX,KAAK;YACL,OAAO;YACP,SAAS;YACT,aAAa;SACf;IACF,CAAC;AACF,CAAA,CAAA;;;;"}
@@ -6,6 +6,7 @@ import '../animations/animate-translate/animate-translate.style.css.js';
6
6
  import '../animations/animate-width.vue2.js';
7
7
  import '../animations/animate-width.vue3.js';
8
8
  import '../animations/change-height.vue2.js';
9
+ import '../animations/change-height.vue3.js';
9
10
  import '../animations/collapse-height.vue2.js';
10
11
  import '../animations/collapse-height.vue3.js';
11
12
  import '../animations/collapse-width.vue2.js';
@@ -1 +1 @@
1
- {"version":3,"file":"base-result-image.vue2.js","sources":["../../../../src/components/result/base-result-image.vue"],"sourcesContent":["<template>\n <!-- This is a div because using a picture causes the onload event of the image to fire twice. -->\n <!-- eslint-disable-next-line vuejs-accessibility/mouse-events-have-key-events -->\n <div\n class=\"x-result-picture x-picture\"\n data-test=\"result-picture\"\n @mouseenter.once=\"userHasHoveredImage = true\"\n @mouseenter=\"isHovering = true\"\n @mouseleave=\"isHovering = false\"\n >\n <img\n v-if=\"shouldLoadNextImage\"\n loading=\"lazy\"\n :src=\"pendingImages[0]\"\n :style=\"loaderStyles\"\n class=\"x-picture-image\"\n data-test=\"result-picture-loader\"\n alt=\"\"\n role=\"presentation\"\n @load=\"flagImageLoaded\"\n @error=\"flagImageAsFailed\"\n />\n <component :is=\"animation\" class=\"x-picture-image\" :appear=\"false\">\n <!-- @slot Fallback image content. It will be rendered when all the images failed -->\n <slot v-if=\"!loadedImages.length && !pendingImages.length\" name=\"fallback\" />\n\n <!-- @slot Loading image content. It will be rendered while the real image is not loaded -->\n <slot v-else-if=\"!loadedImages.length\" name=\"placeholder\" />\n\n <img\n v-else\n :key=\"imageSrc\"\n :alt=\"result.name\"\n :src=\"imageSrc\"\n class=\"x-result-picture-image\"\n data-test=\"result-picture-image\"\n />\n </component>\n </div>\n</template>\n\n<script lang=\"ts\">\nimport type { Result } from '@empathyco/x-types'\nimport type { PropType, Ref, StyleValue } from 'vue'\nimport { computed, defineComponent, ref, watch } from 'vue'\nimport { AnimationProp } from '../../types'\nimport { NoAnimation } from '../animations'\n\n/**\n * Component to be reused that renders an `<img>`.\n *\n * @public\n */\nexport default defineComponent({\n name: 'BaseResultImage',\n props: {\n /** (Required) The {@link @empathyco/x-types#Result} information. */\n result: {\n type: Object as PropType<Result>,\n required: true,\n },\n /**\n * Animation to use when switching between the placeholder, the loaded image, or the failed\n * image fallback.\n */\n loadAnimation: {\n type: AnimationProp,\n default: () => NoAnimation,\n },\n /** Animation to use when switching between the loaded image and the hover image. */\n hoverAnimation: {\n type: AnimationProp,\n },\n /**\n * Indicates if the next valid image should be displayed on hover.\n *\n * @public\n */\n showNextImageOnHover: {\n type: Boolean,\n default: false,\n },\n },\n setup(props) {\n /**\n * Copy of the images of the result.\n *\n * It is used as a queue of images to load, once an image loads/fails to load, it is removed\n * from this array.\n *\n * @internal\n */\n const pendingImages: Ref<string[]> = ref([])\n\n /**\n * Contains the images that have been loaded successfully.\n *\n * @internal\n */\n const loadedImages: Ref<string[]> = ref([])\n\n /**\n * Indicates if the user is hovering the image.\n *\n * @internal\n */\n const isHovering = ref(false)\n\n /**\n * Indicates if the user has hovered the image.\n *\n * @internal\n */\n const userHasHoveredImage = ref(false)\n\n /**\n * Styles to use inline in the image loader, to prevent override from CSS\n *\n * @internal\n */\n const loaderStyles: StyleValue = {\n // csstype issue: https://github.com/frenic/csstype/issues/160\n position: 'absolute !important' as 'absolute',\n top: '0 !important' as '0',\n left: '0 !important' as '0',\n width: '100% !important' as '100%',\n height: '100% !important' as '100%',\n pointerEvents: 'none !important' as 'none',\n visibility: 'hidden !important' as 'hidden',\n }\n\n /**\n * Initializes images state and resets when the result's images change.\n *\n * @internal\n */\n watch(\n () => props.result.images,\n () => {\n pendingImages.value = [...(props.result.images ?? [])]\n loadedImages.value = pendingImages.value.filter(image => loadedImages.value.includes(image))\n },\n { immediate: true },\n )\n\n /**\n * Animation to be used.\n *\n * @returns The animation to be used, taking into account if the user has hovered the image.\n *\n * @internal\n */\n const animation = computed(() => {\n return userHasHoveredImage.value\n ? (props.hoverAnimation ?? props.loadAnimation)\n : props.loadAnimation\n })\n\n /**\n * Gets the src from the result image.\n *\n * @returns The result image src.\n *\n * @internal\n */\n const imageSrc = computed(() => {\n return loadedImages.value[\n !props.showNextImageOnHover || !isHovering.value ? 0 : loadedImages.value.length - 1\n ]\n })\n\n /**\n * Indicates if the loader should try to load the next image.\n *\n * @returns True if it should try to load the next image.\n *\n * @internal\n */\n const shouldLoadNextImage = computed(() => {\n const numImagesToLoad = props.showNextImageOnHover && userHasHoveredImage.value ? 2 : 1\n return !!pendingImages.value.length && loadedImages.value.length < numImagesToLoad\n })\n\n /**\n * Sets an image as failed.\n *\n * @internal\n */\n const flagImageAsFailed = () => {\n pendingImages.value.shift()\n }\n\n /**\n * Sets an image as loaded.\n *\n * @internal\n */\n const flagImageLoaded = () => {\n const image = pendingImages.value.shift()\n if (image) {\n loadedImages.value.push(image)\n }\n }\n\n return {\n pendingImages,\n loadedImages,\n isHovering,\n userHasHoveredImage,\n loaderStyles,\n animation,\n imageSrc,\n shouldLoadNextImage,\n flagImageAsFailed,\n flagImageLoaded,\n }\n },\n})\n</script>\n\n<style lang=\"css\" scoped>\n.x-result-picture {\n position: relative;\n min-width: 1px;\n min-height: 1px;\n}\n\n.x-result-picture-image {\n max-width: 100%;\n max-height: 100%;\n}\n</style>\n\n<docs lang=\"mdx\">\n## Examples\n\n### Basic example\n\nThis component is for the result image. It may be part of the search result page, recommendations or\nother section which needs to include results.\n\nThe result prop is required. It will render a `<img/>` with the result image:\n\n```vue\n<template>\n <BaseResultImage :result=\"result\" />\n</template>\n\n<script setup>\nimport { BaseResultImage } from '@empathyco/x-components'\nconst result = {\n name: 'Jacket',\n images: ['https://some-image-url.com/image1.jpg'],\n}\n</script>\n```\n\n### Showing the next image on hover\n\nIf a result has multiple images, it can show the next one on hover.\n\n```vue\n<template>\n <BaseResultImage :result=\"result\" showNextImageOnHover />\n</template>\n\n<script setup>\nimport { BaseResultImage } from '@empathyco/x-components'\nconst result = {\n name: 'Jacket',\n images: ['https://some-image-url.com/image1.jpg', 'https://some-image-url.com/image2.jpg'],\n}\n</script>\n```\n\n### Customizing slots content\n\nFallback and placeholder contents can be customized.\n\nThe fallback slot allows you to replace the content of the fallback image.\n\nThe other slot is called `placeholder`, and allows you to set the image that its going to be\ndisplayed while the real one is loaded.\n\n```vue\n<template>\n <BaseResultImage :result=\"result\">\n <template #placeholder>\n <img alt=\"Placeholder image\" src=\"./placeholder-image.svg\" />\n </template>\n <template #fallback>\n <img alt=\"Fallback image\" src=\"./fallback-image.svg\" />\n </template>\n </BaseResultImage>\n</template>\n\n<script setup>\nimport { BaseResultImage } from '@empathyco/x-components'\nconst result = {\n name: 'Jacket',\n images: ['https://some-image-url.com/image1.jpg'],\n}\n</script>\n```\n\n### Customizing the animations\n\nTwo animations can be used in this component.\n\nThe `loadAnimation` is used to transition between the placeholder, the fallback and the image.\n\nThe `hoverAnimation` is used to transition between the image and the hover image, if the\n`showNextImageOnHover` prop is `true`.\n\n`hoverAnimation` will default to `loadAnimation` if it is not provided.\n\n```vue\n<template>\n <BaseResultImage\n :result=\"result\"\n :loadAnimation=\"loadAnimation\"\n :hoverAnimation=\"hoverAnimation\"\n showNextImageOnHover\n />\n</template>\n\n<script setup>\nimport { BaseResultImage } from '@empathyco/x-components'\nimport { CrossFade, CollapseHeight } from '@empathyco/x-components/animations'\nconst loadAnimation = CrossFade\nconst hoverAnimation = CollapseHeight\nconst result = {\n name: 'Jacket',\n images: ['https://some-image-url.com/image1.jpg', 'https://some-image-url.com/image2.jpg'],\n}\n</script>\n```\n</docs>\n"],"names":["NoAnimation"],"mappings":"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"}
1
+ {"version":3,"file":"base-result-image.vue2.js","sources":["../../../../src/components/result/base-result-image.vue"],"sourcesContent":["<template>\n <!-- This is a div because using a picture causes the onload event of the image to fire twice. -->\n <!-- eslint-disable-next-line vuejs-accessibility/mouse-events-have-key-events -->\n <div\n class=\"x-result-picture x-picture\"\n data-test=\"result-picture\"\n @mouseenter.once=\"userHasHoveredImage = true\"\n @mouseenter=\"isHovering = true\"\n @mouseleave=\"isHovering = false\"\n >\n <img\n v-if=\"shouldLoadNextImage\"\n loading=\"lazy\"\n :src=\"pendingImages[0]\"\n :style=\"loaderStyles\"\n class=\"x-picture-image\"\n data-test=\"result-picture-loader\"\n alt=\"\"\n role=\"presentation\"\n @load=\"flagImageLoaded\"\n @error=\"flagImageAsFailed\"\n />\n <component :is=\"animation\" class=\"x-picture-image\" :appear=\"false\">\n <!-- @slot Fallback image content. It will be rendered when all the images failed -->\n <slot v-if=\"!loadedImages.length && !pendingImages.length\" name=\"fallback\" />\n\n <!-- @slot Loading image content. It will be rendered while the real image is not loaded -->\n <slot v-else-if=\"!loadedImages.length\" name=\"placeholder\" />\n\n <img\n v-else\n :key=\"imageSrc\"\n :alt=\"result.name\"\n :src=\"imageSrc\"\n class=\"x-result-picture-image\"\n data-test=\"result-picture-image\"\n />\n </component>\n </div>\n</template>\n\n<script lang=\"ts\">\nimport type { Result } from '@empathyco/x-types'\nimport type { PropType, Ref, StyleValue } from 'vue'\nimport { computed, defineComponent, ref, watch } from 'vue'\nimport { AnimationProp } from '../../types'\nimport { NoAnimation } from '../animations'\n\n/**\n * Component to be reused that renders an `<img>`.\n *\n * @public\n */\nexport default defineComponent({\n name: 'BaseResultImage',\n props: {\n /** (Required) The {@link @empathyco/x-types#Result} information. */\n result: {\n type: Object as PropType<Result>,\n required: true,\n },\n /**\n * Animation to use when switching between the placeholder, the loaded image, or the failed\n * image fallback.\n */\n loadAnimation: {\n type: AnimationProp,\n default: () => NoAnimation,\n },\n /** Animation to use when switching between the loaded image and the hover image. */\n hoverAnimation: {\n type: AnimationProp,\n },\n /**\n * Indicates if the next valid image should be displayed on hover.\n *\n * @public\n */\n showNextImageOnHover: {\n type: Boolean,\n default: false,\n },\n },\n setup(props) {\n /**\n * Copy of the images of the result.\n *\n * It is used as a queue of images to load, once an image loads/fails to load, it is removed\n * from this array.\n *\n * @internal\n */\n const pendingImages: Ref<string[]> = ref([])\n\n /**\n * Contains the images that have been loaded successfully.\n *\n * @internal\n */\n const loadedImages: Ref<string[]> = ref([])\n\n /**\n * Indicates if the user is hovering the image.\n *\n * @internal\n */\n const isHovering = ref(false)\n\n /**\n * Indicates if the user has hovered the image.\n *\n * @internal\n */\n const userHasHoveredImage = ref(false)\n\n /**\n * Styles to use inline in the image loader, to prevent override from CSS\n *\n * @internal\n */\n const loaderStyles: StyleValue = {\n // csstype issue: https://github.com/frenic/csstype/issues/160\n position: 'absolute !important' as 'absolute',\n top: '0 !important' as '0',\n left: '0 !important' as '0',\n width: '100% !important' as '100%',\n height: '100% !important' as '100%',\n pointerEvents: 'none !important' as 'none',\n visibility: 'hidden !important' as 'hidden',\n }\n\n /**\n * Initializes images state and resets when the result's images change.\n *\n * @internal\n */\n watch(\n () => props.result.images,\n () => {\n pendingImages.value = [...(props.result.images ?? [])]\n loadedImages.value = pendingImages.value.filter(image => loadedImages.value.includes(image))\n },\n { immediate: true },\n )\n\n /**\n * Animation to be used.\n *\n * @returns The animation to be used, taking into account if the user has hovered the image.\n *\n * @internal\n */\n const animation = computed(() => {\n return userHasHoveredImage.value\n ? (props.hoverAnimation ?? props.loadAnimation)\n : props.loadAnimation\n })\n\n /**\n * Gets the src from the result image.\n *\n * @returns The result image src.\n *\n * @internal\n */\n const imageSrc = computed(() => {\n return loadedImages.value[\n !props.showNextImageOnHover || !isHovering.value ? 0 : loadedImages.value.length - 1\n ]\n })\n\n /**\n * Indicates if the loader should try to load the next image.\n *\n * @returns True if it should try to load the next image.\n *\n * @internal\n */\n const shouldLoadNextImage = computed(() => {\n const numImagesToLoad = props.showNextImageOnHover && userHasHoveredImage.value ? 2 : 1\n return !!pendingImages.value.length && loadedImages.value.length < numImagesToLoad\n })\n\n /**\n * Sets an image as failed.\n *\n * @internal\n */\n const flagImageAsFailed = () => {\n pendingImages.value.shift()\n }\n\n /**\n * Sets an image as loaded.\n *\n * @internal\n */\n const flagImageLoaded = () => {\n const image = pendingImages.value.shift()\n if (image) {\n loadedImages.value.push(image)\n }\n }\n\n return {\n pendingImages,\n loadedImages,\n isHovering,\n userHasHoveredImage,\n loaderStyles,\n animation,\n imageSrc,\n shouldLoadNextImage,\n flagImageAsFailed,\n flagImageLoaded,\n }\n },\n})\n</script>\n\n<style lang=\"css\" scoped>\n.x-result-picture {\n position: relative;\n min-width: 1px;\n min-height: 1px;\n}\n\n.x-result-picture-image {\n max-width: 100%;\n max-height: 100%;\n}\n</style>\n\n<docs lang=\"mdx\">\n## Examples\n\n### Basic example\n\nThis component is for the result image. It may be part of the search result page, recommendations or\nother section which needs to include results.\n\nThe result prop is required. It will render a `<img/>` with the result image:\n\n```vue\n<template>\n <BaseResultImage :result=\"result\" />\n</template>\n\n<script setup>\nimport { BaseResultImage } from '@empathyco/x-components'\nconst result = {\n name: 'Jacket',\n images: ['https://some-image-url.com/image1.jpg'],\n}\n</script>\n```\n\n### Showing the next image on hover\n\nIf a result has multiple images, it can show the next one on hover.\n\n```vue\n<template>\n <BaseResultImage :result=\"result\" showNextImageOnHover />\n</template>\n\n<script setup>\nimport { BaseResultImage } from '@empathyco/x-components'\nconst result = {\n name: 'Jacket',\n images: ['https://some-image-url.com/image1.jpg', 'https://some-image-url.com/image2.jpg'],\n}\n</script>\n```\n\n### Customizing slots content\n\nFallback and placeholder contents can be customized.\n\nThe fallback slot allows you to replace the content of the fallback image.\n\nThe other slot is called `placeholder`, and allows you to set the image that its going to be\ndisplayed while the real one is loaded.\n\n```vue\n<template>\n <BaseResultImage :result=\"result\">\n <template #placeholder>\n <img alt=\"Placeholder image\" src=\"./placeholder-image.svg\" />\n </template>\n <template #fallback>\n <img alt=\"Fallback image\" src=\"./fallback-image.svg\" />\n </template>\n </BaseResultImage>\n</template>\n\n<script setup>\nimport { BaseResultImage } from '@empathyco/x-components'\nconst result = {\n name: 'Jacket',\n images: ['https://some-image-url.com/image1.jpg'],\n}\n</script>\n```\n\n### Customizing the animations\n\nTwo animations can be used in this component.\n\nThe `loadAnimation` is used to transition between the placeholder, the fallback and the image.\n\nThe `hoverAnimation` is used to transition between the image and the hover image, if the\n`showNextImageOnHover` prop is `true`.\n\n`hoverAnimation` will default to `loadAnimation` if it is not provided.\n\n```vue\n<template>\n <BaseResultImage\n :result=\"result\"\n :loadAnimation=\"loadAnimation\"\n :hoverAnimation=\"hoverAnimation\"\n showNextImageOnHover\n />\n</template>\n\n<script setup>\nimport { BaseResultImage } from '@empathyco/x-components'\nimport { CrossFade, CollapseHeight } from '@empathyco/x-components/animations'\nconst loadAnimation = CrossFade\nconst hoverAnimation = CollapseHeight\nconst result = {\n name: 'Jacket',\n images: ['https://some-image-url.com/image1.jpg', 'https://some-image-url.com/image2.jpg'],\n}\n</script>\n```\n</docs>\n"],"names":["NoAnimation"],"mappings":"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"}
@@ -74,7 +74,10 @@ function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
74
74
  _: 1
75
75
  /* STABLE */
76
76
  }, 8, ["payload", "event-metadata"]),
77
- renderSlot(_ctx.$slots, "sliding-panel", { results: _ctx.suggestionsSearch }, () => [
77
+ renderSlot(_ctx.$slots, "sliding-panel", {
78
+ suggestions: _ctx.suggestionsSearch,
79
+ tagging: _ctx.tagging
80
+ }, () => [
78
81
  createVNode(_component_SlidingPanel, {
79
82
  class: normalizeClass(_ctx.slidingPanelClasses),
80
83
  "scroll-container-class": _ctx.slidingPanelContainerClasses,
@@ -1 +1 @@
1
- {"version":3,"file":"ai-carousel.vue.js","sources":["../../../../../src/x-modules/ai/components/ai-carousel.vue"],"sourcesContent":["<template>\n <CollapseHeight>\n <div v-if=\"suggestionsSearch.length\" class=\"x-ai-carousel\">\n <DisplayEmitter\n :payload=\"tagging?.toolingDisplay ?? emptyTaggingRequest\"\n :event-metadata=\"{\n feature: 'ai_carousel',\n displayOriginalQuery: query || 'ai-carousel-without-query',\n replaceable: false,\n }\"\n data-test=\"ai-carousel-display-emitter\"\n >\n <span\n class=\"x-ai-carousel-title\"\n :class=\"{ 'x-ai-carousel-title--expanded': titleExpanded }\"\n data-test=\"ai-carousel-title\"\n @click=\"toggleTitleExpansion\"\n >\n <AIStarIcon class=\"x-ai-carousel-title-icon\" />\n <ChangeHeight>\n <span\n ref=\"titleRef\"\n class=\"x-ai-carousel-title-text\"\n :class=\"{ 'x-ai-carousel-title-text--expanded': titleExpanded }\"\n >\n {{ title }}\n </span>\n </ChangeHeight>\n <button\n v-if=\"isTitleOverflowing\"\n class=\"x-ai-carousel-title-button\"\n data-test=\"ai-carousel-title-button\"\n :aria-label=\"titleExpanded ? 'Collapse' : 'Expand'\"\n >\n <ChevronDownIcon\n class=\"x-ai-carousel-title-button-icon\"\n :class=\"{ 'x-ai-carousel-title-button-icon--expanded': titleExpanded }\"\n />\n </button>\n </span>\n </DisplayEmitter>\n <slot name=\"sliding-panel\" :results=\"suggestionsSearch\">\n <SlidingPanel\n :class=\"slidingPanelClasses\"\n :scroll-container-class=\"slidingPanelContainerClasses\"\n :button-class=\"slidingPanelButtonsClasses\"\n :reset-on-content-change=\"false\"\n >\n <template #sliding-panel-addons=\"{ arrivedState }\">\n <slot name=\"sliding-panels-addons\" :arrived-state=\"arrivedState\" />\n </template>\n <template #sliding-panel-left-button>\n <slot name=\"sliding-panels-left-button\" />\n </template>\n <template #sliding-panel-right-button>\n <slot name=\"sliding-panels-right-button\" />\n </template>\n <div class=\"x-flex x-gap-8\">\n <ul class=\"x-ai-carousel-suggestion-results\">\n <DisplayClickProvider\n v-for=\"suggestion in suggestionsSearch\"\n :key=\"suggestion.query\"\n :tooling-display-tagging=\"\n tagging?.searchQueries[suggestion.query].toolingDisplayClick\n \"\n :tooling-add2-cart-tagging=\"\n tagging?.searchQueries[suggestion.query].toolingDisplayAdd2Cart\n \"\n result-feature=\"ai_carousel\"\n >\n <li\n v-for=\"result in suggestion.results\"\n :key=\"result.id\"\n data-test=\"ai-carousel-suggestion-result\"\n >\n <!-- @slot (required) result card -->\n <slot name=\"result\" :result=\"result\" />\n </li>\n </DisplayClickProvider>\n </ul>\n </div>\n </SlidingPanel>\n </slot>\n <slot name=\"extra-content\" />\n <slot name=\"cta-button\" />\n </div>\n </CollapseHeight>\n</template>\n\n<script lang=\"ts\">\nimport type { TaggingRequest } from '@empathyco/x-types'\nimport { useResizeObserver } from '@vueuse/core'\nimport { computed, defineComponent, ref, watch } from 'vue'\nimport {\n AIStarIcon,\n ChangeHeight,\n ChevronDownIcon,\n CollapseHeight,\n DisplayClickProvider,\n SlidingPanel,\n} from '../../../components'\nimport DisplayEmitter from '../../../components/display-emitter.vue'\nimport { use$x, useState } from '../../../composables'\nimport { aiXModule } from '../x-module'\n\nexport default defineComponent({\n xModule: aiXModule.name,\n components: {\n ChangeHeight,\n DisplayClickProvider,\n DisplayEmitter,\n CollapseHeight,\n AIStarIcon,\n ChevronDownIcon,\n SlidingPanel,\n },\n props: {\n /* The title text displayed */\n title: {\n type: String,\n },\n /* The classes added to the sliding panel. */\n slidingPanelClasses: {\n type: String,\n },\n /* The classes added to the sliding panel container. */\n slidingPanelContainerClasses: {\n type: String,\n },\n /* The classes added to the sliding panel buttons. */\n slidingPanelButtonsClasses: {\n type: String,\n },\n },\n setup(props) {\n const $x = use$x()\n const { query, isNoResults, suggestionsSearch, queries, tagging } = useState('ai')\n const emptyTaggingRequest: TaggingRequest = { url: '', params: {} }\n\n const titleRef = ref<HTMLElement | null>(null)\n const titleExpanded = ref(false)\n const isTitleOverflowing = ref(false)\n\n /**\n * Checks if the title is overflowing and updates the state.\n */\n function updateTitleOverflow() {\n if (titleExpanded.value) {\n return\n }\n if (titleRef.value) {\n isTitleOverflowing.value = titleRef.value.scrollWidth > titleRef.value.clientWidth\n }\n }\n\n /**\n * Toggles the title expanded state if it is overflowing.\n */\n function toggleTitleExpansion() {\n if (isTitleOverflowing.value) {\n titleExpanded.value = !titleExpanded.value\n }\n }\n\n const title = computed(() => {\n if (!props.title) {\n const queriesList = new Intl.ListFormat('en', {\n style: 'long',\n type: 'conjunction',\n }).format(queries.value.map(({ query }) => query))\n return `Searching for ${queriesList}`\n }\n return props.title\n })\n\n watch(queries, () => {\n if (queries.value.length > 0) {\n $x.emit('AiSuggestionsSearchRequestUpdated')\n }\n })\n\n useResizeObserver(titleRef, updateTitleOverflow)\n\n return {\n emptyTaggingRequest,\n isNoResults,\n isTitleOverflowing,\n queries,\n query,\n suggestionsSearch,\n tagging,\n title,\n titleExpanded,\n titleRef,\n toggleTitleExpansion,\n }\n },\n})\n</script>\n<style lang=\"css\">\n.x-ai-carousel {\n --color: var(--x-ai-carousel-color, #bbc9cf);\n --color-lighter: var(--x-ai-carousel-color-lighter, color-mix(in srgb, var(--color) 25%, white));\n\n padding: 8px 0;\n position: relative;\n border-radius: 1.5rem;\n background-color: var(--color-lighter);\n}\n\n.x-ai-carousel-title {\n display: flex;\n font-size: 12px;\n gap: 8px;\n align-items: flex-start;\n margin: 0 14px 8px;\n cursor: pointer;\n}\n.x-ai-carousel-title-text {\n display: block;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.x-ai-carousel-title-text--expanded {\n white-space: normal;\n}\n.x-ai-carousel-title-icon {\n height: 1rem;\n aspect-ratio: 1 / 1;\n color: var(--color);\n flex-shrink: 0;\n margin-bottom: auto;\n}\n.x-ai-carousel-title-button {\n background: none;\n border: none;\n padding: 0;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--color);\n margin-bottom: auto;\n}\n.x-ai-carousel-title-button-icon {\n height: 1rem;\n aspect-ratio: 1 / 1;\n transition: transform 0.3s ease;\n}\n.x-ai-carousel-title-button-icon--expanded {\n transform: rotate(180deg);\n}\n.x-ai-carousel-suggestion-results {\n display: flex;\n gap: 1rem;\n padding-left: 1rem;\n padding-right: 1rem;\n}\n</style>\n"],"names":["_createBlock","_withCtx","_openBlock","_createElementBlock","_createVNode","_createElementVNode","_normalizeClass","_renderSlot","_Fragment","_renderList","_createCommentVNode"],"mappings":"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"}
1
+ {"version":3,"file":"ai-carousel.vue.js","sources":["../../../../../src/x-modules/ai/components/ai-carousel.vue"],"sourcesContent":["<template>\n <CollapseHeight>\n <div v-if=\"suggestionsSearch.length\" class=\"x-ai-carousel\">\n <DisplayEmitter\n :payload=\"tagging?.toolingDisplay ?? emptyTaggingRequest\"\n :event-metadata=\"{\n feature: 'ai_carousel',\n displayOriginalQuery: query || 'ai-carousel-without-query',\n replaceable: false,\n }\"\n data-test=\"ai-carousel-display-emitter\"\n >\n <span\n class=\"x-ai-carousel-title\"\n :class=\"{ 'x-ai-carousel-title--expanded': titleExpanded }\"\n data-test=\"ai-carousel-title\"\n @click=\"toggleTitleExpansion\"\n >\n <AIStarIcon class=\"x-ai-carousel-title-icon\" />\n <ChangeHeight>\n <span\n ref=\"titleRef\"\n class=\"x-ai-carousel-title-text\"\n :class=\"{ 'x-ai-carousel-title-text--expanded': titleExpanded }\"\n >\n {{ title }}\n </span>\n </ChangeHeight>\n <button\n v-if=\"isTitleOverflowing\"\n class=\"x-ai-carousel-title-button\"\n data-test=\"ai-carousel-title-button\"\n :aria-label=\"titleExpanded ? 'Collapse' : 'Expand'\"\n >\n <ChevronDownIcon\n class=\"x-ai-carousel-title-button-icon\"\n :class=\"{ 'x-ai-carousel-title-button-icon--expanded': titleExpanded }\"\n />\n </button>\n </span>\n </DisplayEmitter>\n <slot name=\"sliding-panel\" :suggestions=\"suggestionsSearch\" :tagging=\"tagging\">\n <SlidingPanel\n :class=\"slidingPanelClasses\"\n :scroll-container-class=\"slidingPanelContainerClasses\"\n :button-class=\"slidingPanelButtonsClasses\"\n :reset-on-content-change=\"false\"\n >\n <template #sliding-panel-addons=\"{ arrivedState }\">\n <slot name=\"sliding-panels-addons\" :arrived-state=\"arrivedState\" />\n </template>\n <template #sliding-panel-left-button>\n <slot name=\"sliding-panels-left-button\" />\n </template>\n <template #sliding-panel-right-button>\n <slot name=\"sliding-panels-right-button\" />\n </template>\n <div class=\"x-flex x-gap-8\">\n <ul class=\"x-ai-carousel-suggestion-results\">\n <DisplayClickProvider\n v-for=\"suggestion in suggestionsSearch\"\n :key=\"suggestion.query\"\n :tooling-display-tagging=\"\n tagging?.searchQueries[suggestion.query].toolingDisplayClick\n \"\n :tooling-add2-cart-tagging=\"\n tagging?.searchQueries[suggestion.query].toolingDisplayAdd2Cart\n \"\n result-feature=\"ai_carousel\"\n >\n <li\n v-for=\"result in suggestion.results\"\n :key=\"result.id\"\n data-test=\"ai-carousel-suggestion-result\"\n >\n <!-- @slot (required) result card -->\n <slot name=\"result\" :result=\"result\" />\n </li>\n </DisplayClickProvider>\n </ul>\n </div>\n </SlidingPanel>\n </slot>\n <slot name=\"extra-content\" />\n <slot name=\"cta-button\" />\n </div>\n </CollapseHeight>\n</template>\n\n<script lang=\"ts\">\nimport type { TaggingRequest } from '@empathyco/x-types'\nimport { useResizeObserver } from '@vueuse/core'\nimport { computed, defineComponent, ref, watch } from 'vue'\nimport {\n AIStarIcon,\n ChangeHeight,\n ChevronDownIcon,\n CollapseHeight,\n DisplayClickProvider,\n SlidingPanel,\n} from '../../../components'\nimport DisplayEmitter from '../../../components/display-emitter.vue'\nimport { use$x, useState } from '../../../composables'\nimport { aiXModule } from '../x-module'\n\nexport default defineComponent({\n xModule: aiXModule.name,\n components: {\n ChangeHeight,\n DisplayClickProvider,\n DisplayEmitter,\n CollapseHeight,\n AIStarIcon,\n ChevronDownIcon,\n SlidingPanel,\n },\n props: {\n /* The title text displayed */\n title: {\n type: String,\n },\n /* The classes added to the sliding panel. */\n slidingPanelClasses: {\n type: String,\n },\n /* The classes added to the sliding panel container. */\n slidingPanelContainerClasses: {\n type: String,\n },\n /* The classes added to the sliding panel buttons. */\n slidingPanelButtonsClasses: {\n type: String,\n },\n },\n setup(props) {\n const $x = use$x()\n const { query, isNoResults, suggestionsSearch, queries, tagging } = useState('ai')\n const emptyTaggingRequest: TaggingRequest = { url: '', params: {} }\n\n const titleRef = ref<HTMLElement | null>(null)\n const titleExpanded = ref(false)\n const isTitleOverflowing = ref(false)\n\n /**\n * Checks if the title is overflowing and updates the state.\n */\n function updateTitleOverflow() {\n if (titleExpanded.value) {\n return\n }\n if (titleRef.value) {\n isTitleOverflowing.value = titleRef.value.scrollWidth > titleRef.value.clientWidth\n }\n }\n\n /**\n * Toggles the title expanded state if it is overflowing.\n */\n function toggleTitleExpansion() {\n if (isTitleOverflowing.value) {\n titleExpanded.value = !titleExpanded.value\n }\n }\n\n const title = computed(() => {\n if (!props.title) {\n const queriesList = new Intl.ListFormat('en', {\n style: 'long',\n type: 'conjunction',\n }).format(queries.value.map(({ query }) => query))\n return `Searching for ${queriesList}`\n }\n return props.title\n })\n\n watch(queries, () => {\n if (queries.value.length > 0) {\n $x.emit('AiSuggestionsSearchRequestUpdated')\n }\n })\n\n useResizeObserver(titleRef, updateTitleOverflow)\n\n return {\n emptyTaggingRequest,\n isNoResults,\n isTitleOverflowing,\n queries,\n query,\n suggestionsSearch,\n tagging,\n title,\n titleExpanded,\n titleRef,\n toggleTitleExpansion,\n }\n },\n})\n</script>\n<style lang=\"css\">\n.x-ai-carousel {\n --color: var(--x-ai-carousel-color, #bbc9cf);\n --color-lighter: var(--x-ai-carousel-color-lighter, color-mix(in srgb, var(--color) 25%, white));\n\n padding: 8px 0;\n position: relative;\n border-radius: 1.5rem;\n background-color: var(--color-lighter);\n}\n\n.x-ai-carousel-title {\n display: flex;\n font-size: 12px;\n gap: 8px;\n align-items: flex-start;\n margin: 0 14px 8px;\n cursor: pointer;\n}\n.x-ai-carousel-title-text {\n display: block;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.x-ai-carousel-title-text--expanded {\n white-space: normal;\n}\n.x-ai-carousel-title-icon {\n height: 1rem;\n aspect-ratio: 1 / 1;\n color: var(--color);\n flex-shrink: 0;\n margin-bottom: auto;\n}\n.x-ai-carousel-title-button {\n background: none;\n border: none;\n padding: 0;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--color);\n margin-bottom: auto;\n}\n.x-ai-carousel-title-button-icon {\n height: 1rem;\n aspect-ratio: 1 / 1;\n transition: transform 0.3s ease;\n}\n.x-ai-carousel-title-button-icon--expanded {\n transform: rotate(180deg);\n}\n.x-ai-carousel-suggestion-results {\n display: flex;\n gap: 1rem;\n padding-left: 1rem;\n padding-right: 1rem;\n}\n</style>\n"],"names":["_createBlock","_withCtx","_openBlock","_createElementBlock","_createVNode","_createElementVNode","_normalizeClass","_renderSlot","_Fragment","_renderList","_createCommentVNode"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"ai-carousel.vue2.js","sources":["../../../../../src/x-modules/ai/components/ai-carousel.vue"],"sourcesContent":["<template>\n <CollapseHeight>\n <div v-if=\"suggestionsSearch.length\" class=\"x-ai-carousel\">\n <DisplayEmitter\n :payload=\"tagging?.toolingDisplay ?? emptyTaggingRequest\"\n :event-metadata=\"{\n feature: 'ai_carousel',\n displayOriginalQuery: query || 'ai-carousel-without-query',\n replaceable: false,\n }\"\n data-test=\"ai-carousel-display-emitter\"\n >\n <span\n class=\"x-ai-carousel-title\"\n :class=\"{ 'x-ai-carousel-title--expanded': titleExpanded }\"\n data-test=\"ai-carousel-title\"\n @click=\"toggleTitleExpansion\"\n >\n <AIStarIcon class=\"x-ai-carousel-title-icon\" />\n <ChangeHeight>\n <span\n ref=\"titleRef\"\n class=\"x-ai-carousel-title-text\"\n :class=\"{ 'x-ai-carousel-title-text--expanded': titleExpanded }\"\n >\n {{ title }}\n </span>\n </ChangeHeight>\n <button\n v-if=\"isTitleOverflowing\"\n class=\"x-ai-carousel-title-button\"\n data-test=\"ai-carousel-title-button\"\n :aria-label=\"titleExpanded ? 'Collapse' : 'Expand'\"\n >\n <ChevronDownIcon\n class=\"x-ai-carousel-title-button-icon\"\n :class=\"{ 'x-ai-carousel-title-button-icon--expanded': titleExpanded }\"\n />\n </button>\n </span>\n </DisplayEmitter>\n <slot name=\"sliding-panel\" :results=\"suggestionsSearch\">\n <SlidingPanel\n :class=\"slidingPanelClasses\"\n :scroll-container-class=\"slidingPanelContainerClasses\"\n :button-class=\"slidingPanelButtonsClasses\"\n :reset-on-content-change=\"false\"\n >\n <template #sliding-panel-addons=\"{ arrivedState }\">\n <slot name=\"sliding-panels-addons\" :arrived-state=\"arrivedState\" />\n </template>\n <template #sliding-panel-left-button>\n <slot name=\"sliding-panels-left-button\" />\n </template>\n <template #sliding-panel-right-button>\n <slot name=\"sliding-panels-right-button\" />\n </template>\n <div class=\"x-flex x-gap-8\">\n <ul class=\"x-ai-carousel-suggestion-results\">\n <DisplayClickProvider\n v-for=\"suggestion in suggestionsSearch\"\n :key=\"suggestion.query\"\n :tooling-display-tagging=\"\n tagging?.searchQueries[suggestion.query].toolingDisplayClick\n \"\n :tooling-add2-cart-tagging=\"\n tagging?.searchQueries[suggestion.query].toolingDisplayAdd2Cart\n \"\n result-feature=\"ai_carousel\"\n >\n <li\n v-for=\"result in suggestion.results\"\n :key=\"result.id\"\n data-test=\"ai-carousel-suggestion-result\"\n >\n <!-- @slot (required) result card -->\n <slot name=\"result\" :result=\"result\" />\n </li>\n </DisplayClickProvider>\n </ul>\n </div>\n </SlidingPanel>\n </slot>\n <slot name=\"extra-content\" />\n <slot name=\"cta-button\" />\n </div>\n </CollapseHeight>\n</template>\n\n<script lang=\"ts\">\nimport type { TaggingRequest } from '@empathyco/x-types'\nimport { useResizeObserver } from '@vueuse/core'\nimport { computed, defineComponent, ref, watch } from 'vue'\nimport {\n AIStarIcon,\n ChangeHeight,\n ChevronDownIcon,\n CollapseHeight,\n DisplayClickProvider,\n SlidingPanel,\n} from '../../../components'\nimport DisplayEmitter from '../../../components/display-emitter.vue'\nimport { use$x, useState } from '../../../composables'\nimport { aiXModule } from '../x-module'\n\nexport default defineComponent({\n xModule: aiXModule.name,\n components: {\n ChangeHeight,\n DisplayClickProvider,\n DisplayEmitter,\n CollapseHeight,\n AIStarIcon,\n ChevronDownIcon,\n SlidingPanel,\n },\n props: {\n /* The title text displayed */\n title: {\n type: String,\n },\n /* The classes added to the sliding panel. */\n slidingPanelClasses: {\n type: String,\n },\n /* The classes added to the sliding panel container. */\n slidingPanelContainerClasses: {\n type: String,\n },\n /* The classes added to the sliding panel buttons. */\n slidingPanelButtonsClasses: {\n type: String,\n },\n },\n setup(props) {\n const $x = use$x()\n const { query, isNoResults, suggestionsSearch, queries, tagging } = useState('ai')\n const emptyTaggingRequest: TaggingRequest = { url: '', params: {} }\n\n const titleRef = ref<HTMLElement | null>(null)\n const titleExpanded = ref(false)\n const isTitleOverflowing = ref(false)\n\n /**\n * Checks if the title is overflowing and updates the state.\n */\n function updateTitleOverflow() {\n if (titleExpanded.value) {\n return\n }\n if (titleRef.value) {\n isTitleOverflowing.value = titleRef.value.scrollWidth > titleRef.value.clientWidth\n }\n }\n\n /**\n * Toggles the title expanded state if it is overflowing.\n */\n function toggleTitleExpansion() {\n if (isTitleOverflowing.value) {\n titleExpanded.value = !titleExpanded.value\n }\n }\n\n const title = computed(() => {\n if (!props.title) {\n const queriesList = new Intl.ListFormat('en', {\n style: 'long',\n type: 'conjunction',\n }).format(queries.value.map(({ query }) => query))\n return `Searching for ${queriesList}`\n }\n return props.title\n })\n\n watch(queries, () => {\n if (queries.value.length > 0) {\n $x.emit('AiSuggestionsSearchRequestUpdated')\n }\n })\n\n useResizeObserver(titleRef, updateTitleOverflow)\n\n return {\n emptyTaggingRequest,\n isNoResults,\n isTitleOverflowing,\n queries,\n query,\n suggestionsSearch,\n tagging,\n title,\n titleExpanded,\n titleRef,\n toggleTitleExpansion,\n }\n },\n})\n</script>\n<style lang=\"css\">\n.x-ai-carousel {\n --color: var(--x-ai-carousel-color, #bbc9cf);\n --color-lighter: var(--x-ai-carousel-color-lighter, color-mix(in srgb, var(--color) 25%, white));\n\n padding: 8px 0;\n position: relative;\n border-radius: 1.5rem;\n background-color: var(--color-lighter);\n}\n\n.x-ai-carousel-title {\n display: flex;\n font-size: 12px;\n gap: 8px;\n align-items: flex-start;\n margin: 0 14px 8px;\n cursor: pointer;\n}\n.x-ai-carousel-title-text {\n display: block;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.x-ai-carousel-title-text--expanded {\n white-space: normal;\n}\n.x-ai-carousel-title-icon {\n height: 1rem;\n aspect-ratio: 1 / 1;\n color: var(--color);\n flex-shrink: 0;\n margin-bottom: auto;\n}\n.x-ai-carousel-title-button {\n background: none;\n border: none;\n padding: 0;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--color);\n margin-bottom: auto;\n}\n.x-ai-carousel-title-button-icon {\n height: 1rem;\n aspect-ratio: 1 / 1;\n transition: transform 0.3s ease;\n}\n.x-ai-carousel-title-button-icon--expanded {\n transform: rotate(180deg);\n}\n.x-ai-carousel-suggestion-results {\n display: flex;\n gap: 1rem;\n padding-left: 1rem;\n padding-right: 1rem;\n}\n</style>\n"],"names":["DisplayClickProvider","DisplayEmitter"],"mappings":"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"}
1
+ {"version":3,"file":"ai-carousel.vue2.js","sources":["../../../../../src/x-modules/ai/components/ai-carousel.vue"],"sourcesContent":["<template>\n <CollapseHeight>\n <div v-if=\"suggestionsSearch.length\" class=\"x-ai-carousel\">\n <DisplayEmitter\n :payload=\"tagging?.toolingDisplay ?? emptyTaggingRequest\"\n :event-metadata=\"{\n feature: 'ai_carousel',\n displayOriginalQuery: query || 'ai-carousel-without-query',\n replaceable: false,\n }\"\n data-test=\"ai-carousel-display-emitter\"\n >\n <span\n class=\"x-ai-carousel-title\"\n :class=\"{ 'x-ai-carousel-title--expanded': titleExpanded }\"\n data-test=\"ai-carousel-title\"\n @click=\"toggleTitleExpansion\"\n >\n <AIStarIcon class=\"x-ai-carousel-title-icon\" />\n <ChangeHeight>\n <span\n ref=\"titleRef\"\n class=\"x-ai-carousel-title-text\"\n :class=\"{ 'x-ai-carousel-title-text--expanded': titleExpanded }\"\n >\n {{ title }}\n </span>\n </ChangeHeight>\n <button\n v-if=\"isTitleOverflowing\"\n class=\"x-ai-carousel-title-button\"\n data-test=\"ai-carousel-title-button\"\n :aria-label=\"titleExpanded ? 'Collapse' : 'Expand'\"\n >\n <ChevronDownIcon\n class=\"x-ai-carousel-title-button-icon\"\n :class=\"{ 'x-ai-carousel-title-button-icon--expanded': titleExpanded }\"\n />\n </button>\n </span>\n </DisplayEmitter>\n <slot name=\"sliding-panel\" :suggestions=\"suggestionsSearch\" :tagging=\"tagging\">\n <SlidingPanel\n :class=\"slidingPanelClasses\"\n :scroll-container-class=\"slidingPanelContainerClasses\"\n :button-class=\"slidingPanelButtonsClasses\"\n :reset-on-content-change=\"false\"\n >\n <template #sliding-panel-addons=\"{ arrivedState }\">\n <slot name=\"sliding-panels-addons\" :arrived-state=\"arrivedState\" />\n </template>\n <template #sliding-panel-left-button>\n <slot name=\"sliding-panels-left-button\" />\n </template>\n <template #sliding-panel-right-button>\n <slot name=\"sliding-panels-right-button\" />\n </template>\n <div class=\"x-flex x-gap-8\">\n <ul class=\"x-ai-carousel-suggestion-results\">\n <DisplayClickProvider\n v-for=\"suggestion in suggestionsSearch\"\n :key=\"suggestion.query\"\n :tooling-display-tagging=\"\n tagging?.searchQueries[suggestion.query].toolingDisplayClick\n \"\n :tooling-add2-cart-tagging=\"\n tagging?.searchQueries[suggestion.query].toolingDisplayAdd2Cart\n \"\n result-feature=\"ai_carousel\"\n >\n <li\n v-for=\"result in suggestion.results\"\n :key=\"result.id\"\n data-test=\"ai-carousel-suggestion-result\"\n >\n <!-- @slot (required) result card -->\n <slot name=\"result\" :result=\"result\" />\n </li>\n </DisplayClickProvider>\n </ul>\n </div>\n </SlidingPanel>\n </slot>\n <slot name=\"extra-content\" />\n <slot name=\"cta-button\" />\n </div>\n </CollapseHeight>\n</template>\n\n<script lang=\"ts\">\nimport type { TaggingRequest } from '@empathyco/x-types'\nimport { useResizeObserver } from '@vueuse/core'\nimport { computed, defineComponent, ref, watch } from 'vue'\nimport {\n AIStarIcon,\n ChangeHeight,\n ChevronDownIcon,\n CollapseHeight,\n DisplayClickProvider,\n SlidingPanel,\n} from '../../../components'\nimport DisplayEmitter from '../../../components/display-emitter.vue'\nimport { use$x, useState } from '../../../composables'\nimport { aiXModule } from '../x-module'\n\nexport default defineComponent({\n xModule: aiXModule.name,\n components: {\n ChangeHeight,\n DisplayClickProvider,\n DisplayEmitter,\n CollapseHeight,\n AIStarIcon,\n ChevronDownIcon,\n SlidingPanel,\n },\n props: {\n /* The title text displayed */\n title: {\n type: String,\n },\n /* The classes added to the sliding panel. */\n slidingPanelClasses: {\n type: String,\n },\n /* The classes added to the sliding panel container. */\n slidingPanelContainerClasses: {\n type: String,\n },\n /* The classes added to the sliding panel buttons. */\n slidingPanelButtonsClasses: {\n type: String,\n },\n },\n setup(props) {\n const $x = use$x()\n const { query, isNoResults, suggestionsSearch, queries, tagging } = useState('ai')\n const emptyTaggingRequest: TaggingRequest = { url: '', params: {} }\n\n const titleRef = ref<HTMLElement | null>(null)\n const titleExpanded = ref(false)\n const isTitleOverflowing = ref(false)\n\n /**\n * Checks if the title is overflowing and updates the state.\n */\n function updateTitleOverflow() {\n if (titleExpanded.value) {\n return\n }\n if (titleRef.value) {\n isTitleOverflowing.value = titleRef.value.scrollWidth > titleRef.value.clientWidth\n }\n }\n\n /**\n * Toggles the title expanded state if it is overflowing.\n */\n function toggleTitleExpansion() {\n if (isTitleOverflowing.value) {\n titleExpanded.value = !titleExpanded.value\n }\n }\n\n const title = computed(() => {\n if (!props.title) {\n const queriesList = new Intl.ListFormat('en', {\n style: 'long',\n type: 'conjunction',\n }).format(queries.value.map(({ query }) => query))\n return `Searching for ${queriesList}`\n }\n return props.title\n })\n\n watch(queries, () => {\n if (queries.value.length > 0) {\n $x.emit('AiSuggestionsSearchRequestUpdated')\n }\n })\n\n useResizeObserver(titleRef, updateTitleOverflow)\n\n return {\n emptyTaggingRequest,\n isNoResults,\n isTitleOverflowing,\n queries,\n query,\n suggestionsSearch,\n tagging,\n title,\n titleExpanded,\n titleRef,\n toggleTitleExpansion,\n }\n },\n})\n</script>\n<style lang=\"css\">\n.x-ai-carousel {\n --color: var(--x-ai-carousel-color, #bbc9cf);\n --color-lighter: var(--x-ai-carousel-color-lighter, color-mix(in srgb, var(--color) 25%, white));\n\n padding: 8px 0;\n position: relative;\n border-radius: 1.5rem;\n background-color: var(--color-lighter);\n}\n\n.x-ai-carousel-title {\n display: flex;\n font-size: 12px;\n gap: 8px;\n align-items: flex-start;\n margin: 0 14px 8px;\n cursor: pointer;\n}\n.x-ai-carousel-title-text {\n display: block;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.x-ai-carousel-title-text--expanded {\n white-space: normal;\n}\n.x-ai-carousel-title-icon {\n height: 1rem;\n aspect-ratio: 1 / 1;\n color: var(--color);\n flex-shrink: 0;\n margin-bottom: auto;\n}\n.x-ai-carousel-title-button {\n background: none;\n border: none;\n padding: 0;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--color);\n margin-bottom: auto;\n}\n.x-ai-carousel-title-button-icon {\n height: 1rem;\n aspect-ratio: 1 / 1;\n transition: transform 0.3s ease;\n}\n.x-ai-carousel-title-button-icon--expanded {\n transform: rotate(180deg);\n}\n.x-ai-carousel-suggestion-results {\n display: flex;\n gap: 1rem;\n padding-left: 1rem;\n padding-right: 1rem;\n}\n</style>\n"],"names":["DisplayClickProvider","DisplayEmitter"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyGA,gBAAe,eAAe,CAAC;IAC7B,OAAO,EAAE,SAAS,CAAC,IAAI;AACvB,IAAA,UAAU,EAAE;QACV,YAAY;8BACZA,WAAoB;wBACpBC,WAAc;QACd,cAAc;QACd,UAAU;QACV,eAAe;QACf,YAAY;AACb,KAAA;AACD,IAAA,KAAK,EAAE;;AAEL,QAAA,KAAK,EAAE;AACL,YAAA,IAAI,EAAE,MAAM;AACb,SAAA;;AAED,QAAA,mBAAmB,EAAE;AACnB,YAAA,IAAI,EAAE,MAAM;AACb,SAAA;;AAED,QAAA,4BAA4B,EAAE;AAC5B,YAAA,IAAI,EAAE,MAAM;AACb,SAAA;;AAED,QAAA,0BAA0B,EAAE;AAC1B,YAAA,IAAI,EAAE,MAAM;AACb,SAAA;AACF,KAAA;AACD,IAAA,KAAK,CAAC,KAAK,EAAA;AACT,QAAA,MAAM,EAAC,GAAI,KAAK,EAAC;AACjB,QAAA,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,OAAO,EAAE,OAAM,EAAE,GAAI,QAAQ,CAAC,IAAI,CAAA;QACjF,MAAM,mBAAmB,GAAmB,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,EAAE,EAAC,EAAE;AAElE,QAAA,MAAM,QAAO,GAAI,GAAG,CAAqB,IAAI,CAAA;AAC7C,QAAA,MAAM,aAAY,GAAI,GAAG,CAAC,KAAK,CAAA;AAC/B,QAAA,MAAM,qBAAqB,GAAG,CAAC,KAAK,CAAA;AAEpC;;AAEE;AACF,QAAA,SAAS,mBAAmB,GAAA;AAC1B,YAAA,IAAI,aAAa,CAAC,KAAK,EAAE;gBACvB;YACF;AACA,YAAA,IAAI,QAAQ,CAAC,KAAK,EAAE;AAClB,gBAAA,kBAAkB,CAAC,KAAI,GAAI,QAAQ,CAAC,KAAK,CAAC,cAAc,QAAQ,CAAC,KAAK,CAAC,WAAU;YACnF;QACF;AAEA;;AAEE;AACF,QAAA,SAAS,oBAAoB,GAAA;AAC3B,YAAA,IAAI,kBAAkB,CAAC,KAAK,EAAE;AAC5B,gBAAA,aAAa,CAAC,KAAI,GAAI,CAAC,aAAa,CAAC,KAAI;YAC3C;QACF;AAEA,QAAA,MAAM,KAAI,GAAI,QAAQ,CAAC,MAAI;AACzB,YAAA,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE;gBAChB,MAAM,WAAU,GAAI,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE;AAC5C,oBAAA,KAAK,EAAE,MAAM;AACb,oBAAA,IAAI,EAAE,aAAa;AACpB,iBAAA,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,KAAK,KAAK,CAAC,CAAA;gBACjD,OAAO,CAAA,cAAA,EAAiB,WAAW,CAAA,CAAC;YACtC;YACA,OAAO,KAAK,CAAC,KAAI;AACnB,QAAA,CAAC,CAAA;AAED,QAAA,KAAK,CAAC,OAAO,EAAE,MAAI;YACjB,IAAI,OAAO,CAAC,KAAK,CAAC,MAAK,GAAI,CAAC,EAAE;AAC5B,gBAAA,EAAE,CAAC,IAAI,CAAC,mCAAmC,CAAA;YAC7C;AACF,QAAA,CAAC,CAAA;AAED,QAAA,iBAAiB,CAAC,QAAQ,EAAE,mBAAmB,CAAA;QAE/C,OAAO;YACL,mBAAmB;YACnB,WAAW;YACX,kBAAkB;YAClB,OAAO;YACP,KAAK;YACL,iBAAiB;YACjB,OAAO;YACP,KAAK;YACL,aAAa;YACb,QAAQ;YACR,oBAAoB;SACtB;IACF,CAAC;AACF,CAAA,CAAA;;;;"}
@@ -5,6 +5,7 @@ import '../../../components/animations/animate-translate/animate-translate.style
5
5
  import '../../../components/animations/animate-width.vue2.js';
6
6
  import '../../../components/animations/animate-width.vue3.js';
7
7
  import '../../../components/animations/change-height.vue2.js';
8
+ import '../../../components/animations/change-height.vue3.js';
8
9
  import '../../../components/animations/collapse-height.vue2.js';
9
10
  import '../../../components/animations/collapse-height.vue3.js';
10
11
  import '../../../components/animations/collapse-width.vue2.js';
@@ -1 +1 @@
1
- {"version":3,"file":"empathize.vue2.js","sources":["../../../../../src/x-modules/empathize/components/empathize.vue"],"sourcesContent":["<template>\n <component :is=\"animation\">\n <div\n v-show=\"isOpenAndHasContent\"\n ref=\"empathizeRef\"\n class=\"x-empathize\"\n data-test=\"empathize\"\n @mousedown.prevent\n @focusin=\"open\"\n @focusout=\"close\"\n >\n <!-- @slot (Required) Modal container content -->\n <slot />\n </div>\n </component>\n</template>\n\n<script lang=\"ts\">\nimport type { PropType, WatchStopHandle } from 'vue'\nimport type { XEvent } from '../../../wiring'\nimport { computed, defineComponent, ref, watch } from 'vue'\nimport { NoAnimation } from '../../../components'\nimport { use$x, useDebounce } from '../../../composables'\nimport { AnimationProp } from '../../../types'\nimport { getActiveElement } from '../../../utils'\nimport { empathizeXModule } from '../x-module'\n\n/**\n * Component containing the empathize. It has a required slot to define its content.\n *\n * @public\n */\nexport default defineComponent({\n name: 'Empathize',\n xModule: empathizeXModule.name,\n props: {\n /** Array of {@link XEvent} to open the empathize. */\n eventsToOpenEmpathize: {\n type: Array as PropType<XEvent[]>,\n default: () => ['UserFocusedSearchBox', 'UserIsTypingAQuery', 'UserClickedSearchBox'],\n },\n /** Array of {@link XEvent} to close the empathize. */\n eventsToCloseEmpathize: {\n type: Array as PropType<XEvent[]>,\n default: () => [\n 'UserClosedEmpathize',\n 'UserSelectedASuggestion',\n 'UserPressedEnterKey',\n 'UserBlurredSearchBox',\n ],\n },\n /** Animation component that will be used to animate the empathize. */\n animation: {\n type: AnimationProp,\n default: () => NoAnimation,\n },\n /** Whether the empathize has content or not. As it is only known in the client, it is a prop. */\n hasContent: {\n type: Boolean,\n default: true,\n },\n /** Fallback flag to trigger a search and close the empathize when has no-content. */\n searchAndCloseOnNoContent: {\n type: Boolean,\n default: false,\n },\n /** Debounce time in milliseconds to search and close the empathize when has no-content. */\n searchAndCloseDebounceInMs: {\n type: Number,\n default: 1000,\n },\n },\n setup(props) {\n const $x = use$x()\n\n const empathizeRef = ref<HTMLDivElement | null>(null)\n const isOpen = ref(false)\n const isOpenAndHasContent = computed(() => isOpen.value && props.hasContent)\n\n /** Emit 'EmpathizeOpened' or 'EmpathizeClosed' event when computed changes. */\n watch(isOpenAndHasContent, () => {\n const empathizeEvent = isOpenAndHasContent.value ? 'EmpathizeOpened' : 'EmpathizeClosed'\n $x.emit(empathizeEvent, undefined, { target: empathizeRef.value })\n })\n\n /** Debounce function to change the state `isOpen` to the new value. */\n const changeOpenDebounced = useDebounce((newOpen: boolean) => (isOpen.value = newOpen), 0)\n\n /**\n * Open empathize. This function will be executed on any event in\n * {@link Empathize.eventsToOpenEmpathize} and on DOM event `focusin` on the Empathize root\n * element.\n */\n function open() {\n changeOpenDebounced(true)\n }\n\n /**\n * Close empathize. This function will be executed on any event in\n * {@link Empathize.eventsToCloseEmpathize} and on DOM event `focusout` on the Empathize root\n * element.\n */\n function close() {\n const activeElement = getActiveElement()\n if (!empathizeRef.value?.contains(activeElement)) {\n changeOpenDebounced(false)\n }\n }\n\n /** Events subscriptions to open and close empathize. */\n props.eventsToOpenEmpathize.forEach(event => $x.on(event, false).subscribe(open))\n props.eventsToCloseEmpathize.forEach(event => $x.on(event, false).subscribe(close))\n\n let unwatchSearchBoxQuery: WatchStopHandle = () => {}\n\n /** Debounced function to unwatch the search-box query and also search and close empathize. */\n const searchAndCloseDebounced = useDebounce(async () => {\n if (isOpen.value) {\n unwatchSearchBoxQuery()\n await $x.emit('UserAcceptedAQuery', $x.query.searchBox)\n close()\n }\n }, props.searchAndCloseDebounceInMs)\n\n /**\n * Watcher triggered when `hasContent` change and the `searchAndCloseOnNoContent` flag is active\n * with the following casuistics:\n * 1. Empathize has content: unwatch the search-box query and cancel debounced search&close.\n * 2. Empathize has NO content: create a watcher for the search-box query. It is to debounce the\n * search fallback when the user types in the search-box during debounced time.\n */\n watch(\n () => props.hasContent,\n () => {\n if (props.searchAndCloseOnNoContent) {\n if (props.hasContent) {\n unwatchSearchBoxQuery()\n searchAndCloseDebounced.cancel()\n } else {\n unwatchSearchBoxQuery = watch(() => $x.query.searchBox, searchAndCloseDebounced, {\n immediate: true,\n })\n }\n }\n },\n )\n\n return { empathizeRef, isOpenAndHasContent, open, close }\n },\n})\n</script>\n\n<docs lang=\"mdx\">\n## Events\n\nA list of events that the component will emit:\n\n- [`EmpathizeOpened`](https://github.com/empathyco/x/blob/main/packages/x-components/src/wiring/events.types.ts):\n the event is emitted after receiving an event to change the state `isOpen` to `true` and `hasContent` to `true`.\n The event payload is undefined and can have a metadata with the module and the element that emitted it.\n- [`EmpathizeClosed`](https://github.com/empathyco/x/blob/main/packages/x-components/src/wiring/events.types.ts):\n the event is emitted after receiving an event to change the state `isOpen` to `false` and `hasContent` to `true`.\n The event payload is undefined and can have a metadata with the module and the element that emitted it.\n\n## Examples\n\nThis component will listen to the configured events in `eventsToOpenEmpathize` and\n`eventsToCloseEmpathize` props and open/close itself accordingly. By default, those props values\nare:\n\n- Open: `UserFocusedSearchBox`, `UserIsTypingAQuery`, `UserClickedSearchBox`\n- Close: `UserClosedEmpathize`, `UserSelectedASuggestion`, `UserPressedEnterKey`, `UserBlurredSearchBox`\n\n### Basic example\n\nThe component rendering the query suggestions, popular searches and history queries with keyboard\nnavigation.\n\n```vue\n<template>\n <Empathize>\n <BaseKeyboardNavigation>\n <QuerySuggestions />\n <PopularSearches />\n <HistoryQueries />\n </BaseKeyboardNavigation>\n </Empathize>\n</template>\n\n<script setup>\nimport Empathize from '@empathyco/x-components/js/x-modules/empathize/components/empathize.vue'\nimport BaseKeyboardNavigation from '@empathyco/x-components/js/components/base-keyboard-navigation.vue'\nimport QuerySuggestions from '@empathyco/x-components/js/x-modules/query-suggestions/components/query-suggestions.vue'\nimport PopularSearches from '@empathyco/x-components/js/x-modules/popular-searches/components/popular-searches.vue'\nimport HistoryQueries from '@empathyco/x-components/js/x-modules/history-queries/components/history-queries.vue'\n</script>\n```\n\nDefining custom values for the events to open and close the Empathize. For example, opening it when\nthe search box loses the focus and closing it when the search box receives the focus:\n\n```vue\n<template>\n <Empathize\n :events-to-open-empathize=\"['UserBlurredSearchBox']\"\n :events-to-close-empathize=\"['UserFocusedSearchBox']\"\n >\n Please, type a query in the Search Box.\n </Empathize>\n</template>\n\n<script setup>\nimport Empathize from '@empathyco/x-components/js/x-modules/empathize/components/empathize.vue'\n</script>\n```\n\nAn animation can be used for the opening and closing using the `animation` prop. The animation must\nbe a component with a `Transition` and a slot inside:\n\n```vue\n<template>\n <Empathize :animation=\"collapseFromTop\">\n <PopularSearches />\n </Empathize>\n</template>\n\n<script setup>\nimport Empathize from '@empathyco/x-components/js/x-modules/empathize/components/empathize.vue'\nimport PopularSearches from '@empathyco/x-components/js/x-modules/popular-searches/components/popular-searches.vue'\nimport CollapseFromTop from './collapseFromTop.vue'\nconst animation = CollapseFromTop\n</script>\n```\n\n### Advanced example\n\nThe component rendering the query suggestions, popular searches and history queries with keyboard\nnavigation. It also configures `searchAndCloseOnNoContent` to trigger a search and close the empathize\nwhen it has no content as fallback behaviour. To do that, `hasContent` prop must be reactive to know\nif the empathize has content or not. It also configures `searchAndCloseDebounceInMs` to 500ms as debounce time to search and close the empathize when it has no content.\n\n```vue\n<template>\n <Empathize\n :animation=\"empathizeAnimation\"\n :events-to-close-empathize=\"empathizeCloseEvents\"\n :has-content=\"showEmpathize\"\n :search-and-close-debounce-in-ms=\"500\"\n search-and-close-on-no-content\n >\n <BaseKeyboardNavigation>\n <QuerySuggestions />\n <PopularSearches />\n <HistoryQueries />\n </BaseKeyboardNavigation>\n </Empathize>\n</template>\n\n<script setup>\nimport Empathize from '@empathyco/x-components/js/x-modules/empathize/components/empathize.vue'\nimport BaseKeyboardNavigation from '@empathyco/x-components/js/components/base-keyboard-navigation.vue'\nimport QuerySuggestions from '@empathyco/x-components/js/x-modules/query-suggestions/components/query-suggestions.vue'\nimport PopularSearches from '@empathyco/x-components/js/x-modules/popular-searches/components/popular-searches.vue'\nimport HistoryQueries from '@empathyco/x-components/js/x-modules/history-queries/components/history-queries.vue'\nimport CollapseFromTop from './collapseFromTop.vue'\nimport { ref } from 'vue'\nconst empathizeAnimation = CollapseFromTop\nconst empathizeCloseEvents = ['UserClosedEmpathize', 'UserSelectedASuggestion']\nconst showEmpathize = ref(true)\n</script>\n```\n</docs>\n"],"names":["NoAnimation"],"mappings":"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"}
1
+ {"version":3,"file":"empathize.vue2.js","sources":["../../../../../src/x-modules/empathize/components/empathize.vue"],"sourcesContent":["<template>\n <component :is=\"animation\">\n <div\n v-show=\"isOpenAndHasContent\"\n ref=\"empathizeRef\"\n class=\"x-empathize\"\n data-test=\"empathize\"\n @mousedown.prevent\n @focusin=\"open\"\n @focusout=\"close\"\n >\n <!-- @slot (Required) Modal container content -->\n <slot />\n </div>\n </component>\n</template>\n\n<script lang=\"ts\">\nimport type { PropType, WatchStopHandle } from 'vue'\nimport type { XEvent } from '../../../wiring'\nimport { computed, defineComponent, ref, watch } from 'vue'\nimport { NoAnimation } from '../../../components'\nimport { use$x, useDebounce } from '../../../composables'\nimport { AnimationProp } from '../../../types'\nimport { getActiveElement } from '../../../utils'\nimport { empathizeXModule } from '../x-module'\n\n/**\n * Component containing the empathize. It has a required slot to define its content.\n *\n * @public\n */\nexport default defineComponent({\n name: 'Empathize',\n xModule: empathizeXModule.name,\n props: {\n /** Array of {@link XEvent} to open the empathize. */\n eventsToOpenEmpathize: {\n type: Array as PropType<XEvent[]>,\n default: () => ['UserFocusedSearchBox', 'UserIsTypingAQuery', 'UserClickedSearchBox'],\n },\n /** Array of {@link XEvent} to close the empathize. */\n eventsToCloseEmpathize: {\n type: Array as PropType<XEvent[]>,\n default: () => [\n 'UserClosedEmpathize',\n 'UserSelectedASuggestion',\n 'UserPressedEnterKey',\n 'UserBlurredSearchBox',\n ],\n },\n /** Animation component that will be used to animate the empathize. */\n animation: {\n type: AnimationProp,\n default: () => NoAnimation,\n },\n /** Whether the empathize has content or not. As it is only known in the client, it is a prop. */\n hasContent: {\n type: Boolean,\n default: true,\n },\n /** Fallback flag to trigger a search and close the empathize when has no-content. */\n searchAndCloseOnNoContent: {\n type: Boolean,\n default: false,\n },\n /** Debounce time in milliseconds to search and close the empathize when has no-content. */\n searchAndCloseDebounceInMs: {\n type: Number,\n default: 1000,\n },\n },\n setup(props) {\n const $x = use$x()\n\n const empathizeRef = ref<HTMLDivElement | null>(null)\n const isOpen = ref(false)\n const isOpenAndHasContent = computed(() => isOpen.value && props.hasContent)\n\n /** Emit 'EmpathizeOpened' or 'EmpathizeClosed' event when computed changes. */\n watch(isOpenAndHasContent, () => {\n const empathizeEvent = isOpenAndHasContent.value ? 'EmpathizeOpened' : 'EmpathizeClosed'\n $x.emit(empathizeEvent, undefined, { target: empathizeRef.value })\n })\n\n /** Debounce function to change the state `isOpen` to the new value. */\n const changeOpenDebounced = useDebounce((newOpen: boolean) => (isOpen.value = newOpen), 0)\n\n /**\n * Open empathize. This function will be executed on any event in\n * {@link Empathize.eventsToOpenEmpathize} and on DOM event `focusin` on the Empathize root\n * element.\n */\n function open() {\n changeOpenDebounced(true)\n }\n\n /**\n * Close empathize. This function will be executed on any event in\n * {@link Empathize.eventsToCloseEmpathize} and on DOM event `focusout` on the Empathize root\n * element.\n */\n function close() {\n const activeElement = getActiveElement()\n if (!empathizeRef.value?.contains(activeElement)) {\n changeOpenDebounced(false)\n }\n }\n\n /** Events subscriptions to open and close empathize. */\n props.eventsToOpenEmpathize.forEach(event => $x.on(event, false).subscribe(open))\n props.eventsToCloseEmpathize.forEach(event => $x.on(event, false).subscribe(close))\n\n let unwatchSearchBoxQuery: WatchStopHandle = () => {}\n\n /** Debounced function to unwatch the search-box query and also search and close empathize. */\n const searchAndCloseDebounced = useDebounce(async () => {\n if (isOpen.value) {\n unwatchSearchBoxQuery()\n await $x.emit('UserAcceptedAQuery', $x.query.searchBox)\n close()\n }\n }, props.searchAndCloseDebounceInMs)\n\n /**\n * Watcher triggered when `hasContent` change and the `searchAndCloseOnNoContent` flag is active\n * with the following casuistics:\n * 1. Empathize has content: unwatch the search-box query and cancel debounced search&close.\n * 2. Empathize has NO content: create a watcher for the search-box query. It is to debounce the\n * search fallback when the user types in the search-box during debounced time.\n */\n watch(\n () => props.hasContent,\n () => {\n if (props.searchAndCloseOnNoContent) {\n if (props.hasContent) {\n unwatchSearchBoxQuery()\n searchAndCloseDebounced.cancel()\n } else {\n unwatchSearchBoxQuery = watch(() => $x.query.searchBox, searchAndCloseDebounced, {\n immediate: true,\n })\n }\n }\n },\n )\n\n return { empathizeRef, isOpenAndHasContent, open, close }\n },\n})\n</script>\n\n<docs lang=\"mdx\">\n## Events\n\nA list of events that the component will emit:\n\n- [`EmpathizeOpened`](https://github.com/empathyco/x/blob/main/packages/x-components/src/wiring/events.types.ts):\n the event is emitted after receiving an event to change the state `isOpen` to `true` and `hasContent` to `true`.\n The event payload is undefined and can have a metadata with the module and the element that emitted it.\n- [`EmpathizeClosed`](https://github.com/empathyco/x/blob/main/packages/x-components/src/wiring/events.types.ts):\n the event is emitted after receiving an event to change the state `isOpen` to `false` and `hasContent` to `true`.\n The event payload is undefined and can have a metadata with the module and the element that emitted it.\n\n## Examples\n\nThis component will listen to the configured events in `eventsToOpenEmpathize` and\n`eventsToCloseEmpathize` props and open/close itself accordingly. By default, those props values\nare:\n\n- Open: `UserFocusedSearchBox`, `UserIsTypingAQuery`, `UserClickedSearchBox`\n- Close: `UserClosedEmpathize`, `UserSelectedASuggestion`, `UserPressedEnterKey`, `UserBlurredSearchBox`\n\n### Basic example\n\nThe component rendering the query suggestions, popular searches and history queries with keyboard\nnavigation.\n\n```vue\n<template>\n <Empathize>\n <BaseKeyboardNavigation>\n <QuerySuggestions />\n <PopularSearches />\n <HistoryQueries />\n </BaseKeyboardNavigation>\n </Empathize>\n</template>\n\n<script setup>\nimport Empathize from '@empathyco/x-components/js/x-modules/empathize/components/empathize.vue'\nimport BaseKeyboardNavigation from '@empathyco/x-components/js/components/base-keyboard-navigation.vue'\nimport QuerySuggestions from '@empathyco/x-components/js/x-modules/query-suggestions/components/query-suggestions.vue'\nimport PopularSearches from '@empathyco/x-components/js/x-modules/popular-searches/components/popular-searches.vue'\nimport HistoryQueries from '@empathyco/x-components/js/x-modules/history-queries/components/history-queries.vue'\n</script>\n```\n\nDefining custom values for the events to open and close the Empathize. For example, opening it when\nthe search box loses the focus and closing it when the search box receives the focus:\n\n```vue\n<template>\n <Empathize\n :events-to-open-empathize=\"['UserBlurredSearchBox']\"\n :events-to-close-empathize=\"['UserFocusedSearchBox']\"\n >\n Please, type a query in the Search Box.\n </Empathize>\n</template>\n\n<script setup>\nimport Empathize from '@empathyco/x-components/js/x-modules/empathize/components/empathize.vue'\n</script>\n```\n\nAn animation can be used for the opening and closing using the `animation` prop. The animation must\nbe a component with a `Transition` and a slot inside:\n\n```vue\n<template>\n <Empathize :animation=\"collapseFromTop\">\n <PopularSearches />\n </Empathize>\n</template>\n\n<script setup>\nimport Empathize from '@empathyco/x-components/js/x-modules/empathize/components/empathize.vue'\nimport PopularSearches from '@empathyco/x-components/js/x-modules/popular-searches/components/popular-searches.vue'\nimport CollapseFromTop from './collapseFromTop.vue'\nconst animation = CollapseFromTop\n</script>\n```\n\n### Advanced example\n\nThe component rendering the query suggestions, popular searches and history queries with keyboard\nnavigation. It also configures `searchAndCloseOnNoContent` to trigger a search and close the empathize\nwhen it has no content as fallback behaviour. To do that, `hasContent` prop must be reactive to know\nif the empathize has content or not. It also configures `searchAndCloseDebounceInMs` to 500ms as debounce time to search and close the empathize when it has no content.\n\n```vue\n<template>\n <Empathize\n :animation=\"empathizeAnimation\"\n :events-to-close-empathize=\"empathizeCloseEvents\"\n :has-content=\"showEmpathize\"\n :search-and-close-debounce-in-ms=\"500\"\n search-and-close-on-no-content\n >\n <BaseKeyboardNavigation>\n <QuerySuggestions />\n <PopularSearches />\n <HistoryQueries />\n </BaseKeyboardNavigation>\n </Empathize>\n</template>\n\n<script setup>\nimport Empathize from '@empathyco/x-components/js/x-modules/empathize/components/empathize.vue'\nimport BaseKeyboardNavigation from '@empathyco/x-components/js/components/base-keyboard-navigation.vue'\nimport QuerySuggestions from '@empathyco/x-components/js/x-modules/query-suggestions/components/query-suggestions.vue'\nimport PopularSearches from '@empathyco/x-components/js/x-modules/popular-searches/components/popular-searches.vue'\nimport HistoryQueries from '@empathyco/x-components/js/x-modules/history-queries/components/history-queries.vue'\nimport CollapseFromTop from './collapseFromTop.vue'\nimport { ref } from 'vue'\nconst empathizeAnimation = CollapseFromTop\nconst empathizeCloseEvents = ['UserClosedEmpathize', 'UserSelectedASuggestion']\nconst showEmpathize = ref(true)\n</script>\n```\n</docs>\n"],"names":["NoAnimation"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2BA;;;;AAIE;AACF,gBAAe,eAAe,CAAC;AAC7B,IAAA,IAAI,EAAE,WAAW;IACjB,OAAO,EAAE,gBAAgB,CAAC,IAAI;AAC9B,IAAA,KAAK,EAAE;;AAEL,QAAA,qBAAqB,EAAE;AACrB,YAAA,IAAI,EAAE,KAA2B;YACjC,OAAO,EAAE,MAAM,CAAC,sBAAsB,EAAE,oBAAoB,EAAE,sBAAsB,CAAC;AACtF,SAAA;;AAED,QAAA,sBAAsB,EAAE;AACtB,YAAA,IAAI,EAAE,KAA2B;YACjC,OAAO,EAAE,MAAM;gBACb,qBAAqB;gBACrB,yBAAyB;gBACzB,qBAAqB;gBACrB,sBAAsB;AACvB,aAAA;AACF,SAAA;;AAED,QAAA,SAAS,EAAE;AACT,YAAA,IAAI,EAAE,aAAa;AACnB,YAAA,OAAO,EAAE,MAAMA,WAAW;AAC3B,SAAA;;AAED,QAAA,UAAU,EAAE;AACV,YAAA,IAAI,EAAE,OAAO;AACb,YAAA,OAAO,EAAE,IAAI;AACd,SAAA;;AAED,QAAA,yBAAyB,EAAE;AACzB,YAAA,IAAI,EAAE,OAAO;AACb,YAAA,OAAO,EAAE,KAAK;AACf,SAAA;;AAED,QAAA,0BAA0B,EAAE;AAC1B,YAAA,IAAI,EAAE,MAAM;AACZ,YAAA,OAAO,EAAE,IAAI;AACd,SAAA;AACF,KAAA;AACD,IAAA,KAAK,CAAC,KAAK,EAAA;AACT,QAAA,MAAM,EAAC,GAAI,KAAK,EAAC;AAEjB,QAAA,MAAM,YAAW,GAAI,GAAG,CAAwB,IAAI,CAAA;AACpD,QAAA,MAAM,SAAS,GAAG,CAAC,KAAK,CAAA;AACxB,QAAA,MAAM,mBAAkB,GAAI,QAAQ,CAAC,MAAM,MAAM,CAAC,KAAI,IAAK,KAAK,CAAC,UAAU,CAAA;;AAG3E,QAAA,KAAK,CAAC,mBAAmB,EAAE,MAAI;AAC7B,YAAA,MAAM,cAAa,GAAI,mBAAmB,CAAC,KAAI,GAAI,oBAAoB,iBAAgB;AACvF,YAAA,EAAE,CAAC,IAAI,CAAC,cAAc,EAAE,SAAS,EAAE,EAAE,MAAM,EAAE,YAAY,CAAC,KAAI,EAAG,CAAA;AACnE,QAAA,CAAC,CAAA;;AAGD,QAAA,MAAM,mBAAkB,GAAI,WAAW,CAAC,CAAC,OAAgB,MAAM,MAAM,CAAC,KAAI,GAAI,OAAO,CAAC,EAAE,CAAC,CAAA;AAEzF;;;;AAIE;AACF,QAAA,SAAS,IAAI,GAAA;YACX,mBAAmB,CAAC,IAAI,CAAA;QAC1B;AAEA;;;;AAIE;AACF,QAAA,SAAS,KAAK,GAAA;AACZ,YAAA,MAAM,aAAY,GAAI,gBAAgB,EAAC;YACvC,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,QAAQ,CAAC,aAAa,CAAC,EAAE;gBAChD,mBAAmB,CAAC,KAAK,CAAA;YAC3B;QACF;;QAGA,KAAK,CAAC,qBAAqB,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAA;QAChF,KAAK,CAAC,sBAAsB,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CAAA;AAElF,QAAA,IAAI,qBAAqB,GAAoB,QAAO,CAAA;;AAGpD,QAAA,MAAM,uBAAsB,GAAI,WAAW,CAAC,YAAU;AACpD,YAAA,IAAI,MAAM,CAAC,KAAK,EAAE;AAChB,gBAAA,qBAAqB,EAAC;AACtB,gBAAA,MAAM,EAAE,CAAC,IAAI,CAAC,oBAAoB,EAAE,EAAE,CAAC,KAAK,CAAC,SAAS,CAAA;AACtD,gBAAA,KAAK,EAAC;YACR;AACF,QAAA,CAAC,EAAE,KAAK,CAAC,0BAA0B,CAAA;AAEnC;;;;;;AAME;QACF,KAAK,CACH,MAAM,KAAK,CAAC,UAAU,EACtB,MAAI;AACF,YAAA,IAAI,KAAK,CAAC,yBAAyB,EAAE;AACnC,gBAAA,IAAI,KAAK,CAAC,UAAU,EAAE;AACpB,oBAAA,qBAAqB,EAAC;oBACtB,uBAAuB,CAAC,MAAM,EAAC;gBACjC;qBAAO;AACL,oBAAA,qBAAoB,GAAI,KAAK,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,SAAS,EAAE,uBAAuB,EAAE;AAC/E,wBAAA,SAAS,EAAE,IAAI;AAChB,qBAAA,CAAA;gBACH;YACF;AACF,QAAA,CAAC,CACH;QAEA,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,IAAI,EAAE,KAAI,EAAE;IAC1D,CAAC;AACF,CAAA,CAAA;;;;"}
@@ -5,6 +5,7 @@ import '../../../components/animations/animate-translate/animate-translate.style
5
5
  import '../../../components/animations/animate-width.vue2.js';
6
6
  import '../../../components/animations/animate-width.vue3.js';
7
7
  import '../../../components/animations/change-height.vue2.js';
8
+ import '../../../components/animations/change-height.vue3.js';
8
9
  import '../../../components/animations/collapse-height.vue2.js';
9
10
  import '../../../components/animations/collapse-height.vue3.js';
10
11
  import '../../../components/animations/collapse-width.vue2.js';
@@ -1 +1 @@
1
- {"version":3,"file":"query-preview-button.vue2.js","sources":["../../../../../src/x-modules/queries-preview/components/query-preview-button.vue"],"sourcesContent":["<template>\n <BaseEventButton\n :events=\"events\"\n :metadata=\"metadata\"\n class=\"x-query-preview-button x-button\"\n data-test=\"query-preview-button\"\n >\n <!-- @slot Button content with a text, an icon or both -->\n <slot :query-preview-info=\"fullQueryPreviewInfo\">\n {{ queryPreviewInfo.query }}\n </slot>\n </BaseEventButton>\n</template>\n\n<script lang=\"ts\">\nimport type { PropType } from 'vue'\nimport type { XEventsTypes } from '../../../wiring/events.types'\nimport type { WireMetadata } from '../../../wiring/index'\nimport type { QueryPreviewInfo } from '../store/types'\nimport { computed, defineComponent } from 'vue'\nimport { BaseEventButton } from '../../../components'\nimport { useState } from '../../../composables/use-state'\nimport { queriesPreviewXModule } from '../x-module'\n\n/**\n * Component containing an event button that emits\n * {@link QueriesPreviewXEvents.UserAcceptedAQueryPreview} when clicked with\n * the full query preview info as payload.\n *\n * It has a default slot to customize its contents.\n *\n * @public\n */\nexport default defineComponent({\n name: 'QueryPreviewButton',\n xModule: queriesPreviewXModule.name,\n components: { BaseEventButton },\n props: {\n /**\n * The information about the request of the query preview.\n *\n * @public\n */\n queryPreviewInfo: {\n type: Object as PropType<QueryPreviewInfo>,\n required: true,\n },\n /**\n * The metadata property for the request on each query preview.\n *\n * @public\n */\n metadata: {\n type: Object as PropType<Omit<WireMetadata, 'moduleName'>>,\n },\n },\n setup(props) {\n /**\n * We use the module extra params to combine them with the query preview's extra params.\n */\n const { params } = useState('queriesPreview')\n\n /**\n * The provided query preview with the base extra params from the module merged in.\n *\n * @returns The query preview info with the base extra params merged in.\n * @public\n */\n const fullQueryPreviewInfo = computed(\n (): QueryPreviewInfo => ({\n ...props.queryPreviewInfo,\n extraParams: {\n ...params.value,\n ...props.queryPreviewInfo.extraParams,\n },\n filters: props.queryPreviewInfo.filters,\n }),\n )\n\n /**\n * List of events to emit by the BaseEventButton.\n *\n * @returns An object with the event and payload.\n *\n * @internal\n */\n const events = computed(\n (): Partial<XEventsTypes> => ({ UserAcceptedAQueryPreview: fullQueryPreviewInfo.value }),\n )\n\n return {\n fullQueryPreviewInfo,\n events,\n }\n },\n})\n</script>\n\n<docs lang=\"mdx\">\n## Examples\n\n### Basic example\n\nThe component content has the query preview query as default.\n\n```vue\n<template>\n <QueryPreviewButton queryPreviewInfo=\"queryPreviewInfo\" />\n</template>\n\n<script setup>\nimport { QueryPreviewButton } from '@empathyco/x-components/queries-preview'\nimport { reactive } from 'vue'\nconst queryPreviewInfo = reactive({ query: 'shoes' })\n</script>\n```\n\n### Customizing slots\n\nThe content of the button is customizable via its default slot.\n\n```vue\n<template>\n <QueryPreviewButton queryPreviewInfo=\"queryPreviewInfo\">\n {{ `Search for: ${queryPreviewInfo.query}` }}\n </QueryPreviewButton>\n</template>\n\n<script setup>\nimport { QueryPreviewButton } from '@empathyco/x-components/queries-preview'\nimport { reactive } from 'vue'\nconst queryPreviewInfo = reactive({ query: 'shoes' })\n</script>\n```\n\n## Events\n\nA list of events that the component will emit:\n\n- `UserAcceptedAQueryPreview`: the event is emitted after the user clicks the button. The event\n payload is the `QueryPreviewInfo` of the query.\n</docs>\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwBA;;;;;;;;AAQE;AACF,gBAAe,eAAe,CAAC;AAC7B,IAAA,IAAI,EAAE,oBAAoB;IAC1B,OAAO,EAAE,qBAAqB,CAAC,IAAI;IACnC,UAAU,EAAE,EAAE,eAAc,EAAG;AAC/B,IAAA,KAAK,EAAE;AACL;;;;AAIE;AACF,QAAA,gBAAgB,EAAE;AAChB,YAAA,IAAI,EAAE,MAAoC;AAC1C,YAAA,QAAQ,EAAE,IAAI;AACf,SAAA;AACD;;;;AAIE;AACF,QAAA,QAAQ,EAAE;AACR,YAAA,IAAI,EAAE,MAAoD;AAC3D,SAAA;AACF,KAAA;AACD,IAAA,KAAK,CAAC,KAAK,EAAA;AACT;;AAEE;QACF,MAAM,EAAE,QAAO,GAAI,QAAQ,CAAC,gBAAgB,CAAA;AAE5C;;;;;AAKE;AACF,QAAA,MAAM,oBAAmB,GAAI,QAAQ,CACnC,OAAyB;YACvB,GAAG,KAAK,CAAC,gBAAgB;AACzB,YAAA,WAAW,EAAE;gBACX,GAAG,MAAM,CAAC,KAAK;AACf,gBAAA,GAAG,KAAK,CAAC,gBAAgB,CAAC,WAAW;AACtC,aAAA;AACD,YAAA,OAAO,EAAE,KAAK,CAAC,gBAAgB,CAAC,OAAO;AACxC,SAAA,CAAC,CACJ;AAEA;;;;;;AAME;AACF,QAAA,MAAM,MAAK,GAAI,QAAQ,CACrB,OAA8B,EAAE,yBAAyB,EAAE,oBAAoB,CAAC,KAAI,EAAG,CAAC,CAC1F;QAEA,OAAO;YACL,oBAAoB;YACpB,MAAM;SACR;IACF,CAAC;AACF,CAAA,CAAA;;;;"}
1
+ {"version":3,"file":"query-preview-button.vue2.js","sources":["../../../../../src/x-modules/queries-preview/components/query-preview-button.vue"],"sourcesContent":["<template>\n <BaseEventButton\n :events=\"events\"\n :metadata=\"metadata\"\n class=\"x-query-preview-button x-button\"\n data-test=\"query-preview-button\"\n >\n <!-- @slot Button content with a text, an icon or both -->\n <slot :query-preview-info=\"fullQueryPreviewInfo\">\n {{ queryPreviewInfo.query }}\n </slot>\n </BaseEventButton>\n</template>\n\n<script lang=\"ts\">\nimport type { PropType } from 'vue'\nimport type { XEventsTypes } from '../../../wiring/events.types'\nimport type { WireMetadata } from '../../../wiring/index'\nimport type { QueryPreviewInfo } from '../store/types'\nimport { computed, defineComponent } from 'vue'\nimport { BaseEventButton } from '../../../components'\nimport { useState } from '../../../composables/use-state'\nimport { queriesPreviewXModule } from '../x-module'\n\n/**\n * Component containing an event button that emits\n * {@link QueriesPreviewXEvents.UserAcceptedAQueryPreview} when clicked with\n * the full query preview info as payload.\n *\n * It has a default slot to customize its contents.\n *\n * @public\n */\nexport default defineComponent({\n name: 'QueryPreviewButton',\n xModule: queriesPreviewXModule.name,\n components: { BaseEventButton },\n props: {\n /**\n * The information about the request of the query preview.\n *\n * @public\n */\n queryPreviewInfo: {\n type: Object as PropType<QueryPreviewInfo>,\n required: true,\n },\n /**\n * The metadata property for the request on each query preview.\n *\n * @public\n */\n metadata: {\n type: Object as PropType<Omit<WireMetadata, 'moduleName'>>,\n },\n },\n setup(props) {\n /**\n * We use the module extra params to combine them with the query preview's extra params.\n */\n const { params } = useState('queriesPreview')\n\n /**\n * The provided query preview with the base extra params from the module merged in.\n *\n * @returns The query preview info with the base extra params merged in.\n * @public\n */\n const fullQueryPreviewInfo = computed(\n (): QueryPreviewInfo => ({\n ...props.queryPreviewInfo,\n extraParams: {\n ...params.value,\n ...props.queryPreviewInfo.extraParams,\n },\n filters: props.queryPreviewInfo.filters,\n }),\n )\n\n /**\n * List of events to emit by the BaseEventButton.\n *\n * @returns An object with the event and payload.\n *\n * @internal\n */\n const events = computed(\n (): Partial<XEventsTypes> => ({ UserAcceptedAQueryPreview: fullQueryPreviewInfo.value }),\n )\n\n return {\n fullQueryPreviewInfo,\n events,\n }\n },\n})\n</script>\n\n<docs lang=\"mdx\">\n## Examples\n\n### Basic example\n\nThe component content has the query preview query as default.\n\n```vue\n<template>\n <QueryPreviewButton queryPreviewInfo=\"queryPreviewInfo\" />\n</template>\n\n<script setup>\nimport { QueryPreviewButton } from '@empathyco/x-components/queries-preview'\nimport { reactive } from 'vue'\nconst queryPreviewInfo = reactive({ query: 'shoes' })\n</script>\n```\n\n### Customizing slots\n\nThe content of the button is customizable via its default slot.\n\n```vue\n<template>\n <QueryPreviewButton queryPreviewInfo=\"queryPreviewInfo\">\n {{ `Search for: ${queryPreviewInfo.query}` }}\n </QueryPreviewButton>\n</template>\n\n<script setup>\nimport { QueryPreviewButton } from '@empathyco/x-components/queries-preview'\nimport { reactive } from 'vue'\nconst queryPreviewInfo = reactive({ query: 'shoes' })\n</script>\n```\n\n## Events\n\nA list of events that the component will emit:\n\n- `UserAcceptedAQueryPreview`: the event is emitted after the user clicks the button. The event\n payload is the `QueryPreviewInfo` of the query.\n</docs>\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwBA;;;;;;;;AAQE;AACF,gBAAe,eAAe,CAAC;AAC7B,IAAA,IAAI,EAAE,oBAAoB;IAC1B,OAAO,EAAE,qBAAqB,CAAC,IAAI;IACnC,UAAU,EAAE,EAAE,eAAc,EAAG;AAC/B,IAAA,KAAK,EAAE;AACL;;;;AAIE;AACF,QAAA,gBAAgB,EAAE;AAChB,YAAA,IAAI,EAAE,MAAoC;AAC1C,YAAA,QAAQ,EAAE,IAAI;AACf,SAAA;AACD;;;;AAIE;AACF,QAAA,QAAQ,EAAE;AACR,YAAA,IAAI,EAAE,MAAoD;AAC3D,SAAA;AACF,KAAA;AACD,IAAA,KAAK,CAAC,KAAK,EAAA;AACT;;AAEE;QACF,MAAM,EAAE,QAAO,GAAI,QAAQ,CAAC,gBAAgB,CAAA;AAE5C;;;;;AAKE;AACF,QAAA,MAAM,oBAAmB,GAAI,QAAQ,CACnC,OAAyB;YACvB,GAAG,KAAK,CAAC,gBAAgB;AACzB,YAAA,WAAW,EAAE;gBACX,GAAG,MAAM,CAAC,KAAK;AACf,gBAAA,GAAG,KAAK,CAAC,gBAAgB,CAAC,WAAW;AACtC,aAAA;AACD,YAAA,OAAO,EAAE,KAAK,CAAC,gBAAgB,CAAC,OAAO;AACxC,SAAA,CAAC,CACJ;AAEA;;;;;;AAME;AACF,QAAA,MAAM,MAAK,GAAI,QAAQ,CACrB,OAA8B,EAAE,yBAAyB,EAAE,oBAAoB,CAAC,KAAI,EAAG,CAAC,CAC1F;QAEA,OAAO;YACL,oBAAoB;YACpB,MAAM;SACR;IACF,CAAC;AACF,CAAA,CAAA;;;;"}
@@ -6,6 +6,7 @@ import '../../../components/animations/animate-translate/animate-translate.style
6
6
  import '../../../components/animations/animate-width.vue2.js';
7
7
  import '../../../components/animations/animate-width.vue3.js';
8
8
  import '../../../components/animations/change-height.vue2.js';
9
+ import '../../../components/animations/change-height.vue3.js';
9
10
  import '../../../components/animations/collapse-height.vue2.js';
10
11
  import '../../../components/animations/collapse-height.vue3.js';
11
12
  import '../../../components/animations/collapse-width.vue2.js';
@@ -1 +1 @@
1
- {"version":3,"file":"query-preview.vue2.js","sources":["../../../../../src/x-modules/queries-preview/components/query-preview.vue"],"sourcesContent":["<template>\n <!-- eslint-disable-next-line vue/no-unused-refs -->\n <section ref=\"queryPreviewElement\" class=\"x-query-preview-wrapper__default-content\">\n <ul v-if=\"hasResults\" data-test=\"query-preview\" class=\"x-query-preview\">\n <li\n v-for=\"result in queryPreviewResults?.results\"\n :key=\"result.id\"\n class=\"x-query-preview__item\"\n data-test=\"query-preview-item\"\n >\n <!--\n @slot Query Preview result slot.\n @binding {Result} result - A Query Preview result\n -->\n <slot name=\"result\" :result=\"result\">\n <span data-test=\"result-name\">{{ result.name }}</span>\n </slot>\n </li>\n </ul>\n </section>\n</template>\n\n<script lang=\"ts\">\nimport type { Filter, SearchRequest } from '@empathyco/x-types'\nimport type { PropType, Ref } from 'vue'\nimport type { FeatureLocation, QueryFeature } from '../../../types'\nimport type { DebouncedFunction } from '../../../utils'\nimport type { QueryPreviewInfo } from '../store/types'\nimport { deepEqual } from '@empathyco/x-utils'\nimport { computed, defineComponent, h, inject, onBeforeUnmount, provide, ref, watch } from 'vue'\nimport { LIST_ITEMS_KEY } from '../../../components'\nimport { useOnDisplay, useState, useXBus } from '../../../composables'\nimport { createOrigin, createRawFilters, debounceFunction } from '../../../utils'\nimport { getHashFromQueryPreviewInfo } from '../utils/get-hash-from-query-preview'\nimport { queriesPreviewXModule } from '../x-module'\n\n/**\n * Retrieves a preview of the results of a query and exposes them in the default slot,\n * along with the query preview and the totalResults of the search request.\n * By default, it renders the names of the results.\n *\n * @public\n */\nexport default defineComponent({\n name: 'QueryPreview',\n xModule: queriesPreviewXModule.name,\n props: {\n /** The information about the request of the query preview. */\n queryPreviewInfo: {\n type: Object as PropType<QueryPreviewInfo>,\n required: true,\n },\n /** The origin property for the request. */\n queryFeature: {\n type: String as PropType<QueryFeature>,\n },\n /** Number of query preview results to be rendered. */\n maxItemsToRender: {\n type: Number,\n },\n /**\n * Controls whether the query preview requests should be triggered when the component is visible in the viewport.\n */\n loadWhenVisible: {\n type: Boolean,\n default: false,\n },\n /**\n * Debounce time in milliseconds for triggering the search requests.\n * It will default to 0 to fit the most common use case (pre-search),\n * and it would work properly with a 250 value inside empathize.\n */\n debounceTimeMs: {\n type: Number,\n default: 0,\n },\n /**\n * Controls whether the QueryPreview should be removed from the state\n * when the component is destroyed.\n */\n persistInCache: {\n type: Boolean,\n default: false,\n },\n },\n emits: ['load', 'error'],\n setup(props, { emit, slots }) {\n const xBus = useXBus()\n\n /**\n * previewResults: The results preview of the queries preview cacheable mounted.\n * It is a dictionary, indexed by the query preview query.\n *\n * params: As the request is handled in this component, we need\n * the extra params that will be used in the request.\n *\n * config: As the request is handled in this component, we need\n * the config that will be used in the request.\n */\n const { queriesPreview: previewResults, params, config } = useState('queriesPreview')\n\n /**\n * Template ref for the root element to track visibility.\n */\n const queryPreviewElement = ref<HTMLElement | null>(null)\n\n /**\n * Query Preview key converted into a unique id.\n *\n * @returns The query hash.\n */\n const queryPreviewHash = computed(() =>\n getHashFromQueryPreviewInfo(props.queryPreviewInfo, {\n ...params.value,\n ...props.queryPreviewInfo.extraParams,\n }),\n )\n\n provide('queryPreviewHash', queryPreviewHash)\n\n /**\n * Gets from the state the results preview of the query preview.\n *\n * @returns The results preview of the actual query preview.\n */\n const queryPreviewResults = computed(() => {\n const resultsPreview = previewResults.value[queryPreviewHash.value]\n return resultsPreview?.results\n ? {\n ...resultsPreview,\n results: resultsPreview.results.slice(0, props.maxItemsToRender),\n }\n : undefined\n })\n\n /**\n * The results to render from the state.\n *\n * @remarks The results list are provided with `items` key. It can be\n * concatenated with list items from components such as `BannersList`, `PromotedsList`,\n * `BaseGrid` or any component that injects the list.\n *\n * @returns A list of results.\n */\n const results = computed(() => queryPreviewResults.value?.results)\n provide(LIST_ITEMS_KEY as string, results)\n\n /**\n * It injects the provided {@link FeatureLocation} of the selected query in the search request.\n *\n * @internal\n */\n const injectedLocation = inject<Ref<FeatureLocation> | FeatureLocation>('location', 'none')\n const location =\n typeof injectedLocation === 'object' && 'value' in injectedLocation\n ? injectedLocation.value\n : injectedLocation\n\n /**\n * The computed request object to be used to retrieve the query preview results.\n *\n * @returns The search request object.\n */\n const queryPreviewRequest = computed<SearchRequest>(() => {\n const origin = createOrigin({\n feature: props.queryFeature,\n location,\n })\n const filters = props.queryPreviewInfo.filters?.reduce(\n (filtersList, filterId) => {\n const facetId = filterId.split(':')[0]\n const rawFilter = createRawFilters([filterId])[0]\n filtersList[facetId] = filtersList[facetId]\n ? filtersList[facetId].concat(rawFilter)\n : [rawFilter]\n\n return filtersList\n },\n {} as Record<string, Filter[]>,\n )\n\n return {\n query: props.queryPreviewInfo.query,\n rows: config.value.maxItemsToRequest,\n extraParams: {\n ...params.value,\n ...props.queryPreviewInfo.extraParams,\n },\n filters,\n ...(origin && { origin }),\n }\n })\n\n /**\n * The debounce method to trigger the request after the debounceTimeMs defined\n * for cacheable queries.\n *\n * @returns The search request object.\n */\n const emitQueryPreviewRequestUpdated = computed<DebouncedFunction<[SearchRequest]>>(() =>\n debounceFunction(request => {\n xBus.emit('QueryPreviewRequestUpdated', request, { priority: 0, replaceable: false })\n }, props.debounceTimeMs),\n )\n\n /**\n * Initialises watcher to emit debounced requests, and first value for the requests.\n *\n * @internal\n */\n watch(queryPreviewRequest, (newRequest, oldRequest) => {\n if (!deepEqual(newRequest, oldRequest) && !props.loadWhenVisible) {\n emitQueryPreviewRequestUpdated.value(newRequest)\n }\n })\n\n const cachedQueryPreview = previewResults.value[queryPreviewHash.value]\n\n // If the query has been saved it will emit load instead of the emitting the updated request.\n if (cachedQueryPreview?.status === 'success') {\n emit('load', queryPreviewHash.value)\n xBus.emit('QueryPreviewMounted', queryPreviewHash.value, {\n priority: 0,\n replaceable: false,\n })\n } else if (!props.loadWhenVisible) {\n emitQueryPreviewRequestUpdated.value(queryPreviewRequest.value)\n }\n\n /**\n * Watch element visibility and emit request when it becomes visible for the first time\n * (only when loadWhenVisible is true).\n */\n const { unwatchDisplay } = useOnDisplay({\n element: queryPreviewElement,\n callback: () => {\n if (props.loadWhenVisible && cachedQueryPreview?.status !== 'success') {\n emitQueryPreviewRequestUpdated.value(queryPreviewRequest.value)\n }\n },\n })\n\n /**\n * Cancels the (remaining) requests when the component is destroyed\n * via the `debounce.cancel()` method.\n * If the prop 'persistInCache' is set to false, it also removes the QueryPreview\n * from the state when the component is destroyed.\n */\n onBeforeUnmount(() => {\n unwatchDisplay?.()\n emitQueryPreviewRequestUpdated.value.cancel()\n xBus.emit(\n 'QueryPreviewUnmounted',\n { queryPreviewHash: queryPreviewHash.value, cache: props.persistInCache },\n {\n priority: 0,\n replaceable: false,\n },\n )\n })\n\n /**\n * Cancels the previous request when the debounced function changes (e.g: the debounceTimeMs\n * prop changes or there is a request in progress that cancels it).\n *\n * @param _new - The new debounced function.\n * @param old - The previous debounced function.\n * @internal\n */\n watch(\n emitQueryPreviewRequestUpdated,\n (_new: DebouncedFunction<[SearchRequest]>, old: DebouncedFunction<[SearchRequest]>) => {\n old.cancel()\n },\n )\n\n const queryPreviewResultsStatus = computed(() => queryPreviewResults.value?.status)\n\n /**\n * Emits an event when the query results are loaded or fail to load.\n *\n * @param status - The status of the query preview request.\n */\n watch(queryPreviewResultsStatus, () => {\n if (queryPreviewResultsStatus.value === 'success') {\n emit(results.value?.length ? 'load' : 'error', queryPreviewHash.value)\n } else if (queryPreviewResultsStatus.value === 'error') {\n emit('error', queryPreviewHash.value)\n }\n })\n\n const hasResults = computed(() => (queryPreviewResults.value?.totalResults ?? 0) > 0)\n\n /**\n * Render function to execute the `default` slot, binding `slotsProps` and getting only the\n * first `vNode` to avoid Fragments and Text root nodes.\n *\n * @remarks `slotProps` must be values without Vue reactivity and located inside the\n * render-function to update the binding data properly.\n *\n * @returns The root `vNode` of the `default` slot or empty string if there are no results. Always wrapped in a section\n * element with the `x-query-preview-wrapper__slot-content` class.\n */\n function renderDefaultSlot() {\n const slotProps = {\n queryPreviewInfo: props.queryPreviewInfo,\n results: queryPreviewResults.value?.results,\n totalResults: queryPreviewResults.value?.totalResults,\n displayTagging: queryPreviewResults.value?.displayTagging,\n queryTagging: queryPreviewResults.value?.queryTagging,\n }\n\n const slotContent = hasResults.value ? slots.default?.(slotProps)[0] : ''\n\n return h(\n 'section',\n { ref: queryPreviewElement, class: 'x-query-preview-wrapper__slot-content' },\n [slotContent],\n )\n }\n\n /* Hack to render through a render-function, the `default` slot or, in its absence,\n the component itself. It is the alternative for the NoElement antipattern. */\n const componentProps = { hasResults, queryPreviewResults, queryPreviewElement }\n return (slots.default ? renderDefaultSlot : componentProps) as typeof componentProps\n },\n})\n</script>\n<docs lang=\"mdx\">\n## Events\n\nA list of events that the component will emit:\n\n- [`QueryPreviewRequestUpdated`](https://github.com/empathyco/x/blob/main/packages/x-components/src/wiring/events.types.ts):\n the event is emitted when the component is mounted and when the properties of the request object\n changes. The event payload is the `queryPreviewRequest` object.\n\n## Vue Events\n\nA list of vue events that the component will emit:\n\n- `load`: the event is emitted when the query results have been loaded.\n- `error`: the event is emitted if there is some error when retrieving the query results.\n\n## See it in action\n\nHere you have a basic example of how the QueryPreview is rendered. Keep in mind that this component\nis intended to be used overriding its default slot. By default it will only render the names of the\nresults.\n\n```vue live\n<template>\n <QueryPreview :queryPreviewInfo=\"queryPreviewInfo\" />\n</template>\n\n<script setup>\nimport { QueryPreview } from '@empathyco/x-components/queries-preview'\nimport { reactive } from 'vue'\nconst queryPreviewInfo = reactive({ query: 'sandals' })\n</script>\n```\n\n### Play with the default slot\n\nIn this example, the results will be rendered inside a sliding panel.\n\n```vue live\n<template>\n <QueryPreview :queryPreviewInfo=\"queryPreviewInfo\" #default=\"{ totalResults, results }\">\n <section>\n <p>Total results: {{ totalResults }}</p>\n <SlidingPanel :resetOnContentChange=\"false\">\n <article\n v-for=\"result in results\"\n :key=\"result.id\"\n class=\"x-result\"\n style=\"max-width: 300px; overflow: hidden\"\n >\n <BaseResultLink :result=\"result\">\n <BaseResultImage :result=\"result\" class=\"x-result__picture\" />\n </BaseResultLink>\n <div class=\"x-result__description\">\n <BaseResultLink :result=\"result\">\n <h1 class=\"x-title3\">{{ result.name }}</h1>\n </BaseResultLink>\n </div>\n </article>\n </SlidingPanel>\n </section>\n </QueryPreview>\n</template>\n\n<script setup>\nimport { QueryPreview } from '@empathyco/x-components/queries-preview'\nimport { BaseResultImage, BaseResultLink, SlidingPanel } from '@empathyco/x-components'\nimport { reactive } from 'vue'\nconst queryPreviewInfo = reactive({ query: 'flip-flops' })\n</script>\n```\n\n### Play with the result slot\n\nThe component exposes a slot to override the result content, without modifying the list.\n\nIn this example, the ID of the results will be rendered along with the name.\n\n```vue\n<template>\n <QueryPreview :queryPreviewInfo=\"queryPreviewInfo\" #result=\"{ result }\">\n <span>{{ result.id }}</span>\n <span>{{ result.name }}</span>\n </QueryPreview>\n</template>\n\n<script setup>\nimport { QueryPreview } from '@empathyco/x-components/queries-preview'\nimport { reactive } from 'vue'\nconst queryPreviewInfo = reactive({ query: 'flip-flops' })\n</script>\n```\n\n### Play with props\n\nIn this example, the query preview has been limited to render a maximum of 4 results.\n\n```vue\n<template>\n <QueryPreview\n :maxItemsToRender=\"maxItemsToRender\"\n :queryPreviewInfo=\"queryPreviewInfo\"\n #default=\"{ results }\"\n >\n <BaseGrid #default=\"{ item }\" :items=\"results\">\n <BaseResultLink :result=\"item\">\n <BaseResultImage :result=\"item\" />\n </BaseResultLink>\n </BaseGrid>\n </QueryPreview>\n</template>\n\n<script setup>\nimport { BaseGrid, BaseResultImage, BaseResultLink } from '@empathyco/x-components'\nimport { QueryPreview } from '@empathyco/x-components/queries-preview'\nimport { reactive } from 'vue'\nconst maxItemsToRender = 4\nconst queryPreviewInfo = reactive({ query: 'flip-flops' })\n</script>\n```\n</docs>\n"],"names":["debounceFunction"],"mappings":"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"}
1
+ {"version":3,"file":"query-preview.vue2.js","sources":["../../../../../src/x-modules/queries-preview/components/query-preview.vue"],"sourcesContent":["<template>\n <!-- eslint-disable-next-line vue/no-unused-refs -->\n <section ref=\"queryPreviewElement\" class=\"x-query-preview-wrapper__default-content\">\n <ul v-if=\"hasResults\" data-test=\"query-preview\" class=\"x-query-preview\">\n <li\n v-for=\"result in queryPreviewResults?.results\"\n :key=\"result.id\"\n class=\"x-query-preview__item\"\n data-test=\"query-preview-item\"\n >\n <!--\n @slot Query Preview result slot.\n @binding {Result} result - A Query Preview result\n -->\n <slot name=\"result\" :result=\"result\">\n <span data-test=\"result-name\">{{ result.name }}</span>\n </slot>\n </li>\n </ul>\n </section>\n</template>\n\n<script lang=\"ts\">\nimport type { Filter, SearchRequest } from '@empathyco/x-types'\nimport type { PropType, Ref } from 'vue'\nimport type { FeatureLocation, QueryFeature } from '../../../types'\nimport type { DebouncedFunction } from '../../../utils'\nimport type { QueryPreviewInfo } from '../store/types'\nimport { deepEqual } from '@empathyco/x-utils'\nimport { computed, defineComponent, h, inject, onBeforeUnmount, provide, ref, watch } from 'vue'\nimport { LIST_ITEMS_KEY } from '../../../components'\nimport { useOnDisplay, useState, useXBus } from '../../../composables'\nimport { createOrigin, createRawFilters, debounceFunction } from '../../../utils'\nimport { getHashFromQueryPreviewInfo } from '../utils/get-hash-from-query-preview'\nimport { queriesPreviewXModule } from '../x-module'\n\n/**\n * Retrieves a preview of the results of a query and exposes them in the default slot,\n * along with the query preview and the totalResults of the search request.\n * By default, it renders the names of the results.\n *\n * @public\n */\nexport default defineComponent({\n name: 'QueryPreview',\n xModule: queriesPreviewXModule.name,\n props: {\n /** The information about the request of the query preview. */\n queryPreviewInfo: {\n type: Object as PropType<QueryPreviewInfo>,\n required: true,\n },\n /** The origin property for the request. */\n queryFeature: {\n type: String as PropType<QueryFeature>,\n },\n /** Number of query preview results to be rendered. */\n maxItemsToRender: {\n type: Number,\n },\n /**\n * Controls whether the query preview requests should be triggered when the component is visible in the viewport.\n */\n loadWhenVisible: {\n type: Boolean,\n default: false,\n },\n /**\n * Debounce time in milliseconds for triggering the search requests.\n * It will default to 0 to fit the most common use case (pre-search),\n * and it would work properly with a 250 value inside empathize.\n */\n debounceTimeMs: {\n type: Number,\n default: 0,\n },\n /**\n * Controls whether the QueryPreview should be removed from the state\n * when the component is destroyed.\n */\n persistInCache: {\n type: Boolean,\n default: false,\n },\n },\n emits: ['load', 'error'],\n setup(props, { emit, slots }) {\n const xBus = useXBus()\n\n /**\n * previewResults: The results preview of the queries preview cacheable mounted.\n * It is a dictionary, indexed by the query preview query.\n *\n * params: As the request is handled in this component, we need\n * the extra params that will be used in the request.\n *\n * config: As the request is handled in this component, we need\n * the config that will be used in the request.\n */\n const { queriesPreview: previewResults, params, config } = useState('queriesPreview')\n\n /**\n * Template ref for the root element to track visibility.\n */\n const queryPreviewElement = ref<HTMLElement | null>(null)\n\n /**\n * Query Preview key converted into a unique id.\n *\n * @returns The query hash.\n */\n const queryPreviewHash = computed(() =>\n getHashFromQueryPreviewInfo(props.queryPreviewInfo, {\n ...params.value,\n ...props.queryPreviewInfo.extraParams,\n }),\n )\n\n provide('queryPreviewHash', queryPreviewHash)\n\n /**\n * Gets from the state the results preview of the query preview.\n *\n * @returns The results preview of the actual query preview.\n */\n const queryPreviewResults = computed(() => {\n const resultsPreview = previewResults.value[queryPreviewHash.value]\n return resultsPreview?.results\n ? {\n ...resultsPreview,\n results: resultsPreview.results.slice(0, props.maxItemsToRender),\n }\n : undefined\n })\n\n /**\n * The results to render from the state.\n *\n * @remarks The results list are provided with `items` key. It can be\n * concatenated with list items from components such as `BannersList`, `PromotedsList`,\n * `BaseGrid` or any component that injects the list.\n *\n * @returns A list of results.\n */\n const results = computed(() => queryPreviewResults.value?.results)\n provide(LIST_ITEMS_KEY as string, results)\n\n /**\n * It injects the provided {@link FeatureLocation} of the selected query in the search request.\n *\n * @internal\n */\n const injectedLocation = inject<Ref<FeatureLocation> | FeatureLocation>('location', 'none')\n const location =\n typeof injectedLocation === 'object' && 'value' in injectedLocation\n ? injectedLocation.value\n : injectedLocation\n\n /**\n * The computed request object to be used to retrieve the query preview results.\n *\n * @returns The search request object.\n */\n const queryPreviewRequest = computed<SearchRequest>(() => {\n const origin = createOrigin({\n feature: props.queryFeature,\n location,\n })\n const filters = props.queryPreviewInfo.filters?.reduce(\n (filtersList, filterId) => {\n const facetId = filterId.split(':')[0]\n const rawFilter = createRawFilters([filterId])[0]\n filtersList[facetId] = filtersList[facetId]\n ? filtersList[facetId].concat(rawFilter)\n : [rawFilter]\n\n return filtersList\n },\n {} as Record<string, Filter[]>,\n )\n\n return {\n query: props.queryPreviewInfo.query,\n rows: config.value.maxItemsToRequest,\n extraParams: {\n ...params.value,\n ...props.queryPreviewInfo.extraParams,\n },\n filters,\n ...(origin && { origin }),\n }\n })\n\n /**\n * The debounce method to trigger the request after the debounceTimeMs defined\n * for cacheable queries.\n *\n * @returns The search request object.\n */\n const emitQueryPreviewRequestUpdated = computed<DebouncedFunction<[SearchRequest]>>(() =>\n debounceFunction(request => {\n xBus.emit('QueryPreviewRequestUpdated', request, { priority: 0, replaceable: false })\n }, props.debounceTimeMs),\n )\n\n /**\n * Initialises watcher to emit debounced requests, and first value for the requests.\n *\n * @internal\n */\n watch(queryPreviewRequest, (newRequest, oldRequest) => {\n if (!deepEqual(newRequest, oldRequest) && !props.loadWhenVisible) {\n emitQueryPreviewRequestUpdated.value(newRequest)\n }\n })\n\n const cachedQueryPreview = previewResults.value[queryPreviewHash.value]\n\n // If the query has been saved it will emit load instead of the emitting the updated request.\n if (cachedQueryPreview?.status === 'success') {\n emit('load', queryPreviewHash.value)\n xBus.emit('QueryPreviewMounted', queryPreviewHash.value, {\n priority: 0,\n replaceable: false,\n })\n } else if (!props.loadWhenVisible) {\n emitQueryPreviewRequestUpdated.value(queryPreviewRequest.value)\n }\n\n /**\n * Watch element visibility and emit request when it becomes visible for the first time\n * (only when loadWhenVisible is true).\n */\n const { unwatchDisplay } = useOnDisplay({\n element: queryPreviewElement,\n callback: () => {\n if (props.loadWhenVisible && cachedQueryPreview?.status !== 'success') {\n emitQueryPreviewRequestUpdated.value(queryPreviewRequest.value)\n }\n },\n })\n\n /**\n * Cancels the (remaining) requests when the component is destroyed\n * via the `debounce.cancel()` method.\n * If the prop 'persistInCache' is set to false, it also removes the QueryPreview\n * from the state when the component is destroyed.\n */\n onBeforeUnmount(() => {\n unwatchDisplay?.()\n emitQueryPreviewRequestUpdated.value.cancel()\n xBus.emit(\n 'QueryPreviewUnmounted',\n { queryPreviewHash: queryPreviewHash.value, cache: props.persistInCache },\n {\n priority: 0,\n replaceable: false,\n },\n )\n })\n\n /**\n * Cancels the previous request when the debounced function changes (e.g: the debounceTimeMs\n * prop changes or there is a request in progress that cancels it).\n *\n * @param _new - The new debounced function.\n * @param old - The previous debounced function.\n * @internal\n */\n watch(\n emitQueryPreviewRequestUpdated,\n (_new: DebouncedFunction<[SearchRequest]>, old: DebouncedFunction<[SearchRequest]>) => {\n old.cancel()\n },\n )\n\n const queryPreviewResultsStatus = computed(() => queryPreviewResults.value?.status)\n\n /**\n * Emits an event when the query results are loaded or fail to load.\n *\n * @param status - The status of the query preview request.\n */\n watch(queryPreviewResultsStatus, () => {\n if (queryPreviewResultsStatus.value === 'success') {\n emit(results.value?.length ? 'load' : 'error', queryPreviewHash.value)\n } else if (queryPreviewResultsStatus.value === 'error') {\n emit('error', queryPreviewHash.value)\n }\n })\n\n const hasResults = computed(() => (queryPreviewResults.value?.totalResults ?? 0) > 0)\n\n /**\n * Render function to execute the `default` slot, binding `slotsProps` and getting only the\n * first `vNode` to avoid Fragments and Text root nodes.\n *\n * @remarks `slotProps` must be values without Vue reactivity and located inside the\n * render-function to update the binding data properly.\n *\n * @returns The root `vNode` of the `default` slot or empty string if there are no results. Always wrapped in a section\n * element with the `x-query-preview-wrapper__slot-content` class.\n */\n function renderDefaultSlot() {\n const slotProps = {\n queryPreviewInfo: props.queryPreviewInfo,\n results: queryPreviewResults.value?.results,\n totalResults: queryPreviewResults.value?.totalResults,\n displayTagging: queryPreviewResults.value?.displayTagging,\n queryTagging: queryPreviewResults.value?.queryTagging,\n }\n\n const slotContent = hasResults.value ? slots.default?.(slotProps)[0] : ''\n\n return h(\n 'section',\n { ref: queryPreviewElement, class: 'x-query-preview-wrapper__slot-content' },\n [slotContent],\n )\n }\n\n /* Hack to render through a render-function, the `default` slot or, in its absence,\n the component itself. It is the alternative for the NoElement antipattern. */\n const componentProps = { hasResults, queryPreviewResults, queryPreviewElement }\n return (slots.default ? renderDefaultSlot : componentProps) as typeof componentProps\n },\n})\n</script>\n<docs lang=\"mdx\">\n## Events\n\nA list of events that the component will emit:\n\n- [`QueryPreviewRequestUpdated`](https://github.com/empathyco/x/blob/main/packages/x-components/src/wiring/events.types.ts):\n the event is emitted when the component is mounted and when the properties of the request object\n changes. The event payload is the `queryPreviewRequest` object.\n\n## Vue Events\n\nA list of vue events that the component will emit:\n\n- `load`: the event is emitted when the query results have been loaded.\n- `error`: the event is emitted if there is some error when retrieving the query results.\n\n## See it in action\n\nHere you have a basic example of how the QueryPreview is rendered. Keep in mind that this component\nis intended to be used overriding its default slot. By default it will only render the names of the\nresults.\n\n```vue live\n<template>\n <QueryPreview :queryPreviewInfo=\"queryPreviewInfo\" />\n</template>\n\n<script setup>\nimport { QueryPreview } from '@empathyco/x-components/queries-preview'\nimport { reactive } from 'vue'\nconst queryPreviewInfo = reactive({ query: 'sandals' })\n</script>\n```\n\n### Play with the default slot\n\nIn this example, the results will be rendered inside a sliding panel.\n\n```vue live\n<template>\n <QueryPreview :queryPreviewInfo=\"queryPreviewInfo\" #default=\"{ totalResults, results }\">\n <section>\n <p>Total results: {{ totalResults }}</p>\n <SlidingPanel :resetOnContentChange=\"false\">\n <article\n v-for=\"result in results\"\n :key=\"result.id\"\n class=\"x-result\"\n style=\"max-width: 300px; overflow: hidden\"\n >\n <BaseResultLink :result=\"result\">\n <BaseResultImage :result=\"result\" class=\"x-result__picture\" />\n </BaseResultLink>\n <div class=\"x-result__description\">\n <BaseResultLink :result=\"result\">\n <h1 class=\"x-title3\">{{ result.name }}</h1>\n </BaseResultLink>\n </div>\n </article>\n </SlidingPanel>\n </section>\n </QueryPreview>\n</template>\n\n<script setup>\nimport { QueryPreview } from '@empathyco/x-components/queries-preview'\nimport { BaseResultImage, BaseResultLink, SlidingPanel } from '@empathyco/x-components'\nimport { reactive } from 'vue'\nconst queryPreviewInfo = reactive({ query: 'flip-flops' })\n</script>\n```\n\n### Play with the result slot\n\nThe component exposes a slot to override the result content, without modifying the list.\n\nIn this example, the ID of the results will be rendered along with the name.\n\n```vue\n<template>\n <QueryPreview :queryPreviewInfo=\"queryPreviewInfo\" #result=\"{ result }\">\n <span>{{ result.id }}</span>\n <span>{{ result.name }}</span>\n </QueryPreview>\n</template>\n\n<script setup>\nimport { QueryPreview } from '@empathyco/x-components/queries-preview'\nimport { reactive } from 'vue'\nconst queryPreviewInfo = reactive({ query: 'flip-flops' })\n</script>\n```\n\n### Play with props\n\nIn this example, the query preview has been limited to render a maximum of 4 results.\n\n```vue\n<template>\n <QueryPreview\n :maxItemsToRender=\"maxItemsToRender\"\n :queryPreviewInfo=\"queryPreviewInfo\"\n #default=\"{ results }\"\n >\n <BaseGrid #default=\"{ item }\" :items=\"results\">\n <BaseResultLink :result=\"item\">\n <BaseResultImage :result=\"item\" />\n </BaseResultLink>\n </BaseGrid>\n </QueryPreview>\n</template>\n\n<script setup>\nimport { BaseGrid, BaseResultImage, BaseResultLink } from '@empathyco/x-components'\nimport { QueryPreview } from '@empathyco/x-components/queries-preview'\nimport { reactive } from 'vue'\nconst maxItemsToRender = 4\nconst queryPreviewInfo = reactive({ query: 'flip-flops' })\n</script>\n```\n</docs>\n"],"names":["debounceFunction"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoCA;;;;;;AAME;AACF,gBAAe,eAAe,CAAC;AAC7B,IAAA,IAAI,EAAE,cAAc;IACpB,OAAO,EAAE,qBAAqB,CAAC,IAAI;AACnC,IAAA,KAAK,EAAE;;AAEL,QAAA,gBAAgB,EAAE;AAChB,YAAA,IAAI,EAAE,MAAoC;AAC1C,YAAA,QAAQ,EAAE,IAAI;AACf,SAAA;;AAED,QAAA,YAAY,EAAE;AACZ,YAAA,IAAI,EAAE,MAAgC;AACvC,SAAA;;AAED,QAAA,gBAAgB,EAAE;AAChB,YAAA,IAAI,EAAE,MAAM;AACb,SAAA;AACD;;AAEE;AACF,QAAA,eAAe,EAAE;AACf,YAAA,IAAI,EAAE,OAAO;AACb,YAAA,OAAO,EAAE,KAAK;AACf,SAAA;AACD;;;;AAIE;AACF,QAAA,cAAc,EAAE;AACd,YAAA,IAAI,EAAE,MAAM;AACZ,YAAA,OAAO,EAAE,CAAC;AACX,SAAA;AACD;;;AAGE;AACF,QAAA,cAAc,EAAE;AACd,YAAA,IAAI,EAAE,OAAO;AACb,YAAA,OAAO,EAAE,KAAK;AACf,SAAA;AACF,KAAA;AACD,IAAA,KAAK,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC;AACxB,IAAA,KAAK,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,KAAI,EAAG,EAAA;AAC1B,QAAA,MAAM,IAAG,GAAI,OAAO,EAAC;AAErB;;;;;;;;;AASE;AACF,QAAA,MAAM,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,EAAE,MAAK,KAAM,QAAQ,CAAC,gBAAgB,CAAA;AAEpF;;AAEE;AACF,QAAA,MAAM,mBAAkB,GAAI,GAAG,CAAqB,IAAI,CAAA;AAExD;;;;AAIE;AACF,QAAA,MAAM,gBAAe,GAAI,QAAQ,CAAC,MAChC,2BAA2B,CAAC,KAAK,CAAC,gBAAgB,EAAE;YAClD,GAAG,MAAM,CAAC,KAAK;AACf,YAAA,GAAG,KAAK,CAAC,gBAAgB,CAAC,WAAW;AACtC,SAAA,CAAC,CACJ;AAEA,QAAA,OAAO,CAAC,kBAAkB,EAAE,gBAAgB,CAAA;AAE5C;;;;AAIE;AACF,QAAA,MAAM,mBAAkB,GAAI,QAAQ,CAAC,MAAI;YACvC,MAAM,cAAa,GAAI,cAAc,CAAC,KAAK,CAAC,gBAAgB,CAAC,KAAK,CAAA;YAClE,OAAO,cAAc,EAAE;AACrB,kBAAE;AACE,oBAAA,GAAG,cAAc;AACjB,oBAAA,OAAO,EAAE,cAAc,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,gBAAgB,CAAC;AAClE;kBACA,SAAQ;AACd,QAAA,CAAC,CAAA;AAED;;;;;;;;AAQE;AACF,QAAA,MAAM,OAAM,GAAI,QAAQ,CAAC,MAAM,mBAAmB,CAAC,KAAK,EAAE,OAAO,CAAA;AACjE,QAAA,OAAO,CAAC,cAAwB,EAAE,OAAO,CAAA;AAEzC;;;;AAIE;QACF,MAAM,gBAAe,GAAI,MAAM,CAAyC,UAAU,EAAE,MAAM,CAAA;QAC1F,MAAM,QAAO,GACX,OAAO,gBAAe,KAAM,QAAO,IAAK,OAAM,IAAK;cAC/C,gBAAgB,CAAC;cACjB,gBAAe;AAErB;;;;AAIE;AACF,QAAA,MAAM,mBAAkB,GAAI,QAAQ,CAAgB,MAAI;YACtD,MAAM,MAAK,GAAI,YAAY,CAAC;gBAC1B,OAAO,EAAE,KAAK,CAAC,YAAY;gBAC3B,QAAQ;AACT,aAAA,CAAA;AACD,YAAA,MAAM,OAAM,GAAI,KAAK,CAAC,gBAAgB,CAAC,OAAO,EAAE,MAAM,CACpD,CAAC,WAAW,EAAE,QAAQ,KAAG;gBACvB,MAAM,OAAM,GAAI,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAA;gBACrC,MAAM,YAAY,gBAAgB,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAA;AAChD,gBAAA,WAAW,CAAC,OAAO,CAAA,GAAI,WAAW,CAAC,OAAO;sBACtC,WAAW,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,SAAS;AACvC,sBAAE,CAAC,SAAS,CAAA;AAEd,gBAAA,OAAO,WAAU;YACnB,CAAC,EACD,EAA8B,CAChC;YAEA,OAAO;AACL,gBAAA,KAAK,EAAE,KAAK,CAAC,gBAAgB,CAAC,KAAK;AACnC,gBAAA,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,iBAAiB;AACpC,gBAAA,WAAW,EAAE;oBACX,GAAG,MAAM,CAAC,KAAK;AACf,oBAAA,GAAG,KAAK,CAAC,gBAAgB,CAAC,WAAW;AACtC,iBAAA;gBACD,OAAO;AACP,gBAAA,IAAI,MAAK,IAAK,EAAE,MAAK,EAAG,CAAC;aAC3B;AACF,QAAA,CAAC,CAAA;AAED;;;;;AAKE;QACF,MAAM,8BAA6B,GAAI,QAAQ,CAAqC,MAClFA,QAAgB,CAAC,OAAM,IAAG;AACxB,YAAA,IAAI,CAAC,IAAI,CAAC,4BAA4B,EAAE,OAAO,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,WAAW,EAAE,OAAO,CAAA;AACtF,QAAA,CAAC,EAAE,KAAK,CAAC,cAAc,CAAC,CAC1B;AAEA;;;;AAIE;QACF,KAAK,CAAC,mBAAmB,EAAE,CAAC,UAAU,EAAE,UAAU,KAAG;AACnD,YAAA,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,UAAU,CAAA,IAAK,CAAC,KAAK,CAAC,eAAe,EAAE;AAChE,gBAAA,8BAA8B,CAAC,KAAK,CAAC,UAAU,CAAA;YACjD;AACF,QAAA,CAAC,CAAA;QAED,MAAM,kBAAiB,GAAI,cAAc,CAAC,KAAK,CAAC,gBAAgB,CAAC,KAAK,CAAA;;AAGtE,QAAA,IAAI,kBAAkB,EAAE,MAAK,KAAM,SAAS,EAAE;AAC5C,YAAA,IAAI,CAAC,MAAM,EAAE,gBAAgB,CAAC,KAAK,CAAA;YACnC,IAAI,CAAC,IAAI,CAAC,qBAAqB,EAAE,gBAAgB,CAAC,KAAK,EAAE;AACvD,gBAAA,QAAQ,EAAE,CAAC;AACX,gBAAA,WAAW,EAAE,KAAK;AACnB,aAAA,CAAA;QACH;AAAO,aAAA,IAAI,CAAC,KAAK,CAAC,eAAe,EAAE;AACjC,YAAA,8BAA8B,CAAC,KAAK,CAAC,mBAAmB,CAAC,KAAK,CAAA;QAChE;AAEA;;;AAGE;AACF,QAAA,MAAM,EAAE,cAAa,EAAE,GAAI,YAAY,CAAC;AACtC,YAAA,OAAO,EAAE,mBAAmB;YAC5B,QAAQ,EAAE,MAAI;gBACZ,IAAI,KAAK,CAAC,eAAc,IAAK,kBAAkB,EAAE,MAAK,KAAM,SAAS,EAAE;AACrE,oBAAA,8BAA8B,CAAC,KAAK,CAAC,mBAAmB,CAAC,KAAK,CAAA;gBAChE;YACF,CAAC;AACF,SAAA,CAAA;AAED;;;;;AAKE;QACF,eAAe,CAAC,MAAI;YAClB,cAAc,IAAG;AACjB,YAAA,8BAA8B,CAAC,KAAK,CAAC,MAAM,EAAC;AAC5C,YAAA,IAAI,CAAC,IAAI,CACP,uBAAuB,EACvB,EAAE,gBAAgB,EAAE,gBAAgB,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,gBAAgB,EACzE;AACE,gBAAA,QAAQ,EAAE,CAAC;AACX,gBAAA,WAAW,EAAE,KAAK;AACnB,aAAA,CACH;AACF,QAAA,CAAC,CAAA;AAED;;;;;;;AAOE;QACF,KAAK,CACH,8BAA8B,EAC9B,CAAC,IAAwC,EAAE,GAAuC,KAAG;YACnF,GAAG,CAAC,MAAM,EAAC;AACb,QAAA,CAAC,CACH;AAEA,QAAA,MAAM,yBAAwB,GAAI,QAAQ,CAAC,MAAM,mBAAmB,CAAC,KAAK,EAAE,MAAM,CAAA;AAElF;;;;AAIE;AACF,QAAA,KAAK,CAAC,yBAAyB,EAAE,MAAI;AACnC,YAAA,IAAI,yBAAyB,CAAC,KAAI,KAAM,SAAS,EAAE;AACjD,gBAAA,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,MAAK,GAAI,MAAK,GAAI,OAAO,EAAE,gBAAgB,CAAC,KAAK,CAAA;YACvE;AAAO,iBAAA,IAAI,yBAAyB,CAAC,UAAU,OAAO,EAAE;AACtD,gBAAA,IAAI,CAAC,OAAO,EAAE,gBAAgB,CAAC,KAAK,CAAA;YACtC;AACF,QAAA,CAAC,CAAA;AAED,QAAA,MAAM,UAAS,GAAI,QAAQ,CAAC,MAAM,CAAC,mBAAmB,CAAC,KAAK,EAAE,gBAAgB,CAAC,IAAI,CAAC,CAAA;AAEpF;;;;;;;;;AASE;AACF,QAAA,SAAS,iBAAiB,GAAA;AACxB,YAAA,MAAM,YAAY;gBAChB,gBAAgB,EAAE,KAAK,CAAC,gBAAgB;AACxC,gBAAA,OAAO,EAAE,mBAAmB,CAAC,KAAK,EAAE,OAAO;AAC3C,gBAAA,YAAY,EAAE,mBAAmB,CAAC,KAAK,EAAE,YAAY;AACrD,gBAAA,cAAc,EAAE,mBAAmB,CAAC,KAAK,EAAE,cAAc;AACzD,gBAAA,YAAY,EAAE,mBAAmB,CAAC,KAAK,EAAE,YAAY;aACvD;YAEA,MAAM,WAAU,GAAI,UAAU,CAAC,KAAI,GAAI,KAAK,CAAC,OAAO,GAAG,SAAS,CAAC,CAAC,CAAC,CAAA,GAAI,EAAC;AAExE,YAAA,OAAO,CAAC,CACN,SAAS,EACT,EAAE,GAAG,EAAE,mBAAmB,EAAE,KAAK,EAAE,yCAAyC,EAC5E,CAAC,WAAW,CAAC,CACf;QACF;AAEA;AAC+E;QAC/E,MAAM,cAAa,GAAI,EAAE,UAAU,EAAE,mBAAmB,EAAE,mBAAkB,EAAE;AAC9E,QAAA,QAAQ,KAAK,CAAC,OAAM,GAAI,iBAAgB,GAAI,cAAc;IAC5D,CAAC;AACF,CAAA,CAAA;;;;"}
@@ -5,6 +5,7 @@ import '../../../components/animations/animate-translate/animate-translate.style
5
5
  import '../../../components/animations/animate-width.vue2.js';
6
6
  import '../../../components/animations/animate-width.vue3.js';
7
7
  import '../../../components/animations/change-height.vue2.js';
8
+ import '../../../components/animations/change-height.vue3.js';
8
9
  import '../../../components/animations/collapse-height.vue2.js';
9
10
  import '../../../components/animations/collapse-height.vue3.js';
10
11
  import '../../../components/animations/collapse-width.vue2.js';
@@ -1 +1 @@
1
- {"version":3,"file":"scroll-to-top.vue2.js","sources":["../../../../../src/x-modules/scroll/components/scroll-to-top.vue"],"sourcesContent":["<template>\n <component :is=\"animation\">\n <BaseEventButton\n v-if=\"isVisible\"\n class=\"x-scroll-to-top x-button\"\n data-test=\"scroll-to-top\"\n aria-label=\"Scroll to top\"\n :events=\"events\"\n >\n <!-- @slot (Required) Button content with a text, an icon or both -->\n <slot />\n </BaseEventButton>\n </component>\n</template>\n\n<script lang=\"ts\">\nimport type { XEventsTypes } from '../../../wiring'\nimport { computed, defineComponent } from 'vue'\nimport { BaseEventButton, NoAnimation } from '../../../components'\nimport { useState } from '../../../composables'\nimport { AnimationProp } from '../../../types'\nimport { scrollXModule } from '../x-module'\nimport { MainScrollId } from './scroll.const'\n\n/**\n * The `ScrollToTop` component is a button that the user can click to make a container scroll\n * up to its initial position.\n *\n * @public\n */\nexport default defineComponent({\n name: 'ScrollToTop',\n xModule: scrollXModule.name,\n components: { BaseEventButton },\n props: {\n /**\n * Animation to use for showing/hiding the button.\n *\n * @public\n */\n animation: {\n type: AnimationProp,\n default: () => NoAnimation,\n },\n /**\n * Threshold in pixels from the top to show the button.\n *\n * @public\n */\n thresholdPx: Number,\n /**\n * Id of the target scroll component.\n *\n * @public\n */\n scrollId: {\n type: String,\n default: MainScrollId,\n },\n },\n setup(props) {\n /**\n * State of all the scroll components in this module.\n *\n * @internal\n */\n // TODO: Directly retrieve the needed data in this computed property\n const { data } = useState('scroll')\n\n /**\n * The scroll data retrieved for this component.\n *\n * @returns The scroll data for this component if a valid {@link ScrollToTop.scrollId} has been\n * passed. Otherwise it returns `null`.\n * @internal\n */\n const scrollData = computed(() => {\n return props.scrollId && data.value[props.scrollId]\n ? data.value[props.scrollId]\n : {\n position: 0,\n direction: 'UP',\n hasReachedStart: false,\n hasAlmostReachedEnd: false,\n hasReachedEnd: false,\n }\n })\n\n /**\n * Event that will be emitted when the scroll to top is clicked.\n *\n * @returns The event to be emitted when the scroll to top is clicked. The id as a payload.\n * @internal\n */\n const events = computed(\n (): Partial<XEventsTypes> => ({ UserClickedScrollToTop: props.scrollId }),\n )\n\n /**\n * Checks if the thresholdPx prop has been provided and if it is a number.\n *\n * @returns If the thresholdPx is a number or not.\n * @internal\n */\n const useThresholdStrategy = computed(() => typeof props.thresholdPx === 'number')\n\n /**\n * Checks if the threshold has been reached in case the threshold strategy is in use.\n *\n * @returns If the scrollTop is bigger than the thresholdPx.\n * @internal\n */\n const isThresholdReached = computed(\n () => useThresholdStrategy.value && scrollData.value.position > props.thresholdPx!,\n )\n\n /**\n * Returns if the scroll has almost reached its end or not.\n *\n * @returns True if the scroll has almost reached the end and the user is still scrolling down.\n * @internal\n */\n const hasAlmostReachedScrollEnd = computed(\n () => scrollData.value.hasAlmostReachedEnd && scrollData.value.direction === 'DOWN',\n )\n\n /**\n * Whether if the button is visible or not depending on the strategy being used.\n *\n * @returns If the button should be visible or not.\n * @internal\n */\n const isVisible = computed(() =>\n useThresholdStrategy.value ? isThresholdReached.value : hasAlmostReachedScrollEnd.value,\n )\n\n return {\n events,\n isVisible,\n }\n },\n})\n</script>\n\n<docs lang=\"mdx\">\n## Events\n\nA list of events that the component will emit:\n\n- [`UserClickedScrollToTop`](https://github.com/empathyco/x/blob/main/packages/x-components/src/wiring/events.types.ts):\n the event is emitted after the user clicks the button. The event payload is the id of the scroll\n that is going to be scrolled.\n\n## Examples\n\n### Basic example\n\nThe component renders whatever is passed to it in the default slot and scrolls to top the scroll\nwith an id `scrollId`.\n\nIt also receives an optional threshold in pixels. When the threshold is reached from the top, the\ncomponent will be shown once the user scrolls `UP`.\n\nIf this parameter is not provided the button will be visible when the user almost reaches the end of\nthe scroll.\n\n```vue\n<template>\n <div>\n <ScrollToTop scroll-id=\"scrollId\" :threshold-px=\"1000\">\n <span>Scroll to top</span>\n </ScrollToTop>\n </div>\n</template>\n\n<script setup>\nimport { ScrollToTop } from '@empathyco/x-components/scroll'\n</script>\n```\n</docs>\n"],"names":["NoAnimation"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwBA;;;;;AAKE;AACF,gBAAe,eAAe,CAAC;AAC7B,IAAA,IAAI,EAAE,aAAa;IACnB,OAAO,EAAE,aAAa,CAAC,IAAI;IAC3B,UAAU,EAAE,EAAE,eAAc,EAAG;AAC/B,IAAA,KAAK,EAAE;AACL;;;;AAIE;AACF,QAAA,SAAS,EAAE;AACT,YAAA,IAAI,EAAE,aAAa;AACnB,YAAA,OAAO,EAAE,MAAMA,WAAW;AAC3B,SAAA;AACD;;;;AAIE;AACF,QAAA,WAAW,EAAE,MAAM;AACnB;;;;AAIE;AACF,QAAA,QAAQ,EAAE;AACR,YAAA,IAAI,EAAE,MAAM;AACZ,YAAA,OAAO,EAAE,YAAY;AACtB,SAAA;AACF,KAAA;AACD,IAAA,KAAK,CAAC,KAAK,EAAA;AACT;;;;AAIE;;QAEF,MAAM,EAAE,IAAG,EAAE,GAAI,QAAQ,CAAC,QAAQ,CAAA;AAElC;;;;;;AAME;AACF,QAAA,MAAM,UAAS,GAAI,QAAQ,CAAC,MAAI;YAC9B,OAAO,KAAK,CAAC,QAAO,IAAK,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ;kBAC9C,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ;AAC3B,kBAAE;AACE,oBAAA,QAAQ,EAAE,CAAC;AACX,oBAAA,SAAS,EAAE,IAAI;AACf,oBAAA,eAAe,EAAE,KAAK;AACtB,oBAAA,mBAAmB,EAAE,KAAK;AAC1B,oBAAA,aAAa,EAAE,KAAK;iBACtB;AACN,QAAA,CAAC,CAAA;AAED;;;;;AAKE;AACF,QAAA,MAAM,MAAK,GAAI,QAAQ,CACrB,OAA8B,EAAE,sBAAsB,EAAE,KAAK,CAAC,QAAO,EAAG,CAAC,CAC3E;AAEA;;;;;AAKE;AACF,QAAA,MAAM,oBAAmB,GAAI,QAAQ,CAAC,MAAM,OAAO,KAAK,CAAC,gBAAgB,QAAQ,CAAA;AAEjF;;;;;AAKE;QACF,MAAM,kBAAiB,GAAI,QAAQ,CACjC,MAAM,oBAAoB,CAAC,KAAI,IAAK,UAAU,CAAC,KAAK,CAAC,QAAO,GAAI,KAAK,CAAC,WAAY,CACpF;AAEA;;;;;AAKE;QACF,MAAM,yBAAwB,GAAI,QAAQ,CACxC,MAAM,UAAU,CAAC,KAAK,CAAC,mBAAkB,IAAK,UAAU,CAAC,KAAK,CAAC,SAAQ,KAAM,MAAM,CACrF;AAEA;;;;;AAKE;QACF,MAAM,YAAY,QAAQ,CAAC,MACzB,oBAAoB,CAAC,KAAI,GAAI,kBAAkB,CAAC,KAAI,GAAI,yBAAyB,CAAC,KAAK,CACzF;QAEA,OAAO;YACL,MAAM;YACN,SAAS;SACX;IACF,CAAC;AACF,CAAA,CAAA;;;;"}
1
+ {"version":3,"file":"scroll-to-top.vue2.js","sources":["../../../../../src/x-modules/scroll/components/scroll-to-top.vue"],"sourcesContent":["<template>\n <component :is=\"animation\">\n <BaseEventButton\n v-if=\"isVisible\"\n class=\"x-scroll-to-top x-button\"\n data-test=\"scroll-to-top\"\n aria-label=\"Scroll to top\"\n :events=\"events\"\n >\n <!-- @slot (Required) Button content with a text, an icon or both -->\n <slot />\n </BaseEventButton>\n </component>\n</template>\n\n<script lang=\"ts\">\nimport type { XEventsTypes } from '../../../wiring'\nimport { computed, defineComponent } from 'vue'\nimport { BaseEventButton, NoAnimation } from '../../../components'\nimport { useState } from '../../../composables'\nimport { AnimationProp } from '../../../types'\nimport { scrollXModule } from '../x-module'\nimport { MainScrollId } from './scroll.const'\n\n/**\n * The `ScrollToTop` component is a button that the user can click to make a container scroll\n * up to its initial position.\n *\n * @public\n */\nexport default defineComponent({\n name: 'ScrollToTop',\n xModule: scrollXModule.name,\n components: { BaseEventButton },\n props: {\n /**\n * Animation to use for showing/hiding the button.\n *\n * @public\n */\n animation: {\n type: AnimationProp,\n default: () => NoAnimation,\n },\n /**\n * Threshold in pixels from the top to show the button.\n *\n * @public\n */\n thresholdPx: Number,\n /**\n * Id of the target scroll component.\n *\n * @public\n */\n scrollId: {\n type: String,\n default: MainScrollId,\n },\n },\n setup(props) {\n /**\n * State of all the scroll components in this module.\n *\n * @internal\n */\n // TODO: Directly retrieve the needed data in this computed property\n const { data } = useState('scroll')\n\n /**\n * The scroll data retrieved for this component.\n *\n * @returns The scroll data for this component if a valid {@link ScrollToTop.scrollId} has been\n * passed. Otherwise it returns `null`.\n * @internal\n */\n const scrollData = computed(() => {\n return props.scrollId && data.value[props.scrollId]\n ? data.value[props.scrollId]\n : {\n position: 0,\n direction: 'UP',\n hasReachedStart: false,\n hasAlmostReachedEnd: false,\n hasReachedEnd: false,\n }\n })\n\n /**\n * Event that will be emitted when the scroll to top is clicked.\n *\n * @returns The event to be emitted when the scroll to top is clicked. The id as a payload.\n * @internal\n */\n const events = computed(\n (): Partial<XEventsTypes> => ({ UserClickedScrollToTop: props.scrollId }),\n )\n\n /**\n * Checks if the thresholdPx prop has been provided and if it is a number.\n *\n * @returns If the thresholdPx is a number or not.\n * @internal\n */\n const useThresholdStrategy = computed(() => typeof props.thresholdPx === 'number')\n\n /**\n * Checks if the threshold has been reached in case the threshold strategy is in use.\n *\n * @returns If the scrollTop is bigger than the thresholdPx.\n * @internal\n */\n const isThresholdReached = computed(\n () => useThresholdStrategy.value && scrollData.value.position > props.thresholdPx!,\n )\n\n /**\n * Returns if the scroll has almost reached its end or not.\n *\n * @returns True if the scroll has almost reached the end and the user is still scrolling down.\n * @internal\n */\n const hasAlmostReachedScrollEnd = computed(\n () => scrollData.value.hasAlmostReachedEnd && scrollData.value.direction === 'DOWN',\n )\n\n /**\n * Whether if the button is visible or not depending on the strategy being used.\n *\n * @returns If the button should be visible or not.\n * @internal\n */\n const isVisible = computed(() =>\n useThresholdStrategy.value ? isThresholdReached.value : hasAlmostReachedScrollEnd.value,\n )\n\n return {\n events,\n isVisible,\n }\n },\n})\n</script>\n\n<docs lang=\"mdx\">\n## Events\n\nA list of events that the component will emit:\n\n- [`UserClickedScrollToTop`](https://github.com/empathyco/x/blob/main/packages/x-components/src/wiring/events.types.ts):\n the event is emitted after the user clicks the button. The event payload is the id of the scroll\n that is going to be scrolled.\n\n## Examples\n\n### Basic example\n\nThe component renders whatever is passed to it in the default slot and scrolls to top the scroll\nwith an id `scrollId`.\n\nIt also receives an optional threshold in pixels. When the threshold is reached from the top, the\ncomponent will be shown once the user scrolls `UP`.\n\nIf this parameter is not provided the button will be visible when the user almost reaches the end of\nthe scroll.\n\n```vue\n<template>\n <div>\n <ScrollToTop scroll-id=\"scrollId\" :threshold-px=\"1000\">\n <span>Scroll to top</span>\n </ScrollToTop>\n </div>\n</template>\n\n<script setup>\nimport { ScrollToTop } from '@empathyco/x-components/scroll'\n</script>\n```\n</docs>\n"],"names":["NoAnimation"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwBA;;;;;AAKE;AACF,gBAAe,eAAe,CAAC;AAC7B,IAAA,IAAI,EAAE,aAAa;IACnB,OAAO,EAAE,aAAa,CAAC,IAAI;IAC3B,UAAU,EAAE,EAAE,eAAc,EAAG;AAC/B,IAAA,KAAK,EAAE;AACL;;;;AAIE;AACF,QAAA,SAAS,EAAE;AACT,YAAA,IAAI,EAAE,aAAa;AACnB,YAAA,OAAO,EAAE,MAAMA,WAAW;AAC3B,SAAA;AACD;;;;AAIE;AACF,QAAA,WAAW,EAAE,MAAM;AACnB;;;;AAIE;AACF,QAAA,QAAQ,EAAE;AACR,YAAA,IAAI,EAAE,MAAM;AACZ,YAAA,OAAO,EAAE,YAAY;AACtB,SAAA;AACF,KAAA;AACD,IAAA,KAAK,CAAC,KAAK,EAAA;AACT;;;;AAIE;;QAEF,MAAM,EAAE,IAAG,EAAE,GAAI,QAAQ,CAAC,QAAQ,CAAA;AAElC;;;;;;AAME;AACF,QAAA,MAAM,UAAS,GAAI,QAAQ,CAAC,MAAI;YAC9B,OAAO,KAAK,CAAC,QAAO,IAAK,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ;kBAC9C,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ;AAC3B,kBAAE;AACE,oBAAA,QAAQ,EAAE,CAAC;AACX,oBAAA,SAAS,EAAE,IAAI;AACf,oBAAA,eAAe,EAAE,KAAK;AACtB,oBAAA,mBAAmB,EAAE,KAAK;AAC1B,oBAAA,aAAa,EAAE,KAAK;iBACtB;AACN,QAAA,CAAC,CAAA;AAED;;;;;AAKE;AACF,QAAA,MAAM,MAAK,GAAI,QAAQ,CACrB,OAA8B,EAAE,sBAAsB,EAAE,KAAK,CAAC,QAAO,EAAG,CAAC,CAC3E;AAEA;;;;;AAKE;AACF,QAAA,MAAM,oBAAmB,GAAI,QAAQ,CAAC,MAAM,OAAO,KAAK,CAAC,gBAAgB,QAAQ,CAAA;AAEjF;;;;;AAKE;QACF,MAAM,kBAAiB,GAAI,QAAQ,CACjC,MAAM,oBAAoB,CAAC,KAAI,IAAK,UAAU,CAAC,KAAK,CAAC,QAAO,GAAI,KAAK,CAAC,WAAY,CACpF;AAEA;;;;;AAKE;QACF,MAAM,yBAAwB,GAAI,QAAQ,CACxC,MAAM,UAAU,CAAC,KAAK,CAAC,mBAAkB,IAAK,UAAU,CAAC,KAAK,CAAC,SAAQ,KAAM,MAAM,CACrF;AAEA;;;;;AAKE;QACF,MAAM,YAAY,QAAQ,CAAC,MACzB,oBAAoB,CAAC,KAAI,GAAI,kBAAkB,CAAC,KAAI,GAAI,yBAAyB,CAAC,KAAK,CACzF;QAEA,OAAO;YACL,MAAM;YACN,SAAS;SACX;IACF,CAAC;AACF,CAAA,CAAA;;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@empathyco/x-components",
3
- "version": "6.0.0-alpha.215",
3
+ "version": "6.0.0-alpha.217",
4
4
  "description": "Empathy X Components",
5
5
  "author": "Empathy Systems Corporation S.L.",
6
6
  "license": "Apache-2.0",
@@ -67,12 +67,12 @@
67
67
  "vuex": "4.0.2"
68
68
  },
69
69
  "dependencies": {
70
- "@empathyco/x-adapter": "8.1.0-alpha.17",
71
- "@empathyco/x-adapter-platform": "1.1.0-alpha.54",
70
+ "@empathyco/x-adapter": "8.1.0-alpha.18",
71
+ "@empathyco/x-adapter-platform": "1.1.0-alpha.55",
72
72
  "@empathyco/x-deep-merge": "2.0.3-alpha.14",
73
73
  "@empathyco/x-storage-service": "2.0.3-alpha.11",
74
- "@empathyco/x-types": "10.1.0-alpha.46",
75
- "@empathyco/x-utils": "1.0.3-alpha.14",
74
+ "@empathyco/x-types": "10.1.0-alpha.47",
75
+ "@empathyco/x-utils": "1.0.3-alpha.15",
76
76
  "@vue/devtools-api": "~6.6.4",
77
77
  "@vueuse/core": "~12.8.2",
78
78
  "js-md5": "~0.8.3",
@@ -85,7 +85,7 @@
85
85
  },
86
86
  "devDependencies": {
87
87
  "@babel/preset-env": "7.29.0",
88
- "@empathyco/x-tailwindcss": "2.0.0-alpha.30",
88
+ "@empathyco/x-tailwindcss": "2.0.0-alpha.31",
89
89
  "@microsoft/api-documenter": "7.29.2",
90
90
  "@microsoft/api-extractor": "7.57.2",
91
91
  "@testing-library/vue": "8.1.0",
@@ -125,5 +125,5 @@
125
125
  "access": "public",
126
126
  "directory": "dist"
127
127
  },
128
- "gitHead": "0af65e39d1d128472cc9840f157e9a713a501eb9"
128
+ "gitHead": "01ba0d6d8c302024b26bd78dcba5f324395b264a"
129
129
  }