@empathyco/x-components 6.0.0-alpha.62 → 6.0.0-alpha.64
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24 -0
- package/design-system/deprecated-full-theme.css +3942 -3942
- package/docs/API-reference/api/x-adapter-platform.md +1 -1
- package/docs/API-reference/api/x-adapter-platform.nextqueriesrelatedpromptsschema.md +2 -0
- package/docs/API-reference/api/x-components.fallbackdisclaimer.md +2 -2
- package/docs/API-reference/api/x-components.historyqueriesswitch.md +1 -1
- package/docs/API-reference/api/x-components.identifierresult.md +1 -1
- package/docs/API-reference/api/x-components.identifierresults.md +1 -1
- package/docs/API-reference/api/x-components.md +1 -1
- package/docs/API-reference/api/x-components.myhistory.md +1 -1
- package/docs/API-reference/api/x-components.partialresultslist.md +1 -1
- package/docs/API-reference/api/x-components.querypreview.md +2 -2
- package/docs/API-reference/api/x-components.recommendations.md +1 -1
- package/docs/API-reference/api/x-components.relatedpromptstaglist.md +2 -2
- package/docs/API-reference/api/x-components.relatedtag.md +3 -3
- package/docs/API-reference/api/x-components.scrolltotop.md +1 -1
- package/docs/API-reference/api/x-components.searchbutton.md +1 -1
- package/docs/API-reference/api/x-components.searchinput.md +1 -1
- package/docs/API-reference/api/x-components.semanticqueries.md +1 -1
- package/docs/API-reference/api/x-components.sortdropdown.md +1 -1
- package/docs/API-reference/api/x-components.sortlist.md +1 -1
- package/docs/API-reference/api/x-components.sortpickerlist.md +1 -1
- package/docs/API-reference/api/x-components.spellcheck.md +2 -2
- package/docs/API-reference/api/x-components.spellcheckbutton.md +1 -1
- package/docs/API-reference/api/x-components.usestate.md +5 -4
- package/docs/API-reference/api/x-types.md +1 -1
- package/docs/API-reference/api/x-types.xcomponentsadapter.md +2 -0
- package/js/composables/use-state.js +7 -9
- package/js/composables/use-state.js.map +1 -1
- package/js/directives/infinite-scroll.js +1 -1
- package/js/directives/infinite-scroll.js.map +1 -1
- package/js/x-installer/api/base-api.js +2 -2
- package/js/x-installer/api/base-api.js.map +1 -1
- package/js/x-modules/extra-params/components/extra-params.vue.js +1 -1
- package/js/x-modules/extra-params/components/extra-params.vue.js.map +1 -1
- package/js/x-modules/extra-params/components/renderless-extra-param.vue.js +1 -1
- package/js/x-modules/extra-params/components/renderless-extra-param.vue.js.map +1 -1
- package/js/x-modules/history-queries/components/clear-history-queries.vue.js.map +1 -1
- package/js/x-modules/history-queries/components/clear-history-queries.vue2.js +1 -1
- package/js/x-modules/history-queries/components/clear-history-queries.vue2.js.map +1 -1
- package/js/x-modules/history-queries/components/history-queries-switch.vue.js.map +1 -1
- package/js/x-modules/history-queries/components/history-queries-switch.vue2.js +1 -4
- package/js/x-modules/history-queries/components/history-queries-switch.vue2.js.map +1 -1
- package/js/x-modules/history-queries/components/my-history.vue.js.map +1 -1
- package/js/x-modules/history-queries/components/my-history.vue2.js +1 -1
- package/js/x-modules/history-queries/components/my-history.vue2.js.map +1 -1
- package/js/x-modules/identifier-results/components/identifier-result.vue.js.map +1 -1
- package/js/x-modules/identifier-results/components/identifier-result.vue2.js +1 -1
- package/js/x-modules/identifier-results/components/identifier-result.vue2.js.map +1 -1
- package/js/x-modules/identifier-results/components/identifier-results.vue.js.map +1 -1
- package/js/x-modules/identifier-results/components/identifier-results.vue2.js +1 -1
- package/js/x-modules/identifier-results/components/identifier-results.vue2.js.map +1 -1
- package/js/x-modules/next-queries/components/next-queries-list.vue.js +1 -1
- package/js/x-modules/next-queries/components/next-queries-list.vue.js.map +1 -1
- package/js/x-modules/next-queries/components/next-query-preview.vue.js.map +1 -1
- package/js/x-modules/next-queries/components/next-query-preview.vue2.js +1 -1
- package/js/x-modules/next-queries/components/next-query-preview.vue2.js.map +1 -1
- package/js/x-modules/queries-preview/components/query-preview-button.vue.js.map +1 -1
- package/js/x-modules/queries-preview/components/query-preview-button.vue2.js +1 -1
- package/js/x-modules/queries-preview/components/query-preview-button.vue2.js.map +1 -1
- package/js/x-modules/queries-preview/components/query-preview-list.vue.js.map +1 -1
- package/js/x-modules/queries-preview/components/query-preview-list.vue2.js +1 -1
- package/js/x-modules/queries-preview/components/query-preview-list.vue2.js.map +1 -1
- package/js/x-modules/queries-preview/components/query-preview.vue.js.map +1 -1
- package/js/x-modules/queries-preview/components/query-preview.vue2.js +6 -11
- package/js/x-modules/queries-preview/components/query-preview.vue2.js.map +1 -1
- package/js/x-modules/recommendations/components/recommendations.vue.js.map +1 -1
- package/js/x-modules/recommendations/components/recommendations.vue2.js +1 -3
- package/js/x-modules/recommendations/components/recommendations.vue2.js.map +1 -1
- package/js/x-modules/related-prompts/components/related-prompts-list.vue.js +1 -4
- package/js/x-modules/related-prompts/components/related-prompts-list.vue.js.map +1 -1
- package/js/x-modules/related-prompts/components/related-prompts-tag-list.vue.js.map +1 -1
- package/js/x-modules/related-prompts/components/related-prompts-tag-list.vue2.js +1 -6
- package/js/x-modules/related-prompts/components/related-prompts-tag-list.vue2.js.map +1 -1
- package/js/x-modules/related-tags/components/related-tag.vue.js.map +1 -1
- package/js/x-modules/related-tags/components/related-tag.vue2.js +1 -1
- package/js/x-modules/related-tags/components/related-tag.vue2.js.map +1 -1
- package/js/x-modules/scroll/components/main-scroll-item.vue.js.map +1 -1
- package/js/x-modules/scroll/components/main-scroll-item.vue2.js +1 -1
- package/js/x-modules/scroll/components/main-scroll-item.vue2.js.map +1 -1
- package/js/x-modules/scroll/components/main-scroll.vue.js +1 -1
- package/js/x-modules/scroll/components/main-scroll.vue.js.map +1 -1
- package/js/x-modules/scroll/components/scroll-to-top.vue.js.map +1 -1
- package/js/x-modules/scroll/components/scroll-to-top.vue2.js +1 -1
- package/js/x-modules/scroll/components/scroll-to-top.vue2.js.map +1 -1
- package/js/x-modules/search/components/banners-list.vue.js +1 -1
- package/js/x-modules/search/components/banners-list.vue.js.map +1 -1
- package/js/x-modules/search/components/fallback-disclaimer.vue.js.map +1 -1
- package/js/x-modules/search/components/fallback-disclaimer.vue2.js +1 -4
- package/js/x-modules/search/components/fallback-disclaimer.vue2.js.map +1 -1
- package/js/x-modules/search/components/partial-results-list.vue.js.map +1 -1
- package/js/x-modules/search/components/partial-results-list.vue2.js +1 -3
- package/js/x-modules/search/components/partial-results-list.vue2.js.map +1 -1
- package/js/x-modules/search/components/promoteds-list.vue.js +1 -1
- package/js/x-modules/search/components/promoteds-list.vue.js.map +1 -1
- package/js/x-modules/search/components/redirection.vue.js.map +1 -1
- package/js/x-modules/search/components/redirection.vue2.js +1 -1
- package/js/x-modules/search/components/redirection.vue2.js.map +1 -1
- package/js/x-modules/search/components/results-list.vue.js +1 -7
- package/js/x-modules/search/components/results-list.vue.js.map +1 -1
- package/js/x-modules/search/components/sort-dropdown.vue.js.map +1 -1
- package/js/x-modules/search/components/sort-dropdown.vue2.js +1 -1
- package/js/x-modules/search/components/sort-dropdown.vue2.js.map +1 -1
- package/js/x-modules/search/components/sort-list.vue.js.map +1 -1
- package/js/x-modules/search/components/sort-list.vue2.js +1 -1
- package/js/x-modules/search/components/sort-list.vue2.js.map +1 -1
- package/js/x-modules/search/components/sort-picker-list.vue.js.map +1 -1
- package/js/x-modules/search/components/sort-picker-list.vue2.js +1 -1
- package/js/x-modules/search/components/sort-picker-list.vue2.js.map +1 -1
- package/js/x-modules/search/components/spellcheck-button.vue.js.map +1 -1
- package/js/x-modules/search/components/spellcheck-button.vue2.js +1 -1
- package/js/x-modules/search/components/spellcheck-button.vue2.js.map +1 -1
- package/js/x-modules/search/components/spellcheck.vue.js.map +1 -1
- package/js/x-modules/search/components/spellcheck.vue2.js +1 -1
- package/js/x-modules/search/components/spellcheck.vue2.js.map +1 -1
- package/js/x-modules/search-box/components/clear-search-input.vue.js.map +1 -1
- package/js/x-modules/search-box/components/clear-search-input.vue2.js +1 -1
- package/js/x-modules/search-box/components/clear-search-input.vue2.js.map +1 -1
- package/js/x-modules/search-box/components/search-button.vue.js.map +1 -1
- package/js/x-modules/search-box/components/search-button.vue2.js +1 -1
- package/js/x-modules/search-box/components/search-button.vue2.js.map +1 -1
- package/js/x-modules/search-box/components/search-input-placeholder.vue.js.map +1 -1
- package/js/x-modules/search-box/components/search-input-placeholder.vue2.js +1 -1
- package/js/x-modules/search-box/components/search-input-placeholder.vue2.js.map +1 -1
- package/js/x-modules/search-box/components/search-input.vue.js.map +1 -1
- package/js/x-modules/search-box/components/search-input.vue2.js +1 -1
- package/js/x-modules/search-box/components/search-input.vue2.js.map +1 -1
- package/js/x-modules/semantic-queries/components/semantic-queries.vue.js.map +1 -1
- package/js/x-modules/semantic-queries/components/semantic-queries.vue2.js +1 -3
- package/js/x-modules/semantic-queries/components/semantic-queries.vue2.js.map +1 -1
- package/js/x-modules/url/components/url-handler.vue.js.map +1 -1
- package/js/x-modules/url/components/url-handler.vue2.js +1 -1
- package/js/x-modules/url/components/url-handler.vue2.js.map +1 -1
- package/package.json +3 -3
- package/report/x-adapter-platform.api.json +2 -2
- package/report/x-components.api.json +63 -70
- package/report/x-components.api.md +22 -20
- package/report/x-types.api.json +3 -3
- package/types/composables/use-state.d.ts +4 -5
- package/types/composables/use-state.d.ts.map +1 -1
- package/types/x-modules/extra-params/components/extra-params.vue.d.ts.map +1 -1
- package/types/x-modules/history-queries/components/history-queries-switch.vue.d.ts +1 -1
- package/types/x-modules/history-queries/components/history-queries-switch.vue.d.ts.map +1 -1
- package/types/x-modules/history-queries/components/my-history.vue.d.ts +1 -1
- package/types/x-modules/identifier-results/components/identifier-result.vue.d.ts +1 -1
- package/types/x-modules/identifier-results/components/identifier-results.vue.d.ts +1 -1
- package/types/x-modules/queries-preview/components/query-preview.vue.d.ts +3 -3
- package/types/x-modules/queries-preview/components/query-preview.vue.d.ts.map +1 -1
- package/types/x-modules/recommendations/components/recommendations.vue.d.ts +1 -2
- package/types/x-modules/recommendations/components/recommendations.vue.d.ts.map +1 -1
- package/types/x-modules/related-prompts/components/related-prompts-list.vue.d.ts.map +1 -1
- package/types/x-modules/related-prompts/components/related-prompts-tag-list.vue.d.ts +3 -3
- package/types/x-modules/related-prompts/components/related-prompts-tag-list.vue.d.ts.map +1 -1
- package/types/x-modules/related-tags/components/related-tag.vue.d.ts +4 -4
- package/types/x-modules/related-tags/components/related-tag.vue.d.ts.map +1 -1
- package/types/x-modules/scroll/components/scroll-to-top.vue.d.ts +1 -1
- package/types/x-modules/search/components/banners-list.vue.d.ts.map +1 -1
- package/types/x-modules/search/components/fallback-disclaimer.vue.d.ts +2 -2
- package/types/x-modules/search/components/fallback-disclaimer.vue.d.ts.map +1 -1
- package/types/x-modules/search/components/partial-results-list.vue.d.ts +1 -3
- package/types/x-modules/search/components/partial-results-list.vue.d.ts.map +1 -1
- package/types/x-modules/search/components/promoteds-list.vue.d.ts.map +1 -1
- package/types/x-modules/search/components/results-list.vue.d.ts.map +1 -1
- package/types/x-modules/search/components/sort-dropdown.vue.d.ts +1 -1
- package/types/x-modules/search/components/sort-list.vue.d.ts +1 -1
- package/types/x-modules/search/components/sort-picker-list.vue.d.ts +1 -1
- package/types/x-modules/search/components/spellcheck-button.vue.d.ts +1 -1
- package/types/x-modules/search/components/spellcheck.vue.d.ts +2 -2
- package/types/x-modules/search-box/components/search-button.vue.d.ts +1 -2
- package/types/x-modules/search-box/components/search-button.vue.d.ts.map +1 -1
- package/types/x-modules/search-box/components/search-input.vue.d.ts +1 -1
- package/types/x-modules/semantic-queries/components/semantic-queries.vue.d.ts +1 -3
- package/types/x-modules/semantic-queries/components/semantic-queries.vue.d.ts.map +1 -1
|
@@ -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=\"[\n 'x-related-prompts-tag-list-scroll-container',\n scrollContainerClass || '',\n ]\"\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 class=\"x-related-prompts-tag-list\"\n :css=\"false\"\n tag=\"ul\"\n appear\n @before-enter=\"onBeforeEnter\"\n @enter=\"onEnter\"\n @leave=\"onLeave\"\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=\"{\n ...(selectedPromptIndex === index && { width: '100%' }),\n ...(isAnimating && { pointerEvents: 'none' }),\n }\"\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 :related-prompt=\"relatedPrompt\"\n :on-select=\"() => onSelect(index)\"\n :is-selected=\"selectedPromptIndex === index\"\n >\n <DisplayEmitter\n :payload=\"relatedPrompt.tagging?.toolingDisplayTagging\"\n :event-metadata=\"{\n feature: 'related-prompts',\n displayOriginalQuery: x.query.searchBox,\n replaceable: false,\n }\"\n >\n <RelatedPrompt\n :related-prompt=\"relatedPrompt\"\n :selected=\"selectedPromptIndex === index\"\n @click=\"onSelect(index)\"\n >\n <template #related-prompt-extra-content>\n <slot name=\"related-prompt-extra-content\" :related-prompt=\"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\">\nimport type { RelatedPrompt as RelatedPromptModel } from '@empathyco/x-types'\nimport type { ComputedRef, PropType } from 'vue'\nimport { computed, defineComponent, ref } from 'vue'\nimport DisplayEmitter from '../../../components/display-emitter.vue'\nimport SlidingPanel from '../../../components/sliding-panel.vue'\nimport { use$x, useState } from '../../../composables'\nimport { relatedPromptsXModule } from '../x-module'\nimport 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 */\nexport 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: ComputedRef<RelatedPromptModel[]> = useState('relatedPrompts', [\n 'relatedPrompts',\n ]).relatedPrompts\n const selectedPromptIndex: ComputedRef<number> = useState('relatedPrompts', [\n 'selectedPrompt',\n ]).selectedPrompt\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.map((relatedPrompt, index) => ({ ...relatedPrompt, index })),\n )\n\n const visibleRelatedPrompts = computed(() =>\n selectedPromptIndex.value !== -1\n ? [indexRelatedPrompts.value[selectedPromptIndex.value]]\n : indexRelatedPrompts.value,\n )\n\n let timeOutId: number\n const resetTransitionStyle = (excludedProperties: Array<string> = ['width']) => {\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 (!excludedProperties.includes(property)) {\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 // Changing the request will trigger the appear animation, so we need to reset the\n // style after it finishes\n x.on('SearchRequestChanged', false).subscribe(() => {\n resetTransitionStyle([])\n })\n\n return {\n onSelect,\n onBeforeEnter,\n onEnter,\n onLeave,\n selectedPromptIndex,\n visibleRelatedPrompts,\n listItems,\n isAnimating,\n x,\n }\n },\n})\n</script>\n\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 width: 100%;\n}\n.x-related-prompts-tag-list-item {\n height: 100%;\n flex-shrink: 0;\n}\n</style>\n"],"names":["_resolveComponent","_openBlock","_createBlock","buttonClass","showButtons","scrollContainerClass","_renderSlot","_withCtx","_createVNode","_TransitionGroup","onLeave","_createElementBlock","_Fragment","_renderList","_normalizeClass","selectedPromptIndex","isAnimating","_normalizeStyle","onSelect","x"],"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=\"[\n 'x-related-prompts-tag-list-scroll-container',\n scrollContainerClass || '',\n ]\"\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 class=\"x-related-prompts-tag-list\"\n :css=\"false\"\n tag=\"ul\"\n appear\n @before-enter=\"onBeforeEnter\"\n @enter=\"onEnter\"\n @leave=\"onLeave\"\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=\"{\n ...(selectedPromptIndex === index && { width: '100%' }),\n ...(isAnimating && { pointerEvents: 'none' }),\n }\"\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 :related-prompt=\"relatedPrompt\"\n :on-select=\"() => onSelect(index)\"\n :is-selected=\"selectedPromptIndex === index\"\n >\n <DisplayEmitter\n :payload=\"relatedPrompt.tagging?.toolingDisplayTagging\"\n :event-metadata=\"{\n feature: 'related-prompts',\n displayOriginalQuery: x.query.searchBox,\n replaceable: false,\n }\"\n >\n <RelatedPrompt\n :related-prompt=\"relatedPrompt\"\n :selected=\"selectedPromptIndex === index\"\n @click=\"onSelect(index)\"\n >\n <template #related-prompt-extra-content>\n <slot name=\"related-prompt-extra-content\" :related-prompt=\"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\">\nimport type { PropType } from 'vue'\nimport { computed, defineComponent, ref } from 'vue'\nimport DisplayEmitter from '../../../components/display-emitter.vue'\nimport SlidingPanel from '../../../components/sliding-panel.vue'\nimport { use$x, useState } from '../../../composables'\nimport { relatedPromptsXModule } from '../x-module'\nimport 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 */\nexport 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\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.map((relatedPrompt, index) => ({ ...relatedPrompt, index })),\n )\n\n const visibleRelatedPrompts = computed(() =>\n selectedPromptIndex.value !== -1\n ? [indexRelatedPrompts.value[selectedPromptIndex.value]]\n : indexRelatedPrompts.value,\n )\n\n let timeOutId: number\n const resetTransitionStyle = (excludedProperties: Array<string> = ['width']) => {\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 (!excludedProperties.includes(property)) {\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 // Changing the request will trigger the appear animation, so we need to reset the\n // style after it finishes\n x.on('SearchRequestChanged', false).subscribe(() => {\n resetTransitionStyle([])\n })\n\n return {\n onSelect,\n onBeforeEnter,\n onEnter,\n onLeave,\n selectedPromptIndex,\n visibleRelatedPrompts,\n listItems,\n isAnimating,\n x,\n }\n },\n})\n</script>\n\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 width: 100%;\n}\n.x-related-prompts-tag-list-item {\n height: 100%;\n flex-shrink: 0;\n}\n</style>\n"],"names":["_resolveComponent","_openBlock","_createBlock","buttonClass","showButtons","scrollContainerClass","_renderSlot","_withCtx","_createVNode","_TransitionGroup","onLeave","_createElementBlock","_Fragment","_renderList","_normalizeClass","selectedPromptIndex","isAnimating","_normalizeStyle","onSelect","x"],"mappings":"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"}
|
|
@@ -73,12 +73,7 @@ var _sfc_main = defineComponent({
|
|
|
73
73
|
},
|
|
74
74
|
setup(props) {
|
|
75
75
|
const x = use$x();
|
|
76
|
-
const relatedPrompts = useState('relatedPrompts'
|
|
77
|
-
'relatedPrompts',
|
|
78
|
-
]).relatedPrompts;
|
|
79
|
-
const selectedPromptIndex = useState('relatedPrompts', [
|
|
80
|
-
'selectedPrompt',
|
|
81
|
-
]).selectedPrompt;
|
|
76
|
+
const { relatedPrompts, selectedPrompt: selectedPromptIndex } = useState('relatedPrompts');
|
|
82
77
|
const clickedListItemIndex = ref(null);
|
|
83
78
|
const initialOffsetLefts = {};
|
|
84
79
|
const isAnimating = ref(false);
|
|
@@ -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=\"[\n 'x-related-prompts-tag-list-scroll-container',\n scrollContainerClass || '',\n ]\"\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 class=\"x-related-prompts-tag-list\"\n :css=\"false\"\n tag=\"ul\"\n appear\n @before-enter=\"onBeforeEnter\"\n @enter=\"onEnter\"\n @leave=\"onLeave\"\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=\"{\n ...(selectedPromptIndex === index && { width: '100%' }),\n ...(isAnimating && { pointerEvents: 'none' }),\n }\"\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 :related-prompt=\"relatedPrompt\"\n :on-select=\"() => onSelect(index)\"\n :is-selected=\"selectedPromptIndex === index\"\n >\n <DisplayEmitter\n :payload=\"relatedPrompt.tagging?.toolingDisplayTagging\"\n :event-metadata=\"{\n feature: 'related-prompts',\n displayOriginalQuery: x.query.searchBox,\n replaceable: false,\n }\"\n >\n <RelatedPrompt\n :related-prompt=\"relatedPrompt\"\n :selected=\"selectedPromptIndex === index\"\n @click=\"onSelect(index)\"\n >\n <template #related-prompt-extra-content>\n <slot name=\"related-prompt-extra-content\" :related-prompt=\"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\">\nimport type { RelatedPrompt as RelatedPromptModel } from '@empathyco/x-types'\nimport type { ComputedRef, PropType } from 'vue'\nimport { computed, defineComponent, ref } from 'vue'\nimport DisplayEmitter from '../../../components/display-emitter.vue'\nimport SlidingPanel from '../../../components/sliding-panel.vue'\nimport { use$x, useState } from '../../../composables'\nimport { relatedPromptsXModule } from '../x-module'\nimport 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 */\nexport 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: ComputedRef<RelatedPromptModel[]> = useState('relatedPrompts', [\n 'relatedPrompts',\n ]).relatedPrompts\n const selectedPromptIndex: ComputedRef<number> = useState('relatedPrompts', [\n 'selectedPrompt',\n ]).selectedPrompt\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.map((relatedPrompt, index) => ({ ...relatedPrompt, index })),\n )\n\n const visibleRelatedPrompts = computed(() =>\n selectedPromptIndex.value !== -1\n ? [indexRelatedPrompts.value[selectedPromptIndex.value]]\n : indexRelatedPrompts.value,\n )\n\n let timeOutId: number\n const resetTransitionStyle = (excludedProperties: Array<string> = ['width']) => {\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 (!excludedProperties.includes(property)) {\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 // Changing the request will trigger the appear animation, so we need to reset the\n // style after it finishes\n x.on('SearchRequestChanged', false).subscribe(() => {\n resetTransitionStyle([])\n })\n\n return {\n onSelect,\n onBeforeEnter,\n onEnter,\n onLeave,\n selectedPromptIndex,\n visibleRelatedPrompts,\n listItems,\n isAnimating,\n x,\n }\n },\n})\n</script>\n\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 width: 100%;\n}\n.x-related-prompts-tag-list-item {\n height: 100%;\n flex-shrink: 0;\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=\"[\n 'x-related-prompts-tag-list-scroll-container',\n scrollContainerClass || '',\n ]\"\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 class=\"x-related-prompts-tag-list\"\n :css=\"false\"\n tag=\"ul\"\n appear\n @before-enter=\"onBeforeEnter\"\n @enter=\"onEnter\"\n @leave=\"onLeave\"\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=\"{\n ...(selectedPromptIndex === index && { width: '100%' }),\n ...(isAnimating && { pointerEvents: 'none' }),\n }\"\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 :related-prompt=\"relatedPrompt\"\n :on-select=\"() => onSelect(index)\"\n :is-selected=\"selectedPromptIndex === index\"\n >\n <DisplayEmitter\n :payload=\"relatedPrompt.tagging?.toolingDisplayTagging\"\n :event-metadata=\"{\n feature: 'related-prompts',\n displayOriginalQuery: x.query.searchBox,\n replaceable: false,\n }\"\n >\n <RelatedPrompt\n :related-prompt=\"relatedPrompt\"\n :selected=\"selectedPromptIndex === index\"\n @click=\"onSelect(index)\"\n >\n <template #related-prompt-extra-content>\n <slot name=\"related-prompt-extra-content\" :related-prompt=\"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\">\nimport type { PropType } from 'vue'\nimport { computed, defineComponent, ref } from 'vue'\nimport DisplayEmitter from '../../../components/display-emitter.vue'\nimport SlidingPanel from '../../../components/sliding-panel.vue'\nimport { use$x, useState } from '../../../composables'\nimport { relatedPromptsXModule } from '../x-module'\nimport 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 */\nexport 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\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.map((relatedPrompt, index) => ({ ...relatedPrompt, index })),\n )\n\n const visibleRelatedPrompts = computed(() =>\n selectedPromptIndex.value !== -1\n ? [indexRelatedPrompts.value[selectedPromptIndex.value]]\n : indexRelatedPrompts.value,\n )\n\n let timeOutId: number\n const resetTransitionStyle = (excludedProperties: Array<string> = ['width']) => {\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 (!excludedProperties.includes(property)) {\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 // Changing the request will trigger the appear animation, so we need to reset the\n // style after it finishes\n x.on('SearchRequestChanged', false).subscribe(() => {\n resetTransitionStyle([])\n })\n\n return {\n onSelect,\n onBeforeEnter,\n onEnter,\n onLeave,\n selectedPromptIndex,\n visibleRelatedPrompts,\n listItems,\n isAnimating,\n x,\n }\n },\n})\n</script>\n\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 width: 100%;\n}\n.x-related-prompts-tag-list-item {\n height: 100%;\n flex-shrink: 0;\n}\n</style>\n"],"names":["DisplayEmitter"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"related-tag.vue.js","sources":["../../../../../src/x-modules/related-tags/components/related-tag.vue"],"sourcesContent":["<template>\n <button\n ref=\"buttonEl\"\n class=\"x-tag x-related-tag\"\n data-test=\"related-tag\"\n :class=\"dynamicClasses\"\n @click=\"clickRelatedTag\"\n >\n <!--\n @slot Custom content that replaces the RelatedTag default content.\n @binding {RelatedTag} relatedTag - Related tag data.\n @binding {boolean} isSelected - Related tag status.\n @binding {boolean} shouldHighlightCurated - True if the curated RTs should be displayed.\n -->\n <slot v-bind=\"{ relatedTag, isSelected, shouldHighlightCurated }\">{{ relatedTag.tag }}</slot>\n </button>\n</template>\n\n<script lang=\"ts\">\nimport type { RelatedTag as RelatedTagModel } from '@empathyco/x-types'\nimport type {
|
|
1
|
+
{"version":3,"file":"related-tag.vue.js","sources":["../../../../../src/x-modules/related-tags/components/related-tag.vue"],"sourcesContent":["<template>\n <button\n ref=\"buttonEl\"\n class=\"x-tag x-related-tag\"\n data-test=\"related-tag\"\n :class=\"dynamicClasses\"\n @click=\"clickRelatedTag\"\n >\n <!--\n @slot Custom content that replaces the RelatedTag default content.\n @binding {RelatedTag} relatedTag - Related tag data.\n @binding {boolean} isSelected - Related tag status.\n @binding {boolean} shouldHighlightCurated - True if the curated RTs should be displayed.\n -->\n <slot v-bind=\"{ relatedTag, isSelected, shouldHighlightCurated }\">{{ relatedTag.tag }}</slot>\n </button>\n</template>\n\n<script lang=\"ts\">\nimport type { RelatedTag as RelatedTagModel } from '@empathyco/x-types'\nimport type { PropType } from 'vue'\nimport type { VueCSSClasses } from '../../../utils/types'\nimport type { WireMetadata } from '../../../wiring/wiring.types'\nimport { computed, defineComponent, ref } from 'vue'\nimport { use$x } from '../../../composables/use-$x'\nimport { useState } from '../../../composables/use-state'\nimport { relatedTagsXModule } from '../x-module'\n\n/**\n * This component renders a related tag for a query. A related tag is a descriptive keyword\n * related to the current query to fine-tune the search. For example, if you are searching\n * for *lego*, a related tag could be *city*, refining the search with *lego city*.\n *\n * @public\n */\nexport default defineComponent({\n name: 'RelatedTag',\n xModule: relatedTagsXModule.name,\n props: {\n /**\n * Indicates if the curated related tag should be highlighted.\n *\n * @public\n */\n highlightCurated: {\n type: Boolean,\n default: false,\n },\n /**\n * The related tag model data.\n *\n * @public\n */\n relatedTag: {\n type: Object as PropType<RelatedTagModel>,\n required: true,\n },\n },\n setup(props) {\n const $x = use$x()\n\n const buttonEl = ref<HTMLElement | undefined>()\n\n /**\n * The selected related tags.\n *\n * @internal\n */\n const { selectedRelatedTags } = useState('relatedTags')\n\n /**\n * Check if the related tag is selected or not.\n *\n * @returns If the related tag is selected.\n *\n * @internal\n */\n const isSelected = computed(() => selectedRelatedTags.value.includes(props.relatedTag))\n\n /**\n * Blurs the related tag if it is selected.\n *\n * @public\n */\n const blurRelatedTag = () => {\n if (isSelected.value) {\n buttonEl.value?.blur()\n }\n }\n\n /**\n * Generates the {@link WireMetadata} object omitting the moduleName.\n *\n * @returns The {@link WireMetadata} object omitting the moduleName.\n * @internal\n */\n const createEventMetadata = (): Omit<WireMetadata, 'moduleName'> => ({\n target: buttonEl.value as HTMLElement,\n feature: 'related_tag',\n })\n\n /**\n * Emits events when the button is clicked.\n *\n * @public\n */\n const emitEvents = () => {\n // We have to emit this events first to avoid the UserPickedARelatedTag wires to change the\n // isSelected value before emitting this selection events.\n $x.emit(\n isSelected.value ? 'UserDeselectedARelatedTag' : 'UserSelectedARelatedTag',\n props.relatedTag,\n createEventMetadata(),\n )\n $x.emit('UserPickedARelatedTag', props.relatedTag, createEventMetadata())\n }\n\n /**\n * Handles the click on the button.\n *\n * @public\n */\n const clickRelatedTag = () => {\n emitEvents()\n blurRelatedTag()\n }\n\n /**\n * Check if the related tag is curated and should be highlighted.\n *\n * @returns True if the related tag is curated and should be highlighted.\n *\n * @internal\n */\n const shouldHighlightCurated = computed(\n () => props.highlightCurated && (props.relatedTag.isCurated ?? false),\n )\n\n /**\n * Adds the dynamic css classes to the component.\n *\n * @returns The class to be added to the component.\n *\n * @internal\n */\n const dynamicClasses = computed(\n (): VueCSSClasses => ({\n 'x-selected x-related-tag--is-selected': isSelected.value,\n 'x-related-tag--is-curated': shouldHighlightCurated.value,\n }),\n )\n\n return {\n buttonEl,\n dynamicClasses,\n isSelected,\n clickRelatedTag,\n shouldHighlightCurated,\n }\n },\n})\n</script>\n\n<docs lang=\"mdx\">\n## Dynamic classes\n\n`RelatedTag` uses the following dynamic CSS classes so you can style it when is:\n\n- Selected: `x-related-tag--is-selected`.\n- Curated: `x-related-tag--is-curated`.\n\n## Events\n\nThis component emits the following events:\n\n- [`UserDeselectedARelatedTag`](https://github.com/empathyco/x/blob/main/packages/x-components/src/wiring/events.types.ts)\n- [`UserPickedARelatedTag`](https://github.com/empathyco/x/blob/main/packages/x-components/src/wiring/events.types.ts)\n- [`UserSelectedARelatedTag`](https://github.com/empathyco/x/blob/main/packages/x-components/src/wiring/events.types.ts)\n\n## See it in action\n\n<!-- prettier-ignore-start -->\n:::warning Backend service required\nThe QuerySignals microservice must be implemented.\n:::\n<!-- prettier-ignore-end -->\n\nIn this example related tag data is passed as a prop.\n\n_Here you can see how the RelatedTag component is rendered._\n\n```vue live\n<template>\n <RelatedTag :relatedTag=\"tag\" />\n</template>\n\n<script>\nimport { RelatedTag } from '@empathyco/x-components/related-tags'\n\nexport default {\n name: 'RelatedTagDemo',\n components: {\n RelatedTag,\n },\n data() {\n return {\n tag: {\n modelName: 'RelatedTag',\n query: 'high heel',\n isCurated: false,\n tag: 'heel',\n },\n }\n },\n}\n</script>\n```\n\n### Play with default slot\n\nIn this example, an HTML span element is passed in the `default` slot.\n\n_See how the related tag can be rendered._\n\n```vue live\n<template>\n <RelatedTag :relatedTag=\"tag\" #default=\"{ relatedTag }\">\n <span :aria-label=\"relatedTag.tag\">{{ relatedTag.tag }}</span>\n </RelatedTag>\n</template>\n\n<script>\nimport { RelatedTag } from '@empathyco/x-components/related-tags'\n\nexport default {\n name: 'RelatedTagDemo',\n components: {\n RelatedTag,\n },\n data() {\n return {\n tag: {\n modelName: 'RelatedTag',\n query: 'high heel',\n isCurated: false,\n tag: 'heel',\n },\n }\n },\n}\n</script>\n```\n\n### Play with events\n\nIn this example, the [`UserSelectedARelatedTag`](./../../api/x-components.relatedtagsxevents.md)\nevent is implemented, as illustrated by the “Tag” message returned.\n\n_See how the event is triggered when the related tag is clicked._\n\n```vue live\n<template>\n <RelatedTag :relatedTag=\"tag\" @UserSelectedARelatedTag=\"alertRelatedTag\" />\n</template>\n\n<script>\nimport { RelatedTag } from '@empathyco/x-components/related-tags'\n\nexport default {\n name: 'RelatedTagDemo',\n components: {\n RelatedTag,\n },\n data() {\n return {\n tag: {\n modelName: 'RelatedTag',\n query: 'high heel',\n isCurated: false,\n tag: 'heel',\n },\n }\n },\n methods: {\n alertRelatedTag(relatedTag) {\n alert(`You have clicked the related tag: ${relatedTag.query}`)\n },\n },\n}\n</script>\n```\n</docs>\n"],"names":["_openBlock","_createElementBlock","dynamicClasses","_normalizeClass","relatedTag"],"mappings":";;;;SAEQ,WAAU,CAAA,IAAA,EAAA,MAAA,EAAA,MAAA,EAAA,MAAA,EAAA,KAAA,EAAA,QAAA,EAAA;SACTA,SAHT,EAAA,EAAAC,kBAAA;AAAA,IAGU,QAAA;AAAA,IAEEC;AAAAA,MADR,GAAA,EAAA,UAAA;AAAA,MAEC,OAAKC,cAAE,CAAA,CAAA,qBAAA,EAAA,IAAA,CAAA,cAAA,CAAA,CAAA;AAAA,MAAA,WAAA,EAAA,aAAA;MAQR,OAA6F,EAAA,MAAA,CAAA,CAAA,CAAA,KAAA,MAAA,CAAA,CAAA,CAAA,GAAA,CAAA,GAdjG,sDAcoBC,GAAU,IAAA,CAAA,CAAA;AAAA,KAAA;;;;;;;;;;;;;;;;;;"}
|
|
@@ -41,7 +41,7 @@ var _sfc_main = defineComponent({
|
|
|
41
41
|
*
|
|
42
42
|
* @internal
|
|
43
43
|
*/
|
|
44
|
-
const { selectedRelatedTags } = useState('relatedTags'
|
|
44
|
+
const { selectedRelatedTags } = useState('relatedTags');
|
|
45
45
|
/**
|
|
46
46
|
* Check if the related tag is selected or not.
|
|
47
47
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"related-tag.vue2.js","sources":["../../../../../src/x-modules/related-tags/components/related-tag.vue"],"sourcesContent":["<template>\n <button\n ref=\"buttonEl\"\n class=\"x-tag x-related-tag\"\n data-test=\"related-tag\"\n :class=\"dynamicClasses\"\n @click=\"clickRelatedTag\"\n >\n <!--\n @slot Custom content that replaces the RelatedTag default content.\n @binding {RelatedTag} relatedTag - Related tag data.\n @binding {boolean} isSelected - Related tag status.\n @binding {boolean} shouldHighlightCurated - True if the curated RTs should be displayed.\n -->\n <slot v-bind=\"{ relatedTag, isSelected, shouldHighlightCurated }\">{{ relatedTag.tag }}</slot>\n </button>\n</template>\n\n<script lang=\"ts\">\nimport type { RelatedTag as RelatedTagModel } from '@empathyco/x-types'\nimport type {
|
|
1
|
+
{"version":3,"file":"related-tag.vue2.js","sources":["../../../../../src/x-modules/related-tags/components/related-tag.vue"],"sourcesContent":["<template>\n <button\n ref=\"buttonEl\"\n class=\"x-tag x-related-tag\"\n data-test=\"related-tag\"\n :class=\"dynamicClasses\"\n @click=\"clickRelatedTag\"\n >\n <!--\n @slot Custom content that replaces the RelatedTag default content.\n @binding {RelatedTag} relatedTag - Related tag data.\n @binding {boolean} isSelected - Related tag status.\n @binding {boolean} shouldHighlightCurated - True if the curated RTs should be displayed.\n -->\n <slot v-bind=\"{ relatedTag, isSelected, shouldHighlightCurated }\">{{ relatedTag.tag }}</slot>\n </button>\n</template>\n\n<script lang=\"ts\">\nimport type { RelatedTag as RelatedTagModel } from '@empathyco/x-types'\nimport type { PropType } from 'vue'\nimport type { VueCSSClasses } from '../../../utils/types'\nimport type { WireMetadata } from '../../../wiring/wiring.types'\nimport { computed, defineComponent, ref } from 'vue'\nimport { use$x } from '../../../composables/use-$x'\nimport { useState } from '../../../composables/use-state'\nimport { relatedTagsXModule } from '../x-module'\n\n/**\n * This component renders a related tag for a query. A related tag is a descriptive keyword\n * related to the current query to fine-tune the search. For example, if you are searching\n * for *lego*, a related tag could be *city*, refining the search with *lego city*.\n *\n * @public\n */\nexport default defineComponent({\n name: 'RelatedTag',\n xModule: relatedTagsXModule.name,\n props: {\n /**\n * Indicates if the curated related tag should be highlighted.\n *\n * @public\n */\n highlightCurated: {\n type: Boolean,\n default: false,\n },\n /**\n * The related tag model data.\n *\n * @public\n */\n relatedTag: {\n type: Object as PropType<RelatedTagModel>,\n required: true,\n },\n },\n setup(props) {\n const $x = use$x()\n\n const buttonEl = ref<HTMLElement | undefined>()\n\n /**\n * The selected related tags.\n *\n * @internal\n */\n const { selectedRelatedTags } = useState('relatedTags')\n\n /**\n * Check if the related tag is selected or not.\n *\n * @returns If the related tag is selected.\n *\n * @internal\n */\n const isSelected = computed(() => selectedRelatedTags.value.includes(props.relatedTag))\n\n /**\n * Blurs the related tag if it is selected.\n *\n * @public\n */\n const blurRelatedTag = () => {\n if (isSelected.value) {\n buttonEl.value?.blur()\n }\n }\n\n /**\n * Generates the {@link WireMetadata} object omitting the moduleName.\n *\n * @returns The {@link WireMetadata} object omitting the moduleName.\n * @internal\n */\n const createEventMetadata = (): Omit<WireMetadata, 'moduleName'> => ({\n target: buttonEl.value as HTMLElement,\n feature: 'related_tag',\n })\n\n /**\n * Emits events when the button is clicked.\n *\n * @public\n */\n const emitEvents = () => {\n // We have to emit this events first to avoid the UserPickedARelatedTag wires to change the\n // isSelected value before emitting this selection events.\n $x.emit(\n isSelected.value ? 'UserDeselectedARelatedTag' : 'UserSelectedARelatedTag',\n props.relatedTag,\n createEventMetadata(),\n )\n $x.emit('UserPickedARelatedTag', props.relatedTag, createEventMetadata())\n }\n\n /**\n * Handles the click on the button.\n *\n * @public\n */\n const clickRelatedTag = () => {\n emitEvents()\n blurRelatedTag()\n }\n\n /**\n * Check if the related tag is curated and should be highlighted.\n *\n * @returns True if the related tag is curated and should be highlighted.\n *\n * @internal\n */\n const shouldHighlightCurated = computed(\n () => props.highlightCurated && (props.relatedTag.isCurated ?? false),\n )\n\n /**\n * Adds the dynamic css classes to the component.\n *\n * @returns The class to be added to the component.\n *\n * @internal\n */\n const dynamicClasses = computed(\n (): VueCSSClasses => ({\n 'x-selected x-related-tag--is-selected': isSelected.value,\n 'x-related-tag--is-curated': shouldHighlightCurated.value,\n }),\n )\n\n return {\n buttonEl,\n dynamicClasses,\n isSelected,\n clickRelatedTag,\n shouldHighlightCurated,\n }\n },\n})\n</script>\n\n<docs lang=\"mdx\">\n## Dynamic classes\n\n`RelatedTag` uses the following dynamic CSS classes so you can style it when is:\n\n- Selected: `x-related-tag--is-selected`.\n- Curated: `x-related-tag--is-curated`.\n\n## Events\n\nThis component emits the following events:\n\n- [`UserDeselectedARelatedTag`](https://github.com/empathyco/x/blob/main/packages/x-components/src/wiring/events.types.ts)\n- [`UserPickedARelatedTag`](https://github.com/empathyco/x/blob/main/packages/x-components/src/wiring/events.types.ts)\n- [`UserSelectedARelatedTag`](https://github.com/empathyco/x/blob/main/packages/x-components/src/wiring/events.types.ts)\n\n## See it in action\n\n<!-- prettier-ignore-start -->\n:::warning Backend service required\nThe QuerySignals microservice must be implemented.\n:::\n<!-- prettier-ignore-end -->\n\nIn this example related tag data is passed as a prop.\n\n_Here you can see how the RelatedTag component is rendered._\n\n```vue live\n<template>\n <RelatedTag :relatedTag=\"tag\" />\n</template>\n\n<script>\nimport { RelatedTag } from '@empathyco/x-components/related-tags'\n\nexport default {\n name: 'RelatedTagDemo',\n components: {\n RelatedTag,\n },\n data() {\n return {\n tag: {\n modelName: 'RelatedTag',\n query: 'high heel',\n isCurated: false,\n tag: 'heel',\n },\n }\n },\n}\n</script>\n```\n\n### Play with default slot\n\nIn this example, an HTML span element is passed in the `default` slot.\n\n_See how the related tag can be rendered._\n\n```vue live\n<template>\n <RelatedTag :relatedTag=\"tag\" #default=\"{ relatedTag }\">\n <span :aria-label=\"relatedTag.tag\">{{ relatedTag.tag }}</span>\n </RelatedTag>\n</template>\n\n<script>\nimport { RelatedTag } from '@empathyco/x-components/related-tags'\n\nexport default {\n name: 'RelatedTagDemo',\n components: {\n RelatedTag,\n },\n data() {\n return {\n tag: {\n modelName: 'RelatedTag',\n query: 'high heel',\n isCurated: false,\n tag: 'heel',\n },\n }\n },\n}\n</script>\n```\n\n### Play with events\n\nIn this example, the [`UserSelectedARelatedTag`](./../../api/x-components.relatedtagsxevents.md)\nevent is implemented, as illustrated by the “Tag” message returned.\n\n_See how the event is triggered when the related tag is clicked._\n\n```vue live\n<template>\n <RelatedTag :relatedTag=\"tag\" @UserSelectedARelatedTag=\"alertRelatedTag\" />\n</template>\n\n<script>\nimport { RelatedTag } from '@empathyco/x-components/related-tags'\n\nexport default {\n name: 'RelatedTagDemo',\n components: {\n RelatedTag,\n },\n data() {\n return {\n tag: {\n modelName: 'RelatedTag',\n query: 'high heel',\n isCurated: false,\n tag: 'heel',\n },\n }\n },\n methods: {\n alertRelatedTag(relatedTag) {\n alert(`You have clicked the related tag: ${relatedTag.query}`)\n },\n },\n}\n</script>\n```\n</docs>\n"],"names":[],"mappings":";;;;;AA4BA;;;;;;AAME;AACF,gBAAe,eAAe,CAAC;AAC7B,IAAA,IAAI,EAAE,YAAY;IAClB,OAAO,EAAE,kBAAkB,CAAC,IAAI;AAChC,IAAA,KAAK,EAAE;AACL;;;;AAIE;AACF,QAAA,gBAAgB,EAAE;AAChB,YAAA,IAAI,EAAE,OAAO;AACb,YAAA,OAAO,EAAE,KAAK;AACf,SAAA;AACD;;;;AAIE;AACF,QAAA,UAAU,EAAE;AACV,YAAA,IAAI,EAAE,MAAmC;AACzC,YAAA,QAAQ,EAAE,IAAI;AACf,SAAA;AACF,KAAA;AACD,IAAA,KAAK,CAAC,KAAK,EAAA;AACT,QAAA,MAAM,EAAC,GAAI,KAAK,EAAC,CAAA;AAEjB,QAAA,MAAM,QAAS,GAAE,GAAG,EAA0B,CAAA;AAE9C;;;;AAIE;QACF,MAAM,EAAE,qBAAsB,GAAE,QAAQ,CAAC,aAAa,CAAA,CAAA;AAEtD;;;;;;AAME;AACF,QAAA,MAAM,UAAW,GAAE,QAAQ,CAAC,MAAM,mBAAmB,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,UAAU,CAAC,CAAA,CAAA;AAEtF;;;;AAIE;QACF,MAAM,iBAAiB,MAAM;YAC3B,IAAI,UAAU,CAAC,KAAK,EAAE;AACpB,gBAAA,QAAQ,CAAC,KAAK,EAAE,IAAI,EAAC,CAAA;AACvB,aAAA;AACF,SAAA,CAAA;AAEA;;;;;AAKE;AACF,QAAA,MAAM,mBAAoB,GAAE,OAAyC;YACnE,MAAM,EAAE,QAAQ,CAAC,KAAoB;AACrC,YAAA,OAAO,EAAE,aAAa;AACvB,SAAA,CAAA,CAAA;AAED;;;;AAIE;QACF,MAAM,UAAS,GAAI,MAAM;;;YAGvB,EAAE,CAAC,IAAI,CACL,UAAU,CAAC,KAAM,GAAE,2BAA0B,GAAI,yBAAyB,EAC1E,KAAK,CAAC,UAAU,EAChB,mBAAmB,EAAE,CACvB,CAAA;AACA,YAAA,EAAE,CAAC,IAAI,CAAC,uBAAuB,EAAE,KAAK,CAAC,UAAU,EAAE,mBAAmB,EAAE,CAAA,CAAA;AAC1E,SAAA,CAAA;AAEA;;;;AAIE;QACF,MAAM,kBAAkB,MAAM;AAC5B,YAAA,UAAU,EAAC,CAAA;AACX,YAAA,cAAc,EAAC,CAAA;AACjB,SAAA,CAAA;AAEA;;;;;;AAME;QACF,MAAM,sBAAqB,GAAI,QAAQ,CACrC,MAAM,KAAK,CAAC,gBAAiB,KAAI,KAAK,CAAC,UAAU,CAAC,SAAQ,IAAK,KAAK,CAAC,CACvE,CAAA;AAEA;;;;;;AAME;AACF,QAAA,MAAM,cAAa,GAAI,QAAQ,CAC7B,OAAsB;YACpB,uCAAuC,EAAE,UAAU,CAAC,KAAK;YACzD,2BAA2B,EAAE,sBAAsB,CAAC,KAAK;AAC1D,SAAA,CAAC,CACJ,CAAA;QAEA,OAAO;YACL,QAAQ;YACR,cAAc;YACd,UAAU;YACV,eAAe;YACf,sBAAsB;SACxB,CAAA;KACD;AACF,CAAA,CAAA;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main-scroll-item.vue.js","sources":["../../../../../src/x-modules/scroll/components/main-scroll-item.vue"],"sourcesContent":["<template>\n <component :is=\"tag\" ref=\"rootRef\" :data-scroll=\"item.id\">\n <slot />\n </component>\n</template>\n\n<script lang=\"ts\">\nimport type { Identifiable } from '@empathyco/x-types'\nimport type { Component, PropType, Ref, WatchCallback } from 'vue'\nimport type { ScrollVisibilityObserver } from './scroll.types'\nimport { defineComponent, inject, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'\nimport { useState } from '../../../composables/use-state'\nimport { useXBus } from '../../../composables/use-x-bus'\nimport { scrollXModule } from '../x-module'\nimport { ScrollObserverKey } from './scroll.const'\n\n/**\n * Wrapper for elements contained in the {@link MainScroll} that should store/restore its\n * position.\n *\n * @public\n */\nexport default defineComponent({\n name: 'MainScrollItem',\n xModule: scrollXModule.name,\n props: {\n /** The item data. Used to set the scroll identifier. */\n item: {\n type: Object as PropType<Identifiable>,\n required: true,\n },\n /** The tag to render. */\n tag: {\n type: [String, Object] as PropType<string | Component>,\n default: 'div',\n },\n },\n setup(props) {\n const xBus = useXBus()\n\n /** Rendered HTML node. */\n const rootRef = ref<HTMLElement>()\n\n /**\n * Pending identifier scroll position to restore. If it matches the {@link MainScrollItem} item\n * `id` property, this component should be scrolled into view.\n */\n const { pendingScrollTo } = useState('scroll'
|
|
1
|
+
{"version":3,"file":"main-scroll-item.vue.js","sources":["../../../../../src/x-modules/scroll/components/main-scroll-item.vue"],"sourcesContent":["<template>\n <component :is=\"tag\" ref=\"rootRef\" :data-scroll=\"item.id\">\n <slot />\n </component>\n</template>\n\n<script lang=\"ts\">\nimport type { Identifiable } from '@empathyco/x-types'\nimport type { Component, PropType, Ref, WatchCallback } from 'vue'\nimport type { ScrollVisibilityObserver } from './scroll.types'\nimport { defineComponent, inject, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'\nimport { useState } from '../../../composables/use-state'\nimport { useXBus } from '../../../composables/use-x-bus'\nimport { scrollXModule } from '../x-module'\nimport { ScrollObserverKey } from './scroll.const'\n\n/**\n * Wrapper for elements contained in the {@link MainScroll} that should store/restore its\n * position.\n *\n * @public\n */\nexport default defineComponent({\n name: 'MainScrollItem',\n xModule: scrollXModule.name,\n props: {\n /** The item data. Used to set the scroll identifier. */\n item: {\n type: Object as PropType<Identifiable>,\n required: true,\n },\n /** The tag to render. */\n tag: {\n type: [String, Object] as PropType<string | Component>,\n default: 'div',\n },\n },\n setup(props) {\n const xBus = useXBus()\n\n /** Rendered HTML node. */\n const rootRef = ref<HTMLElement>()\n\n /**\n * Pending identifier scroll position to restore. If it matches the {@link MainScrollItem} item\n * `id` property, this component should be scrolled into view.\n */\n const { pendingScrollTo } = useState('scroll')\n\n /** Observer to detect the first visible element. */\n const firstVisibleItemObserver = inject<Ref<ScrollVisibilityObserver> | null>(\n ScrollObserverKey as string,\n null,\n )\n\n /**\n * Initialises the element visibility observation, stopping the previous one if it has.\n *\n * @param newObserver - The new observer for the HTML element.\n * @param oldObserver - The old observer for the HTML element.\n */\n const observeItem: WatchCallback<ScrollVisibilityObserver> = (\n newObserver: ScrollVisibilityObserver | null,\n oldObserver: ScrollVisibilityObserver | null,\n ): void => {\n if (rootRef.value) {\n oldObserver?.unobserve(rootRef.value)\n newObserver?.observe(rootRef.value)\n if (pendingScrollTo.value === props.item.id) {\n nextTick(() => {\n rootRef.value!.scrollIntoView({\n block: 'center',\n })\n })\n xBus.emit('ScrollRestoreSucceeded')\n }\n }\n }\n\n /** Detaches the observer from the rendered element to prevent memory leaks. */\n onBeforeUnmount(() => {\n if (rootRef.value) {\n firstVisibleItemObserver?.value.unobserve(rootRef.value)\n }\n })\n\n /**\n * Initialise scroll behavior.\n * - Observes the rendered element to detect if it is the first visible item.\n * - If the rendered element matches the {@link MainScrollItem.pendingScrollTo}, scrolls the\n * element into the first position of the view.\n */\n onMounted(() => {\n nextTick(() => {\n // Mounted does not guarantee that child components are mounted too\n if (firstVisibleItemObserver) {\n watch(firstVisibleItemObserver, observeItem, { immediate: true })\n }\n })\n })\n\n return { rootRef }\n },\n})\n</script>\n\n<docs lang=\"mdx\">\n## Events\n\nThis components emits the following events:\n\n- [`ScrollRestoreSucceeded`](https://github.com/empathyco/x/blob/main/packages/x-components/src/wiring/events.types.ts)\n\n## See it in action\n\nThis component has no predefined template. It renders whatever you decide using the `tag` prop. It's\nmain purpose is to help storing and restoring the scroll position so URLs can be shared, and also to\nallow users to smoothly navigate back and forth.\n\nTo do so, it must be wrapped with the `MainScroll` component. In the following example we make use\nof all of these components. The URL is modified as the user scrolls.\n\n```vue\n<template>\n <div>\n <UrlHandler />\n <SearchInput />\n\n <MainScroll>\n <Scroll>\n <ResultsList #result=\"{ item }\">\n <MainScrollItem :item=\"item\" tag=\"article\">\n <BaseResultLink :item=\"item\">\n <img :src=\"item.images[0]\" />\n <p>{{ item.title }}</p>\n </BaseResultLink>\n </MainScrollItem>\n </ResultsList>\n </Scroll>\n </MainScroll>\n </div>\n</template>\n\n<script>\nimport { MainScroll, Scroll, MainScrollItem } from '@empathyco/x-components/scroll'\nimport { ResultsList } from '@empathyco/x-components/search'\nimport { SearchInput } from '@empathyco/x-components/search-box'\nimport { UrlHandler } from '@empathyco/x-components/url'\n\nexport default {\n name: 'ScrollItemDemo',\n components: {\n Scroll,\n ResultsList,\n MainScroll,\n MainScrollItem,\n SearchInput,\n UrlHandler,\n },\n}\n</script>\n```\n</docs>\n"],"names":["_openBlock","_createBlock","_resolveDynamicComponent","_renderSlot"],"mappings":";;;;SAC2B,WAAS,CAAA,IAAA,EAAA,MAAA,EAAA,MAAA,EAAA,MAAA,EAAA,KAAA,EAAA,QAAA,EAAA;AAAE,EAAA,OAAAA,SAAA,EAAoB,EAAAC,WAAA,CAAAC,uBAAA,CAAA,IAAA,CAAA,GAAA,CAAA,EAAA;AAAA,IAAA,GAAA,EAAA,SAAA;IAD1D,aAEY,EAAA,IAAA,CAAA,IAAA,CAAA,EAAA;AAAA,GAAA,EAAA;;AAFZ,MAAAC,UAAA,CAAA,IAAA,CAAA,MAAA,EAAA,SAAA,CAAA;AAAA,KAAA,CAAA;;;;;;;;;"}
|
|
@@ -33,7 +33,7 @@ var _sfc_main = defineComponent({
|
|
|
33
33
|
* Pending identifier scroll position to restore. If it matches the {@link MainScrollItem} item
|
|
34
34
|
* `id` property, this component should be scrolled into view.
|
|
35
35
|
*/
|
|
36
|
-
const { pendingScrollTo } = useState('scroll'
|
|
36
|
+
const { pendingScrollTo } = useState('scroll');
|
|
37
37
|
/** Observer to detect the first visible element. */
|
|
38
38
|
const firstVisibleItemObserver = inject(ScrollObserverKey, null);
|
|
39
39
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main-scroll-item.vue2.js","sources":["../../../../../src/x-modules/scroll/components/main-scroll-item.vue"],"sourcesContent":["<template>\n <component :is=\"tag\" ref=\"rootRef\" :data-scroll=\"item.id\">\n <slot />\n </component>\n</template>\n\n<script lang=\"ts\">\nimport type { Identifiable } from '@empathyco/x-types'\nimport type { Component, PropType, Ref, WatchCallback } from 'vue'\nimport type { ScrollVisibilityObserver } from './scroll.types'\nimport { defineComponent, inject, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'\nimport { useState } from '../../../composables/use-state'\nimport { useXBus } from '../../../composables/use-x-bus'\nimport { scrollXModule } from '../x-module'\nimport { ScrollObserverKey } from './scroll.const'\n\n/**\n * Wrapper for elements contained in the {@link MainScroll} that should store/restore its\n * position.\n *\n * @public\n */\nexport default defineComponent({\n name: 'MainScrollItem',\n xModule: scrollXModule.name,\n props: {\n /** The item data. Used to set the scroll identifier. */\n item: {\n type: Object as PropType<Identifiable>,\n required: true,\n },\n /** The tag to render. */\n tag: {\n type: [String, Object] as PropType<string | Component>,\n default: 'div',\n },\n },\n setup(props) {\n const xBus = useXBus()\n\n /** Rendered HTML node. */\n const rootRef = ref<HTMLElement>()\n\n /**\n * Pending identifier scroll position to restore. If it matches the {@link MainScrollItem} item\n * `id` property, this component should be scrolled into view.\n */\n const { pendingScrollTo } = useState('scroll'
|
|
1
|
+
{"version":3,"file":"main-scroll-item.vue2.js","sources":["../../../../../src/x-modules/scroll/components/main-scroll-item.vue"],"sourcesContent":["<template>\n <component :is=\"tag\" ref=\"rootRef\" :data-scroll=\"item.id\">\n <slot />\n </component>\n</template>\n\n<script lang=\"ts\">\nimport type { Identifiable } from '@empathyco/x-types'\nimport type { Component, PropType, Ref, WatchCallback } from 'vue'\nimport type { ScrollVisibilityObserver } from './scroll.types'\nimport { defineComponent, inject, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'\nimport { useState } from '../../../composables/use-state'\nimport { useXBus } from '../../../composables/use-x-bus'\nimport { scrollXModule } from '../x-module'\nimport { ScrollObserverKey } from './scroll.const'\n\n/**\n * Wrapper for elements contained in the {@link MainScroll} that should store/restore its\n * position.\n *\n * @public\n */\nexport default defineComponent({\n name: 'MainScrollItem',\n xModule: scrollXModule.name,\n props: {\n /** The item data. Used to set the scroll identifier. */\n item: {\n type: Object as PropType<Identifiable>,\n required: true,\n },\n /** The tag to render. */\n tag: {\n type: [String, Object] as PropType<string | Component>,\n default: 'div',\n },\n },\n setup(props) {\n const xBus = useXBus()\n\n /** Rendered HTML node. */\n const rootRef = ref<HTMLElement>()\n\n /**\n * Pending identifier scroll position to restore. If it matches the {@link MainScrollItem} item\n * `id` property, this component should be scrolled into view.\n */\n const { pendingScrollTo } = useState('scroll')\n\n /** Observer to detect the first visible element. */\n const firstVisibleItemObserver = inject<Ref<ScrollVisibilityObserver> | null>(\n ScrollObserverKey as string,\n null,\n )\n\n /**\n * Initialises the element visibility observation, stopping the previous one if it has.\n *\n * @param newObserver - The new observer for the HTML element.\n * @param oldObserver - The old observer for the HTML element.\n */\n const observeItem: WatchCallback<ScrollVisibilityObserver> = (\n newObserver: ScrollVisibilityObserver | null,\n oldObserver: ScrollVisibilityObserver | null,\n ): void => {\n if (rootRef.value) {\n oldObserver?.unobserve(rootRef.value)\n newObserver?.observe(rootRef.value)\n if (pendingScrollTo.value === props.item.id) {\n nextTick(() => {\n rootRef.value!.scrollIntoView({\n block: 'center',\n })\n })\n xBus.emit('ScrollRestoreSucceeded')\n }\n }\n }\n\n /** Detaches the observer from the rendered element to prevent memory leaks. */\n onBeforeUnmount(() => {\n if (rootRef.value) {\n firstVisibleItemObserver?.value.unobserve(rootRef.value)\n }\n })\n\n /**\n * Initialise scroll behavior.\n * - Observes the rendered element to detect if it is the first visible item.\n * - If the rendered element matches the {@link MainScrollItem.pendingScrollTo}, scrolls the\n * element into the first position of the view.\n */\n onMounted(() => {\n nextTick(() => {\n // Mounted does not guarantee that child components are mounted too\n if (firstVisibleItemObserver) {\n watch(firstVisibleItemObserver, observeItem, { immediate: true })\n }\n })\n })\n\n return { rootRef }\n },\n})\n</script>\n\n<docs lang=\"mdx\">\n## Events\n\nThis components emits the following events:\n\n- [`ScrollRestoreSucceeded`](https://github.com/empathyco/x/blob/main/packages/x-components/src/wiring/events.types.ts)\n\n## See it in action\n\nThis component has no predefined template. It renders whatever you decide using the `tag` prop. It's\nmain purpose is to help storing and restoring the scroll position so URLs can be shared, and also to\nallow users to smoothly navigate back and forth.\n\nTo do so, it must be wrapped with the `MainScroll` component. In the following example we make use\nof all of these components. The URL is modified as the user scrolls.\n\n```vue\n<template>\n <div>\n <UrlHandler />\n <SearchInput />\n\n <MainScroll>\n <Scroll>\n <ResultsList #result=\"{ item }\">\n <MainScrollItem :item=\"item\" tag=\"article\">\n <BaseResultLink :item=\"item\">\n <img :src=\"item.images[0]\" />\n <p>{{ item.title }}</p>\n </BaseResultLink>\n </MainScrollItem>\n </ResultsList>\n </Scroll>\n </MainScroll>\n </div>\n</template>\n\n<script>\nimport { MainScroll, Scroll, MainScrollItem } from '@empathyco/x-components/scroll'\nimport { ResultsList } from '@empathyco/x-components/search'\nimport { SearchInput } from '@empathyco/x-components/search-box'\nimport { UrlHandler } from '@empathyco/x-components/url'\n\nexport default {\n name: 'ScrollItemDemo',\n components: {\n Scroll,\n ResultsList,\n MainScroll,\n MainScrollItem,\n SearchInput,\n UrlHandler,\n },\n}\n</script>\n```\n</docs>\n"],"names":[],"mappings":";;;;;;AAgBA;;;;;AAKE;AACF,gBAAe,eAAe,CAAC;AAC7B,IAAA,IAAI,EAAE,gBAAgB;IACtB,OAAO,EAAE,aAAa,CAAC,IAAI;AAC3B,IAAA,KAAK,EAAE;;AAEL,QAAA,IAAI,EAAE;AACJ,YAAA,IAAI,EAAE,MAAgC;AACtC,YAAA,QAAQ,EAAE,IAAI;AACf,SAAA;;AAED,QAAA,GAAG,EAAE;AACH,YAAA,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,CAAiC;AACtD,YAAA,OAAO,EAAE,KAAK;AACf,SAAA;AACF,KAAA;AACD,IAAA,KAAK,CAAC,KAAK,EAAA;AACT,QAAA,MAAM,IAAG,GAAI,OAAO,EAAC,CAAA;;AAGrB,QAAA,MAAM,OAAQ,GAAE,GAAG,EAAc,CAAA;AAEjC;;;AAGE;QACF,MAAM,EAAE,eAAgB,EAAA,GAAI,QAAQ,CAAC,QAAQ,CAAA,CAAA;;QAG7C,MAAM,2BAA2B,MAAM,CACrC,iBAA2B,EAC3B,IAAI,CACN,CAAA;AAEA;;;;;AAKE;AACF,QAAA,MAAM,WAAW,GAA4C,CAC3D,WAA4C,EAC5C,WAA4C,KACnC;YACT,IAAI,OAAO,CAAC,KAAK,EAAE;AACjB,gBAAA,WAAW,EAAE,SAAS,CAAC,OAAO,CAAC,KAAK,CAAA,CAAA;AACpC,gBAAA,WAAW,EAAE,OAAO,CAAC,OAAO,CAAC,KAAK,CAAA,CAAA;gBAClC,IAAI,eAAe,CAAC,KAAM,KAAI,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE;oBAC3C,QAAQ,CAAC,MAAM;AACb,wBAAA,OAAO,CAAC,KAAM,CAAC,cAAc,CAAC;AAC5B,4BAAA,KAAK,EAAE,QAAQ;AAChB,yBAAA,CAAA,CAAA;AACH,qBAAC,CAAA,CAAA;AACD,oBAAA,IAAI,CAAC,IAAI,CAAC,wBAAwB,CAAA,CAAA;AACpC,iBAAA;AACF,aAAA;AACF,SAAA,CAAA;;QAGA,eAAe,CAAC,MAAM;YACpB,IAAI,OAAO,CAAC,KAAK,EAAE;gBACjB,wBAAwB,EAAE,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,CAAA,CAAA;AACzD,aAAA;AACF,SAAC,CAAA,CAAA;AAED;;;;;AAKE;QACF,SAAS,CAAC,MAAM;YACd,QAAQ,CAAC,MAAM;;AAEb,gBAAA,IAAI,wBAAwB,EAAE;oBAC5B,KAAK,CAAC,wBAAwB,EAAE,WAAW,EAAE,EAAE,SAAS,EAAE,IAAG,EAAG,CAAA,CAAA;AAClE,iBAAA;AACF,aAAC,CAAA,CAAA;AACH,SAAC,CAAA,CAAA;QAED,OAAO,EAAE,OAAQ,EAAA,CAAA;KAClB;AACF,CAAA,CAAA;;;;"}
|
|
@@ -58,7 +58,7 @@ var _sfc_main = defineComponent({
|
|
|
58
58
|
* Pending identifier scroll position to restore. If it matches the {@link MainScrollItem} item
|
|
59
59
|
* `id` property, this component should be scrolled into view.
|
|
60
60
|
*/
|
|
61
|
-
const { pendingScrollTo } = useState('scroll'
|
|
61
|
+
const { pendingScrollTo } = useState('scroll');
|
|
62
62
|
/** Disables the animations. */
|
|
63
63
|
const disableAnimations = computed(() => !!pendingScrollTo.value);
|
|
64
64
|
provide(DISABLE_ANIMATIONS_KEY, disableAnimations);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main-scroll.vue.js","sources":["../../../../../src/x-modules/scroll/components/main-scroll.vue"],"sourcesContent":["<script lang=\"ts\">\nimport type { ScrollVisibilityObserver } from './scroll.types'\nimport {\n computed,\n defineComponent,\n getCurrentInstance,\n onBeforeUnmount,\n onMounted,\n provide,\n ref,\n watch,\n} from 'vue'\nimport { DISABLE_ANIMATIONS_KEY } from '../../../components/decorators/injection.consts'\nimport { useState } from '../../../composables/use-state'\nimport { useXBus } from '../../../composables/use-x-bus'\nimport { scrollXModule } from '../x-module'\nimport { ScrollObserverKey } from './scroll.const'\n\n/**\n * Extends the scroll making it able to sync the first visible element, and allowing\n * the children position to be restored.\n *\n * Each child element that wants to have this support must be wrapped in a {@link MainScrollItem}\n * component.\n *\n * @public\n */\nexport default defineComponent({\n name: 'MainScroll',\n xModule: scrollXModule.name,\n props: {\n /**\n * If `true`, sets this scroll instance to the main of the application. Being the main\n * scroll implies that features like restoring the scroll when the query changes, or storing\n * the scroll position in the URL will be enabled for this container.\n */\n useWindow: {\n type: Boolean,\n default: false,\n },\n /**\n * Timeout in milliseconds to abort trying to restore the scroll position to the target\n * element.\n */\n restoreScrollTimeoutMs: {\n type: Number,\n default: 5000,\n },\n /** Intersection percentage to consider an element visible. */\n threshold: {\n type: Number,\n default: 0.3,\n },\n /** Adjusts the size of the scroll container bounds. */\n margin: {\n type: String,\n default: '0px',\n },\n },\n setup(props, { slots }) {\n const xBus = useXBus()\n\n /** The reference to the root element of the component. */\n const rootRef = ref<Element>()\n /** The elements that are currently considered visible. */\n const intersectingElements = ref<HTMLElement[]>([])\n\n /** Intersection observer to determine visibility of the elements. */\n const intersectionObserver = ref<IntersectionObserver | null>(null)\n\n /**\n * Pending identifier scroll position to restore. If it matches the {@link MainScrollItem} item\n * `id` property, this component should be scrolled into view.\n */\n const { pendingScrollTo } = useState('scroll', ['pendingScrollTo'])\n\n /** Disables the animations. */\n const disableAnimations = computed((): boolean => !!pendingScrollTo.value)\n provide(DISABLE_ANIMATIONS_KEY as string, disableAnimations)\n\n /**\n * Removes an element from the {@link MainScroll.intersectingElements} list.\n *\n * @param element - The element to remove from the visible elements.\n */\n const removeVisibleElement = (element: HTMLElement) => {\n const index = intersectingElements.value.indexOf(element)\n if (index !== -1) {\n intersectingElements.value.splice(index, 1)\n }\n }\n\n /**\n * Creates an `IntersectionObserver` to detect the first visible elements. Children of this\n * component should register themselves if they want to be observed.\n *\n * @returns The intersection observer.\n */\n const visibleElementsObserver = computed((): ScrollVisibilityObserver | null => {\n const observer = intersectionObserver.value\n return observer\n ? {\n observe: observer.observe.bind(observer),\n unobserve: element => {\n removeVisibleElement(element)\n observer.unobserve(element)\n },\n }\n : null\n })\n provide(ScrollObserverKey as string, visibleElementsObserver)\n\n /**\n * Updates the visible elements given a list of intersection observer entries.\n *\n * @param entries - The entries from whom update the visibility.\n */\n const updateVisibleElements = (entries: IntersectionObserverEntry[]) => {\n entries.forEach(entry => {\n const target = entry.target as HTMLElement\n if (entry.isIntersecting) {\n intersectingElements.value.push(target)\n } else {\n removeVisibleElement(target)\n }\n })\n }\n\n /** Stores the root element and initialise the observer after mounting the component. */\n onMounted(() => {\n rootRef.value = getCurrentInstance()?.proxy?.$el\n if (rootRef.value) {\n intersectionObserver.value = new IntersectionObserver(updateVisibleElements, {\n root: props.useWindow ? document : rootRef.value,\n threshold: props.threshold,\n rootMargin: props.margin,\n })\n }\n })\n\n /** Disconnects the intersection observer. */\n onBeforeUnmount(() => {\n intersectionObserver.value?.disconnect()\n xBus.emit('UserScrolledToElement', '')\n })\n\n /** Disconnects the previous observer. */\n watch(intersectionObserver, (_new, old) => old?.disconnect())\n\n /** Stores the identifier of the timeout that will consider the scroll failed to restore. */\n let restoreScrollFailTimeoutId: number\n\n /**\n * If there is a pending scroll, starts a countdown to stop trying to restore the scroll.\n *\n * @param pendingScrollTo - The position the scroll should be restored to.\n */\n watch(pendingScrollTo, () => {\n // TODO Move this logic to the wiring. A cancelable delay operator is needed\n clearTimeout(restoreScrollFailTimeoutId)\n if (pendingScrollTo.value) {\n restoreScrollFailTimeoutId = window.setTimeout(() => {\n xBus.emit('ScrollRestoreFailed')\n }, props.restoreScrollTimeoutMs)\n }\n })\n\n /**\n * The first visible element contained in this component.\n *\n * @returns The first visible element in this component.\n */\n const firstVisibleElement = computed(() => {\n if (intersectingElements.value.length === 0) {\n return ''\n }\n const firstVisibleElement = intersectingElements.value.reduce(\n (firstVisibleElement, anotherElement) => {\n // FIXME: This algorithm only takes into account LTR layouts\n const firstVisibleElementBounds = firstVisibleElement.getBoundingClientRect()\n const anotherElementBounds = anotherElement.getBoundingClientRect()\n return anotherElementBounds.left <= firstVisibleElementBounds.left &&\n anotherElementBounds.top <= firstVisibleElementBounds.top\n ? anotherElement\n : firstVisibleElement\n },\n )\n return firstVisibleElement === rootRef.value?.querySelector('[data-scroll]')\n ? ''\n : firstVisibleElement.dataset.scroll!\n })\n\n watch(\n firstVisibleElement,\n () => xBus.emit('UserScrolledToElement', firstVisibleElement.value),\n { immediate: true },\n )\n\n /*\n * Obtains the vNodes array of the default slot and renders only the first one.\n * It avoids to render a `Fragment` with the vNodes in Vue3 and the same behaviour in Vue2\n * because Vue2 only allows a single root node. Then, `getCurrentInstance()?.proxy?.$el` to\n * retrieve the HTML element in both versions.\n */\n return () => slots.default?.()[0] ?? ''\n },\n})\n</script>\n\n<docs lang=\"mdx\">\n## Events\n\nThis component emits the following events:\n\n- [`UserScrolledToElement`](https://github.com/empathyco/x/blob/main/packages/x-components/src/wiring/events.types.ts)\n- [`ScrollRestoreFailed`](https://github.com/empathyco/x/blob/main/packages/x-components/src/wiring/events.types.ts)\n\n## See it in action\n\nThe `MainScroll` component must be an ancestor of the `MainScrollItem` components. This is because\nit injects the needed utilities to determine the first visible item.\n\n```vue\n<template>\n <MainScroll>\n <ul>\n <MainScrollItem v-for=\"item in 24\" tag=\"li\">Item {{ item }}</MainScrollItem>\n </ul>\n </MainScroll>\n</template>\n\n<script>\nimport { MainScroll, MainScrollItem } from '@empathyco/x-components/scroll'\n\nexport default {\n name: 'MainScrollDemo',\n components: {\n MainScroll,\n MainScrollItem,\n },\n}\n</script>\n\n<style lang=\"css\" scoped>\nul {\n overflow: auto;\n max-height: 200px;\n}\n\nli {\n height: 50px;\n line-height: 50px;\n}\n</style>\n```\n\n### Play with props\n\n#### Window scroll\n\nIn case you aren't using a custom scrolling element like the `Scroll` panel, and want to use the\ndefault browser scroll, you can do so by using the `useWindow` prop:\n\n```vue\n<template>\n <MainScroll useWindow>\n <ul>\n <MainScrollItem v-for=\"item in 24\" tag=\"li\">Item {{ item }}</MainScrollItem>\n </ul>\n </MainScroll>\n</template>\n\n<script>\nimport { MainScroll, MainScrollItem } from '@empathyco/x-components/scroll'\n\nexport default {\n name: 'MainScrollDemo',\n components: {\n MainScroll,\n MainScrollItem,\n },\n}\n</script>\n```\n\n#### Timeout for restoring scroll\n\nWhen your application is loaded, this component, together with the `MainScrollItem` will try to\nrestore the scroll to the provided position (if available). Because of the dynamic nature of\nJavaScript applications, the element that it tries to restore the scroll to might not be available\nanymore. For this reason after a defined time, the scroll restoration will be considered failed.\n\nThis time can be configured through the `restore-scroll-timeout-ms` prop. This is specially useful\nwhen combined with the URL X Module.\n\n```vue\n<template>\n <MainScroll :restoreScrollTimeoutMs=\"1000\">\n <ul>\n <MainScrollItem v-for=\"item in 24\" tag=\"li\">Item {{ item }}</MainScrollItem>\n </ul>\n </MainScroll>\n</template>\n\n<script>\nimport { MainScroll, MainScrollItem } from '@empathyco/x-components/scroll'\n\nexport default {\n name: 'MainScrollDemo',\n components: {\n MainScroll,\n MainScrollItem,\n },\n}\n</script>\n```\n\n#### Adjust first visible item\n\nBy default this component will consider the first visible item, the first element that is at least\nintersecting a 50% with its container. However this arbitrary number might not always be the best.\n\nTo configure this, you can use the `margin` and `threshold` props, which work exactly like in the\n`IntersectionObserver` API. In this example we are reducing the bounds of the intersection by 50px,\nand adjusting the element to be at least 75% intersecting.\n\n```vue\n<template>\n <MainScroll :threshold=\"0.75\" margin=\"-50px\">\n <ul>\n <MainScrollItem v-for=\"item in 24\" tag=\"li\">Item {{ item }}</MainScrollItem>\n </ul>\n </MainScroll>\n</template>\n\n<script>\nimport { MainScroll, MainScrollItem } from '@empathyco/x-components/scroll'\n\nexport default {\n name: 'MainScrollDemo',\n components: {\n MainScroll,\n MainScrollItem,\n },\n}\n</script>\n```\n</docs>\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"main-scroll.vue.js","sources":["../../../../../src/x-modules/scroll/components/main-scroll.vue"],"sourcesContent":["<script lang=\"ts\">\nimport type { ScrollVisibilityObserver } from './scroll.types'\nimport {\n computed,\n defineComponent,\n getCurrentInstance,\n onBeforeUnmount,\n onMounted,\n provide,\n ref,\n watch,\n} from 'vue'\nimport { DISABLE_ANIMATIONS_KEY } from '../../../components/decorators/injection.consts'\nimport { useState } from '../../../composables/use-state'\nimport { useXBus } from '../../../composables/use-x-bus'\nimport { scrollXModule } from '../x-module'\nimport { ScrollObserverKey } from './scroll.const'\n\n/**\n * Extends the scroll making it able to sync the first visible element, and allowing\n * the children position to be restored.\n *\n * Each child element that wants to have this support must be wrapped in a {@link MainScrollItem}\n * component.\n *\n * @public\n */\nexport default defineComponent({\n name: 'MainScroll',\n xModule: scrollXModule.name,\n props: {\n /**\n * If `true`, sets this scroll instance to the main of the application. Being the main\n * scroll implies that features like restoring the scroll when the query changes, or storing\n * the scroll position in the URL will be enabled for this container.\n */\n useWindow: {\n type: Boolean,\n default: false,\n },\n /**\n * Timeout in milliseconds to abort trying to restore the scroll position to the target\n * element.\n */\n restoreScrollTimeoutMs: {\n type: Number,\n default: 5000,\n },\n /** Intersection percentage to consider an element visible. */\n threshold: {\n type: Number,\n default: 0.3,\n },\n /** Adjusts the size of the scroll container bounds. */\n margin: {\n type: String,\n default: '0px',\n },\n },\n setup(props, { slots }) {\n const xBus = useXBus()\n\n /** The reference to the root element of the component. */\n const rootRef = ref<Element>()\n /** The elements that are currently considered visible. */\n const intersectingElements = ref<HTMLElement[]>([])\n\n /** Intersection observer to determine visibility of the elements. */\n const intersectionObserver = ref<IntersectionObserver | null>(null)\n\n /**\n * Pending identifier scroll position to restore. If it matches the {@link MainScrollItem} item\n * `id` property, this component should be scrolled into view.\n */\n const { pendingScrollTo } = useState('scroll')\n\n /** Disables the animations. */\n const disableAnimations = computed((): boolean => !!pendingScrollTo.value)\n provide(DISABLE_ANIMATIONS_KEY as string, disableAnimations)\n\n /**\n * Removes an element from the {@link MainScroll.intersectingElements} list.\n *\n * @param element - The element to remove from the visible elements.\n */\n const removeVisibleElement = (element: HTMLElement) => {\n const index = intersectingElements.value.indexOf(element)\n if (index !== -1) {\n intersectingElements.value.splice(index, 1)\n }\n }\n\n /**\n * Creates an `IntersectionObserver` to detect the first visible elements. Children of this\n * component should register themselves if they want to be observed.\n *\n * @returns The intersection observer.\n */\n const visibleElementsObserver = computed((): ScrollVisibilityObserver | null => {\n const observer = intersectionObserver.value\n return observer\n ? {\n observe: observer.observe.bind(observer),\n unobserve: element => {\n removeVisibleElement(element)\n observer.unobserve(element)\n },\n }\n : null\n })\n provide(ScrollObserverKey as string, visibleElementsObserver)\n\n /**\n * Updates the visible elements given a list of intersection observer entries.\n *\n * @param entries - The entries from whom update the visibility.\n */\n const updateVisibleElements = (entries: IntersectionObserverEntry[]) => {\n entries.forEach(entry => {\n const target = entry.target as HTMLElement\n if (entry.isIntersecting) {\n intersectingElements.value.push(target)\n } else {\n removeVisibleElement(target)\n }\n })\n }\n\n /** Stores the root element and initialise the observer after mounting the component. */\n onMounted(() => {\n rootRef.value = getCurrentInstance()?.proxy?.$el\n if (rootRef.value) {\n intersectionObserver.value = new IntersectionObserver(updateVisibleElements, {\n root: props.useWindow ? document : rootRef.value,\n threshold: props.threshold,\n rootMargin: props.margin,\n })\n }\n })\n\n /** Disconnects the intersection observer. */\n onBeforeUnmount(() => {\n intersectionObserver.value?.disconnect()\n xBus.emit('UserScrolledToElement', '')\n })\n\n /** Disconnects the previous observer. */\n watch(intersectionObserver, (_new, old) => old?.disconnect())\n\n /** Stores the identifier of the timeout that will consider the scroll failed to restore. */\n let restoreScrollFailTimeoutId: number\n\n /**\n * If there is a pending scroll, starts a countdown to stop trying to restore the scroll.\n *\n * @param pendingScrollTo - The position the scroll should be restored to.\n */\n watch(pendingScrollTo, () => {\n // TODO Move this logic to the wiring. A cancelable delay operator is needed\n clearTimeout(restoreScrollFailTimeoutId)\n if (pendingScrollTo.value) {\n restoreScrollFailTimeoutId = window.setTimeout(() => {\n xBus.emit('ScrollRestoreFailed')\n }, props.restoreScrollTimeoutMs)\n }\n })\n\n /**\n * The first visible element contained in this component.\n *\n * @returns The first visible element in this component.\n */\n const firstVisibleElement = computed(() => {\n if (intersectingElements.value.length === 0) {\n return ''\n }\n const firstVisibleElement = intersectingElements.value.reduce(\n (firstVisibleElement, anotherElement) => {\n // FIXME: This algorithm only takes into account LTR layouts\n const firstVisibleElementBounds = firstVisibleElement.getBoundingClientRect()\n const anotherElementBounds = anotherElement.getBoundingClientRect()\n return anotherElementBounds.left <= firstVisibleElementBounds.left &&\n anotherElementBounds.top <= firstVisibleElementBounds.top\n ? anotherElement\n : firstVisibleElement\n },\n )\n return firstVisibleElement === rootRef.value?.querySelector('[data-scroll]')\n ? ''\n : firstVisibleElement.dataset.scroll!\n })\n\n watch(\n firstVisibleElement,\n () => xBus.emit('UserScrolledToElement', firstVisibleElement.value),\n { immediate: true },\n )\n\n /*\n * Obtains the vNodes array of the default slot and renders only the first one.\n * It avoids to render a `Fragment` with the vNodes in Vue3 and the same behaviour in Vue2\n * because Vue2 only allows a single root node. Then, `getCurrentInstance()?.proxy?.$el` to\n * retrieve the HTML element in both versions.\n */\n return () => slots.default?.()[0] ?? ''\n },\n})\n</script>\n\n<docs lang=\"mdx\">\n## Events\n\nThis component emits the following events:\n\n- [`UserScrolledToElement`](https://github.com/empathyco/x/blob/main/packages/x-components/src/wiring/events.types.ts)\n- [`ScrollRestoreFailed`](https://github.com/empathyco/x/blob/main/packages/x-components/src/wiring/events.types.ts)\n\n## See it in action\n\nThe `MainScroll` component must be an ancestor of the `MainScrollItem` components. This is because\nit injects the needed utilities to determine the first visible item.\n\n```vue\n<template>\n <MainScroll>\n <ul>\n <MainScrollItem v-for=\"item in 24\" tag=\"li\">Item {{ item }}</MainScrollItem>\n </ul>\n </MainScroll>\n</template>\n\n<script>\nimport { MainScroll, MainScrollItem } from '@empathyco/x-components/scroll'\n\nexport default {\n name: 'MainScrollDemo',\n components: {\n MainScroll,\n MainScrollItem,\n },\n}\n</script>\n\n<style lang=\"css\" scoped>\nul {\n overflow: auto;\n max-height: 200px;\n}\n\nli {\n height: 50px;\n line-height: 50px;\n}\n</style>\n```\n\n### Play with props\n\n#### Window scroll\n\nIn case you aren't using a custom scrolling element like the `Scroll` panel, and want to use the\ndefault browser scroll, you can do so by using the `useWindow` prop:\n\n```vue\n<template>\n <MainScroll useWindow>\n <ul>\n <MainScrollItem v-for=\"item in 24\" tag=\"li\">Item {{ item }}</MainScrollItem>\n </ul>\n </MainScroll>\n</template>\n\n<script>\nimport { MainScroll, MainScrollItem } from '@empathyco/x-components/scroll'\n\nexport default {\n name: 'MainScrollDemo',\n components: {\n MainScroll,\n MainScrollItem,\n },\n}\n</script>\n```\n\n#### Timeout for restoring scroll\n\nWhen your application is loaded, this component, together with the `MainScrollItem` will try to\nrestore the scroll to the provided position (if available). Because of the dynamic nature of\nJavaScript applications, the element that it tries to restore the scroll to might not be available\nanymore. For this reason after a defined time, the scroll restoration will be considered failed.\n\nThis time can be configured through the `restore-scroll-timeout-ms` prop. This is specially useful\nwhen combined with the URL X Module.\n\n```vue\n<template>\n <MainScroll :restoreScrollTimeoutMs=\"1000\">\n <ul>\n <MainScrollItem v-for=\"item in 24\" tag=\"li\">Item {{ item }}</MainScrollItem>\n </ul>\n </MainScroll>\n</template>\n\n<script>\nimport { MainScroll, MainScrollItem } from '@empathyco/x-components/scroll'\n\nexport default {\n name: 'MainScrollDemo',\n components: {\n MainScroll,\n MainScrollItem,\n },\n}\n</script>\n```\n\n#### Adjust first visible item\n\nBy default this component will consider the first visible item, the first element that is at least\nintersecting a 50% with its container. However this arbitrary number might not always be the best.\n\nTo configure this, you can use the `margin` and `threshold` props, which work exactly like in the\n`IntersectionObserver` API. In this example we are reducing the bounds of the intersection by 50px,\nand adjusting the element to be at least 75% intersecting.\n\n```vue\n<template>\n <MainScroll :threshold=\"0.75\" margin=\"-50px\">\n <ul>\n <MainScrollItem v-for=\"item in 24\" tag=\"li\">Item {{ item }}</MainScrollItem>\n </ul>\n </MainScroll>\n</template>\n\n<script>\nimport { MainScroll, MainScrollItem } from '@empathyco/x-components/scroll'\n\nexport default {\n name: 'MainScrollDemo',\n components: {\n MainScroll,\n MainScrollItem,\n },\n}\n</script>\n```\n</docs>\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scroll-to-top.vue.js","sources":["../../../../../src/x-modules/scroll/components/scroll-to-top.vue"],"sourcesContent":["<template>\n <component :is=\"animation\">\n <BaseEventButton\n v-if=\"isVisible\"\n class=\"x-scroll-to-top x-button\"\n data-test=\"scroll-to-top\"\n aria-label=\"Scroll to top\"\n :events=\"events\"\n >\n <!-- @slot (Required) Button content with a text, an icon or both -->\n <slot />\n </BaseEventButton>\n </component>\n</template>\n\n<script lang=\"ts\">\nimport type { XEventsTypes } from '../../../wiring'\nimport { computed, defineComponent } from 'vue'\nimport { BaseEventButton, NoAnimation } from '../../../components'\nimport { useState } from '../../../composables'\nimport { AnimationProp } from '../../../types'\nimport { scrollXModule } from '../x-module'\nimport { MainScrollId } from './scroll.const'\n\n/**\n * The `ScrollToTop` component is a button that the user can click to make a container scroll\n * up to its initial position.\n *\n * @public\n */\nexport default defineComponent({\n name: 'ScrollToTop',\n xModule: scrollXModule.name,\n components: { BaseEventButton },\n props: {\n /**\n * Animation to use for showing/hiding the button.\n *\n * @public\n */\n animation: {\n type: AnimationProp,\n default: () => NoAnimation,\n },\n /**\n * Threshold in pixels from the top to show the button.\n *\n * @public\n */\n thresholdPx: Number,\n /**\n * Id of the target scroll component.\n *\n * @public\n */\n scrollId: {\n type: String,\n default: MainScrollId,\n },\n },\n setup(props) {\n /**\n * State of all the scroll components in this module.\n *\n * @internal\n */\n // TODO: Directly retrieve the needed data in this computed property\n const { data } = useState('scroll'
|
|
1
|
+
{"version":3,"file":"scroll-to-top.vue.js","sources":["../../../../../src/x-modules/scroll/components/scroll-to-top.vue"],"sourcesContent":["<template>\n <component :is=\"animation\">\n <BaseEventButton\n v-if=\"isVisible\"\n class=\"x-scroll-to-top x-button\"\n data-test=\"scroll-to-top\"\n aria-label=\"Scroll to top\"\n :events=\"events\"\n >\n <!-- @slot (Required) Button content with a text, an icon or both -->\n <slot />\n </BaseEventButton>\n </component>\n</template>\n\n<script lang=\"ts\">\nimport type { XEventsTypes } from '../../../wiring'\nimport { computed, defineComponent } from 'vue'\nimport { BaseEventButton, NoAnimation } from '../../../components'\nimport { useState } from '../../../composables'\nimport { AnimationProp } from '../../../types'\nimport { scrollXModule } from '../x-module'\nimport { MainScrollId } from './scroll.const'\n\n/**\n * The `ScrollToTop` component is a button that the user can click to make a container scroll\n * up to its initial position.\n *\n * @public\n */\nexport default defineComponent({\n name: 'ScrollToTop',\n xModule: scrollXModule.name,\n components: { BaseEventButton },\n props: {\n /**\n * Animation to use for showing/hiding the button.\n *\n * @public\n */\n animation: {\n type: AnimationProp,\n default: () => NoAnimation,\n },\n /**\n * Threshold in pixels from the top to show the button.\n *\n * @public\n */\n thresholdPx: Number,\n /**\n * Id of the target scroll component.\n *\n * @public\n */\n scrollId: {\n type: String,\n default: MainScrollId,\n },\n },\n setup(props) {\n /**\n * State of all the scroll components in this module.\n *\n * @internal\n */\n // TODO: Directly retrieve the needed data in this computed property\n const { data } = useState('scroll')\n\n /**\n * The scroll data retrieved for this component.\n *\n * @returns The scroll data for this component if a valid {@link ScrollToTop.scrollId} has been\n * passed. Otherwise it returns `null`.\n * @internal\n */\n const scrollData = computed(() => {\n return props.scrollId && data.value[props.scrollId]\n ? data.value[props.scrollId]\n : {\n position: 0,\n direction: 'UP',\n hasReachedStart: false,\n hasAlmostReachedEnd: false,\n hasReachedEnd: false,\n }\n })\n\n /**\n * Event that will be emitted when the scroll to top is clicked.\n *\n * @returns The event to be emitted when the scroll to top is clicked. The id as a payload.\n * @internal\n */\n const events = computed(\n (): Partial<XEventsTypes> => ({ UserClickedScrollToTop: props.scrollId }),\n )\n\n /**\n * Checks if the thresholdPx prop has been provided and if it is a number.\n *\n * @returns If the thresholdPx is a number or not.\n * @internal\n */\n const useThresholdStrategy = computed(() => typeof props.thresholdPx === 'number')\n\n /**\n * Checks if the threshold has been reached in case the threshold strategy is in use.\n *\n * @returns If the scrollTop is bigger than the thresholdPx.\n * @internal\n */\n const isThresholdReached = computed(\n () => useThresholdStrategy.value && scrollData.value.position > props.thresholdPx!,\n )\n\n /**\n * Returns if the scroll has almost reached its end or not.\n *\n * @returns True if the scroll has almost reached the end and the user is still scrolling down.\n * @internal\n */\n const hasAlmostReachedScrollEnd = computed(\n () => scrollData.value.hasAlmostReachedEnd && scrollData.value.direction === 'DOWN',\n )\n\n /**\n * Whether if the button is visible or not depending on the strategy being used.\n *\n * @returns If the button should be visible or not.\n * @internal\n */\n const isVisible = computed(() =>\n useThresholdStrategy.value ? isThresholdReached.value : hasAlmostReachedScrollEnd.value,\n )\n\n return {\n events,\n isVisible,\n }\n },\n})\n</script>\n\n<docs lang=\"mdx\">\n## Events\n\nA list of events that the component will emit:\n\n- [`UserClickedScrollToTop`](https://github.com/empathyco/x/blob/main/packages/x-components/src/wiring/events.types.ts):\n the event is emitted after the user clicks the button. The event payload is the id of the scroll\n that it going to be scrolled.\n\n## Examples\n\n### Basic example\n\nThe component renders whatever is passed to it in the default slot and scrolls to top the scroll\nwith an id `scrollId`.\n\nIt also receives an optional threshold in pixels. When the threshold is reached from the top, the\ncomponent will be shown once the user scrolls `UP`.\n\nIf this parameter is not provided the button will be visible when the user almost reaches the end of\nthe scroll.\n\n```vue\n<template>\n <div>\n <ScrollToTop scroll-id=\"scrollId\" :threshold-px=\"1000\">\n <span>Scroll to top</span>\n </ScrollToTop>\n </div>\n</template>\n\n<script>\nimport { ScrollToTop } from '@empathyco/x-components/scroll'\n\nexport default {\n name: 'ScrollToTopTest',\n components: {\n ScrollToTop,\n },\n}\n</script>\n```\n</docs>\n"],"names":["_resolveComponent","animation","isVisible","_createBlock","_resolveDynamicComponent","_withCtx","events","_renderSlot","_createCommentVNode"],"mappings":";;;;;AAYc,EAAA,MAAA,0BAAA,GAAAA,gBAAA,CAZd,iBACkBC,CAAAA,CAAAA;SAENC,SAAS,EAAA,EAAAC,WAAA,CAAAC,uBAAA,CAAA,IAAA,CAAA,SAAA,CAAA,EAAA,IAAA,EAAA;AAAA,IADjB,OAAA,EAAAC,OAAA,CAAA,MAAA;AAAA,MAAA,IAAA,CAFJ,wBAIsC,EAAAF,WAAA,CAAA,0BAAA,EAAA;AAAA,QAChC,GAAA,EAAA,CAAA;AAAA,QACA,KAAA,EAAA,0BAAA;AAAA,QACC,WAAQG,EAAAA,eAAAA;AAAAA,QAAAA,YAAAA,EAAAA,eAAAA;AAPf,QAAA,MAAA,EAAA,IAAA,CAAA,MAAA;AAAA,OAAA,EAAA;;AAAA,UAAAC,UAAA,CAAA,IAAA,CAAA,MAAA,EAAA,SAAA,CAAA;AAAA,SAAA,CAAA;AAAA,QAAA,CAAA,EAAA,CAAA;AAAA;AAAA,OAAA,EAAA,CAAA,EAAA,CAAA,QAAA,CAAA,CAAA,IAAAC,kBAAA,CAAA,MAAA,EAAA,IAAA,CAAA;AAAA,KAAA,CAAA;;;;;;;;;"}
|
|
@@ -152,7 +152,7 @@ var _sfc_main = defineComponent({
|
|
|
152
152
|
* @internal
|
|
153
153
|
*/
|
|
154
154
|
// TODO: Directly retrieve the needed data in this computed property
|
|
155
|
-
const { data } = useState('scroll'
|
|
155
|
+
const { data } = useState('scroll');
|
|
156
156
|
/**
|
|
157
157
|
* The scroll data retrieved for this component.
|
|
158
158
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scroll-to-top.vue2.js","sources":["../../../../../src/x-modules/scroll/components/scroll-to-top.vue"],"sourcesContent":["<template>\n <component :is=\"animation\">\n <BaseEventButton\n v-if=\"isVisible\"\n class=\"x-scroll-to-top x-button\"\n data-test=\"scroll-to-top\"\n aria-label=\"Scroll to top\"\n :events=\"events\"\n >\n <!-- @slot (Required) Button content with a text, an icon or both -->\n <slot />\n </BaseEventButton>\n </component>\n</template>\n\n<script lang=\"ts\">\nimport type { XEventsTypes } from '../../../wiring'\nimport { computed, defineComponent } from 'vue'\nimport { BaseEventButton, NoAnimation } from '../../../components'\nimport { useState } from '../../../composables'\nimport { AnimationProp } from '../../../types'\nimport { scrollXModule } from '../x-module'\nimport { MainScrollId } from './scroll.const'\n\n/**\n * The `ScrollToTop` component is a button that the user can click to make a container scroll\n * up to its initial position.\n *\n * @public\n */\nexport default defineComponent({\n name: 'ScrollToTop',\n xModule: scrollXModule.name,\n components: { BaseEventButton },\n props: {\n /**\n * Animation to use for showing/hiding the button.\n *\n * @public\n */\n animation: {\n type: AnimationProp,\n default: () => NoAnimation,\n },\n /**\n * Threshold in pixels from the top to show the button.\n *\n * @public\n */\n thresholdPx: Number,\n /**\n * Id of the target scroll component.\n *\n * @public\n */\n scrollId: {\n type: String,\n default: MainScrollId,\n },\n },\n setup(props) {\n /**\n * State of all the scroll components in this module.\n *\n * @internal\n */\n // TODO: Directly retrieve the needed data in this computed property\n const { data } = useState('scroll'
|
|
1
|
+
{"version":3,"file":"scroll-to-top.vue2.js","sources":["../../../../../src/x-modules/scroll/components/scroll-to-top.vue"],"sourcesContent":["<template>\n <component :is=\"animation\">\n <BaseEventButton\n v-if=\"isVisible\"\n class=\"x-scroll-to-top x-button\"\n data-test=\"scroll-to-top\"\n aria-label=\"Scroll to top\"\n :events=\"events\"\n >\n <!-- @slot (Required) Button content with a text, an icon or both -->\n <slot />\n </BaseEventButton>\n </component>\n</template>\n\n<script lang=\"ts\">\nimport type { XEventsTypes } from '../../../wiring'\nimport { computed, defineComponent } from 'vue'\nimport { BaseEventButton, NoAnimation } from '../../../components'\nimport { useState } from '../../../composables'\nimport { AnimationProp } from '../../../types'\nimport { scrollXModule } from '../x-module'\nimport { MainScrollId } from './scroll.const'\n\n/**\n * The `ScrollToTop` component is a button that the user can click to make a container scroll\n * up to its initial position.\n *\n * @public\n */\nexport default defineComponent({\n name: 'ScrollToTop',\n xModule: scrollXModule.name,\n components: { BaseEventButton },\n props: {\n /**\n * Animation to use for showing/hiding the button.\n *\n * @public\n */\n animation: {\n type: AnimationProp,\n default: () => NoAnimation,\n },\n /**\n * Threshold in pixels from the top to show the button.\n *\n * @public\n */\n thresholdPx: Number,\n /**\n * Id of the target scroll component.\n *\n * @public\n */\n scrollId: {\n type: String,\n default: MainScrollId,\n },\n },\n setup(props) {\n /**\n * State of all the scroll components in this module.\n *\n * @internal\n */\n // TODO: Directly retrieve the needed data in this computed property\n const { data } = useState('scroll')\n\n /**\n * The scroll data retrieved for this component.\n *\n * @returns The scroll data for this component if a valid {@link ScrollToTop.scrollId} has been\n * passed. Otherwise it returns `null`.\n * @internal\n */\n const scrollData = computed(() => {\n return props.scrollId && data.value[props.scrollId]\n ? data.value[props.scrollId]\n : {\n position: 0,\n direction: 'UP',\n hasReachedStart: false,\n hasAlmostReachedEnd: false,\n hasReachedEnd: false,\n }\n })\n\n /**\n * Event that will be emitted when the scroll to top is clicked.\n *\n * @returns The event to be emitted when the scroll to top is clicked. The id as a payload.\n * @internal\n */\n const events = computed(\n (): Partial<XEventsTypes> => ({ UserClickedScrollToTop: props.scrollId }),\n )\n\n /**\n * Checks if the thresholdPx prop has been provided and if it is a number.\n *\n * @returns If the thresholdPx is a number or not.\n * @internal\n */\n const useThresholdStrategy = computed(() => typeof props.thresholdPx === 'number')\n\n /**\n * Checks if the threshold has been reached in case the threshold strategy is in use.\n *\n * @returns If the scrollTop is bigger than the thresholdPx.\n * @internal\n */\n const isThresholdReached = computed(\n () => useThresholdStrategy.value && scrollData.value.position > props.thresholdPx!,\n )\n\n /**\n * Returns if the scroll has almost reached its end or not.\n *\n * @returns True if the scroll has almost reached the end and the user is still scrolling down.\n * @internal\n */\n const hasAlmostReachedScrollEnd = computed(\n () => scrollData.value.hasAlmostReachedEnd && scrollData.value.direction === 'DOWN',\n )\n\n /**\n * Whether if the button is visible or not depending on the strategy being used.\n *\n * @returns If the button should be visible or not.\n * @internal\n */\n const isVisible = computed(() =>\n useThresholdStrategy.value ? isThresholdReached.value : hasAlmostReachedScrollEnd.value,\n )\n\n return {\n events,\n isVisible,\n }\n },\n})\n</script>\n\n<docs lang=\"mdx\">\n## Events\n\nA list of events that the component will emit:\n\n- [`UserClickedScrollToTop`](https://github.com/empathyco/x/blob/main/packages/x-components/src/wiring/events.types.ts):\n the event is emitted after the user clicks the button. The event payload is the id of the scroll\n that it going to be scrolled.\n\n## Examples\n\n### Basic example\n\nThe component renders whatever is passed to it in the default slot and scrolls to top the scroll\nwith an id `scrollId`.\n\nIt also receives an optional threshold in pixels. When the threshold is reached from the top, the\ncomponent will be shown once the user scrolls `UP`.\n\nIf this parameter is not provided the button will be visible when the user almost reaches the end of\nthe scroll.\n\n```vue\n<template>\n <div>\n <ScrollToTop scroll-id=\"scrollId\" :threshold-px=\"1000\">\n <span>Scroll to top</span>\n </ScrollToTop>\n </div>\n</template>\n\n<script>\nimport { ScrollToTop } from '@empathyco/x-components/scroll'\n\nexport default {\n name: 'ScrollToTopTest',\n components: {\n ScrollToTop,\n },\n}\n</script>\n```\n</docs>\n"],"names":["NoAnimation"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwBA;;;;;AAKE;AACF,gBAAe,eAAe,CAAC;AAC7B,IAAA,IAAI,EAAE,aAAa;IACnB,OAAO,EAAE,aAAa,CAAC,IAAI;IAC3B,UAAU,EAAE,EAAE,eAAc,EAAG;AAC/B,IAAA,KAAK,EAAE;AACL;;;;AAIE;AACF,QAAA,SAAS,EAAE;AACT,YAAA,IAAI,EAAE,aAAa;AACnB,YAAA,OAAO,EAAE,MAAMA,WAAW;AAC3B,SAAA;AACD;;;;AAIE;AACF,QAAA,WAAW,EAAE,MAAM;AACnB;;;;AAIE;AACF,QAAA,QAAQ,EAAE;AACR,YAAA,IAAI,EAAE,MAAM;AACZ,YAAA,OAAO,EAAE,YAAY;AACtB,SAAA;AACF,KAAA;AACD,IAAA,KAAK,CAAC,KAAK,EAAA;AACT;;;;AAIE;;QAEF,MAAM,EAAE,IAAG,EAAI,GAAE,QAAQ,CAAC,QAAQ,CAAA,CAAA;AAElC;;;;;;AAME;AACF,QAAA,MAAM,UAAW,GAAE,QAAQ,CAAC,MAAM;YAChC,OAAO,KAAK,CAAC,QAAO,IAAK,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAA;kBAC9C,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAA;AAC3B,kBAAE;AACE,oBAAA,QAAQ,EAAE,CAAC;AACX,oBAAA,SAAS,EAAE,IAAI;AACf,oBAAA,eAAe,EAAE,KAAK;AACtB,oBAAA,mBAAmB,EAAE,KAAK;AAC1B,oBAAA,aAAa,EAAE,KAAK;iBACtB,CAAA;AACN,SAAC,CAAA,CAAA;AAED;;;;;AAKE;AACF,QAAA,MAAM,MAAO,GAAE,QAAQ,CACrB,OAA8B,EAAE,sBAAsB,EAAE,KAAK,CAAC,QAAO,EAAG,CAAC,CAC3E,CAAA;AAEA;;;;;AAKE;AACF,QAAA,MAAM,oBAAqB,GAAE,QAAQ,CAAC,MAAM,OAAO,KAAK,CAAC,gBAAgB,QAAQ,CAAA,CAAA;AAEjF;;;;;AAKE;QACF,MAAM,kBAAmB,GAAE,QAAQ,CACjC,MAAM,oBAAoB,CAAC,KAAI,IAAK,UAAU,CAAC,KAAK,CAAC,QAAO,GAAI,KAAK,CAAC,WAAY,CACpF,CAAA;AAEA;;;;;AAKE;QACF,MAAM,yBAA0B,GAAE,QAAQ,CACxC,MAAM,UAAU,CAAC,KAAK,CAAC,mBAAkB,IAAK,UAAU,CAAC,KAAK,CAAC,SAAQ,KAAM,MAAM,CACrF,CAAA;AAEA;;;;;AAKE;QACF,MAAM,YAAY,QAAQ,CAAC,MACzB,oBAAoB,CAAC,KAAM,GAAE,kBAAkB,CAAC,KAAI,GAAI,yBAAyB,CAAC,KAAK,CACzF,CAAA;QAEA,OAAO;YACL,MAAM;YACN,SAAS;SACX,CAAA;KACD;AACF,CAAA,CAAA;;;;"}
|
|
@@ -30,7 +30,7 @@ var _sfc_main = defineComponent({
|
|
|
30
30
|
setup(props, { slots }) {
|
|
31
31
|
const $x = use$x();
|
|
32
32
|
/** The banners to render from the state. */
|
|
33
|
-
const stateItems = useState('search'
|
|
33
|
+
const stateItems = useState('search').banners;
|
|
34
34
|
/** The provided {@link FeatureLocation} for the component. */
|
|
35
35
|
const injectedLocation = inject('location');
|
|
36
36
|
const location = isRef(injectedLocation) ? injectedLocation.value : injectedLocation;
|