@empathyco/x-components 6.0.0-alpha.46 → 6.0.0-alpha.48

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.
@@ -14,3 +14,9 @@ This component shows a suggested related prompt.
14
14
  | -------------------------- | ----------- | -------------------------- | ------------------ |
15
15
  | <code>relatedPrompt</code> | | <code>RelatedPrompt</code> | <code></code> |
16
16
  | <code>selected</code> | | <code>boolean</code> | <code>false</code> |
17
+
18
+ ## Slots
19
+
20
+ | Name | Description | Bindings<br />(name - type - description) |
21
+ | ----------------------------------------- | ----------- | ----------------------------------------- |
22
+ | <code>related-prompt-extra-content</code> | | |
@@ -24,8 +24,9 @@ handle the selection of the related prompt and to trigger the stagger-fade-slide
24
24
 
25
25
  ## Slots
26
26
 
27
- | Name | Description | Bindings<br />(name - type - description) |
28
- | --------------------------------------- | ------------------------------------------------------------------ | ----------------------------------------- |
29
- | <code>sliding-panel-left-button</code> | The button to be displayed on the left side of the sliding panel. | None |
30
- | <code>default</code> | - The slot to render related prompt information. | <br /><br /> |
31
- | <code>sliding-panel-right-button</code> | The button to be displayed on the right side of the sliding panel. | None |
27
+ | Name | Description | Bindings<br />(name - type - description) |
28
+ | ----------------------------------------- | ------------------------------------------------------------------ | ----------------------------------------- |
29
+ | <code>sliding-panel-left-button</code> | The button to be displayed on the left side of the sliding panel. | None |
30
+ | <code>default</code> | - The slot to render related prompt information. | <br /><br /> |
31
+ | <code>related-prompt-extra-content</code> | | |
32
+ | <code>sliding-panel-right-button</code> | The button to be displayed on the right side of the sliding panel. | None |
@@ -1,5 +1,5 @@
1
1
  import _sfc_main from './related-prompt.vue2.js';
2
- import { resolveDirective, openBlock, createElementBlock, withDirectives, createElementVNode, createBlock, resolveDynamicComponent } from 'vue';
2
+ import { resolveDirective, openBlock, createElementBlock, renderSlot, withDirectives, createElementVNode, createBlock, resolveDynamicComponent } from 'vue';
3
3
  import './related-prompt.vue3.js';
4
4
  import _export_sfc from '../../../_virtual/_plugin-vue_export-helper.js';
5
5
 
@@ -7,6 +7,7 @@ const _hoisted_1 = { class: "x-related-prompt" };
7
7
  function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
8
8
  const _directive_typing = resolveDirective("typing");
9
9
  return openBlock(), createElementBlock("button", _hoisted_1, [
10
+ renderSlot(_ctx.$slots, "related-prompt-extra-content", { relatedPrompt: _ctx.relatedPrompt }),
10
11
  withDirectives(createElementVNode(
11
12
  "span",
12
13
  null,
@@ -1 +1 @@
1
- {"version":3,"file":"related-prompt.vue.js","sources":["../../../../../src/x-modules/related-prompts/components/related-prompt.vue"],"sourcesContent":["<template>\n <button class=\"x-related-prompt\">\n <span v-typing=\"{ text: relatedPrompt.suggestionText, speed: 50 }\" />\n <component\n :is=\"selected ? 'CrossTinyIcon' : 'PlusIcon'\"\n class=\"x-icon-lg x-related-prompt-icon\"\n />\n </button>\n</template>\n<script lang=\"ts\">\n import { defineComponent, PropType } from 'vue';\n import { RelatedPrompt } from '@empathyco/x-types';\n import CrossTinyIcon from '../../../components/icons/cross-tiny.vue';\n import PlusIcon from '../../../components/icons/plus.vue';\n import { typing } from '../../../directives/typing';\n\n /**\n * This component shows a suggested related prompt.\n */\n export default defineComponent({\n name: 'RelatedPrompt',\n directives: {\n typing\n },\n components: {\n CrossTinyIcon,\n PlusIcon\n },\n props: {\n relatedPrompt: {\n type: Object as PropType<RelatedPrompt>,\n required: true\n },\n selected: {\n type: Boolean,\n default: false\n }\n }\n });\n</script>\n<style lang=\"css\">\n .x-related-prompt {\n display: flex;\n align-items: center;\n justify-content: space-between;\n text-align: start;\n padding: 8px;\n height: 100%;\n width: 100%;\n }\n .x-related-prompt-icon {\n align-self: start;\n }\n</style>\n"],"names":["_resolveDirective","relatedPrompt","_withDirectives","_createElementVNode","selected"],"mappings":";;;;;;;AACE,EAAA,MAAA,iBAAA,GAAAA,gBAAA,CAMS,QANT,CAAA,CAAA;yCAC0BC,QAAc,EAAA,UAAA,EAAA;AAAA,IAAAC,cAAA,CAAAC,kBAAA;;;;;;;AACtC,MAAA,CAAA,iBAAA,EAAA,EAAA,IAAA,EAHJ,mCAIWC,KAAQ,EAAA,EAAA,EAAA,CAAA;AAAA,KAAA,CAAA;;;;;;;;"}
1
+ {"version":3,"file":"related-prompt.vue.js","sources":["../../../../../src/x-modules/related-prompts/components/related-prompt.vue"],"sourcesContent":["<template>\n <button class=\"x-related-prompt\">\n <slot name=\"related-prompt-extra-content\" :relatedPrompt=\"relatedPrompt\" />\n <span v-typing=\"{ text: relatedPrompt.suggestionText, speed: 50 }\" />\n <component\n :is=\"selected ? 'CrossTinyIcon' : 'PlusIcon'\"\n class=\"x-icon-lg x-related-prompt-icon\"\n />\n </button>\n</template>\n<script lang=\"ts\">\n import { defineComponent, PropType } from 'vue';\n import { RelatedPrompt } from '@empathyco/x-types';\n import CrossTinyIcon from '../../../components/icons/cross-tiny.vue';\n import PlusIcon from '../../../components/icons/plus.vue';\n import { typing } from '../../../directives/typing';\n\n /**\n * This component shows a suggested related prompt.\n */\n export default defineComponent({\n name: 'RelatedPrompt',\n directives: {\n typing\n },\n components: {\n CrossTinyIcon,\n PlusIcon\n },\n props: {\n relatedPrompt: {\n type: Object as PropType<RelatedPrompt>,\n required: true\n },\n selected: {\n type: Boolean,\n default: false\n }\n }\n });\n</script>\n<style lang=\"css\">\n .x-related-prompt {\n display: flex;\n align-items: center;\n justify-content: space-between;\n text-align: start;\n padding: 8px;\n height: 100%;\n width: 100%;\n }\n .x-related-prompt-icon {\n align-self: start;\n }\n</style>\n"],"names":["_resolveDirective","_createElementBlock","_renderSlot","_withDirectives","_createElementVNode","selected"],"mappings":";;;;;;;AACE,EAAA,MAAA,iBAAA,GAAAA,gBAAA,CAOS,QAPT,CAAA,CAAA;oBAEuE,EAAAC,kBAAA,CAAA,QAAA,EAAA,UAAA,EAAA;AAAA,IAAAC,UAAA,CAAA,IAAA,CAAA,MAAA,EAAA,8BAAA,EAA/B,EAAc,aAAA,EAAA,IAAA,CAAA,aAAA,EAAA,CAAA;AAAA,IAAAC,cAAA,CAAAC,kBAAA;;;;;;;AACpD,MAAA,CAAA,iBAAA,EAAA,EAAA,IAAA,EAJJ,mCAKWC,KAAQ,EAAA,EAAA,EAAA,CAAA;AAAA,KAAA,CAAA;;;;;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"related-prompt.vue2.js","sources":["../../../../../src/x-modules/related-prompts/components/related-prompt.vue"],"sourcesContent":["<template>\n <button class=\"x-related-prompt\">\n <span v-typing=\"{ text: relatedPrompt.suggestionText, speed: 50 }\" />\n <component\n :is=\"selected ? 'CrossTinyIcon' : 'PlusIcon'\"\n class=\"x-icon-lg x-related-prompt-icon\"\n />\n </button>\n</template>\n<script lang=\"ts\">\n import { defineComponent, PropType } from 'vue';\n import { RelatedPrompt } from '@empathyco/x-types';\n import CrossTinyIcon from '../../../components/icons/cross-tiny.vue';\n import PlusIcon from '../../../components/icons/plus.vue';\n import { typing } from '../../../directives/typing';\n\n /**\n * This component shows a suggested related prompt.\n */\n export default defineComponent({\n name: 'RelatedPrompt',\n directives: {\n typing\n },\n components: {\n CrossTinyIcon,\n PlusIcon\n },\n props: {\n relatedPrompt: {\n type: Object as PropType<RelatedPrompt>,\n required: true\n },\n selected: {\n type: Boolean,\n default: false\n }\n }\n });\n</script>\n<style lang=\"css\">\n .x-related-prompt {\n display: flex;\n align-items: center;\n justify-content: space-between;\n text-align: start;\n padding: 8px;\n height: 100%;\n width: 100%;\n }\n .x-related-prompt-icon {\n align-self: start;\n }\n</style>\n"],"names":[],"mappings":";;;;;AAgBE;;AAEE;AACF,gBAAe,eAAe,CAAC;AAC7B,IAAA,IAAI,EAAE,eAAe;AACrB,IAAA,UAAU,EAAE;QACV,MAAK;AACN,KAAA;AACD,IAAA,UAAU,EAAE;QACV,aAAa;QACb,QAAO;AACR,KAAA;AACD,IAAA,KAAK,EAAE;AACL,QAAA,aAAa,EAAE;AACb,YAAA,IAAI,EAAE,MAAiC;AACvC,YAAA,QAAQ,EAAE,IAAG;AACd,SAAA;AACD,QAAA,QAAQ,EAAE;AACR,YAAA,IAAI,EAAE,OAAO;AACb,YAAA,OAAO,EAAE,KAAI;AACf,SAAA;AACF,KAAA;AACD,CAAA,CAAC;;;;"}
1
+ {"version":3,"file":"related-prompt.vue2.js","sources":["../../../../../src/x-modules/related-prompts/components/related-prompt.vue"],"sourcesContent":["<template>\n <button class=\"x-related-prompt\">\n <slot name=\"related-prompt-extra-content\" :relatedPrompt=\"relatedPrompt\" />\n <span v-typing=\"{ text: relatedPrompt.suggestionText, speed: 50 }\" />\n <component\n :is=\"selected ? 'CrossTinyIcon' : 'PlusIcon'\"\n class=\"x-icon-lg x-related-prompt-icon\"\n />\n </button>\n</template>\n<script lang=\"ts\">\n import { defineComponent, PropType } from 'vue';\n import { RelatedPrompt } from '@empathyco/x-types';\n import CrossTinyIcon from '../../../components/icons/cross-tiny.vue';\n import PlusIcon from '../../../components/icons/plus.vue';\n import { typing } from '../../../directives/typing';\n\n /**\n * This component shows a suggested related prompt.\n */\n export default defineComponent({\n name: 'RelatedPrompt',\n directives: {\n typing\n },\n components: {\n CrossTinyIcon,\n PlusIcon\n },\n props: {\n relatedPrompt: {\n type: Object as PropType<RelatedPrompt>,\n required: true\n },\n selected: {\n type: Boolean,\n default: false\n }\n }\n });\n</script>\n<style lang=\"css\">\n .x-related-prompt {\n display: flex;\n align-items: center;\n justify-content: space-between;\n text-align: start;\n padding: 8px;\n height: 100%;\n width: 100%;\n }\n .x-related-prompt-icon {\n align-self: start;\n }\n</style>\n"],"names":[],"mappings":";;;;;AAiBE;;AAEE;AACF,gBAAe,eAAe,CAAC;AAC7B,IAAA,IAAI,EAAE,eAAe;AACrB,IAAA,UAAU,EAAE;QACV,MAAK;AACN,KAAA;AACD,IAAA,UAAU,EAAE;QACV,aAAa;QACb,QAAO;AACR,KAAA;AACD,IAAA,KAAK,EAAE;AACL,QAAA,aAAa,EAAE;AACb,YAAA,IAAI,EAAE,MAAiC;AACvC,YAAA,QAAQ,EAAE,IAAG;AACd,SAAA;AACD,QAAA,QAAQ,EAAE;AACR,YAAA,IAAI,EAAE,OAAO;AACb,YAAA,OAAO,EAAE,KAAI;AACf,SAAA;AACF,KAAA;AACD,CAAA,CAAC;;;;"}
@@ -63,7 +63,13 @@ function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
63
63
  onClick: ($event) => _ctx.onSelect(index),
64
64
  "related-prompt": relatedPrompt,
65
65
  selected: _ctx.isSelected(index)
66
- }, null, 8, ["onClick", "related-prompt", "selected"])
66
+ }, {
67
+ "related-prompt-extra-content": withCtx(() => [
68
+ renderSlot(_ctx.$slots, "related-prompt-extra-content", { relatedPrompt })
69
+ ]),
70
+ _: 2
71
+ /* DYNAMIC */
72
+ }, 1032, ["onClick", "related-prompt", "selected"])
67
73
  ]),
68
74
  _: 2
69
75
  /* DYNAMIC */
