Tabs.vue 4.1 KB

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