Tabs.vue 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. <template>
  2. <div
  3. class="flex flex-nowrap h-full flex-1"
  4. :class="{ 'flex-col h-auto': !vertical }"
  5. >
  6. <div
  7. class="relative tabs hide-scrollbar"
  8. :class="[{ 'border-r border-dividerLight': vertical }, styles]"
  9. >
  10. <div class="flex flex-1">
  11. <div
  12. class="flex flex-1 justify-between"
  13. :class="{ 'flex-col': vertical }"
  14. >
  15. <div class="flex" :class="{ 'flex-col space-y-2 p-2': vertical }">
  16. <button
  17. v-for="(tab, index) in tabs"
  18. :key="`tab-${index}`"
  19. class="tab"
  20. :class="[{ active: tab.active }, { vertical: vertical }]"
  21. :aria-label="tab.label"
  22. @keyup.enter="selectTab(tab)"
  23. @click="selectTab(tab)"
  24. >
  25. <SmartIcon v-if="tab.icon" class="svg-icons" :name="tab.icon" />
  26. <tippy
  27. v-if="vertical && tab.label"
  28. placement="left"
  29. theme="tooltip"
  30. :content="tab.label"
  31. />
  32. <span v-else-if="tab.label">{{ tab.label }}</span>
  33. <span v-if="tab.info && tab.info !== 'null'" class="tab-info">
  34. {{ tab.info }}
  35. </span>
  36. <span
  37. v-if="tab.indicator"
  38. class="bg-accentLight rounded-full h-1 ml-2 w-1"
  39. ></span>
  40. </button>
  41. </div>
  42. <div class="flex items-center justify-center">
  43. <slot name="actions"></slot>
  44. </div>
  45. </div>
  46. </div>
  47. </div>
  48. <div
  49. class="h-full w-full contents"
  50. :class="{
  51. '!flex flex-col flex-1 overflow-y-auto hide-scrollbar': vertical,
  52. }"
  53. >
  54. <slot></slot>
  55. </div>
  56. </div>
  57. </template>
  58. <script>
  59. import { defineComponent } from "@nuxtjs/composition-api"
  60. export default defineComponent({
  61. props: {
  62. styles: {
  63. type: String,
  64. default: "",
  65. },
  66. vertical: {
  67. type: Boolean,
  68. default: false,
  69. },
  70. },
  71. data() {
  72. return {
  73. tabs: [],
  74. }
  75. },
  76. updated() {
  77. const candidates = this.$children.filter(
  78. (child) => child.$options.name === "SmartTab"
  79. )
  80. if (candidates.length !== this.tabs.length) {
  81. this.tabs = candidates
  82. }
  83. },
  84. mounted() {
  85. this.tabs = this.$children.filter(
  86. (child) => child.$options.name === "SmartTab"
  87. )
  88. },
  89. methods: {
  90. selectTab({ id }) {
  91. this.tabs.forEach((tab) => {
  92. tab.active = tab.id === id
  93. })
  94. this.$emit("tab-changed", id)
  95. },
  96. },
  97. })
  98. </script>
  99. <style scoped lang="scss">
  100. .tabs {
  101. @apply flex;
  102. @apply whitespace-nowrap;
  103. @apply overflow-auto;
  104. @apply flex-shrink-0;
  105. // &::after {
  106. // @apply absolute;
  107. // @apply inset-x-0;
  108. // @apply bottom-0;
  109. // @apply bg-dividerLight;
  110. // @apply z-1;
  111. // @apply h-0.5;
  112. // content: "";
  113. // }
  114. .tab {
  115. @apply relative;
  116. @apply flex;
  117. @apply flex-shrink-0;
  118. @apply items-center;
  119. @apply justify-center;
  120. @apply py-2 px-4;
  121. @apply text-secondary;
  122. @apply font-semibold;
  123. @apply cursor-pointer;
  124. @apply hover:text-secondaryDark;
  125. @apply focus:outline-none;
  126. @apply focus-visible:text-secondaryDark;
  127. .tab-info {
  128. @apply inline-flex;
  129. @apply items-center;
  130. @apply justify-center;
  131. @apply w-5;
  132. @apply h-4;
  133. @apply ml-2;
  134. @apply text-8px;
  135. @apply border border-divider;
  136. @apply rounded;
  137. @apply text-secondaryLight;
  138. }
  139. &::after {
  140. @apply absolute;
  141. @apply left-4;
  142. @apply right-4;
  143. @apply bottom-0;
  144. @apply bg-transparent;
  145. @apply z-2;
  146. @apply h-0.5;
  147. content: "";
  148. }
  149. &:focus::after {
  150. @apply bg-divider;
  151. }
  152. &.active {
  153. @apply text-secondaryDark;
  154. .tab-info {
  155. @apply text-secondary;
  156. @apply border-dividerDark;
  157. }
  158. &::after {
  159. @apply bg-accent;
  160. }
  161. }
  162. &.vertical {
  163. @apply p-2;
  164. @apply rounded;
  165. &:focus::after {
  166. @apply hidden;
  167. }
  168. &.active {
  169. @apply text-accent;
  170. .tab-info {
  171. @apply text-secondary;
  172. @apply border-dividerDark;
  173. }
  174. &::after {
  175. @apply hidden;
  176. }
  177. }
  178. }
  179. }
  180. }
  181. </style>