private-dreamnet/dreamtime

View on GitHub
src/components/UI/BoxItem.vue

Summary

Maintainability
Test Coverage
<template>
  <div v-if="isVisible"
       class="box__item"
       :class="cssClass"
       @click="click">
    <!-- Icon -->
    <slot name="icon">
      <div v-if="icon" class="item__icon">
        <img v-if="isImageIcon" :src="icon">
        <font-awesome-icon v-else :icon="icon" />
      </div>
    </slot>

    <!-- Label & Description -->
    <div v-if="label" class="item__content">
      <span class="item__label" v-html="label" />

      <slot name="description">
        <span v-if="description"
              class="item__description"
              v-html="description" />
      </slot>
    </div>

    <!-- Actions -->
    <div v-if="$slots.default"
         class="item__action"
         :class="{ 'item__action--full': !hasIcon && !label }">
      <slot />
    </div>
  </div>
</template>

<script>
import { isNil, startsWith } from 'lodash'
import { dream } from '~/modules'

const { shell } = $provider.api

export default {
  props: {
    icon: {
      type: [String, Array],
      default: undefined,
    },

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

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

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

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

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

  computed: {
    isVisible() {
      return isNil(this.version) || this.version === dream.version
    },

    hasIcon() {
      return !isNil(this.icon) || !isNil(this.$slots.icon)
    },

    isImageIcon() {
      return startsWith(this.icon, 'http') || startsWith(this.icon, '/')
    },

    cssClass() {
      return {
        'box__item--link': !isNil(this.href) || this.isLink,
      }
    },
  },

  methods: {
    click() {
      this.$emit('click')

      if (!isNil(this.href)) {
        if (startsWith(this.href, '/')) {
          this.$router.push(this.href)
        } else {
          consola.track('CLICK_LINK', { href: this.href })
          shell.openExternal(this.href)
        }
      }
    },

    isURL(str) {
      if (isNil(str)) {
        return false
      }
      if (startsWith(str, '~')) {
        return true
      }
      const pattern = new RegExp(
        '^(https?:\\/\\/)?' // protocol
        + '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' // domain name
        + '((\\d{1,3}\\.){3}\\d{1,3}))' // OR ip (v4) address
        + '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' // port and path
        + '(\\?[;&a-z\\d%_.~+=-]*)?' // query string
          + '(\\#[-a-z\\d_]*)?$',
        'i',
      ) // fragment locator
      return !!pattern.test(str)
    },
  },
}
</script>

<style lang="scss">
.box.box--items {
  &.box--items--horizontal {
    .box__content {
      @apply flex;
    }

    .box__item {
      @apply flex-1;
      @apply border-none;
    }
  }

  .box__content {
    @apply px-0;
  }

  .box__item {
    @apply flex px-4 py-2;
    min-height: 50px;
    //transition: all 0.2s ease-in-out;

    &.box__item--sub {
      @apply pl-8;
    }

    &.box__item--link {
      @apply cursor-pointer;

      &:hover {
        @apply bg-dark-800 text-white;
      }
    }

    &.box__item--active {
      @apply bg-dark-800 text-common-light;
    }

    &:not(:last-child) {
      @apply border-b border-dark-300;
    }

    .item__icon {
      @apply mr-4 flex items-center justify-center text-2xl;
      min-width: 42px;
      width: 42px;
    }

    .item__content {
      @apply flex-1 flex flex-col justify-center;

      .item__label {
        @apply block font-semibold text-common;
      }

      .item__description {
        @apply block text-sm;
      }
    }

    .item__action {
      //@apply flex items-center justify-center;

      .item__label {
        @apply block font-semibold text-sm text-common;
      }

      &:not(.item__action--full) {
        @apply w-1/3 ml-4;
      }

      &.item__action--full {
        @apply flex-1;
      }
    }
  }
}
</style>