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

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