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.4KB

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