SpeciesFileGroup/taxonworks

View on GitHub
app/javascript/vue/components/ui/VIcon/index.vue

Summary

Maintainability
Test Coverage
<template>
  <svg
    xmlns="http://www.w3.org/2000/svg"
    :width="elementSize"
    :height="elementSize"
    :viewBox="viewbox"
    :aria-labelledby="name"
    role="presentation"
  >
    <title
      :id="name"
      lang="en"
    >
      {{ showTitle }}
    </title>
    <g
      ref="svggroup"
      stroke-width="2"
      :fill="selectedColor"
    >
      <path
        v-for="(path, index) in iconPaths"
        :key="index"
        v-bind="path"
      />
    </g>
  </svg>
</template>

<script>
import { onVisible } from '@/helpers/observable.js'
import mixinSizes from '../mixins/sizes.js'
import mixinColors from '../mixins/colors.js'
import * as Icons from './icons.js'

export default {
  name: 'VIcon',

  mixins: [mixinSizes, mixinColors],

  props: {
    disabled: {
      type: Boolean,
      default: false
    },

    name: {
      type: String,
      required: true
    },

    title: {
      type: String,
      default: undefined
    }
  },

  data() {
    return {
      viewbox: '0 0 12 12'
    }
  },

  computed: {
    iconPaths() {
      return Icons[this.name]?.paths || []
    },

    showTitle() {
      return this.title
    }
  },

  watch: {
    name: {
      handler() {
        this.$nextTick(() => {
          this.viewbox = this.getViewboxSize()
        })
      }
    }
  },

  mounted() {
    this.$nextTick(() => {
      this.viewbox = this.getViewboxSize()
    })

    const { observer } = onVisible(this.$el, (visible) => {
      if (visible) {
        this.$nextTick(() => {
          this.viewbox = this.getViewboxSize()
        })
      }
    })

    this.observer = observer
  },

  unmounted() {
    this.observer?.disconnect()
  },

  methods: {
    getViewboxSize() {
      const refGroup = this.$refs.svggroup

      if (refGroup) {
        const groupSize = refGroup.getBBox()
        const strokePaths = this.iconPaths
          .map((path) => path['stroke-width'])
          .filter((stroke) => stroke)
        const strokeWidth = strokePaths.length ? Math.max(...strokePaths) : 0

        return [
          groupSize.x - strokeWidth / 2,
          groupSize.y - strokeWidth / 2,
          groupSize.width + strokeWidth,
          groupSize.height + strokeWidth
        ].join(' ')
      } else {
        return '0 0 12 12'
      }
    }
  }
}
</script>