Skip to content
Snippets Groups Projects
Lexique.vue 2.9 KiB
Newer Older
Emmanuel Salomon's avatar
Emmanuel Salomon committed
<template>
  <span
    class="tooltip-box relative"
    :class="document && 'cursor-pointer'"
    v-on="document ? { click: onClick } : {}"
Emmanuel Salomon's avatar
Emmanuel Salomon committed
  >
    <abbr
      :aria-label="document ? document.title : title"
      :title="document ? '' : null"
    >
Emmanuel Salomon's avatar
Emmanuel Salomon committed
      <slot />
    </abbr>

    <client-only>
      <div
poka's avatar
poka committed
        class="
          tooltip
          absolute
          bg-blue-100
          border-blue-200 border
          invisible
          opacity-0
          left-1/2
          px-4
          py-3
          rounded-xl
          shadow-2xl
          text-gray-600 text-sm
          z-50
          dark:bg-blue-900 dark:text-gray-100 dark:border-blue-800
        "
Emmanuel Salomon's avatar
Emmanuel Salomon committed
      >
        <span class="triangle absolute"></span>

        <span class="block font-bold text-lg uppercase leading-6 pb-2">
          {{ document.title }} :
Emmanuel Salomon's avatar
Emmanuel Salomon committed
        </span>

        <span>{{ document.description }}</span>
poka's avatar
poka committed
          class="
            block
            font-light
            mt-3
            text-xs text-purple-800
            dark:text-purple-500
          "
Emmanuel Salomon's avatar
Emmanuel Salomon committed
          {{ $t('lexique.tooltipReadmore') }}
        </span>
      </div>
    </client-only>
  </span>
</template>

<script>
export default {
  name: 'Lexique',
  props: {
    title: {
      type: String,
      default: null,
    },
  },
  data() {
    return {
Emmanuel Salomon's avatar
Emmanuel Salomon committed
    }
  },
  async fetch() {
    if (this.computedTitle) {
      const term = this.computedTitle.toLowerCase()
      const results = await this.$content('lexique')
        .where({
          $or: [
            { title: { $regex: [`^${term}$`, 'i'] } }, // case insensitive
            { synonyms: { $contains: term } }, // search in synonyms
          ],
        })
        .limit(1)
        .fetch()

      if (results.length) {
        this.document = results[0]
Emmanuel Salomon's avatar
Emmanuel Salomon committed
      }
    }
  },
  computed: {
    computedTitle() {
      // Priority on the prop `title` over slot content
      return this.title || this.$slots.default[0].text
    },
  },
  methods: {
    onClick() {
      this.$router.push(this.document.path)
Emmanuel Salomon's avatar
Emmanuel Salomon committed
    },
  },
}
</script>

<style lang="postcss" scoped>
abbr {
  text-decoration-thickness: from-font;
}

.tooltip {
  width: max-content;
  max-width: 320px;
  top: calc(100% + 11px);
  transform: translateX(-50%) translateY(10px);
  transition: opacity 0.3s ease-out, transform 0.3s ease-out;
}
.tooltip-box:hover .tooltip {
  visibility: visible;
  transform: translateX(-50%) translateY(0);
  opacity: 1;
}
.triangle {
  border-width: 0 10px 10px;
Emmanuel Salomon's avatar
Emmanuel Salomon committed
  border-color: transparent;
  border-bottom-color: #bfdbfe;
  top: -10px;
Emmanuel Salomon's avatar
Emmanuel Salomon committed
  left: 50%;
  transform: translateX(-50%);
}
.triangle:before {
  content: '';
  position: absolute;
  border-width: 0 10px 10px;
  border-color: transparent;
  border-bottom-color: #dbeafe;
  top: 2px;
  left: -10px;
}
.dark .triangle {
  border-bottom-color: #1e40af;
}
.dark .triangle:before {
  border-bottom-color: #1e3a8a;
}
Emmanuel Salomon's avatar
Emmanuel Salomon committed
</style>