diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-11-19 08:27:35 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-11-19 08:27:35 +0000 |
commit | 7e9c479f7de77702622631cff2628a9c8dcbc627 (patch) | |
tree | c8f718a08e110ad7e1894510980d2155a6549197 /app/assets/javascripts/popovers/components/popovers.vue | |
parent | e852b0ae16db4052c1c567d9efa4facc81146e88 (diff) | |
download | gitlab-ce-7e9c479f7de77702622631cff2628a9c8dcbc627.tar.gz |
Add latest changes from gitlab-org/gitlab@13-6-stable-eev13.6.0-rc42
Diffstat (limited to 'app/assets/javascripts/popovers/components/popovers.vue')
-rw-r--r-- | app/assets/javascripts/popovers/components/popovers.vue | 92 |
1 files changed, 92 insertions, 0 deletions
diff --git a/app/assets/javascripts/popovers/components/popovers.vue b/app/assets/javascripts/popovers/components/popovers.vue new file mode 100644 index 00000000000..3bb6d284264 --- /dev/null +++ b/app/assets/javascripts/popovers/components/popovers.vue @@ -0,0 +1,92 @@ +<script> +// We can't use v-safe-html here as the popover's title or content might contains SVGs that would +// be stripped by the directive's sanitizer. Instead, we fallback on v-html and we use GitLab's +// dompurify config that lets SVGs be rendered properly. +// Context: https://gitlab.com/gitlab-org/gitlab/-/issues/247207 +/* eslint-disable vue/no-v-html */ +import { GlPopover } from '@gitlab/ui'; +import { sanitize } from '~/lib/dompurify'; + +const newPopover = element => { + const { content, html, placement, title, triggers = 'focus' } = element.dataset; + + return { + target: element, + content, + html, + placement, + title, + triggers, + }; +}; + +export default { + components: { + GlPopover, + }, + data() { + return { + popovers: [], + }; + }, + created() { + this.observer = new MutationObserver(mutations => { + mutations.forEach(mutation => { + mutation.removedNodes.forEach(this.dispose); + }); + }); + }, + beforeDestroy() { + this.observer.disconnect(); + }, + methods: { + addPopovers(elements) { + const newPopovers = elements.reduce((acc, element) => { + if (this.popoverExists(element)) { + return acc; + } + const popover = newPopover(element); + this.observe(popover); + return [...acc, popover]; + }, []); + + this.popovers.push(...newPopovers); + }, + observe(popover) { + this.observer.observe(popover.target.parentElement, { + childList: true, + }); + }, + dispose(target) { + if (!target) { + this.popovers = []; + } else { + const index = this.popovers.findIndex(popover => popover.target === target); + + if (index > -1) { + this.popovers.splice(index, 1); + } + } + }, + popoverExists(element) { + return this.popovers.some(popover => popover.target === element); + }, + getSafeHtml(html) { + return sanitize(html); + }, + }, +}; +</script> + +<template> + <div> + <gl-popover v-for="(popover, index) in popovers" :key="index" v-bind="popover"> + <template #title> + <span v-if="popover.html" v-html="getSafeHtml(popover.title)"></span> + <span v-else>{{ popover.title }}</span> + </template> + <span v-if="popover.html" v-html="getSafeHtml(popover.content)"></span> + <span v-else>{{ popover.content }}</span> + </gl-popover> + </div> +</template> |