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 4.0KB

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