You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

BoxItem.vue 3.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. <template>
  2. <div v-if="isVisible" class="box__item" :class="cssClass" @click="click">
  3. <!-- Icon -->
  4. <slot name="icon">
  5. <div v-if="icon" class="item__icon">
  6. <img v-if="isImageIcon" :src="icon">
  7. <font-awesome-icon v-else :icon="icon" />
  8. </div>
  9. </slot>
  10. <!-- Label & Description -->
  11. <div v-if="label" class="item__content">
  12. <span class="item__label" v-html="label" />
  13. <slot name="description">
  14. <span v-if="description" class="item__description" v-html="description" />
  15. </slot>
  16. </div>
  17. <!-- Actions -->
  18. <div v-if="$slots.default" class="item__action" :class="{ 'item__action--full': !hasIcon && !label }">
  19. <slot />
  20. </div>
  21. </div>
  22. </template>
  23. <script>
  24. import { isNil, startsWith } from 'lodash'
  25. import { dreamtrack } from '~/modules/services'
  26. import { dream } from '~/modules'
  27. const { shell } = $provider.api
  28. export default {
  29. props: {
  30. icon: {
  31. type: [String, Array],
  32. default: undefined,
  33. },
  34. label: {
  35. type: String,
  36. default: undefined,
  37. },
  38. description: {
  39. type: String,
  40. default: undefined,
  41. },
  42. version: {
  43. type: String,
  44. default: undefined,
  45. },
  46. href: {
  47. type: String,
  48. default: undefined,
  49. },
  50. isLink: {
  51. type: Boolean,
  52. default: false,
  53. },
  54. },
  55. computed: {
  56. isVisible() {
  57. return isNil(this.version) || this.version === dream.version
  58. },
  59. hasIcon() {
  60. return !isNil(this.icon) || !isNil(this.$slots.icon)
  61. },
  62. isImageIcon() {
  63. return startsWith(this.icon, 'http') || startsWith(this.icon, '/')
  64. },
  65. cssClass() {
  66. return {
  67. 'box__item--link': !isNil(this.href) || this.isLink,
  68. }
  69. },
  70. },
  71. methods: {
  72. click() {
  73. this.$emit('click')
  74. if (!isNil(this.href)) {
  75. if (startsWith(this.href, '/')) {
  76. this.$router.push(this.href)
  77. } else {
  78. dreamtrack.track('CLICK_LINK', { href: this.href })
  79. shell.openExternal(this.href)
  80. }
  81. }
  82. },
  83. isURL(str) {
  84. if (isNil(str)) {
  85. return false
  86. }
  87. if (startsWith(str, '~')) {
  88. return true
  89. }
  90. const pattern = new RegExp(
  91. '^(https?:\\/\\/)?' // protocol
  92. + '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' // domain name
  93. + '((\\d{1,3}\\.){3}\\d{1,3}))' // OR ip (v4) address
  94. + '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' // port and path
  95. + '(\\?[;&a-z\\d%_.~+=-]*)?' // query string
  96. + '(\\#[-a-z\\d_]*)?$',
  97. 'i',
  98. ) // fragment locator
  99. return !!pattern.test(str)
  100. },
  101. },
  102. }
  103. </script>
  104. <style lang="scss">
  105. .box.box--items {
  106. &.box--items--horizontal {
  107. .box__content {
  108. @apply flex;
  109. }
  110. .box__item {
  111. @apply flex-1;
  112. @apply border-none;
  113. }
  114. }
  115. .box__content {
  116. @apply px-0;
  117. }
  118. .box__item {
  119. @apply flex px-4 py-2;
  120. min-height: 50px;
  121. transition: all .2s ease-in-out;
  122. &.box__item--sub {
  123. @apply pl-8;
  124. }
  125. &.box__item--link {
  126. @apply cursor-pointer;
  127. &:hover {
  128. @apply bg-dark-800 text-white;
  129. }
  130. }
  131. &.box__item--active {
  132. @apply bg-dark-800 text-generic-100;
  133. }
  134. &:not(:last-child) {
  135. @apply border-b border-dark-300;
  136. }
  137. .item__icon {
  138. @apply mr-4 flex items-center justify-center text-2xl;
  139. width: 42px;
  140. min-width: 42px;
  141. }
  142. .item__content {
  143. @apply flex-1 flex flex-col justify-center;
  144. .item__label {
  145. @apply block font-semibold text-generic-400;
  146. }
  147. .item__description {
  148. @apply block text-sm;
  149. }
  150. }
  151. .item__action {
  152. //@apply flex items-center justify-center;
  153. .item__label {
  154. @apply block font-semibold text-sm text-generic-400;
  155. }
  156. &:not(.item__action--full) {
  157. @apply w-1/3 ml-4;
  158. }
  159. &.item__action--full {
  160. @apply flex-1;
  161. }
  162. }
  163. }
  164. }
  165. </style>