summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/tooltips/components/tooltips.vue
blob: 05927006ea61d98061b11dba973052ea2522bb11 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
<script>
import { GlTooltip, GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
import { uniqueId } from 'lodash';

const getTooltipTitle = element => {
  return element.getAttribute('title') || element.dataset.title;
};

const newTooltip = (element, config = {}) => {
  const { placement, container, boundary, html, triggers } = element.dataset;
  const title = getTooltipTitle(element);

  return {
    id: uniqueId('gl-tooltip'),
    target: element,
    title,
    html,
    placement,
    container,
    boundary,
    triggers,
    disabled: !title,
    ...config,
  };
};

export default {
  components: {
    GlTooltip,
  },
  directives: {
    SafeHtml,
  },
  data() {
    return {
      tooltips: [],
    };
  },
  created() {
    this.observer = new MutationObserver(mutations => {
      mutations.forEach(mutation => {
        mutation.removedNodes.forEach(this.dispose);
      });
    });
  },
  beforeDestroy() {
    this.observer.disconnect();
  },
  methods: {
    addTooltips(elements, config) {
      const newTooltips = elements
        .filter(element => !this.tooltipExists(element))
        .map(element => newTooltip(element, config));

      newTooltips.forEach(tooltip => this.observe(tooltip));

      this.tooltips.push(...newTooltips);
    },
    observe(tooltip) {
      this.observer.observe(tooltip.target.parentElement, {
        childList: true,
      });
    },
    dispose(target) {
      if (!target) {
        this.tooltips = [];
      } else {
        const index = this.tooltips.indexOf(this.findTooltipByTarget(target));

        if (index > -1) {
          this.tooltips.splice(index, 1);
        }
      }
    },
    fixTitle(target) {
      const tooltip = this.findTooltipByTarget(target);

      if (tooltip) {
        tooltip.title = target.getAttribute('title');
      }
    },
    triggerEvent(target, event) {
      const tooltip = this.findTooltipByTarget(target);

      if (tooltip) {
        this.$refs[tooltip.id][0].$emit(event);
      }
    },
    tooltipExists(element) {
      return Boolean(this.findTooltipByTarget(element));
    },
    findTooltipByTarget(element) {
      return this.tooltips.find(tooltip => tooltip.target === element);
    },
  },
};
</script>
<template>
  <div>
    <gl-tooltip
      v-for="(tooltip, index) in tooltips"
      :id="tooltip.id"
      :ref="tooltip.id"
      :key="index"
      :target="tooltip.target"
      :triggers="tooltip.triggers"
      :placement="tooltip.placement"
      :container="tooltip.container"
      :boundary="tooltip.boundary"
      :disabled="tooltip.disabled"
      :show="tooltip.show"
    >
      <span v-if="tooltip.html" v-safe-html="tooltip.title"></span>
      <span v-else>{{ tooltip.title }}</span>
    </gl-tooltip>
  </div>
</template>