SpeciesFileGroup/taxonworks

View on GitHub
app/javascript/vue/components/ui/VSpinner.vue

Summary

Maintainability
Test Coverage
/* target: sets the parent DOM Element logoSize: sets the size of the logo
legend: sets the legend description showSpinner: disable the spinner animation
show-legend: activate the legend */

<template>
  <transition name="fade">
    <div
      class="middle vue-box-spinner"
      :style="cssProperties"
    >
      <div
        class="tw-spinner"
        :class="[`tw-spinner-${spinnerPosition}`]"
      >
        <svg
          v-if="showSpinner"
          id="tw-spinner-logo"
          :style="logoSize"
          version="1.1"
          xmlns="http://www.w3.org/2000/svg"
          xmlns:xlink="http://www.w3.org/1999/xlink"
          x="0px"
          y="0px"
          viewBox="0 0 194.6 200"
          style="enable-background: new 0 0 194.6 200"
          xml:space="preserve"
        >
          <path
            class="st0"
            id="LeftTop"
            d="M14.2,63.1C24.3,63.1,28,76.3,28,76.3s5,15.5,15.3,15.5c7.7,0,9.4-6.3,9.8-9.1c0-0.1,0-0.2,0-0.4s0-0.5,0.1-0.8
                              c0-0.1,0-0.1,0-0.2c0.3-2-0.8-4-2.7-4.8l0,0c-3.1-1.1-5.9-3-8-5.6c-6.4-7.4-3-17,4.5-23.6c-3.5-4-8-8.7-12-10.8
                              c-8.7-4.6-15.7,2.6-16.2,3.2L18.7,40c-0.6,0.8-1.2,1.6-1.8,2.5l-0.1,0.1c-0.6,0.9-1.2,1.8-1.8,2.7L15,45.4c-0.5,0.8-1,1.6-1.5,2.4
                              l-0.3,0.5c-1,1.8-2,3.6-3,5.5L10,54.3c-0.4,0.9-0.9,1.8-1.3,2.7l-0.1,0.1C8.2,58,7.8,59,7.4,59.9l-0.2,0.5c-0.3,0.8-0.6,1.5-0.9,2.3
                              l-0.2,0.6c-0.4,1-0.7,1.9-1,2.9L5,66.7c-0.1,0.3-0.2,0.5-0.2,0.8C7.1,64.7,10.5,63.1,14.2,63.1z"
          />
          <path
            id="Head"
            class="st0"
            d="M36.2,33c5.3,1.8,11.4,6.8,15.7,11c0.5-0.3,1.1-0.6,1.6-0.9c2.3-1.2,4.7-2.1,7.2-2.7
                              c-2.4-4.9-5.6-12.8-4.9-18.3c1.4-11.2,13.6-12,13.6-12S58.3,12.5,59,22.9c0.3,5,4.7,12.5,7.5,16.8c7-0.1,13,2.7,15.8,8
                              c0.3,0.5,0.5,1.1,0.7,1.6l0,0c0.5,1,1.3,1.9,2.3,2.4c3.6,1.2,7.6,2.5,11.7,4.2c5.8,2.3,11.5,5,17,8.1c0.5,0.2,1,0,1.2-0.5
                              c0.1-0.3,0.1-0.7-0.2-0.9c-27.2-26.1-8-36.3-8-36.3c-9.8,14.2,6.6,24.5,25.5,35.1c16.6,9.3,28.4,19.5,41.3,12.7
                              c6.4-3.4,1.9-11-1-15.2c-2.2-2.9-4-6-5.6-9.2c-1-2.1-1.3-4.4-0.8-6.6c0.7-3.1-0.4-6.8-2.8-10.5c-1.8-2.8-5.1-5.7-7.6-7.6
                              c-1.6-1.3-3.1-2.7-4.3-4.4c-9-12.6-32.2-17.9-32.2-17.9l0,0C97.5-2.6,74.3,0.1,54,10.2l0,0c-6.9,3.5-13.5,7.7-19.4,12.7l-0.8,0.6
                              c-1.4,1.2-2.8,2.5-4.2,3.8l0,0c-0.8,0.8-1.6,1.5-2.3,2.3l0,0c-1.5,1.6-3,3.2-4.4,4.9l-0.2,0.2l-0.4,0.5C25.2,32.8,29.6,30.9,36.2,33
                              z M145.8,33.9c3.9-1.7,8.5,0.1,10.2,4c0.6,1.3,0.8,2.7,0.6,4c-2.5,1.2-5.5,0.9-7.7-0.9C146.5,39.5,145.3,36.7,145.8,33.9L145.8,33.9
                              z"
          />
          <path
            id="LeftMid"
            class="st0"
            d="M53.2,106.9c-0.2-2-0.4-4-0.4-6.3c-2.3,1-4.7,1.5-7.2,1.5c-19,0-19.5-18.8-26.2-29.2
                              c-7-10.8-16.8,0.9-17.8,7.2c0,0.3-0.1,0.5-0.1,0.7c-0.1,0.6-0.2,1.3-0.3,1.9C1,83.1,1,83.4,1,83.6c-0.1,0.8-0.2,1.6-0.3,2.3
                              c0,0.3-0.1,0.5-0.1,0.8c-0.1,1-0.2,2.1-0.3,3.1c0,0.1,0,0.2,0,0.3c-0.1,0.9-0.2,1.9-0.2,2.8c0,0.3,0,0.6,0,0.9C0,94.9,0,95.8,0,96.7
                              c0,0.1,0,0.2,0,0.3s0,0.1,0,0.1s0,0.1,0,0.1c0,0.4,0,0.8,0,1.2s0,0.8,0,1.2c0,0.6,0,1.2,0.1,1.7c0,0.2,0,0.5,0,0.7
                              c1.5,30.7,17.5,58.9,43,76c-8-7.8-12.7-17.8-12.7-28.6c0-13.4,7.3-26,19.1-34.4C52,113.2,53.5,110.1,53.2,106.9z"
          />
          <path
            id="LeftBottom"
            class="st0"
            d="M128.3,170c-5.8,2.4-12,3.9-18.3,4.4c-1.1,0.1-2.2,0.1-3.3,0.1c-12.3,0-28.4-4.8-40.7-25.3
                              l-1.6-2.7c-1.9,2.7-2.9,6-2.9,9.3c0,13.9,16.6,26.5,36.7,26.5c5.4,0.1,10.7-0.6,15.9-2c-4.6,2.9-13.8,5.6-21.9,5.6
                              C69.2,186,50,170.8,50,152.7c0.1-6.3,2.1-12.5,5.8-17.6c1.5-2.1,1.9-4.8,1.1-7.2l0,0c-10.5,6.1-17,15.4-17,26.6
                              c0,17.2,15.3,31.9,37,37.9c1.2,0.3,2.5,0.5,3.7,0.7c15.6,2.6,29.5-0.1,38.9-9.1C123.5,180.1,126.5,175.3,128.3,170z"
          />
          <path
            id="Tail"
            class="st0"
            d="M171,33.8c28,39.3,18.8,93.8-20.5,121.8c-2.4,1.7-4.9,3.3-7.5,4.8l-0.2,0.2c0-0.4,0-0.9,0.1-1.3
                              c0.6-3.9,2.1-7.6,4.4-10.8c7.5-10.7,12.4-27-0.3-48.1c-7-11.6-21.7-28-51.7-40.1c-4.1-1.7-8.1-3.1-11.8-4.2L82,55.6l-0.2-0.1
                              c-1.1-0.3-2.1-0.6-3.1-0.9h-0.2l-2.9-0.8h-0.1l-1.3-0.3h-0.1l-2.6-0.6h-0.1l-2.3-0.5h-0.2l-1-0.2l0,0L67,51.7h-0.2l-0.7-0.2h-0.2
                              l-0.8-0.2H65l-0.6-0.1h-0.2L63.7,51l-0.1,0.4v0.1l-0.1,0.3l0,0.2l-0.1,0.3l-0.1,0.2l-0.1,0.3l-0.1,0.2L63,53.5L63,53.7l-0.1,0.4
                              l-0.1,0.2l-0.1,0.5l-0.1,0.3l-0.1,0.6L62.3,56l-0.1,0.5l-0.1,0.3L62,57.2l-0.1,0.4L61.8,58l-0.1,0.4l-0.1,0.5l-0.1,0.4l-0.1,0.5
                              l-0.1,0.4l-0.1,0.6l-0.1,0.5L61,61.9l-0.1,0.4l-0.1,0.5l-0.1,0.5l-0.1,0.5l-0.1,0.5l-0.1,0.5l-0.1,0.5l-0.1,0.5l-0.1,0.5L60,66.9
                              l-0.1,0.5l-0.1,0.7l-0.1,0.5l-0.2,1l-0.1,0.5l-0.1,0.6l-0.1,0.5l-0.1,0.6l-0.1,0.6L59,73.1l-0.1,0.6l-0.1,0.6l-0.1,0.6l-0.1,0.7
                              l-0.1,0.6l-0.1,0.8l-0.1,0.5c-0.2,1.8-0.4,3.6-0.6,5.5V83c0,0.6-0.1,1.1-0.1,1.7l0,0c0,0.6-0.1,1.1-0.1,1.7v0.1
                              c0,0.6-0.1,1.1-0.1,1.7l0,0c-0.2,3-0.2,6.2-0.2,9.5c0.1,4.3,0.4,8.5,0.8,12.7c0,0.3,0.1,0.7,0.1,1.1c0.7,5.8,1.9,11.4,3.6,17
                              c1.5,4.5,3.2,8.9,5.2,13.2c0.9,1.7,1.8,3.4,2.8,5.1c9.2,15.3,21.5,23,36.6,23c1,0,2.1,0,3.2-0.1c7-0.6,13.9-2.5,20.2-5.6
                              c4.9-24.5-8.4-59.3-36.1-88.3C90,71.8,86,68,81.7,64.4c9.2,6.9,13.9,10.7,19.2,16.3c37.8,39.6,49.7,89.7,26.5,111.9
                              c-3.4,3.3-7.5,5.8-11.9,7.5c5.8-1.6,11.1-4.5,15.4-8.6c1.4-1.3,2.6-2.7,3.8-4.2c49.6-20.7,73-77.7,52.4-127.3
                              C183.1,50.4,177.7,41.6,171,33.8z"
          />
        </svg>
        <div :style="legendStyle">
          <span
            v-if="showLegend"
            v-html="legend"
          />
          <slot />
        </div>
      </div>
    </div>
  </transition>