@@ -1 +1 @@
1
- {"version":3,"file":"related-prompts-tag-list.vue.js","sources":["../../../../../src/x-modules/related-prompts/components/related-prompts-tag-list.vue"],"sourcesContent":["<template>\n <SlidingPanel\n :key=\"x.query.search\"\n :reset-on-content-change=\"false\"\n :button-class=\"buttonClass\"\n :show-buttons=\"showButtons && selectedPromptIndex === -1\"\n :scroll-container-class=\"['x-related-prompts-tag-list-scroll-container', scrollContainerClass]\"\n >\n <template #sliding-panel-left-button>\n <!--\n @slot sliding-panel-left-button - The button to be displayed on the left side of the sliding panel.\n -->\n <slot name=\"sliding-panel-left-button\" />\n </template>\n <transition-group\n @before-enter=\"onBeforeEnter\"\n @enter=\"onEnter\"\n @leave=\"onLeave\"\n class=\"x-related-prompts-tag-list\"\n :css=\"false\"\n tag=\"ul\"\n appear\n >\n <li\n v-for=\"{ index, ...relatedPrompt } in visibleRelatedPrompts\"\n ref=\"listItems\"\n :key=\"relatedPrompt.suggestionText\"\n class=\"x-related-prompts-tag-list-item\"\n :class=\"[tagClass, tagColors && tagColors[index % tagColors.length]]\"\n :data-index=\"index\"\n :style=\"isAnimating && { pointerEvents: 'none' }\"\n data-test=\"related-prompts-tag-list-item\"\n >\n <!--\n @slot - The slot to render related prompt information.\n @prop {Object} relatedPrompt - The related prompt object.\n @prop {Function} onSelect - The function to select the related prompt.\n @prop {Boolean} isSelected - Indicates if the related prompt is currently selected.\n -->\n <slot\n :relatedPrompt=\"relatedPrompt\"\n :onSelect=\"() => onSelect(index)\"\n :isSelected=\"isSelected(index)\"\n >\n <DisplayEmitter\n :payload=\"relatedPrompt.toolingDisplayTagging\"\n :eventMetadata=\"{\n feature: 'related-prompts',\n displayOriginalQuery: x.query.searchBox,\n replaceable: false\n }\"\n >\n <RelatedPrompt\n @click=\"onSelect(index)\"\n :related-prompt=\"relatedPrompt\"\n :selected=\"isSelected(index)\"\n />\n </DisplayEmitter>\n </slot>\n </li>\n </transition-group>\n <template #sliding-panel-right-button>\n <!--\n @slot sliding-panel-right-button - The button to be displayed on the right side of the sliding panel.\n -->\n <slot name=\"sliding-panel-right-button\" />\n </template>\n </SlidingPanel>\n</template>\n\n<script lang=\"ts\">\n import { RelatedPrompt as RelatedPromptModel } from '@empathyco/x-types';\n import { computed, defineComponent, PropType, ref, watch } from 'vue';\n import SlidingPanel from '../../../components/sliding-panel.vue';\n import { relatedPromptsXModule } from '../x-module';\n import { use$x, useState } from '../../../composables';\n import DisplayEmitter from '../../../components/display-emitter.vue';\n import RelatedPrompt from './related-prompt.vue';\n\n /**\n * This component shows the list of `RelatedPrompts` components.\n *\n * If the default slot is reimplemented in the consumer, `onSelect` function will be\n * necessary to handle the selection of the related prompt and to trigger the stagger-fade-slide animation.\n *\n * @public\n */\n export default defineComponent({\n name: 'RelatedPromptsTagList',\n xModule: relatedPromptsXModule.name,\n components: { DisplayEmitter, RelatedPrompt, SlidingPanel },\n props: {\n /**\n * The CSS class for the left and right button of the sliding panel.\n *\n * @public\n */\n buttonClass: String,\n /**\n * The boolean prop to handle the visiblity of sliding pannel buttons.\n *\n * @public\n */\n showButtons: { type: Boolean, default: true },\n /**\n * The CSS class for the wrapper of all the related prompt wrapper elements.\n *\n * @public\n */\n scrollContainerClass: String,\n /**\n * The CSS class for all the related prompt wrapper elements.\n *\n * @public\n */\n tagClass: String,\n /**\n * Array of colors to apply to the related prompts. It will be applied to tag\n * elements cyclically according to their index in the nex way: `tagColors[index % tagColors.length]`.\n *\n * @public\n */\n tagColors: Array as PropType<string[]>,\n /**\n * The duration of the total animation in milliseconds.\n *\n * @public\n */\n animationDurationInMs: {\n type: Number,\n default: 700\n }\n },\n setup(props) {\n const x = use$x();\n const { relatedPrompts, selectedPrompt: selectedPromptIndex } = useState('relatedPrompts', [\n 'relatedPrompts',\n 'selectedPrompt'\n ]);\n\n const clickedListItemIndex = ref<number | null>(null);\n const initialOffsetLefts: Record<number, number> = {};\n const isAnimating = ref(false);\n const listItems = ref<HTMLElement[]>([]);\n\n const sortedListItems = computed<HTMLElement[]>(() =>\n [...listItems.value].sort(\n (a: HTMLElement, b: HTMLElement) =>\n Number.parseInt(b.getAttribute('data-index')!) -\n Number.parseInt(a.getAttribute('data-index')!)\n )\n );\n\n // The duration of a single animation (enter or leave) in milliseconds\n // if a related prompt is clicked (clickedListItemIndex.value !== null), the duration is divided by the number of related\n // prompts -1 (the clicked one is synchronized with the last one to leave or the first one to enter)\n const singleAnimationDurationInMs = computed(\n () =>\n props.animationDurationInMs /\n (clickedListItemIndex.value !== null\n ? relatedPrompts.value.length - 1\n : relatedPrompts.value.length)\n );\n\n const indexRelatedPrompts = computed(() =>\n (relatedPrompts.value as RelatedPromptModel[]).map(\n (relatedPrompt: RelatedPromptModel, index: number) => ({\n ...relatedPrompt,\n index\n })\n )\n );\n\n const visibleRelatedPrompts = computed(() => {\n return selectedPromptIndex.value !== -1\n ? [indexRelatedPrompts.value[selectedPromptIndex.value]]\n : indexRelatedPrompts.value;\n });\n\n let timeOutId: number;\n const resetTransitionStyle = () => {\n if (timeOutId) {\n clearTimeout(timeOutId);\n }\n\n isAnimating.value = true;\n timeOutId = +setTimeout(() => {\n isAnimating.value = false;\n clickedListItemIndex.value = null;\n\n sortedListItems.value.forEach(element => {\n element.style.cssText\n .split(';')\n .map(rule => rule.split(':')[0]?.trim())\n .forEach(property => {\n if (property !== 'width') {\n element.style.removeProperty(property);\n }\n });\n });\n }, props.animationDurationInMs);\n };\n\n const onSelect = (selectedIndex: number): void => {\n resetTransitionStyle();\n\n clickedListItemIndex.value = selectedIndex;\n const selected: HTMLElement = sortedListItems.value.find(\n element => Number.parseInt(element.getAttribute('data-index')!) === selectedIndex\n )!;\n\n // selectedPromptIndex.value === -1 ? 'SELECTING' : 'DESELECTING'\n if (selectedPromptIndex.value === -1) {\n // Prepare all the elements for the leave animation (~ 'beforeLeave' hook). Remember the elements are\n // sorted in descending order by index.\n sortedListItems.value.forEach(element => {\n const index = Number.parseInt(element.getAttribute('data-index')!);\n\n initialOffsetLefts[index] = element.offsetLeft;\n element.style.left = `${element.offsetLeft}px`;\n element.style.position = 'absolute';\n element.style.transitionDuration = `${singleAnimationDurationInMs.value}ms`;\n\n if (index !== selectedIndex) {\n element.style.opacity = '1';\n element.style.transitionDelay = `${\n (index < selectedIndex ? index : index - 1) * singleAnimationDurationInMs.value\n }ms`;\n }\n });\n\n // Synchronize the transition delay of the selected element with the last\n // element to leave\n selected.style.transitionDelay = `${\n (relatedPrompts.value.length > 1 ? relatedPrompts.value.length - 2 : 0) *\n singleAnimationDurationInMs.value\n }ms`;\n\n // Trigger the animation (selecting) for the selected element\n requestAnimationFrame(() => {\n const maxWidth = getComputedStyle(selected).maxWidth;\n\n selected.style.left = '0px';\n selected.style.setProperty(\n 'width',\n `${maxWidth !== 'none' ? maxWidth : '100%'}`,\n 'important'\n );\n });\n } else {\n // Prepare the selected element for the deselecting animation\n selected.style.transitionDuration = `${singleAnimationDurationInMs.value}ms`;\n selected.style.left = '0px';\n selected.style.position = 'absolute';\n\n // Trigger the animation (deselecting) for the selected element\n selected.style.removeProperty('width');\n requestAnimationFrame(() => {\n selected.style.left = `${initialOffsetLefts[selectedIndex]}px`;\n });\n }\n\n x.emit('UserSelectedARelatedPrompt', selectedIndex, {\n relatedPrompt: relatedPrompts.value[selectedIndex],\n selectedPrompt: selectedPromptIndex.value\n });\n };\n\n const onBeforeEnter = (el: Element) => {\n const element = el as HTMLElement;\n const index = Number.parseInt(element.getAttribute('data-index')!);\n\n // Prepare the element for the enter animation\n element.style.opacity = '0';\n element.style.transform = 'translateY(5px)';\n element.style.transitionDelay = `${\n (clickedListItemIndex.value !== null && index > clickedListItemIndex.value\n ? index - 1\n : index) * singleAnimationDurationInMs.value\n }ms`;\n element.style.transitionDuration = `${singleAnimationDurationInMs.value}ms`;\n };\n\n const onEnter = (el: Element, done: () => void) => {\n const element = el as HTMLElement;\n const index = Number.parseInt(element.getAttribute('data-index')!);\n\n // Also part of the preparation for the enter animation, but it needs to be done\n // once the element is inserted in DOM (if not the offsetLeft will be always 0)\n element.style.left = `${initialOffsetLefts[index] ?? element.offsetLeft}px`;\n\n // trigger enter animation\n requestAnimationFrame(() => {\n element.style.opacity = '1';\n element.style.position = 'absolute';\n element.style.transform = 'translateY(0)';\n });\n\n done();\n };\n\n const onLeave = (el: Element, done: () => void) => {\n const element = el as HTMLElement;\n\n // trigger leave animation\n requestAnimationFrame(() => {\n element.style.opacity = '0';\n element.style.transform = 'translateY(5px)';\n });\n\n // Wait for the animation to finish (done() exectution extracts the element from the DOM)\n setTimeout(done, props.animationDurationInMs);\n };\n\n const isSelected = (index: number): boolean => selectedPromptIndex.value === index;\n\n // Changing the query will trigger the appear animation, so we need to reset the\n // style after it finishes\n watch(() => x.query.search, resetTransitionStyle, { immediate: true });\n\n return {\n isSelected,\n onSelect,\n onBeforeEnter,\n onEnter,\n onLeave,\n selectedPromptIndex,\n visibleRelatedPrompts,\n listItems,\n isAnimating,\n x\n };\n }\n });\n</script>\n<style lang=\"css\">\n .x-related-prompts-tag-list-scroll-container {\n height: 100%;\n position: relative;\n }\n .x-related-prompts-tag-list {\n display: flex;\n gap: 16px;\n min-width: 100%;\n }\n .x-related-prompts-tag-list-item {\n height: 100%;\n }\n</style>\n"],"names":["_resolveComponent","_openBlock","_createBlock","buttonClass","showButtons","_renderSlot","_withCtx","onBeforeEnter","onEnter","onLeave","_createElementBlock","_Fragment","_renderList","_normalizeClass","isAnimating","_normalizeStyle","onSelect","_createVNode","isSelected"],"mappings":"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"}
1
+ {"version":3,"file":"related-prompts-tag-list.vue.js","sources":["../../../../../src/x-modules/related-prompts/components/related-prompts-tag-list.vue"],"sourcesContent":["<template>\n <SlidingPanel\n :key=\"x.query.search\"\n :reset-on-content-change=\"false\"\n :button-class=\"buttonClass\"\n :show-buttons=\"showButtons && selectedPromptIndex === -1\"\n :scroll-container-class=\"['x-related-prompts-tag-list-scroll-container', scrollContainerClass]\"\n >\n <template #sliding-panel-left-button>\n <!--\n @slot sliding-panel-left-button - The button to be displayed on the left side of the sliding panel.\n -->\n <slot name=\"sliding-panel-left-button\" />\n </template>\n <transition-group\n @before-enter=\"onBeforeEnter\"\n @enter=\"onEnter\"\n @leave=\"onLeave\"\n class=\"x-related-prompts-tag-list\"\n :css=\"false\"\n tag=\"ul\"\n appear\n >\n <li\n v-for=\"{ index, ...relatedPrompt } in visibleRelatedPrompts\"\n ref=\"listItems\"\n :key=\"relatedPrompt.suggestionText\"\n class=\"x-related-prompts-tag-list-item\"\n :class=\"[tagClass, tagColors && tagColors[index % tagColors.length]]\"\n :data-index=\"index\"\n :style=\"isAnimating && { pointerEvents: 'none' }\"\n data-test=\"related-prompts-tag-list-item\"\n >\n <!--\n @slot - The slot to render related prompt information.\n @prop {Object} relatedPrompt - The related prompt object.\n @prop {Function} onSelect - The function to select the related prompt.\n @prop {Boolean} isSelected - Indicates if the related prompt is currently selected.\n -->\n <slot\n :relatedPrompt=\"relatedPrompt\"\n :onSelect=\"() => onSelect(index)\"\n :isSelected=\"isSelected(index)\"\n >\n <DisplayEmitter\n :payload=\"relatedPrompt.toolingDisplayTagging\"\n :eventMetadata=\"{\n feature: 'related-prompts',\n displayOriginalQuery: x.query.searchBox,\n replaceable: false\n }\"\n >\n <RelatedPrompt\n @click=\"onSelect(index)\"\n :related-prompt=\"relatedPrompt\"\n :selected=\"isSelected(index)\"\n >\n <template #related-prompt-extra-content>\n <slot name=\"related-prompt-extra-content\" :relatedPrompt=\"relatedPrompt\" />\n </template>\n </RelatedPrompt>\n </DisplayEmitter>\n </slot>\n </li>\n </transition-group>\n <template #sliding-panel-right-button>\n <!--\n @slot sliding-panel-right-button - The button to be displayed on the right side of the sliding panel.\n -->\n <slot name=\"sliding-panel-right-button\" />\n </template>\n </SlidingPanel>\n</template>\n\n<script lang=\"ts\">\n import { RelatedPrompt as RelatedPromptModel } from '@empathyco/x-types';\n import { computed, defineComponent, PropType, ref, watch } from 'vue';\n import SlidingPanel from '../../../components/sliding-panel.vue';\n import { relatedPromptsXModule } from '../x-module';\n import { use$x, useState } from '../../../composables';\n import DisplayEmitter from '../../../components/display-emitter.vue';\n import RelatedPrompt from './related-prompt.vue';\n\n /**\n * This component shows the list of `RelatedPrompts` components.\n *\n * If the default slot is reimplemented in the consumer, `onSelect` function will be\n * necessary to handle the selection of the related prompt and to trigger the stagger-fade-slide animation.\n *\n * @public\n */\n export default defineComponent({\n name: 'RelatedPromptsTagList',\n xModule: relatedPromptsXModule.name,\n components: { DisplayEmitter, RelatedPrompt, SlidingPanel },\n props: {\n /**\n * The CSS class for the left and right button of the sliding panel.\n *\n * @public\n */\n buttonClass: String,\n /**\n * The boolean prop to handle the visiblity of sliding pannel buttons.\n *\n * @public\n */\n showButtons: { type: Boolean, default: true },\n /**\n * The CSS class for the wrapper of all the related prompt wrapper elements.\n *\n * @public\n */\n scrollContainerClass: String,\n /**\n * The CSS class for all the related prompt wrapper elements.\n *\n * @public\n */\n tagClass: String,\n /**\n * Array of colors to apply to the related prompts. It will be applied to tag\n * elements cyclically according to their index in the nex way: `tagColors[index % tagColors.length]`.\n *\n * @public\n */\n tagColors: Array as PropType<string[]>,\n /**\n * The duration of the total animation in milliseconds.\n *\n * @public\n */\n animationDurationInMs: {\n type: Number,\n default: 700\n }\n },\n setup(props) {\n const x = use$x();\n const { relatedPrompts, selectedPrompt: selectedPromptIndex } = useState('relatedPrompts', [\n 'relatedPrompts',\n 'selectedPrompt'\n ]);\n\n const clickedListItemIndex = ref<number | null>(null);\n const initialOffsetLefts: Record<number, number> = {};\n const isAnimating = ref(false);\n const listItems = ref<HTMLElement[]>([]);\n\n const sortedListItems = computed<HTMLElement[]>(() =>\n [...listItems.value].sort(\n (a: HTMLElement, b: HTMLElement) =>\n Number.parseInt(b.getAttribute('data-index')!) -\n Number.parseInt(a.getAttribute('data-index')!)\n )\n );\n\n // The duration of a single animation (enter or leave) in milliseconds\n // if a related prompt is clicked (clickedListItemIndex.value !== null), the duration is divided by the number of related\n // prompts -1 (the clicked one is synchronized with the last one to leave or the first one to enter)\n const singleAnimationDurationInMs = computed(\n () =>\n props.animationDurationInMs /\n (clickedListItemIndex.value !== null\n ? relatedPrompts.value.length - 1\n : relatedPrompts.value.length)\n );\n\n const indexRelatedPrompts = computed(() =>\n (relatedPrompts.value as RelatedPromptModel[]).map(\n (relatedPrompt: RelatedPromptModel, index: number) => ({\n ...relatedPrompt,\n index\n })\n )\n );\n\n const visibleRelatedPrompts = computed(() => {\n return selectedPromptIndex.value !== -1\n ? [indexRelatedPrompts.value[selectedPromptIndex.value]]\n : indexRelatedPrompts.value;\n });\n\n let timeOutId: number;\n const resetTransitionStyle = () => {\n if (timeOutId) {\n clearTimeout(timeOutId);\n }\n\n isAnimating.value = true;\n timeOutId = +setTimeout(() => {\n isAnimating.value = false;\n clickedListItemIndex.value = null;\n\n sortedListItems.value.forEach(element => {\n element.style.cssText\n .split(';')\n .map(rule => rule.split(':')[0]?.trim())\n .forEach(property => {\n if (property !== 'width') {\n element.style.removeProperty(property);\n }\n });\n });\n }, props.animationDurationInMs);\n };\n\n const onSelect = (selectedIndex: number): void => {\n resetTransitionStyle();\n\n clickedListItemIndex.value = selectedIndex;\n const selected: HTMLElement = sortedListItems.value.find(\n element => Number.parseInt(element.getAttribute('data-index')!) === selectedIndex\n )!;\n\n // selectedPromptIndex.value === -1 ? 'SELECTING' : 'DESELECTING'\n if (selectedPromptIndex.value === -1) {\n // Prepare all the elements for the leave animation (~ 'beforeLeave' hook). Remember the elements are\n // sorted in descending order by index.\n sortedListItems.value.forEach(element => {\n const index = Number.parseInt(element.getAttribute('data-index')!);\n\n initialOffsetLefts[index] = element.offsetLeft;\n element.style.left = `${element.offsetLeft}px`;\n element.style.position = 'absolute';\n element.style.transitionDuration = `${singleAnimationDurationInMs.value}ms`;\n\n if (index !== selectedIndex) {\n element.style.opacity = '1';\n element.style.transitionDelay = `${\n (index < selectedIndex ? index : index - 1) * singleAnimationDurationInMs.value\n }ms`;\n }\n });\n\n // Synchronize the transition delay of the selected element with the last\n // element to leave\n selected.style.transitionDelay = `${\n (relatedPrompts.value.length > 1 ? relatedPrompts.value.length - 2 : 0) *\n singleAnimationDurationInMs.value\n }ms`;\n\n // Trigger the animation (selecting) for the selected element\n requestAnimationFrame(() => {\n const maxWidth = getComputedStyle(selected).maxWidth;\n\n selected.style.left = '0px';\n selected.style.setProperty(\n 'width',\n `${maxWidth !== 'none' ? maxWidth : '100%'}`,\n 'important'\n );\n });\n } else {\n // Prepare the selected element for the deselecting animation\n selected.style.transitionDuration = `${singleAnimationDurationInMs.value}ms`;\n selected.style.left = '0px';\n selected.style.position = 'absolute';\n\n // Trigger the animation (deselecting) for the selected element\n selected.style.removeProperty('width');\n requestAnimationFrame(() => {\n selected.style.left = `${initialOffsetLefts[selectedIndex]}px`;\n });\n }\n\n x.emit('UserSelectedARelatedPrompt', selectedIndex, {\n relatedPrompt: relatedPrompts.value[selectedIndex],\n selectedPrompt: selectedPromptIndex.value\n });\n };\n\n const onBeforeEnter = (el: Element) => {\n const element = el as HTMLElement;\n const index = Number.parseInt(element.getAttribute('data-index')!);\n\n // Prepare the element for the enter animation\n element.style.opacity = '0';\n element.style.transform = 'translateY(5px)';\n element.style.transitionDelay = `${\n (clickedListItemIndex.value !== null && index > clickedListItemIndex.value\n ? index - 1\n : index) * singleAnimationDurationInMs.value\n }ms`;\n element.style.transitionDuration = `${singleAnimationDurationInMs.value}ms`;\n };\n\n const onEnter = (el: Element, done: () => void) => {\n const element = el as HTMLElement;\n const index = Number.parseInt(element.getAttribute('data-index')!);\n\n // Also part of the preparation for the enter animation, but it needs to be done\n // once the element is inserted in DOM (if not the offsetLeft will be always 0)\n element.style.left = `${initialOffsetLefts[index] ?? element.offsetLeft}px`;\n\n // trigger enter animation\n requestAnimationFrame(() => {\n element.style.opacity = '1';\n element.style.position = 'absolute';\n element.style.transform = 'translateY(0)';\n });\n\n done();\n };\n\n const onLeave = (el: Element, done: () => void) => {\n const element = el as HTMLElement;\n\n // trigger leave animation\n requestAnimationFrame(() => {\n element.style.opacity = '0';\n element.style.transform = 'translateY(5px)';\n });\n\n // Wait for the animation to finish (done() exectution extracts the element from the DOM)\n setTimeout(done, props.animationDurationInMs);\n };\n\n const isSelected = (index: number): boolean => selectedPromptIndex.value === index;\n\n // Changing the query will trigger the appear animation, so we need to reset the\n // style after it finishes\n watch(() => x.query.search, resetTransitionStyle, { immediate: true });\n\n return {\n isSelected,\n onSelect,\n onBeforeEnter,\n onEnter,\n onLeave,\n selectedPromptIndex,\n visibleRelatedPrompts,\n listItems,\n isAnimating,\n x\n };\n }\n });\n</script>\n<style lang=\"css\">\n .x-related-prompts-tag-list-scroll-container {\n height: 100%;\n position: relative;\n }\n .x-related-prompts-tag-list {\n display: flex;\n gap: 16px;\n min-width: 100%;\n }\n .x-related-prompts-tag-list-item {\n height: 100%;\n }\n</style>\n"],"names":["_resolveComponent","_openBlock","_createBlock","buttonClass","showButtons","_renderSlot","_withCtx","onBeforeEnter","onEnter","onLeave","_createElementBlock","_Fragment","_renderList","_normalizeClass","isAnimating","_normalizeStyle","onSelect","_createVNode","isSelected"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"related-prompts-tag-list.vue2.js","sources":["../../../../../src/x-modules/related-prompts/components/related-prompts-tag-list.vue"],"sourcesContent":["<template>\n <SlidingPanel\n :key=\"x.query.search\"\n :reset-on-content-change=\"false\"\n :button-class=\"buttonClass\"\n :show-buttons=\"showButtons && selectedPromptIndex === -1\"\n :scroll-container-class=\"['x-related-prompts-tag-list-scroll-container', scrollContainerClass]\"\n >\n <template #sliding-panel-left-button>\n <!--\n @slot sliding-panel-left-button - The button to be displayed on the left side of the sliding panel.\n -->\n <slot name=\"sliding-panel-left-button\" />\n </template>\n <transition-group\n @before-enter=\"onBeforeEnter\"\n @enter=\"onEnter\"\n @leave=\"onLeave\"\n class=\"x-related-prompts-tag-list\"\n :css=\"false\"\n tag=\"ul\"\n appear\n >\n <li\n v-for=\"{ index, ...relatedPrompt } in visibleRelatedPrompts\"\n ref=\"listItems\"\n :key=\"relatedPrompt.suggestionText\"\n class=\"x-related-prompts-tag-list-item\"\n :class=\"[tagClass, tagColors && tagColors[index % tagColors.length]]\"\n :data-index=\"index\"\n :style=\"isAnimating && { pointerEvents: 'none' }\"\n data-test=\"related-prompts-tag-list-item\"\n >\n <!--\n @slot - The slot to render related prompt information.\n @prop {Object} relatedPrompt - The related prompt object.\n @prop {Function} onSelect - The function to select the related prompt.\n @prop {Boolean} isSelected - Indicates if the related prompt is currently selected.\n -->\n <slot\n :relatedPrompt=\"relatedPrompt\"\n :onSelect=\"() => onSelect(index)\"\n :isSelected=\"isSelected(index)\"\n >\n <DisplayEmitter\n :payload=\"relatedPrompt.toolingDisplayTagging\"\n :eventMetadata=\"{\n feature: 'related-prompts',\n displayOriginalQuery: x.query.searchBox,\n replaceable: false\n }\"\n >\n <RelatedPrompt\n @click=\"onSelect(index)\"\n :related-prompt=\"relatedPrompt\"\n :selected=\"isSelected(index)\"\n />\n </DisplayEmitter>\n </slot>\n </li>\n </transition-group>\n <template #sliding-panel-right-button>\n <!--\n @slot sliding-panel-right-button - The button to be displayed on the right side of the sliding panel.\n -->\n <slot name=\"sliding-panel-right-button\" />\n </template>\n </SlidingPanel>\n</template>\n\n<script lang=\"ts\">\n import { RelatedPrompt as RelatedPromptModel } from '@empathyco/x-types';\n import { computed, defineComponent, PropType, ref, watch } from 'vue';\n import SlidingPanel from '../../../components/sliding-panel.vue';\n import { relatedPromptsXModule } from '../x-module';\n import { use$x, useState } from '../../../composables';\n import DisplayEmitter from '../../../components/display-emitter.vue';\n import RelatedPrompt from './related-prompt.vue';\n\n /**\n * This component shows the list of `RelatedPrompts` components.\n *\n * If the default slot is reimplemented in the consumer, `onSelect` function will be\n * necessary to handle the selection of the related prompt and to trigger the stagger-fade-slide animation.\n *\n * @public\n */\n export default defineComponent({\n name: 'RelatedPromptsTagList',\n xModule: relatedPromptsXModule.name,\n components: { DisplayEmitter, RelatedPrompt, SlidingPanel },\n props: {\n /**\n * The CSS class for the left and right button of the sliding panel.\n *\n * @public\n */\n buttonClass: String,\n /**\n * The boolean prop to handle the visiblity of sliding pannel buttons.\n *\n * @public\n */\n showButtons: { type: Boolean, default: true },\n /**\n * The CSS class for the wrapper of all the related prompt wrapper elements.\n *\n * @public\n */\n scrollContainerClass: String,\n /**\n * The CSS class for all the related prompt wrapper elements.\n *\n * @public\n */\n tagClass: String,\n /**\n * Array of colors to apply to the related prompts. It will be applied to tag\n * elements cyclically according to their index in the nex way: `tagColors[index % tagColors.length]`.\n *\n * @public\n */\n tagColors: Array as PropType<string[]>,\n /**\n * The duration of the total animation in milliseconds.\n *\n * @public\n */\n animationDurationInMs: {\n type: Number,\n default: 700\n }\n },\n setup(props) {\n const x = use$x();\n const { relatedPrompts, selectedPrompt: selectedPromptIndex } = useState('relatedPrompts', [\n 'relatedPrompts',\n 'selectedPrompt'\n ]);\n\n const clickedListItemIndex = ref<number | null>(null);\n const initialOffsetLefts: Record<number, number> = {};\n const isAnimating = ref(false);\n const listItems = ref<HTMLElement[]>([]);\n\n const sortedListItems = computed<HTMLElement[]>(() =>\n [...listItems.value].sort(\n (a: HTMLElement, b: HTMLElement) =>\n Number.parseInt(b.getAttribute('data-index')!) -\n Number.parseInt(a.getAttribute('data-index')!)\n )\n );\n\n // The duration of a single animation (enter or leave) in milliseconds\n // if a related prompt is clicked (clickedListItemIndex.value !== null), the duration is divided by the number of related\n // prompts -1 (the clicked one is synchronized with the last one to leave or the first one to enter)\n const singleAnimationDurationInMs = computed(\n () =>\n props.animationDurationInMs /\n (clickedListItemIndex.value !== null\n ? relatedPrompts.value.length - 1\n : relatedPrompts.value.length)\n );\n\n const indexRelatedPrompts = computed(() =>\n (relatedPrompts.value as RelatedPromptModel[]).map(\n (relatedPrompt: RelatedPromptModel, index: number) => ({\n ...relatedPrompt,\n index\n })\n )\n );\n\n const visibleRelatedPrompts = computed(() => {\n return selectedPromptIndex.value !== -1\n ? [indexRelatedPrompts.value[selectedPromptIndex.value]]\n : indexRelatedPrompts.value;\n });\n\n let timeOutId: number;\n const resetTransitionStyle = () => {\n if (timeOutId) {\n clearTimeout(timeOutId);\n }\n\n isAnimating.value = true;\n timeOutId = +setTimeout(() => {\n isAnimating.value = false;\n clickedListItemIndex.value = null;\n\n sortedListItems.value.forEach(element => {\n element.style.cssText\n .split(';')\n .map(rule => rule.split(':')[0]?.trim())\n .forEach(property => {\n if (property !== 'width') {\n element.style.removeProperty(property);\n }\n });\n });\n }, props.animationDurationInMs);\n };\n\n const onSelect = (selectedIndex: number): void => {\n resetTransitionStyle();\n\n clickedListItemIndex.value = selectedIndex;\n const selected: HTMLElement = sortedListItems.value.find(\n element => Number.parseInt(element.getAttribute('data-index')!) === selectedIndex\n )!;\n\n // selectedPromptIndex.value === -1 ? 'SELECTING' : 'DESELECTING'\n if (selectedPromptIndex.value === -1) {\n // Prepare all the elements for the leave animation (~ 'beforeLeave' hook). Remember the elements are\n // sorted in descending order by index.\n sortedListItems.value.forEach(element => {\n const index = Number.parseInt(element.getAttribute('data-index')!);\n\n initialOffsetLefts[index] = element.offsetLeft;\n element.style.left = `${element.offsetLeft}px`;\n element.style.position = 'absolute';\n element.style.transitionDuration = `${singleAnimationDurationInMs.value}ms`;\n\n if (index !== selectedIndex) {\n element.style.opacity = '1';\n element.style.transitionDelay = `${\n (index < selectedIndex ? index : index - 1) * singleAnimationDurationInMs.value\n }ms`;\n }\n });\n\n // Synchronize the transition delay of the selected element with the last\n // element to leave\n selected.style.transitionDelay = `${\n (relatedPrompts.value.length > 1 ? relatedPrompts.value.length - 2 : 0) *\n singleAnimationDurationInMs.value\n }ms`;\n\n // Trigger the animation (selecting) for the selected element\n requestAnimationFrame(() => {\n const maxWidth = getComputedStyle(selected).maxWidth;\n\n selected.style.left = '0px';\n selected.style.setProperty(\n 'width',\n `${maxWidth !== 'none' ? maxWidth : '100%'}`,\n 'important'\n );\n });\n } else {\n // Prepare the selected element for the deselecting animation\n selected.style.transitionDuration = `${singleAnimationDurationInMs.value}ms`;\n selected.style.left = '0px';\n selected.style.position = 'absolute';\n\n // Trigger the animation (deselecting) for the selected element\n selected.style.removeProperty('width');\n requestAnimationFrame(() => {\n selected.style.left = `${initialOffsetLefts[selectedIndex]}px`;\n });\n }\n\n x.emit('UserSelectedARelatedPrompt', selectedIndex, {\n relatedPrompt: relatedPrompts.value[selectedIndex],\n selectedPrompt: selectedPromptIndex.value\n });\n };\n\n const onBeforeEnter = (el: Element) => {\n const element = el as HTMLElement;\n const index = Number.parseInt(element.getAttribute('data-index')!);\n\n // Prepare the element for the enter animation\n element.style.opacity = '0';\n element.style.transform = 'translateY(5px)';\n element.style.transitionDelay = `${\n (clickedListItemIndex.value !== null && index > clickedListItemIndex.value\n ? index - 1\n : index) * singleAnimationDurationInMs.value\n }ms`;\n element.style.transitionDuration = `${singleAnimationDurationInMs.value}ms`;\n };\n\n const onEnter = (el: Element, done: () => void) => {\n const element = el as HTMLElement;\n const index = Number.parseInt(element.getAttribute('data-index')!);\n\n // Also part of the preparation for the enter animation, but it needs to be done\n // once the element is inserted in DOM (if not the offsetLeft will be always 0)\n element.style.left = `${initialOffsetLefts[index] ?? element.offsetLeft}px`;\n\n // trigger enter animation\n requestAnimationFrame(() => {\n element.style.opacity = '1';\n element.style.position = 'absolute';\n element.style.transform = 'translateY(0)';\n });\n\n done();\n };\n\n const onLeave = (el: Element, done: () => void) => {\n const element = el as HTMLElement;\n\n // trigger leave animation\n requestAnimationFrame(() => {\n element.style.opacity = '0';\n element.style.transform = 'translateY(5px)';\n });\n\n // Wait for the animation to finish (done() exectution extracts the element from the DOM)\n setTimeout(done, props.animationDurationInMs);\n };\n\n const isSelected = (index: number): boolean => selectedPromptIndex.value === index;\n\n // Changing the query will trigger the appear animation, so we need to reset the\n // style after it finishes\n watch(() => x.query.search, resetTransitionStyle, { immediate: true });\n\n return {\n isSelected,\n onSelect,\n onBeforeEnter,\n onEnter,\n onLeave,\n selectedPromptIndex,\n visibleRelatedPrompts,\n listItems,\n isAnimating,\n x\n };\n }\n });\n</script>\n<style lang=\"css\">\n .x-related-prompts-tag-list-scroll-container {\n height: 100%;\n position: relative;\n }\n .x-related-prompts-tag-list {\n display: flex;\n gap: 16px;\n min-width: 100%;\n }\n .x-related-prompts-tag-list-item {\n height: 100%;\n }\n</style>\n"],"names":["DisplayEmitter"],"mappings":"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"}
1
+ {"version":3,"file":"related-prompts-tag-list.vue2.js","sources":["../../../../../src/x-modules/related-prompts/components/related-prompts-tag-list.vue"],"sourcesContent":["<template>\n <SlidingPanel\n :key=\"x.query.search\"\n :reset-on-content-change=\"false\"\n :button-class=\"buttonClass\"\n :show-buttons=\"showButtons && selectedPromptIndex === -1\"\n :scroll-container-class=\"['x-related-prompts-tag-list-scroll-container', scrollContainerClass]\"\n >\n <template #sliding-panel-left-button>\n <!--\n @slot sliding-panel-left-button - The button to be displayed on the left side of the sliding panel.\n -->\n <slot name=\"sliding-panel-left-button\" />\n </template>\n <transition-group\n @before-enter=\"onBeforeEnter\"\n @enter=\"onEnter\"\n @leave=\"onLeave\"\n class=\"x-related-prompts-tag-list\"\n :css=\"false\"\n tag=\"ul\"\n appear\n >\n <li\n v-for=\"{ index, ...relatedPrompt } in visibleRelatedPrompts\"\n ref=\"listItems\"\n :key=\"relatedPrompt.suggestionText\"\n class=\"x-related-prompts-tag-list-item\"\n :class=\"[tagClass, tagColors && tagColors[index % tagColors.length]]\"\n :data-index=\"index\"\n :style=\"isAnimating && { pointerEvents: 'none' }\"\n data-test=\"related-prompts-tag-list-item\"\n >\n <!--\n @slot - The slot to render related prompt information.\n @prop {Object} relatedPrompt - The related prompt object.\n @prop {Function} onSelect - The function to select the related prompt.\n @prop {Boolean} isSelected - Indicates if the related prompt is currently selected.\n -->\n <slot\n :relatedPrompt=\"relatedPrompt\"\n :onSelect=\"() => onSelect(index)\"\n :isSelected=\"isSelected(index)\"\n >\n <DisplayEmitter\n :payload=\"relatedPrompt.toolingDisplayTagging\"\n :eventMetadata=\"{\n feature: 'related-prompts',\n displayOriginalQuery: x.query.searchBox,\n replaceable: false\n }\"\n >\n <RelatedPrompt\n @click=\"onSelect(index)\"\n :related-prompt=\"relatedPrompt\"\n :selected=\"isSelected(index)\"\n >\n <template #related-prompt-extra-content>\n <slot name=\"related-prompt-extra-content\" :relatedPrompt=\"relatedPrompt\" />\n </template>\n </RelatedPrompt>\n </DisplayEmitter>\n </slot>\n </li>\n </transition-group>\n <template #sliding-panel-right-button>\n <!--\n @slot sliding-panel-right-button - The button to be displayed on the right side of the sliding panel.\n -->\n <slot name=\"sliding-panel-right-button\" />\n </template>\n </SlidingPanel>\n</template>\n\n<script lang=\"ts\">\n import { RelatedPrompt as RelatedPromptModel } from '@empathyco/x-types';\n import { computed, defineComponent, PropType, ref, watch } from 'vue';\n import SlidingPanel from '../../../components/sliding-panel.vue';\n import { relatedPromptsXModule } from '../x-module';\n import { use$x, useState } from '../../../composables';\n import DisplayEmitter from '../../../components/display-emitter.vue';\n import RelatedPrompt from './related-prompt.vue';\n\n /**\n * This component shows the list of `RelatedPrompts` components.\n *\n * If the default slot is reimplemented in the consumer, `onSelect` function will be\n * necessary to handle the selection of the related prompt and to trigger the stagger-fade-slide animation.\n *\n * @public\n */\n export default defineComponent({\n name: 'RelatedPromptsTagList',\n xModule: relatedPromptsXModule.name,\n components: { DisplayEmitter, RelatedPrompt, SlidingPanel },\n props: {\n /**\n * The CSS class for the left and right button of the sliding panel.\n *\n * @public\n */\n buttonClass: String,\n /**\n * The boolean prop to handle the visiblity of sliding pannel buttons.\n *\n * @public\n */\n showButtons: { type: Boolean, default: true },\n /**\n * The CSS class for the wrapper of all the related prompt wrapper elements.\n *\n * @public\n */\n scrollContainerClass: String,\n /**\n * The CSS class for all the related prompt wrapper elements.\n *\n * @public\n */\n tagClass: String,\n /**\n * Array of colors to apply to the related prompts. It will be applied to tag\n * elements cyclically according to their index in the nex way: `tagColors[index % tagColors.length]`.\n *\n * @public\n */\n tagColors: Array as PropType<string[]>,\n /**\n * The duration of the total animation in milliseconds.\n *\n * @public\n */\n animationDurationInMs: {\n type: Number,\n default: 700\n }\n },\n setup(props) {\n const x = use$x();\n const { relatedPrompts, selectedPrompt: selectedPromptIndex } = useState('relatedPrompts', [\n 'relatedPrompts',\n 'selectedPrompt'\n ]);\n\n const clickedListItemIndex = ref<number | null>(null);\n const initialOffsetLefts: Record<number, number> = {};\n const isAnimating = ref(false);\n const listItems = ref<HTMLElement[]>([]);\n\n const sortedListItems = computed<HTMLElement[]>(() =>\n [...listItems.value].sort(\n (a: HTMLElement, b: HTMLElement) =>\n Number.parseInt(b.getAttribute('data-index')!) -\n Number.parseInt(a.getAttribute('data-index')!)\n )\n );\n\n // The duration of a single animation (enter or leave) in milliseconds\n // if a related prompt is clicked (clickedListItemIndex.value !== null), the duration is divided by the number of related\n // prompts -1 (the clicked one is synchronized with the last one to leave or the first one to enter)\n const singleAnimationDurationInMs = computed(\n () =>\n props.animationDurationInMs /\n (clickedListItemIndex.value !== null\n ? relatedPrompts.value.length - 1\n : relatedPrompts.value.length)\n );\n\n const indexRelatedPrompts = computed(() =>\n (relatedPrompts.value as RelatedPromptModel[]).map(\n (relatedPrompt: RelatedPromptModel, index: number) => ({\n ...relatedPrompt,\n index\n })\n )\n );\n\n const visibleRelatedPrompts = computed(() => {\n return selectedPromptIndex.value !== -1\n ? [indexRelatedPrompts.value[selectedPromptIndex.value]]\n : indexRelatedPrompts.value;\n });\n\n let timeOutId: number;\n const resetTransitionStyle = () => {\n if (timeOutId) {\n clearTimeout(timeOutId);\n }\n\n isAnimating.value = true;\n timeOutId = +setTimeout(() => {\n isAnimating.value = false;\n clickedListItemIndex.value = null;\n\n sortedListItems.value.forEach(element => {\n element.style.cssText\n .split(';')\n .map(rule => rule.split(':')[0]?.trim())\n .forEach(property => {\n if (property !== 'width') {\n element.style.removeProperty(property);\n }\n });\n });\n }, props.animationDurationInMs);\n };\n\n const onSelect = (selectedIndex: number): void => {\n resetTransitionStyle();\n\n clickedListItemIndex.value = selectedIndex;\n const selected: HTMLElement = sortedListItems.value.find(\n element => Number.parseInt(element.getAttribute('data-index')!) === selectedIndex\n )!;\n\n // selectedPromptIndex.value === -1 ? 'SELECTING' : 'DESELECTING'\n if (selectedPromptIndex.value === -1) {\n // Prepare all the elements for the leave animation (~ 'beforeLeave' hook). Remember the elements are\n // sorted in descending order by index.\n sortedListItems.value.forEach(element => {\n const index = Number.parseInt(element.getAttribute('data-index')!);\n\n initialOffsetLefts[index] = element.offsetLeft;\n element.style.left = `${element.offsetLeft}px`;\n element.style.position = 'absolute';\n element.style.transitionDuration = `${singleAnimationDurationInMs.value}ms`;\n\n if (index !== selectedIndex) {\n element.style.opacity = '1';\n element.style.transitionDelay = `${\n (index < selectedIndex ? index : index - 1) * singleAnimationDurationInMs.value\n }ms`;\n }\n });\n\n // Synchronize the transition delay of the selected element with the last\n // element to leave\n selected.style.transitionDelay = `${\n (relatedPrompts.value.length > 1 ? relatedPrompts.value.length - 2 : 0) *\n singleAnimationDurationInMs.value\n }ms`;\n\n // Trigger the animation (selecting) for the selected element\n requestAnimationFrame(() => {\n const maxWidth = getComputedStyle(selected).maxWidth;\n\n selected.style.left = '0px';\n selected.style.setProperty(\n 'width',\n `${maxWidth !== 'none' ? maxWidth : '100%'}`,\n 'important'\n );\n });\n } else {\n // Prepare the selected element for the deselecting animation\n selected.style.transitionDuration = `${singleAnimationDurationInMs.value}ms`;\n selected.style.left = '0px';\n selected.style.position = 'absolute';\n\n // Trigger the animation (deselecting) for the selected element\n selected.style.removeProperty('width');\n requestAnimationFrame(() => {\n selected.style.left = `${initialOffsetLefts[selectedIndex]}px`;\n });\n }\n\n x.emit('UserSelectedARelatedPrompt', selectedIndex, {\n relatedPrompt: relatedPrompts.value[selectedIndex],\n selectedPrompt: selectedPromptIndex.value\n });\n };\n\n const onBeforeEnter = (el: Element) => {\n const element = el as HTMLElement;\n const index = Number.parseInt(element.getAttribute('data-index')!);\n\n // Prepare the element for the enter animation\n element.style.opacity = '0';\n element.style.transform = 'translateY(5px)';\n element.style.transitionDelay = `${\n (clickedListItemIndex.value !== null && index > clickedListItemIndex.value\n ? index - 1\n : index) * singleAnimationDurationInMs.value\n }ms`;\n element.style.transitionDuration = `${singleAnimationDurationInMs.value}ms`;\n };\n\n const onEnter = (el: Element, done: () => void) => {\n const element = el as HTMLElement;\n const index = Number.parseInt(element.getAttribute('data-index')!);\n\n // Also part of the preparation for the enter animation, but it needs to be done\n // once the element is inserted in DOM (if not the offsetLeft will be always 0)\n element.style.left = `${initialOffsetLefts[index] ?? element.offsetLeft}px`;\n\n // trigger enter animation\n requestAnimationFrame(() => {\n element.style.opacity = '1';\n element.style.position = 'absolute';\n element.style.transform = 'translateY(0)';\n });\n\n done();\n };\n\n const onLeave = (el: Element, done: () => void) => {\n const element = el as HTMLElement;\n\n // trigger leave animation\n requestAnimationFrame(() => {\n element.style.opacity = '0';\n element.style.transform = 'translateY(5px)';\n });\n\n // Wait for the animation to finish (done() exectution extracts the element from the DOM)\n setTimeout(done, props.animationDurationInMs);\n };\n\n const isSelected = (index: number): boolean => selectedPromptIndex.value === index;\n\n // Changing the query will trigger the appear animation, so we need to reset the\n // style after it finishes\n watch(() => x.query.search, resetTransitionStyle, { immediate: true });\n\n return {\n isSelected,\n onSelect,\n onBeforeEnter,\n onEnter,\n onLeave,\n selectedPromptIndex,\n visibleRelatedPrompts,\n listItems,\n isAnimating,\n x\n };\n }\n });\n</script>\n<style lang=\"css\">\n .x-related-prompts-tag-list-scroll-container {\n height: 100%;\n position: relative;\n }\n .x-related-prompts-tag-list {\n display: flex;\n gap: 16px;\n min-width: 100%;\n }\n .x-related-prompts-tag-list-item {\n height: 100%;\n }\n</style>\n"],"names":["DisplayEmitter"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"url-handler.vue.js","sources":["../../../../../src/x-modules/url/components/url-handler.vue"],"sourcesContent":["<template>\n <GlobalEvents @pageshow=\"onPageShow\" @popstate=\"emitEvents\" target=\"window\" />\n</template>\n\n<script lang=\"ts\">\n import { Dictionary, objectFilter } from '@empathyco/x-utils';\n import { computed, defineComponent, inject, onMounted, ref } from 'vue';\n import { GlobalEvents } from 'vue-global-events';\n import { FeatureLocation } from '../../../types/origin';\n import { UrlParams } from '../../../types/url-params';\n import { isArrayEmpty } from '../../../utils/array';\n import { WireMetadata } from '../../../wiring/wiring.types';\n import { SnippetConfig } from '../../../x-installer/api/api.types';\n import { initialUrlState } from '../store/initial-state';\n import { UrlParamValue } from '../store/types';\n import { urlXModule } from '../x-module';\n import { use$x } from '../../../composables/use-$x';\n import { useState } from '../../../composables/use-state';\n\n interface ParsedUrlParams {\n all: UrlParams;\n extra: Dictionary<unknown>;\n }\n\n /**\n * This component manages the browser URL parameters to preserve them through reloads and browser\n * history navigation. It allow to configure the default url parameter names using its attributes.\n * This component doesn't render elements to the DOM.\n *\n * @public\n */\n export default defineComponent({\n name: 'UrlHandler',\n components: {\n GlobalEvents\n },\n xModule: urlXModule.name,\n setup: function (props, { attrs }) {\n const $x = use$x();\n\n const initialExtraParams = useState('url', ['initialExtraParams']).initialExtraParams;\n\n /**\n * The {@link SnippetConfig} provided by an ancestor.\n *\n * @internal\n */\n const snippetConfig = inject<SnippetConfig | undefined>('snippetConfig');\n\n /**\n * Flag to know if the params were already loaded from the URL.\n *\n * @internal\n */\n const urlLoaded = ref(false);\n\n /**\n * The page URL. It is used to compare against the current URL to check navigation state.\n *\n * @internal\n */\n const url = ref<URL | undefined>(undefined);\n\n /**\n * Flag to know if the page has been persisted by the browser's back-forward cache.\n *\n * @internal\n */\n const isPagePersisted = ref(false);\n\n /**\n * Computed to know which params we must get from URL. It gets the params names from the initial\n * state, to get all default params names, and also from the `$attrs` to get the extra params\n * names to take into account.\n *\n * @returns An array with the name of the params.\n *\n * @internal\n */\n const managedParamsNames = computed(() => {\n return Object.keys({ ...initialUrlState, ...attrs });\n });\n\n /**\n * Returns the mapping of the param keys used in the URL is configured through $attrs. This way\n * we can support any param and extra param, no matters its name.\n *\n * @param paramName - The param name to get the Url key.\n * @returns The key used in the URL for the `paramName` passed.\n *\n * @internal\n */\n const getUrlKey = (paramName: string): string => {\n const paramValue = attrs[paramName];\n return typeof paramValue === 'string' ? paramValue : paramName;\n };\n\n /**\n * Deletes all the parameters in the passed URL.\n *\n * @param url - The URL to remove parameters from.\n * @internal\n */\n const deleteUrlParameters = (url: URL): void => {\n managedParamsNames.value.forEach(paramName =>\n url.searchParams.delete(getUrlKey(paramName))\n );\n };\n\n /**\n * Sorts the params in a tuple array [key,value] to generate always the same URL with the params\n * in the same order.\n *\n * @param urlParams - The {@link UrlParams} to sort.\n * @returns An array of tuples with the key-value of each paramter, sorted by key.\n * @internal\n */\n const sortParams = (urlParams: UrlParams): Array<[string, unknown]> => {\n return Object.entries(urlParams).sort(([param1], [param2]) => {\n return param1 < param2 ? -1 : 1;\n });\n };\n\n /**\n * Set all the provided parameters to the url with the mapped key.\n *\n * @param url - The current URL.\n * @param urlParams - The list of parameters to add.\n * @remarks The params are filtered because there maybe received extra params which will not be\n * managed by URL. This is defined by the `managedParamsNames` computed. Also, the parameters\n * are sorted Alphabetically to produce always the same URL with the same parameters.This is\n * important for SEO purposes.\n *\n * @internal\n */\n const setUrlParameters = (url: URL, urlParams: UrlParams): void => {\n // Only when there is a query the rest of the parameters are valid.\n if (!urlParams.query) {\n return;\n }\n const filteredParams = objectFilter(urlParams, paramName =>\n managedParamsNames.value.includes(paramName as string)\n );\n const sortedParameters = sortParams(filteredParams);\n sortedParameters.forEach(([paramName, paramValue]) => {\n const urlParamKey = getUrlKey(paramName);\n if (Array.isArray(paramValue)) {\n paramValue.forEach(value => {\n url.searchParams.append(urlParamKey, String(value));\n });\n } else {\n url.searchParams.set(urlParamKey, String(paramValue));\n }\n });\n };\n\n /**\n * Updates the browser URL with the passed `newUrlParams` and using the browser history method\n * passed as `historyMethod`. It only updates the browser history if the new URL is different\n * from the current.\n *\n * @param newUrlParams - The new params to add to the browser URL.\n * @param historyMethod - The browser history method used to add the new URL.\n *\n * @internal\n */\n const updateUrl = (\n newUrlParams: UrlParams,\n historyMethod: History['pushState'] | History['replaceState']\n ): void => {\n if (urlLoaded.value) {\n const newUrl = new URL(window.location.href);\n deleteUrlParameters(newUrl);\n setUrlParameters(newUrl, newUrlParams);\n\n newUrl.href = newUrl.href.replace(/\\+/g, '%20');\n\n if (newUrl.href !== window.location.href) {\n historyMethod({ ...window.history.state }, document.title, newUrl.href);\n }\n url.value = newUrl;\n }\n };\n\n /**\n * Updates the browser URL with the new {@link UrlParams} using the history `pushState` method.\n *\n * @param newUrlParams - The new params to update browser URL.\n */\n $x.on('PushableUrlStateUpdated', false).subscribe((newUrlParams: UrlParams) => {\n updateUrl(newUrlParams, window.history.pushState.bind(window.history));\n });\n\n /**\n * Updates the browser URL with the new {@link UrlParams} using the history `replaceState`\n * method.\n *\n * @param newUrlParams - The new params to update browser URL.\n */\n $x.on('ReplaceableUrlStateUpdated', false).subscribe((newUrlParams: UrlParams) => {\n updateUrl(newUrlParams, window.history.replaceState.bind(window.history));\n });\n\n /**\n * Handler of the\n * [pageshow](https://developer.mozilla.org/en-US/docs/Web/API/Window/pageshow_event)\n * event.\n *\n * @remarks The pageshow event is listened to check if the browser has performed a navigation\n * using the back-forward cache. This information is available in the\n * PageTransitionEvent.persisted property.\n *\n * @param event - The page transition event.\n * @internal\n */\n const onPageShow = (event: PageTransitionEvent) => {\n isPagePersisted.value = event.persisted;\n if (event.persisted) {\n // The internal url is reset due to the back-forward cache storing the previous value which\n // is no longer valid.\n url.value = undefined;\n }\n };\n\n /**\n * Returns the URL param value parsed depending on its type in the initial store state. As we\n * can not know what type can have an extra param, all extra params are parsed as strings. We\n * know if it is an extra param because it is not in the initial state.\n *\n * @param name - The name of the param in {@link UrlParams}.\n * @param value - The `URLSearchParams` value as an arry of strings.\n * @returns The parsed value.\n *\n * @internal\n */\n const parseUrlParam = (name: string, value: string[]): UrlParamValue => {\n switch (typeof initialUrlState[name]) {\n case 'number':\n return Number(value[0]);\n case 'boolean':\n return value[0].toLowerCase() === 'true';\n case 'string':\n return value[0];\n default:\n // array\n return value;\n }\n };\n\n /**\n * Gets the {@link UrlParams} from the URL, including only the params defined by `paramsNames`.\n *\n * @returns ParsedUrlParams obtained from URL.\n * @internal\n */\n const parseUrlParams = (): ParsedUrlParams => {\n const urlSearchParams = new URL(window.location.href).searchParams;\n return managedParamsNames.value.reduce<ParsedUrlParams>(\n (params, name) => {\n const urlKey = getUrlKey(name);\n if (urlSearchParams.has(urlKey)) {\n if (name in initialUrlState) {\n const urlValue = urlSearchParams.getAll(urlKey);\n params.all[name] = parseUrlParam(name, urlValue);\n } else {\n params.all[name] = params.extra[name] = urlSearchParams.get(urlKey);\n }\n }\n return params;\n },\n { all: { ...initialUrlState }, extra: { ...initialExtraParams.value } }\n );\n };\n\n /**\n * Check if the navigation is from a product page.\n *\n * @remarks Due to Safari 14 not supporting the new and standard PerformanceNavigationTiming\n * API, we are falling back to the deprecated one, PerformanceNavigation. We also fallback to\n * this API whenever we get a navigationType equal to reload, because Safari has a bug that the\n * navigationType is permanently set to reload after you have reload the page and it never\n * resets. As some browsers have a back-forward cache implemented, we also take into account if\n * the page is persisted.\n *\n * @returns True if the navigation is from a product page, false otherwise.\n * @internal\n */\n const isNavigatingFromPdp = (): boolean => {\n const isPagePersistedValue = isPagePersisted.value;\n const navigationEntries = window.performance.getEntriesByType('navigation');\n const navigationType = (navigationEntries[0] as PerformanceNavigationTiming)?.type;\n const useFallbackStrategy =\n !navigationEntries.length &&\n (isArrayEmpty(navigationEntries) || navigationType === 'reload');\n\n // Reset internal isPagePersisted property value\n isPagePersisted.value = false;\n\n if (useFallbackStrategy) {\n const isNavigatingInSpa = !!snippetConfig?.isSpa && navigationType === 'navigate';\n return navigationType === 'back_forward' || isNavigatingInSpa || isPagePersistedValue;\n } else {\n const isNavigatingInSpa = !!snippetConfig?.isSpa && navigationType === 'navigate';\n return navigationType === 'back_forward' || isNavigatingInSpa || isPagePersistedValue;\n }\n };\n\n /**\n * Detects the {@link FeatureLocation} used to build the\n * {@link QueryOriginInit} data.\n *\n * @returns The {@link FeatureLocation}.\n * @internal\n */\n const detectLocation = (): FeatureLocation => {\n const currentUrl = new URL(window.location.href);\n const previousUrl = url.value;\n url.value = currentUrl;\n\n const isInternalNavigation =\n previousUrl?.search !== currentUrl.search &&\n previousUrl?.pathname === currentUrl.pathname;\n if (isInternalNavigation) {\n return 'url_history';\n }\n\n if (isNavigatingFromPdp()) {\n return 'url_history_pdp';\n }\n\n return 'external';\n };\n\n /**\n * Creates the wire metadata to include in every emitted {@link XEvent}.\n *\n * @returns The {@link WireMetadata}.\n * @internal\n */\n const createWireMetadata = (): Pick<WireMetadata, 'feature' | 'location'> => {\n return {\n feature: 'url',\n location: detectLocation()\n };\n };\n\n /**\n * Emits the {@link UrlXEvents.ParamsLoadedFromUrl} XEvent,\n * the {@link UrlXEvents.ExtraParamsLoadedFromUrl} XEvent and, if there is query, also emits\n * the {@link XEventsTypes.UserOpenXProgrammatically}.\n *\n * @internal\n */\n const emitEvents = () => {\n const { all, extra } = parseUrlParams();\n const metadata = createWireMetadata();\n $x.emit('ParamsLoadedFromUrl', all, metadata);\n $x.emit('ExtraParamsLoadedFromUrl', extra, metadata);\n if (all.query) {\n $x.emit('UserOpenXProgrammatically', undefined, metadata);\n }\n urlLoaded.value = true;\n };\n\n /**\n * To emit the Url events just when the URL is load, and before the components mounted events\n * and state changes, we do it in the created of this component.\n */\n onMounted(() => {\n emitEvents();\n });\n\n return {\n onPageShow,\n emitEvents\n };\n }\n });\n</script>\n\n<docs lang=\"mdx\">\n## Events\n\nThis component emits the following events:\n\n- [`ParamsLoadedFromUrl`](https://github.com/empathyco/x/blob/main/packages/x-components/src/wiring/events.types.ts)\n- [`ExtraParamsLoadedFromUrl`](https://github.com/empathyco/x/blob/main/packages/x-components/src/wiring/events.types.ts)\n- [`UserOpenXProgrammatically`](https://github.com/empathyco/x/blob/main/packages/x-components/src/wiring/events.types.ts)\n\n## See it in action\n\nThis component manages the browser URL parameters to preserve them through reloads and browser\nhistory navigation. It allow to configure the default url parameter names using its attributes. This\ncomponent doesn't render elements to the DOM.\n\n_Try to make some requests and take a look to the url!_\n\n```vue\n<template>\n <UrlHandler />\n</template>\n\n<script>\n import { UrlHandler } from '@empathyco/x-components/url-handler';\n\n export default {\n name: 'UrlHandlerDemo',\n components: {\n UrlHandler\n }\n };\n</script>\n```\n\n### Play with props\n\nIn this example, the `UrlHandler` component changes the following query parameter names:\n\n- `query` to be `q`.\n- `page` to be `p`.\n- `filter` to be `f`\n- `sort` to be `s`\n\n_Try to make some requests and take a look to the url!_\n\n```vue\n<template>\n <UrlHandler query=\"q\" page=\"p\" filter=\"f\" sort=\"s\" />\n</template>\n\n<script>\n import { UrlHandler } from '@empathyco/x-components/url-handler';\n\n export default {\n name: 'UrlHandlerDemo',\n components: {\n UrlHandler\n }\n };\n</script>\n```\n\n### Play with events\n\nThe `UrlHandler` will emit the `ParamsLoadedFromUrl` when the page is loaded.\n\nThe `UrlHandler` will emit the `ExtraParamsLoadedFromUrl` when the page is loaded with an extra\nparam configured and with a value in URL.\n\nThe `UrlHandler` will emit the `UserOpenXProgrammatically` when the page is loaded with a query in\nthe URL.\n</docs>\n"],"names":["_resolveComponent","_openBlock","_createBlock"],"mappings":";;;;;kCACEA,gBAA8E,CAAA,cAAA,CAAA,CAAA;AAAxC,EAAA,OAAAC,SAAA,EAAoB,EAAAC,WAAA,CAAA,uBAAA,EAAA;AAAA,IAAE,YAAO,IAAQ,CAAA,UAAA;AAAA,IAAA,UAAA,EAAA,IAAA,CAAA,UAAA;;;;;;;;"}
1
+ {"version":3,"file":"url-handler.vue.js","sources":["../../../../../src/x-modules/url/components/url-handler.vue"],"sourcesContent":["<template>\n <GlobalEvents @pageshow=\"onPageShow\" @popstate=\"emitEvents\" target=\"window\" />\n</template>\n\n<script lang=\"ts\">\n import { Dictionary, objectFilter } from '@empathyco/x-utils';\n import { computed, defineComponent, inject, onMounted, ref } from 'vue';\n import { GlobalEvents } from 'vue-global-events';\n import { FeatureLocation } from '../../../types/origin';\n import { UrlParams } from '../../../types/url-params';\n import { isArrayEmpty } from '../../../utils/array';\n import { WireMetadata } from '../../../wiring/wiring.types';\n import { SnippetConfig } from '../../../x-installer/api/api.types';\n import { initialUrlState } from '../store/initial-state';\n import { UrlParamValue } from '../store/types';\n import { urlXModule } from '../x-module';\n import { use$x } from '../../../composables/use-$x';\n import { useState } from '../../../composables/use-state';\n\n interface ParsedUrlParams {\n all: UrlParams;\n extra: Dictionary<unknown>;\n }\n\n /**\n * This component manages the browser URL parameters to preserve them through reloads and browser\n * history navigation. It allow to configure the default url parameter names using its attributes.\n * This component doesn't render elements to the DOM.\n *\n * @public\n */\n export default defineComponent({\n name: 'UrlHandler',\n components: {\n GlobalEvents\n },\n xModule: urlXModule.name,\n setup: function (props, { attrs }) {\n const $x = use$x();\n\n const initialExtraParams = useState('url', ['initialExtraParams']).initialExtraParams;\n\n /**\n * The {@link SnippetConfig} provided by an ancestor.\n *\n * @internal\n */\n const snippetConfig = inject<SnippetConfig | undefined>('snippetConfig');\n\n /**\n * Flag to know if the params were already loaded from the URL.\n *\n * @internal\n */\n const urlLoaded = ref(false);\n\n /**\n * The page URL. It is used to compare against the current URL to check navigation state.\n *\n * @internal\n */\n const url = ref<URL | undefined>(undefined);\n\n /**\n * Flag to know if the page has been persisted by the browser's back-forward cache.\n *\n * @internal\n */\n const isPagePersisted = ref(false);\n\n /**\n * Computed to know which params we must get from URL. It gets the params names from the initial\n * state, to get all default params names, and also from the `$attrs` to get the extra params\n * names to take into account.\n *\n * @returns An array with the name of the params.\n *\n * @internal\n */\n const managedParamsNames = computed(() => {\n return Object.keys({ ...initialUrlState, ...attrs });\n });\n\n /**\n * Returns the mapping of the param keys used in the URL is configured through $attrs. This way\n * we can support any param and extra param, no matters its name.\n *\n * @param paramName - The param name to get the Url key.\n * @returns The key used in the URL for the `paramName` passed.\n *\n * @internal\n */\n const getUrlKey = (paramName: string): string => {\n const paramValue = attrs[paramName];\n return typeof paramValue === 'string' ? paramValue : paramName;\n };\n\n /**\n * Deletes all the parameters in the passed URL.\n *\n * @param url - The URL to remove parameters from.\n * @internal\n */\n const deleteUrlParameters = (url: URL): void => {\n managedParamsNames.value.forEach(paramName =>\n url.searchParams.delete(getUrlKey(paramName))\n );\n };\n\n /**\n * Sorts the params in a tuple array [key,value] to generate always the same URL with the params\n * in the same order.\n *\n * @param urlParams - The {@link UrlParams} to sort.\n * @returns An array of tuples with the key-value of each paramter, sorted by key.\n * @internal\n */\n const sortParams = (urlParams: UrlParams): Array<[string, unknown]> => {\n return Object.entries(urlParams).sort(([param1], [param2]) => {\n return param1 < param2 ? -1 : 1;\n });\n };\n\n /**\n * Set all the provided parameters to the url with the mapped key.\n *\n * @param url - The current URL.\n * @param urlParams - The list of parameters to add.\n * @remarks The params are filtered because there maybe received extra params which will not be\n * managed by URL. This is defined by the `managedParamsNames` computed. Also, the parameters\n * are sorted Alphabetically to produce always the same URL with the same parameters.This is\n * important for SEO purposes.\n *\n * @internal\n */\n const setUrlParameters = (url: URL, urlParams: UrlParams): void => {\n // Only when there is a query the rest of the parameters are valid.\n if (!urlParams.query) {\n return;\n }\n const filteredParams = objectFilter(urlParams, paramName =>\n managedParamsNames.value.includes(paramName as string)\n );\n const sortedParameters = sortParams(filteredParams);\n sortedParameters.forEach(([paramName, paramValue]) => {\n const urlParamKey = getUrlKey(paramName);\n if (Array.isArray(paramValue)) {\n paramValue.forEach(value => {\n url.searchParams.append(urlParamKey, String(value));\n });\n } else {\n url.searchParams.set(urlParamKey, String(paramValue));\n }\n });\n };\n\n /**\n * Updates the browser URL with the passed `newUrlParams` and using the browser history method\n * passed as `historyMethod`. It only updates the browser history if the new URL is different\n * from the current.\n *\n * @param newUrlParams - The new params to add to the browser URL.\n * @param historyMethod - The browser history method used to add the new URL.\n *\n * @internal\n */\n const updateUrl = (\n newUrlParams: UrlParams,\n historyMethod: History['pushState'] | History['replaceState']\n ): void => {\n if (urlLoaded.value) {\n const newUrl = new URL(window.location.href);\n deleteUrlParameters(newUrl);\n setUrlParameters(newUrl, newUrlParams);\n\n // Normalize '+' characters into '%20' for spaces in url params.\n newUrl.search = newUrl.search.replace(/\\+/g, '%20');\n\n if (newUrl.href !== window.location.href) {\n historyMethod({ ...window.history.state }, document.title, newUrl.href);\n }\n url.value = newUrl;\n }\n };\n\n /**\n * Updates the browser URL with the new {@link UrlParams} using the history `pushState` method.\n *\n * @param newUrlParams - The new params to update browser URL.\n */\n $x.on('PushableUrlStateUpdated', false).subscribe((newUrlParams: UrlParams) => {\n updateUrl(newUrlParams, window.history.pushState.bind(window.history));\n });\n\n /**\n * Updates the browser URL with the new {@link UrlParams} using the history `replaceState`\n * method.\n *\n * @param newUrlParams - The new params to update browser URL.\n */\n $x.on('ReplaceableUrlStateUpdated', false).subscribe((newUrlParams: UrlParams) => {\n updateUrl(newUrlParams, window.history.replaceState.bind(window.history));\n });\n\n /**\n * Handler of the\n * [pageshow](https://developer.mozilla.org/en-US/docs/Web/API/Window/pageshow_event)\n * event.\n *\n * @remarks The pageshow event is listened to check if the browser has performed a navigation\n * using the back-forward cache. This information is available in the\n * PageTransitionEvent.persisted property.\n *\n * @param event - The page transition event.\n * @internal\n */\n const onPageShow = (event: PageTransitionEvent) => {\n isPagePersisted.value = event.persisted;\n if (event.persisted) {\n // The internal url is reset due to the back-forward cache storing the previous value which\n // is no longer valid.\n url.value = undefined;\n }\n };\n\n /**\n * Returns the URL param value parsed depending on its type in the initial store state. As we\n * can not know what type can have an extra param, all extra params are parsed as strings. We\n * know if it is an extra param because it is not in the initial state.\n *\n * @param name - The name of the param in {@link UrlParams}.\n * @param value - The `URLSearchParams` value as an arry of strings.\n * @returns The parsed value.\n *\n * @internal\n */\n const parseUrlParam = (name: string, value: string[]): UrlParamValue => {\n switch (typeof initialUrlState[name]) {\n case 'number':\n return Number(value[0]);\n case 'boolean':\n return value[0].toLowerCase() === 'true';\n case 'string':\n return value[0];\n default:\n // array\n return value;\n }\n };\n\n /**\n * Gets the {@link UrlParams} from the URL, including only the params defined by `paramsNames`.\n *\n * @returns ParsedUrlParams obtained from URL.\n * @internal\n */\n const parseUrlParams = (): ParsedUrlParams => {\n const urlSearchParams = new URL(window.location.href).searchParams;\n return managedParamsNames.value.reduce<ParsedUrlParams>(\n (params, name) => {\n const urlKey = getUrlKey(name);\n if (urlSearchParams.has(urlKey)) {\n if (name in initialUrlState) {\n const urlValue = urlSearchParams.getAll(urlKey);\n params.all[name] = parseUrlParam(name, urlValue);\n } else {\n params.all[name] = params.extra[name] = urlSearchParams.get(urlKey);\n }\n }\n return params;\n },\n { all: { ...initialUrlState }, extra: { ...initialExtraParams.value } }\n );\n };\n\n /**\n * Check if the navigation is from a product page.\n *\n * @remarks Due to Safari 14 not supporting the new and standard PerformanceNavigationTiming\n * API, we are falling back to the deprecated one, PerformanceNavigation. We also fallback to\n * this API whenever we get a navigationType equal to reload, because Safari has a bug that the\n * navigationType is permanently set to reload after you have reload the page and it never\n * resets. As some browsers have a back-forward cache implemented, we also take into account if\n * the page is persisted.\n *\n * @returns True if the navigation is from a product page, false otherwise.\n * @internal\n */\n const isNavigatingFromPdp = (): boolean => {\n const isPagePersistedValue = isPagePersisted.value;\n const navigationEntries = window.performance.getEntriesByType('navigation');\n const navigationType = (navigationEntries[0] as PerformanceNavigationTiming)?.type;\n const useFallbackStrategy =\n !navigationEntries.length &&\n (isArrayEmpty(navigationEntries) || navigationType === 'reload');\n\n // Reset internal isPagePersisted property value\n isPagePersisted.value = false;\n\n if (useFallbackStrategy) {\n const isNavigatingInSpa = !!snippetConfig?.isSpa && navigationType === 'navigate';\n return navigationType === 'back_forward' || isNavigatingInSpa || isPagePersistedValue;\n } else {\n const isNavigatingInSpa = !!snippetConfig?.isSpa && navigationType === 'navigate';\n return navigationType === 'back_forward' || isNavigatingInSpa || isPagePersistedValue;\n }\n };\n\n /**\n * Detects the {@link FeatureLocation} used to build the\n * {@link QueryOriginInit} data.\n *\n * @returns The {@link FeatureLocation}.\n * @internal\n */\n const detectLocation = (): FeatureLocation => {\n const currentUrl = new URL(window.location.href);\n const previousUrl = url.value;\n url.value = currentUrl;\n\n const isInternalNavigation =\n previousUrl?.search !== currentUrl.search &&\n previousUrl?.pathname === currentUrl.pathname;\n if (isInternalNavigation) {\n return 'url_history';\n }\n\n if (isNavigatingFromPdp()) {\n return 'url_history_pdp';\n }\n\n return 'external';\n };\n\n /**\n * Creates the wire metadata to include in every emitted {@link XEvent}.\n *\n * @returns The {@link WireMetadata}.\n * @internal\n */\n const createWireMetadata = (): Pick<WireMetadata, 'feature' | 'location'> => {\n return {\n feature: 'url',\n location: detectLocation()\n };\n };\n\n /**\n * Emits the {@link UrlXEvents.ParamsLoadedFromUrl} XEvent,\n * the {@link UrlXEvents.ExtraParamsLoadedFromUrl} XEvent and, if there is query, also emits\n * the {@link XEventsTypes.UserOpenXProgrammatically}.\n *\n * @internal\n */\n const emitEvents = () => {\n const { all, extra } = parseUrlParams();\n const metadata = createWireMetadata();\n $x.emit('ParamsLoadedFromUrl', all, metadata);\n $x.emit('ExtraParamsLoadedFromUrl', extra, metadata);\n if (all.query) {\n $x.emit('UserOpenXProgrammatically', undefined, metadata);\n }\n urlLoaded.value = true;\n };\n\n /**\n * To emit the Url events just when the URL is load, and before the components mounted events\n * and state changes, we do it in the created of this component.\n */\n onMounted(() => {\n emitEvents();\n });\n\n return {\n onPageShow,\n emitEvents\n };\n }\n });\n</script>\n\n<docs lang=\"mdx\">\n## Events\n\nThis component emits the following events:\n\n- [`ParamsLoadedFromUrl`](https://github.com/empathyco/x/blob/main/packages/x-components/src/wiring/events.types.ts)\n- [`ExtraParamsLoadedFromUrl`](https://github.com/empathyco/x/blob/main/packages/x-components/src/wiring/events.types.ts)\n- [`UserOpenXProgrammatically`](https://github.com/empathyco/x/blob/main/packages/x-components/src/wiring/events.types.ts)\n\n## See it in action\n\nThis component manages the browser URL parameters to preserve them through reloads and browser\nhistory navigation. It allow to configure the default url parameter names using its attributes. This\ncomponent doesn't render elements to the DOM.\n\n_Try to make some requests and take a look to the url!_\n\n```vue\n<template>\n <UrlHandler />\n</template>\n\n<script>\n import { UrlHandler } from '@empathyco/x-components/url-handler';\n\n export default {\n name: 'UrlHandlerDemo',\n components: {\n UrlHandler\n }\n };\n</script>\n```\n\n### Play with props\n\nIn this example, the `UrlHandler` component changes the following query parameter names:\n\n- `query` to be `q`.\n- `page` to be `p`.\n- `filter` to be `f`\n- `sort` to be `s`\n\n_Try to make some requests and take a look to the url!_\n\n```vue\n<template>\n <UrlHandler query=\"q\" page=\"p\" filter=\"f\" sort=\"s\" />\n</template>\n\n<script>\n import { UrlHandler } from '@empathyco/x-components/url-handler';\n\n export default {\n name: 'UrlHandlerDemo',\n components: {\n UrlHandler\n }\n };\n</script>\n```\n\n### Play with events\n\nThe `UrlHandler` will emit the `ParamsLoadedFromUrl` when the page is loaded.\n\nThe `UrlHandler` will emit the `ExtraParamsLoadedFromUrl` when the page is loaded with an extra\nparam configured and with a value in URL.\n\nThe `UrlHandler` will emit the `UserOpenXProgrammatically` when the page is loaded with a query in\nthe URL.\n</docs>\n"],"names":["_resolveComponent","_openBlock","_createBlock"],"mappings":";;;;;kCACEA,gBAA8E,CAAA,cAAA,CAAA,CAAA;AAAxC,EAAA,OAAAC,SAAA,EAAoB,EAAAC,WAAA,CAAA,uBAAA,EAAA;AAAA,IAAE,YAAO,IAAQ,CAAA,UAAA;AAAA,IAAA,UAAA,EAAA,IAAA,CAAA,UAAA;;;;;;;;"}
@@ -140,7 +140,8 @@ var _sfc_main = defineComponent({
140
140
  const newUrl = new URL(window.location.href);
141
141
  deleteUrlParameters(newUrl);
142
142
  setUrlParameters(newUrl, newUrlParams);
143
- newUrl.href = newUrl.href.replace(/\+/g, '%20');
143
+ // Normalize '+' characters into '%20' for spaces in url params.
144
+ newUrl.search = newUrl.search.replace(/\+/g, '%20');
144
145
  if (newUrl.href !== window.location.href) {
145
146
  historyMethod({ ...window.history.state }, document.title, newUrl.href);
146
147
  }
@@ -1 +1 @@
1
- {"version":3,"file":"url-handler.vue2.js","sources":["../../../../../src/x-modules/url/components/url-handler.vue"],"sourcesContent":["<template>\n <GlobalEvents @pageshow=\"onPageShow\" @popstate=\"emitEvents\" target=\"window\" />\n</template>\n\n<script lang=\"ts\">\n import { Dictionary, objectFilter } from '@empathyco/x-utils';\n import { computed, defineComponent, inject, onMounted, ref } from 'vue';\n import { GlobalEvents } from 'vue-global-events';\n import { FeatureLocation } from '../../../types/origin';\n import { UrlParams } from '../../../types/url-params';\n import { isArrayEmpty } from '../../../utils/array';\n import { WireMetadata } from '../../../wiring/wiring.types';\n import { SnippetConfig } from '../../../x-installer/api/api.types';\n import { initialUrlState } from '../store/initial-state';\n import { UrlParamValue } from '../store/types';\n import { urlXModule } from '../x-module';\n import { use$x } from '../../../composables/use-$x';\n import { useState } from '../../../composables/use-state';\n\n interface ParsedUrlParams {\n all: UrlParams;\n extra: Dictionary<unknown>;\n }\n\n /**\n * This component manages the browser URL parameters to preserve them through reloads and browser\n * history navigation. It allow to configure the default url parameter names using its attributes.\n * This component doesn't render elements to the DOM.\n *\n * @public\n */\n export default defineComponent({\n name: 'UrlHandler',\n components: {\n GlobalEvents\n },\n xModule: urlXModule.name,\n setup: function (props, { attrs }) {\n const $x = use$x();\n\n const initialExtraParams = useState('url', ['initialExtraParams']).initialExtraParams;\n\n /**\n * The {@link SnippetConfig} provided by an ancestor.\n *\n * @internal\n */\n const snippetConfig = inject<SnippetConfig | undefined>('snippetConfig');\n\n /**\n * Flag to know if the params were already loaded from the URL.\n *\n * @internal\n */\n const urlLoaded = ref(false);\n\n /**\n * The page URL. It is used to compare against the current URL to check navigation state.\n *\n * @internal\n */\n const url = ref<URL | undefined>(undefined);\n\n /**\n * Flag to know if the page has been persisted by the browser's back-forward cache.\n *\n * @internal\n */\n const isPagePersisted = ref(false);\n\n /**\n * Computed to know which params we must get from URL. It gets the params names from the initial\n * state, to get all default params names, and also from the `$attrs` to get the extra params\n * names to take into account.\n *\n * @returns An array with the name of the params.\n *\n * @internal\n */\n const managedParamsNames = computed(() => {\n return Object.keys({ ...initialUrlState, ...attrs });\n });\n\n /**\n * Returns the mapping of the param keys used in the URL is configured through $attrs. This way\n * we can support any param and extra param, no matters its name.\n *\n * @param paramName - The param name to get the Url key.\n * @returns The key used in the URL for the `paramName` passed.\n *\n * @internal\n */\n const getUrlKey = (paramName: string): string => {\n const paramValue = attrs[paramName];\n return typeof paramValue === 'string' ? paramValue : paramName;\n };\n\n /**\n * Deletes all the parameters in the passed URL.\n *\n * @param url - The URL to remove parameters from.\n * @internal\n */\n const deleteUrlParameters = (url: URL): void => {\n managedParamsNames.value.forEach(paramName =>\n url.searchParams.delete(getUrlKey(paramName))\n );\n };\n\n /**\n * Sorts the params in a tuple array [key,value] to generate always the same URL with the params\n * in the same order.\n *\n * @param urlParams - The {@link UrlParams} to sort.\n * @returns An array of tuples with the key-value of each paramter, sorted by key.\n * @internal\n */\n const sortParams = (urlParams: UrlParams): Array<[string, unknown]> => {\n return Object.entries(urlParams).sort(([param1], [param2]) => {\n return param1 < param2 ? -1 : 1;\n });\n };\n\n /**\n * Set all the provided parameters to the url with the mapped key.\n *\n * @param url - The current URL.\n * @param urlParams - The list of parameters to add.\n * @remarks The params are filtered because there maybe received extra params which will not be\n * managed by URL. This is defined by the `managedParamsNames` computed. Also, the parameters\n * are sorted Alphabetically to produce always the same URL with the same parameters.This is\n * important for SEO purposes.\n *\n * @internal\n */\n const setUrlParameters = (url: URL, urlParams: UrlParams): void => {\n // Only when there is a query the rest of the parameters are valid.\n if (!urlParams.query) {\n return;\n }\n const filteredParams = objectFilter(urlParams, paramName =>\n managedParamsNames.value.includes(paramName as string)\n );\n const sortedParameters = sortParams(filteredParams);\n sortedParameters.forEach(([paramName, paramValue]) => {\n const urlParamKey = getUrlKey(paramName);\n if (Array.isArray(paramValue)) {\n paramValue.forEach(value => {\n url.searchParams.append(urlParamKey, String(value));\n });\n } else {\n url.searchParams.set(urlParamKey, String(paramValue));\n }\n });\n };\n\n /**\n * Updates the browser URL with the passed `newUrlParams` and using the browser history method\n * passed as `historyMethod`. It only updates the browser history if the new URL is different\n * from the current.\n *\n * @param newUrlParams - The new params to add to the browser URL.\n * @param historyMethod - The browser history method used to add the new URL.\n *\n * @internal\n */\n const updateUrl = (\n newUrlParams: UrlParams,\n historyMethod: History['pushState'] | History['replaceState']\n ): void => {\n if (urlLoaded.value) {\n const newUrl = new URL(window.location.href);\n deleteUrlParameters(newUrl);\n setUrlParameters(newUrl, newUrlParams);\n\n newUrl.href = newUrl.href.replace(/\\+/g, '%20');\n\n if (newUrl.href !== window.location.href) {\n historyMethod({ ...window.history.state }, document.title, newUrl.href);\n }\n url.value = newUrl;\n }\n };\n\n /**\n * Updates the browser URL with the new {@link UrlParams} using the history `pushState` method.\n *\n * @param newUrlParams - The new params to update browser URL.\n */\n $x.on('PushableUrlStateUpdated', false).subscribe((newUrlParams: UrlParams) => {\n updateUrl(newUrlParams, window.history.pushState.bind(window.history));\n });\n\n /**\n * Updates the browser URL with the new {@link UrlParams} using the history `replaceState`\n * method.\n *\n * @param newUrlParams - The new params to update browser URL.\n */\n $x.on('ReplaceableUrlStateUpdated', false).subscribe((newUrlParams: UrlParams) => {\n updateUrl(newUrlParams, window.history.replaceState.bind(window.history));\n });\n\n /**\n * Handler of the\n * [pageshow](https://developer.mozilla.org/en-US/docs/Web/API/Window/pageshow_event)\n * event.\n *\n * @remarks The pageshow event is listened to check if the browser has performed a navigation\n * using the back-forward cache. This information is available in the\n * PageTransitionEvent.persisted property.\n *\n * @param event - The page transition event.\n * @internal\n */\n const onPageShow = (event: PageTransitionEvent) => {\n isPagePersisted.value = event.persisted;\n if (event.persisted) {\n // The internal url is reset due to the back-forward cache storing the previous value which\n // is no longer valid.\n url.value = undefined;\n }\n };\n\n /**\n * Returns the URL param value parsed depending on its type in the initial store state. As we\n * can not know what type can have an extra param, all extra params are parsed as strings. We\n * know if it is an extra param because it is not in the initial state.\n *\n * @param name - The name of the param in {@link UrlParams}.\n * @param value - The `URLSearchParams` value as an arry of strings.\n * @returns The parsed value.\n *\n * @internal\n */\n const parseUrlParam = (name: string, value: string[]): UrlParamValue => {\n switch (typeof initialUrlState[name]) {\n case 'number':\n return Number(value[0]);\n case 'boolean':\n return value[0].toLowerCase() === 'true';\n case 'string':\n return value[0];\n default:\n // array\n return value;\n }\n };\n\n /**\n * Gets the {@link UrlParams} from the URL, including only the params defined by `paramsNames`.\n *\n * @returns ParsedUrlParams obtained from URL.\n * @internal\n */\n const parseUrlParams = (): ParsedUrlParams => {\n const urlSearchParams = new URL(window.location.href).searchParams;\n return managedParamsNames.value.reduce<ParsedUrlParams>(\n (params, name) => {\n const urlKey = getUrlKey(name);\n if (urlSearchParams.has(urlKey)) {\n if (name in initialUrlState) {\n const urlValue = urlSearchParams.getAll(urlKey);\n params.all[name] = parseUrlParam(name, urlValue);\n } else {\n params.all[name] = params.extra[name] = urlSearchParams.get(urlKey);\n }\n }\n return params;\n },\n { all: { ...initialUrlState }, extra: { ...initialExtraParams.value } }\n );\n };\n\n /**\n * Check if the navigation is from a product page.\n *\n * @remarks Due to Safari 14 not supporting the new and standard PerformanceNavigationTiming\n * API, we are falling back to the deprecated one, PerformanceNavigation. We also fallback to\n * this API whenever we get a navigationType equal to reload, because Safari has a bug that the\n * navigationType is permanently set to reload after you have reload the page and it never\n * resets. As some browsers have a back-forward cache implemented, we also take into account if\n * the page is persisted.\n *\n * @returns True if the navigation is from a product page, false otherwise.\n * @internal\n */\n const isNavigatingFromPdp = (): boolean => {\n const isPagePersistedValue = isPagePersisted.value;\n const navigationEntries = window.performance.getEntriesByType('navigation');\n const navigationType = (navigationEntries[0] as PerformanceNavigationTiming)?.type;\n const useFallbackStrategy =\n !navigationEntries.length &&\n (isArrayEmpty(navigationEntries) || navigationType === 'reload');\n\n // Reset internal isPagePersisted property value\n isPagePersisted.value = false;\n\n if (useFallbackStrategy) {\n const isNavigatingInSpa = !!snippetConfig?.isSpa && navigationType === 'navigate';\n return navigationType === 'back_forward' || isNavigatingInSpa || isPagePersistedValue;\n } else {\n const isNavigatingInSpa = !!snippetConfig?.isSpa && navigationType === 'navigate';\n return navigationType === 'back_forward' || isNavigatingInSpa || isPagePersistedValue;\n }\n };\n\n /**\n * Detects the {@link FeatureLocation} used to build the\n * {@link QueryOriginInit} data.\n *\n * @returns The {@link FeatureLocation}.\n * @internal\n */\n const detectLocation = (): FeatureLocation => {\n const currentUrl = new URL(window.location.href);\n const previousUrl = url.value;\n url.value = currentUrl;\n\n const isInternalNavigation =\n previousUrl?.search !== currentUrl.search &&\n previousUrl?.pathname === currentUrl.pathname;\n if (isInternalNavigation) {\n return 'url_history';\n }\n\n if (isNavigatingFromPdp()) {\n return 'url_history_pdp';\n }\n\n return 'external';\n };\n\n /**\n * Creates the wire metadata to include in every emitted {@link XEvent}.\n *\n * @returns The {@link WireMetadata}.\n * @internal\n */\n const createWireMetadata = (): Pick<WireMetadata, 'feature' | 'location'> => {\n return {\n feature: 'url',\n location: detectLocation()\n };\n };\n\n /**\n * Emits the {@link UrlXEvents.ParamsLoadedFromUrl} XEvent,\n * the {@link UrlXEvents.ExtraParamsLoadedFromUrl} XEvent and, if there is query, also emits\n * the {@link XEventsTypes.UserOpenXProgrammatically}.\n *\n * @internal\n */\n const emitEvents = () => {\n const { all, extra } = parseUrlParams();\n const metadata = createWireMetadata();\n $x.emit('ParamsLoadedFromUrl', all, metadata);\n $x.emit('ExtraParamsLoadedFromUrl', extra, metadata);\n if (all.query) {\n $x.emit('UserOpenXProgrammatically', undefined, metadata);\n }\n urlLoaded.value = true;\n };\n\n /**\n * To emit the Url events just when the URL is load, and before the components mounted events\n * and state changes, we do it in the created of this component.\n */\n onMounted(() => {\n emitEvents();\n });\n\n return {\n onPageShow,\n emitEvents\n };\n }\n });\n</script>\n\n<docs lang=\"mdx\">\n## Events\n\nThis component emits the following events:\n\n- [`ParamsLoadedFromUrl`](https://github.com/empathyco/x/blob/main/packages/x-components/src/wiring/events.types.ts)\n- [`ExtraParamsLoadedFromUrl`](https://github.com/empathyco/x/blob/main/packages/x-components/src/wiring/events.types.ts)\n- [`UserOpenXProgrammatically`](https://github.com/empathyco/x/blob/main/packages/x-components/src/wiring/events.types.ts)\n\n## See it in action\n\nThis component manages the browser URL parameters to preserve them through reloads and browser\nhistory navigation. It allow to configure the default url parameter names using its attributes. This\ncomponent doesn't render elements to the DOM.\n\n_Try to make some requests and take a look to the url!_\n\n```vue\n<template>\n <UrlHandler />\n</template>\n\n<script>\n import { UrlHandler } from '@empathyco/x-components/url-handler';\n\n export default {\n name: 'UrlHandlerDemo',\n components: {\n UrlHandler\n }\n };\n</script>\n```\n\n### Play with props\n\nIn this example, the `UrlHandler` component changes the following query parameter names:\n\n- `query` to be `q`.\n- `page` to be `p`.\n- `filter` to be `f`\n- `sort` to be `s`\n\n_Try to make some requests and take a look to the url!_\n\n```vue\n<template>\n <UrlHandler query=\"q\" page=\"p\" filter=\"f\" sort=\"s\" />\n</template>\n\n<script>\n import { UrlHandler } from '@empathyco/x-components/url-handler';\n\n export default {\n name: 'UrlHandlerDemo',\n components: {\n UrlHandler\n }\n };\n</script>\n```\n\n### Play with events\n\nThe `UrlHandler` will emit the `ParamsLoadedFromUrl` when the page is loaded.\n\nThe `UrlHandler` will emit the `ExtraParamsLoadedFromUrl` when the page is loaded with an extra\nparam configured and with a value in URL.\n\nThe `UrlHandler` will emit the `UserOpenXProgrammatically` when the page is loaded with a query in\nthe URL.\n</docs>\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"url-handler.vue2.js","sources":["../../../../../src/x-modules/url/components/url-handler.vue"],"sourcesContent":["<template>\n <GlobalEvents @pageshow=\"onPageShow\" @popstate=\"emitEvents\" target=\"window\" />\n</template>\n\n<script lang=\"ts\">\n import { Dictionary, objectFilter } from '@empathyco/x-utils';\n import { computed, defineComponent, inject, onMounted, ref } from 'vue';\n import { GlobalEvents } from 'vue-global-events';\n import { FeatureLocation } from '../../../types/origin';\n import { UrlParams } from '../../../types/url-params';\n import { isArrayEmpty } from '../../../utils/array';\n import { WireMetadata } from '../../../wiring/wiring.types';\n import { SnippetConfig } from '../../../x-installer/api/api.types';\n import { initialUrlState } from '../store/initial-state';\n import { UrlParamValue } from '../store/types';\n import { urlXModule } from '../x-module';\n import { use$x } from '../../../composables/use-$x';\n import { useState } from '../../../composables/use-state';\n\n interface ParsedUrlParams {\n all: UrlParams;\n extra: Dictionary<unknown>;\n }\n\n /**\n * This component manages the browser URL parameters to preserve them through reloads and browser\n * history navigation. It allow to configure the default url parameter names using its attributes.\n * This component doesn't render elements to the DOM.\n *\n * @public\n */\n export default defineComponent({\n name: 'UrlHandler',\n components: {\n GlobalEvents\n },\n xModule: urlXModule.name,\n setup: function (props, { attrs }) {\n const $x = use$x();\n\n const initialExtraParams = useState('url', ['initialExtraParams']).initialExtraParams;\n\n /**\n * The {@link SnippetConfig} provided by an ancestor.\n *\n * @internal\n */\n const snippetConfig = inject<SnippetConfig | undefined>('snippetConfig');\n\n /**\n * Flag to know if the params were already loaded from the URL.\n *\n * @internal\n */\n const urlLoaded = ref(false);\n\n /**\n * The page URL. It is used to compare against the current URL to check navigation state.\n *\n * @internal\n */\n const url = ref<URL | undefined>(undefined);\n\n /**\n * Flag to know if the page has been persisted by the browser's back-forward cache.\n *\n * @internal\n */\n const isPagePersisted = ref(false);\n\n /**\n * Computed to know which params we must get from URL. It gets the params names from the initial\n * state, to get all default params names, and also from the `$attrs` to get the extra params\n * names to take into account.\n *\n * @returns An array with the name of the params.\n *\n * @internal\n */\n const managedParamsNames = computed(() => {\n return Object.keys({ ...initialUrlState, ...attrs });\n });\n\n /**\n * Returns the mapping of the param keys used in the URL is configured through $attrs. This way\n * we can support any param and extra param, no matters its name.\n *\n * @param paramName - The param name to get the Url key.\n * @returns The key used in the URL for the `paramName` passed.\n *\n * @internal\n */\n const getUrlKey = (paramName: string): string => {\n const paramValue = attrs[paramName];\n return typeof paramValue === 'string' ? paramValue : paramName;\n };\n\n /**\n * Deletes all the parameters in the passed URL.\n *\n * @param url - The URL to remove parameters from.\n * @internal\n */\n const deleteUrlParameters = (url: URL): void => {\n managedParamsNames.value.forEach(paramName =>\n url.searchParams.delete(getUrlKey(paramName))\n );\n };\n\n /**\n * Sorts the params in a tuple array [key,value] to generate always the same URL with the params\n * in the same order.\n *\n * @param urlParams - The {@link UrlParams} to sort.\n * @returns An array of tuples with the key-value of each paramter, sorted by key.\n * @internal\n */\n const sortParams = (urlParams: UrlParams): Array<[string, unknown]> => {\n return Object.entries(urlParams).sort(([param1], [param2]) => {\n return param1 < param2 ? -1 : 1;\n });\n };\n\n /**\n * Set all the provided parameters to the url with the mapped key.\n *\n * @param url - The current URL.\n * @param urlParams - The list of parameters to add.\n * @remarks The params are filtered because there maybe received extra params which will not be\n * managed by URL. This is defined by the `managedParamsNames` computed. Also, the parameters\n * are sorted Alphabetically to produce always the same URL with the same parameters.This is\n * important for SEO purposes.\n *\n * @internal\n */\n const setUrlParameters = (url: URL, urlParams: UrlParams): void => {\n // Only when there is a query the rest of the parameters are valid.\n if (!urlParams.query) {\n return;\n }\n const filteredParams = objectFilter(urlParams, paramName =>\n managedParamsNames.value.includes(paramName as string)\n );\n const sortedParameters = sortParams(filteredParams);\n sortedParameters.forEach(([paramName, paramValue]) => {\n const urlParamKey = getUrlKey(paramName);\n if (Array.isArray(paramValue)) {\n paramValue.forEach(value => {\n url.searchParams.append(urlParamKey, String(value));\n });\n } else {\n url.searchParams.set(urlParamKey, String(paramValue));\n }\n });\n };\n\n /**\n * Updates the browser URL with the passed `newUrlParams` and using the browser history method\n * passed as `historyMethod`. It only updates the browser history if the new URL is different\n * from the current.\n *\n * @param newUrlParams - The new params to add to the browser URL.\n * @param historyMethod - The browser history method used to add the new URL.\n *\n * @internal\n */\n const updateUrl = (\n newUrlParams: UrlParams,\n historyMethod: History['pushState'] | History['replaceState']\n ): void => {\n if (urlLoaded.value) {\n const newUrl = new URL(window.location.href);\n deleteUrlParameters(newUrl);\n setUrlParameters(newUrl, newUrlParams);\n\n // Normalize '+' characters into '%20' for spaces in url params.\n newUrl.search = newUrl.search.replace(/\\+/g, '%20');\n\n if (newUrl.href !== window.location.href) {\n historyMethod({ ...window.history.state }, document.title, newUrl.href);\n }\n url.value = newUrl;\n }\n };\n\n /**\n * Updates the browser URL with the new {@link UrlParams} using the history `pushState` method.\n *\n * @param newUrlParams - The new params to update browser URL.\n */\n $x.on('PushableUrlStateUpdated', false).subscribe((newUrlParams: UrlParams) => {\n updateUrl(newUrlParams, window.history.pushState.bind(window.history));\n });\n\n /**\n * Updates the browser URL with the new {@link UrlParams} using the history `replaceState`\n * method.\n *\n * @param newUrlParams - The new params to update browser URL.\n */\n $x.on('ReplaceableUrlStateUpdated', false).subscribe((newUrlParams: UrlParams) => {\n updateUrl(newUrlParams, window.history.replaceState.bind(window.history));\n });\n\n /**\n * Handler of the\n * [pageshow](https://developer.mozilla.org/en-US/docs/Web/API/Window/pageshow_event)\n * event.\n *\n * @remarks The pageshow event is listened to check if the browser has performed a navigation\n * using the back-forward cache. This information is available in the\n * PageTransitionEvent.persisted property.\n *\n * @param event - The page transition event.\n * @internal\n */\n const onPageShow = (event: PageTransitionEvent) => {\n isPagePersisted.value = event.persisted;\n if (event.persisted) {\n // The internal url is reset due to the back-forward cache storing the previous value which\n // is no longer valid.\n url.value = undefined;\n }\n };\n\n /**\n * Returns the URL param value parsed depending on its type in the initial store state. As we\n * can not know what type can have an extra param, all extra params are parsed as strings. We\n * know if it is an extra param because it is not in the initial state.\n *\n * @param name - The name of the param in {@link UrlParams}.\n * @param value - The `URLSearchParams` value as an arry of strings.\n * @returns The parsed value.\n *\n * @internal\n */\n const parseUrlParam = (name: string, value: string[]): UrlParamValue => {\n switch (typeof initialUrlState[name]) {\n case 'number':\n return Number(value[0]);\n case 'boolean':\n return value[0].toLowerCase() === 'true';\n case 'string':\n return value[0];\n default:\n // array\n return value;\n }\n };\n\n /**\n * Gets the {@link UrlParams} from the URL, including only the params defined by `paramsNames`.\n *\n * @returns ParsedUrlParams obtained from URL.\n * @internal\n */\n const parseUrlParams = (): ParsedUrlParams => {\n const urlSearchParams = new URL(window.location.href).searchParams;\n return managedParamsNames.value.reduce<ParsedUrlParams>(\n (params, name) => {\n const urlKey = getUrlKey(name);\n if (urlSearchParams.has(urlKey)) {\n if (name in initialUrlState) {\n const urlValue = urlSearchParams.getAll(urlKey);\n params.all[name] = parseUrlParam(name, urlValue);\n } else {\n params.all[name] = params.extra[name] = urlSearchParams.get(urlKey);\n }\n }\n return params;\n },\n { all: { ...initialUrlState }, extra: { ...initialExtraParams.value } }\n );\n };\n\n /**\n * Check if the navigation is from a product page.\n *\n * @remarks Due to Safari 14 not supporting the new and standard PerformanceNavigationTiming\n * API, we are falling back to the deprecated one, PerformanceNavigation. We also fallback to\n * this API whenever we get a navigationType equal to reload, because Safari has a bug that the\n * navigationType is permanently set to reload after you have reload the page and it never\n * resets. As some browsers have a back-forward cache implemented, we also take into account if\n * the page is persisted.\n *\n * @returns True if the navigation is from a product page, false otherwise.\n * @internal\n */\n const isNavigatingFromPdp = (): boolean => {\n const isPagePersistedValue = isPagePersisted.value;\n const navigationEntries = window.performance.getEntriesByType('navigation');\n const navigationType = (navigationEntries[0] as PerformanceNavigationTiming)?.type;\n const useFallbackStrategy =\n !navigationEntries.length &&\n (isArrayEmpty(navigationEntries) || navigationType === 'reload');\n\n // Reset internal isPagePersisted property value\n isPagePersisted.value = false;\n\n if (useFallbackStrategy) {\n const isNavigatingInSpa = !!snippetConfig?.isSpa && navigationType === 'navigate';\n return navigationType === 'back_forward' || isNavigatingInSpa || isPagePersistedValue;\n } else {\n const isNavigatingInSpa = !!snippetConfig?.isSpa && navigationType === 'navigate';\n return navigationType === 'back_forward' || isNavigatingInSpa || isPagePersistedValue;\n }\n };\n\n /**\n * Detects the {@link FeatureLocation} used to build the\n * {@link QueryOriginInit} data.\n *\n * @returns The {@link FeatureLocation}.\n * @internal\n */\n const detectLocation = (): FeatureLocation => {\n const currentUrl = new URL(window.location.href);\n const previousUrl = url.value;\n url.value = currentUrl;\n\n const isInternalNavigation =\n previousUrl?.search !== currentUrl.search &&\n previousUrl?.pathname === currentUrl.pathname;\n if (isInternalNavigation) {\n return 'url_history';\n }\n\n if (isNavigatingFromPdp()) {\n return 'url_history_pdp';\n }\n\n return 'external';\n };\n\n /**\n * Creates the wire metadata to include in every emitted {@link XEvent}.\n *\n * @returns The {@link WireMetadata}.\n * @internal\n */\n const createWireMetadata = (): Pick<WireMetadata, 'feature' | 'location'> => {\n return {\n feature: 'url',\n location: detectLocation()\n };\n };\n\n /**\n * Emits the {@link UrlXEvents.ParamsLoadedFromUrl} XEvent,\n * the {@link UrlXEvents.ExtraParamsLoadedFromUrl} XEvent and, if there is query, also emits\n * the {@link XEventsTypes.UserOpenXProgrammatically}.\n *\n * @internal\n */\n const emitEvents = () => {\n const { all, extra } = parseUrlParams();\n const metadata = createWireMetadata();\n $x.emit('ParamsLoadedFromUrl', all, metadata);\n $x.emit('ExtraParamsLoadedFromUrl', extra, metadata);\n if (all.query) {\n $x.emit('UserOpenXProgrammatically', undefined, metadata);\n }\n urlLoaded.value = true;\n };\n\n /**\n * To emit the Url events just when the URL is load, and before the components mounted events\n * and state changes, we do it in the created of this component.\n */\n onMounted(() => {\n emitEvents();\n });\n\n return {\n onPageShow,\n emitEvents\n };\n }\n });\n</script>\n\n<docs lang=\"mdx\">\n## Events\n\nThis component emits the following events:\n\n- [`ParamsLoadedFromUrl`](https://github.com/empathyco/x/blob/main/packages/x-components/src/wiring/events.types.ts)\n- [`ExtraParamsLoadedFromUrl`](https://github.com/empathyco/x/blob/main/packages/x-components/src/wiring/events.types.ts)\n- [`UserOpenXProgrammatically`](https://github.com/empathyco/x/blob/main/packages/x-components/src/wiring/events.types.ts)\n\n## See it in action\n\nThis component manages the browser URL parameters to preserve them through reloads and browser\nhistory navigation. It allow to configure the default url parameter names using its attributes. This\ncomponent doesn't render elements to the DOM.\n\n_Try to make some requests and take a look to the url!_\n\n```vue\n<template>\n <UrlHandler />\n</template>\n\n<script>\n import { UrlHandler } from '@empathyco/x-components/url-handler';\n\n export default {\n name: 'UrlHandlerDemo',\n components: {\n UrlHandler\n }\n };\n</script>\n```\n\n### Play with props\n\nIn this example, the `UrlHandler` component changes the following query parameter names:\n\n- `query` to be `q`.\n- `page` to be `p`.\n- `filter` to be `f`\n- `sort` to be `s`\n\n_Try to make some requests and take a look to the url!_\n\n```vue\n<template>\n <UrlHandler query=\"q\" page=\"p\" filter=\"f\" sort=\"s\" />\n</template>\n\n<script>\n import { UrlHandler } from '@empathyco/x-components/url-handler';\n\n export default {\n name: 'UrlHandlerDemo',\n components: {\n UrlHandler\n }\n };\n</script>\n```\n\n### Play with events\n\nThe `UrlHandler` will emit the `ParamsLoadedFromUrl` when the page is loaded.\n\nThe `UrlHandler` will emit the `ExtraParamsLoadedFromUrl` when the page is loaded with an extra\nparam configured and with a value in URL.\n\nThe `UrlHandler` will emit the `UserOpenXProgrammatically` when the page is loaded with a query in\nthe URL.\n</docs>\n"],"names":[],"mappings":";;;;;;;;;AAwBE;;;;;;AAME;AACF,gBAAe,eAAe,CAAC;AAC7B,IAAA,IAAI,EAAE,YAAY;AAClB,IAAA,UAAU,EAAE;QACV,YAAW;AACZ,KAAA;IACD,OAAO,EAAE,UAAU,CAAC,IAAI;AACxB,IAAA,KAAK,EAAE,UAAU,KAAK,EAAE,EAAE,KAAM,EAAC,EAAA;AAC/B,QAAA,MAAM,EAAC,GAAI,KAAK,EAAE,CAAA;AAElB,QAAA,MAAM,kBAAmB,GAAE,QAAQ,CAAC,KAAK,EAAE,CAAC,oBAAoB,CAAC,CAAC,CAAC,kBAAkB,CAAA;AAErF;;;;AAIE;AACF,QAAA,MAAM,aAAY,GAAI,MAAM,CAA4B,eAAe,CAAC,CAAA;AAExE;;;;AAIE;AACF,QAAA,MAAM,SAAU,GAAE,GAAG,CAAC,KAAK,CAAC,CAAA;AAE5B;;;;AAIE;AACF,QAAA,MAAM,GAAI,GAAE,GAAG,CAAkB,SAAS,CAAC,CAAA;AAE3C;;;;AAIE;AACF,QAAA,MAAM,eAAgB,GAAE,GAAG,CAAC,KAAK,CAAC,CAAA;AAElC;;;;;;;;AAQE;AACF,QAAA,MAAM,kBAAiB,GAAI,QAAQ,CAAC,MAAM;AACxC,YAAA,OAAO,MAAM,CAAC,IAAI,CAAC,EAAE,GAAG,eAAe,EAAE,GAAG,KAAM,EAAC,CAAC,CAAA;AACtD,SAAC,CAAC,CAAA;AAEF;;;;;;;;AAQE;AACF,QAAA,MAAM,SAAU,GAAE,CAAC,SAAiB,KAAa;AAC/C,YAAA,MAAM,UAAW,GAAE,KAAK,CAAC,SAAS,CAAC,CAAA;AACnC,YAAA,OAAO,OAAO,UAAW,KAAI,QAAS,GAAE,UAAS,GAAI,SAAS,CAAA;AAChE,SAAC,CAAA;AAED;;;;;AAKE;AACF,QAAA,MAAM,mBAAoB,GAAE,CAAC,GAAQ,KAAW;YAC9C,kBAAkB,CAAC,KAAK,CAAC,OAAO,CAAC,aAC/B,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,SAAS,CAAC,SAAS,CAAC,CAAA,CAC7C,CAAA;AACH,SAAC,CAAA;AAED;;;;;;;AAOE;AACF,QAAA,MAAM,UAAS,GAAI,CAAC,SAAoB,KAA+B;AACrE,YAAA,OAAO,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,KAAK;AAC5D,gBAAA,OAAO,MAAK,GAAI,SAAS,CAAC,CAAE,GAAE,CAAC,CAAA;AACjC,aAAC,CAAC,CAAA;AACJ,SAAC,CAAA;AAED;;;;;;;;;;;AAWE;AACF,QAAA,MAAM,gBAAe,GAAI,CAAC,GAAQ,EAAE,SAAoB,KAAW;;AAEjE,YAAA,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE;gBACpB,OAAM;AACR,aAAA;AACA,YAAA,MAAM,cAAa,GAAI,YAAY,CAAC,SAAS,EAAE,SAAQ,IACrD,kBAAkB,CAAC,KAAK,CAAC,QAAQ,CAAC,SAAmB,CAAA,CACtD,CAAA;AACD,YAAA,MAAM,gBAAiB,GAAE,UAAU,CAAC,cAAc,CAAC,CAAA;YACnD,gBAAgB,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,EAAE,UAAU,CAAC,KAAK;AACpD,gBAAA,MAAM,cAAc,SAAS,CAAC,SAAS,CAAC,CAAA;AACxC,gBAAA,IAAI,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE;AAC7B,oBAAA,UAAU,CAAC,OAAO,CAAC,KAAM,IAAG;AAC1B,wBAAA,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAA;AACrD,qBAAC,CAAC,CAAA;AACF,iBAAA;AAAK,qBAAA;AACL,oBAAA,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC,CAAA;AACvD,iBAAA;AACF,aAAC,CAAC,CAAA;AACJ,SAAC,CAAA;AAED;;;;;;;;;AASE;AACF,QAAA,MAAM,YAAY,CAChB,YAAuB,EACvB,aAA4D,KACnD;YACT,IAAI,SAAS,CAAC,KAAK,EAAE;gBACnB,MAAM,MAAK,GAAI,IAAI,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAA;gBAC5C,mBAAmB,CAAC,MAAM,CAAC,CAAA;AAC3B,gBAAA,gBAAgB,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA;;AAGtC,gBAAA,MAAM,CAAC,MAAO,GAAE,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAA;gBAEnD,IAAI,MAAM,CAAC,IAAG,KAAM,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE;AACxC,oBAAA,aAAa,CAAC,EAAE,GAAG,MAAM,CAAC,OAAO,CAAC,KAAM,EAAC,EAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,CAAA;AACzE,iBAAA;AACA,gBAAA,GAAG,CAAC,KAAM,GAAE,MAAM,CAAA;AACpB,aAAA;AACF,SAAC,CAAA;AAED;;;;AAIE;AACF,QAAA,EAAE,CAAC,EAAE,CAAC,yBAAyB,EAAE,KAAK,CAAC,CAAC,SAAS,CAAC,CAAC,YAAuB,KAAK;AAC7E,YAAA,SAAS,CAAC,YAAY,EAAE,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAA;AACxE,SAAC,CAAC,CAAA;AAEF;;;;;AAKE;AACF,QAAA,EAAE,CAAC,EAAE,CAAC,4BAA4B,EAAE,KAAK,CAAC,CAAC,SAAS,CAAC,CAAC,YAAuB,KAAK;AAChF,YAAA,SAAS,CAAC,YAAY,EAAE,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAA;AAC3E,SAAC,CAAC,CAAA;AAEF;;;;;;;;;;;AAWE;AACF,QAAA,MAAM,UAAW,GAAE,CAAC,KAA0B,KAAK;AACjD,YAAA,eAAe,CAAC,QAAQ,KAAK,CAAC,SAAS,CAAA;YACvC,IAAI,KAAK,CAAC,SAAS,EAAE;;;AAGnB,gBAAA,GAAG,CAAC,KAAI,GAAI,SAAS,CAAA;AACvB,aAAA;AACF,SAAC,CAAA;AAED;;;;;;;;;;AAUE;AACF,QAAA,MAAM,aAAc,GAAE,CAAC,IAAY,EAAE,KAAe,KAAoB;AACtE,YAAA,QAAQ,OAAO,eAAe,CAAC,IAAI,CAAC;AAClC,gBAAA,KAAK,QAAQ;AACX,oBAAA,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;AACzB,gBAAA,KAAK,SAAS;oBACZ,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,EAAC,KAAM,MAAM,CAAA;AAC1C,gBAAA,KAAK,QAAQ;AACX,oBAAA,OAAO,KAAK,CAAC,CAAC,CAAC,CAAA;AACjB,gBAAA;;AAEE,oBAAA,OAAO,KAAK,CAAA;AAChB,aAAA;AACF,SAAC,CAAA;AAED;;;;;AAKE;QACF,MAAM,cAAe,GAAE,MAAuB;AAC5C,YAAA,MAAM,eAAgB,GAAE,IAAI,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,YAAY,CAAA;YAClE,OAAO,kBAAkB,CAAC,KAAK,CAAC,MAAM,CACpC,CAAC,MAAM,EAAE,IAAI,KAAK;AAChB,gBAAA,MAAM,MAAK,GAAI,SAAS,CAAC,IAAI,CAAC,CAAA;AAC9B,gBAAA,IAAI,eAAe,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE;oBAC/B,IAAI,IAAK,IAAG,eAAe,EAAE;wBAC3B,MAAM,QAAO,GAAI,eAAe,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;AAC/C,wBAAA,MAAM,CAAC,GAAG,CAAC,IAAI,CAAA,GAAI,aAAa,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAA;AAChD,qBAAA;AAAK,yBAAA;AACL,wBAAA,MAAM,CAAC,GAAG,CAAC,IAAI,CAAE,GAAE,MAAM,CAAC,KAAK,CAAC,IAAI,CAAA,GAAI,eAAe,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;AACrE,qBAAA;AACF,iBAAA;AACA,gBAAA,OAAO,MAAM,CAAA;AACf,aAAC,EACD,EAAE,GAAG,EAAE,EAAE,GAAG,eAAc,EAAG,EAAE,KAAK,EAAE,EAAE,GAAG,kBAAkB,CAAC,KAAI,EAAI,EAAA,CACvE,CAAA;AACH,SAAC,CAAA;AAED;;;;;;;;;;;;AAYE;QACF,MAAM,mBAAoB,GAAE,MAAe;AACzC,YAAA,MAAM,oBAAqB,GAAE,eAAe,CAAC,KAAK,CAAA;YAClD,MAAM,iBAAkB,GAAE,MAAM,CAAC,WAAW,CAAC,gBAAgB,CAAC,YAAY,CAAC,CAAA;YAC3E,MAAM,cAAe,GAAG,iBAAiB,CAAC,CAAC,CAAiC,EAAE,IAAI,CAAA;AAClF,YAAA,MAAM,mBAAoB,GACxB,CAAC,iBAAiB,CAAC,MAAK;iBACvB,YAAY,CAAC,iBAAiB,CAAE,IAAG,cAAe,KAAI,QAAQ,CAAC,CAAA;;AAGlE,YAAA,eAAe,CAAC,KAAM,GAAE,KAAK,CAAA;AAE7B,YAAA,IAAI,mBAAmB,EAAE;gBACvB,MAAM,iBAAgB,GAAI,CAAC,CAAC,aAAa,EAAE,KAAM,IAAG,cAAe,KAAI,UAAU,CAAA;AACjF,gBAAA,OAAO,cAAe,KAAI,kBAAkB,iBAAgB,IAAK,oBAAoB,CAAA;AACrF,aAAA;AAAK,iBAAA;gBACL,MAAM,iBAAgB,GAAI,CAAC,CAAC,aAAa,EAAE,KAAM,IAAG,cAAe,KAAI,UAAU,CAAA;AACjF,gBAAA,OAAO,cAAe,KAAI,kBAAkB,iBAAgB,IAAK,oBAAoB,CAAA;AACvF,aAAA;AACF,SAAC,CAAA;AAED;;;;;;AAME;QACF,MAAM,cAAe,GAAE,MAAuB;YAC5C,MAAM,UAAW,GAAE,IAAI,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAA;AAChD,YAAA,MAAM,WAAU,GAAI,GAAG,CAAC,KAAK,CAAA;AAC7B,YAAA,GAAG,CAAC,KAAM,GAAE,UAAU,CAAA;YAEtB,MAAM,oBAAqB,GACzB,WAAW,EAAE,MAAO,KAAI,UAAU,CAAC;AACnC,gBAAA,WAAW,EAAE,QAAO,KAAM,UAAU,CAAC,QAAQ,CAAA;AAC/C,YAAA,IAAI,oBAAoB,EAAE;AACxB,gBAAA,OAAO,aAAa,CAAA;AACtB,aAAA;YAEA,IAAI,mBAAmB,EAAE,EAAE;AACzB,gBAAA,OAAO,iBAAiB,CAAA;AAC1B,aAAA;AAEA,YAAA,OAAO,UAAU,CAAA;AACnB,SAAC,CAAA;AAED;;;;;AAKE;QACF,MAAM,qBAAqB,MAAkD;YAC3E,OAAO;AACL,gBAAA,OAAO,EAAE,KAAK;gBACd,QAAQ,EAAE,cAAc,EAAC;aAC1B,CAAA;AACH,SAAC,CAAA;AAED;;;;;;AAME;QACF,MAAM,UAAS,GAAI,MAAM;YACvB,MAAM,EAAE,GAAG,EAAE,KAAM,EAAA,GAAI,cAAc,EAAE,CAAA;AACvC,YAAA,MAAM,QAAS,GAAE,kBAAkB,EAAE,CAAA;YACrC,EAAE,CAAC,IAAI,CAAC,qBAAqB,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAA;YAC7C,EAAE,CAAC,IAAI,CAAC,0BAA0B,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAA;YACpD,IAAI,GAAG,CAAC,KAAK,EAAE;gBACb,EAAE,CAAC,IAAI,CAAC,2BAA2B,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAA;AAC3D,aAAA;AACA,YAAA,SAAS,CAAC,KAAI,GAAI,IAAI,CAAA;AACxB,SAAC,CAAA;AAED;;;AAGE;QACF,SAAS,CAAC,MAAM;AACd,YAAA,UAAU,EAAE,CAAA;AACd,SAAC,CAAC,CAAA;QAEF,OAAO;YACL,UAAU;YACV,UAAS;SACV,CAAA;KACH;AACD,CAAA,CAAC;;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@empathyco/x-components",
3
- "version": "6.0.0-alpha.46",
3
+ "version": "6.0.0-alpha.48",
4
4
  "description": "Empathy X Components",
5
5
  "author": "Empathy Systems Corporation S.L.",
6
6
  "license": "Apache-2.0",
@@ -138,5 +138,5 @@
138
138
  "access": "public",
139
139
  "directory": "dist"
140
140
  },
141
- "gitHead": "fb8d87bb740a16f90920f800483000d45c42de4b"
141
+ "gitHead": "1ff54c9cb106325eff36fc4ca7a51a8a47303d65"
142
142
  }
@@ -1 +1 @@
1
- {"version":3,"file":"url-handler.vue?vue&type=script&lang.d.ts","sourceRoot":"","sources":["../../../../../src/x-modules/url/components/url-handler.vue?vue&type=script&lang.ts"],"names":[],"mappings":"AAoBE;;;;;;GAMG;;wBAyL4B,mBAAmB;;;AAxLlD,wBA0VG"}
1
+ {"version":3,"file":"url-handler.vue?vue&type=script&lang.d.ts","sourceRoot":"","sources":["../../../../../src/x-modules/url/components/url-handler.vue?vue&type=script&lang.ts"],"names":[],"mappings":"AAoBE;;;;;;GAMG;;wBA0L4B,mBAAmB;;;AAzLlD,wBA2VG"}