</template>

<script>
export default {
  props: {
    target: {
      type: String,
      default: undefined
    },

    fullScreen: {
      type: Boolean,
      default: false
    },

    legend: {
      type: String,
      default: 'Loading, please wait.'
    },

    zIndex: {
      type: [Number, String],
      default: undefined
    },

    resize: {
      type: Boolean,
      default: true
    },

    legendStyle: {
      type: Object,
      default: () => ({
        color: '#444',
        marginTop: '30px',
        textAlign: 'center'
      })
    },

    showLegend: {
      type: Boolean,
      default: true
    },

    showSpinner: {
      type: Boolean,
      default: true
    },

    spinnerPosition: {
      type: String,
      default: 'top'
    },

    logoSize: {
      type: Object,
      default: () => ({
        width: '50px',
        height: '50px'
      })
    },

    cssPosition: {
      type: String,
      default: 'absolute'
    }
  },

  data: function () {
    return {
      cssProperties: {
        width: undefined,
        height: undefined,
        position: this.cssPosition,
        top: undefined,
        bottom: undefined,
        zIndex: undefined,
        left: undefined,
        right: undefined
      },
      resizeInterval: undefined
    }
  },

  mounted() {
    this.init()
    if (this.resize && !this.fullScreen) {
      this.checkResize()
    }
  },

  unmounted() {
    clearInterval(this.resizeInterval)
  },

  methods: {
    outerWidth(el) {
      const style = getComputedStyle(el)
      const width = el.offsetWidth

      return {
        width: `${width}px`,
        marginLeft: `-${parseInt(style.paddingLeft) || 0}px`
      }
    },
    outerHeight(el) {
      const style = getComputedStyle(el)
      const height = el.offsetHeight

      return {
        height: height + 'px',
        marginTop: `-${parseInt(style.paddingTop) || 0}px`
      }
    },
    init() {
      const domElement =
        this.target !== undefined
          ? this.loadElement(this.target)
          : this.$el.parentNode
      const copyCSS = Object.assign({}, this.cssProperties)

      if (this.fullScreen) {
        copyCSS.position = 'fixed'
        copyCSS.width = '100vw'
        copyCSS.height = '100vh'
        copyCSS.top = '0px'
        copyCSS.left = '0px'
      } else {
        const elementBound = domElement.getBoundingClientRect()

        Object.assign(
          copyCSS,
          this.outerHeight(domElement),
          this.outerWidth(domElement),
          { boxSizing: 'border-box' }
        )

        if (copyCSS.position == 'fixed') {
          copyCSS.top = elementBound.top + 'px'
          copyCSS.left = elementBound.left + 'px'
        }
      }
      if (!this.showSpinner) {
        copyCSS.zIndex =
          this.zIndex ||
          (domElement.style.zIndex === '' ? 2 : domElement.style.zIndex + 1)
      }
      this.cssProperties = copyCSS
    },

    loadElement(el) {
      let domElement

      switch (el.substring(0, 1)) {
        case '#':
          domElement = document.getElementById(el.substring(1))
          break
        case '.':
          domElement = document.getElementsByClassName(el.substring(1))
          break
        default:
          domElement = document.getElementsByTagName(el)[0]
      }
      return domElement
    },

    checkResize() {
      this.resizeInterval = setInterval(() => {
        this.init()
      }, 500)
    }
  }
}
</script>
<style>
.tw-spinner {
  display: flex;
  margin: 0 auto;
  height: auto;
  width: auto;
  align-items: center;
}

.vue-box-spinner {
  box-shadow: 0px 1px 1px 0px rgba(0, 0, 0, 0.2);
  background-image: none !important;
  background-color: #fff;
  z-index: 999999;
  height: 100%;
  opacity: 0.9;
}

.fade-enter-active,
.fade-leave-active {
  transition: opacity 0.5s;
}
.fade-enter,
.fade-leave-to {
  opacity: 0;
}

.tw-spinner-left {
  flex-direction: row;
  gap: 0.5rem;
}

.tw-spinner-right {
  flex-direction: row-reverse;
}

.tw-spinner-top {
  flex-direction: column;
}

.tw-spinner-bottom {
  flex-direction: column-reverse;
}
</style>