Tabs.vue 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. <template>
  2. <div class="flex flex-col flex-nowrap flex-1">
  3. <div class="tabs hide-scrollbar relative" :class="styles">
  4. <div class="flex flex-1">
  5. <div class="flex flex-1 justify-between">
  6. <div class="flex">
  7. <button
  8. v-for="(tab, index) in tabs"
  9. :key="`tab-${index}`"
  10. class="tab"
  11. :class="{ active: tab.active }"
  12. tabindex="0"
  13. @keyup.enter="selectTab(tab)"
  14. @click="selectTab(tab)"
  15. >
  16. <i v-if="tab.icon" class="material-icons">
  17. {{ tab.icon }}
  18. </i>
  19. <span v-if="tab.label">{{ tab.label }}</span>
  20. <span v-if="tab.info" class="tab-info">
  21. {{ tab.info }}
  22. </span>
  23. </button>
  24. </div>
  25. <div class="flex">
  26. <slot name="actions"></slot>
  27. </div>
  28. </div>
  29. </div>
  30. </div>
  31. <slot></slot>
  32. </div>
  33. </template>
  34. <script>
  35. import { defineComponent } from "@nuxtjs/composition-api"
  36. export default defineComponent({
  37. props: {
  38. styles: {
  39. type: String,
  40. default: "",
  41. },
  42. },
  43. data() {
  44. return {
  45. tabs: [],
  46. }
  47. },
  48. created() {
  49. this.tabs = this.$children
  50. },
  51. methods: {
  52. selectTab({ id }) {
  53. this.tabs.forEach((tab) => {
  54. tab.active = tab.id === id
  55. })
  56. this.$emit("tab-changed", id)
  57. },
  58. },
  59. })
  60. </script>
  61. <style scoped lang="scss">
  62. .tabs {
  63. @apply flex;
  64. @apply whitespace-nowrap;
  65. @apply overflow-auto;
  66. // &::after {
  67. // @apply absolute;
  68. // @apply inset-x-0;
  69. // @apply bottom-0;
  70. // @apply bg-dividerLight;
  71. // @apply z-1;
  72. // @apply h-0.5;
  73. // content: "";
  74. // }
  75. .tab {
  76. @apply relative;
  77. @apply flex;
  78. @apply items-center;
  79. @apply justify-center;
  80. @apply px-4 py-2;
  81. @apply text-secondary;
  82. @apply font-semibold;
  83. @apply cursor-pointer;
  84. @apply hover:text-secondaryDark;
  85. @apply focus:outline-none;
  86. @apply focus-visible:text-secondaryDark;
  87. .tab-info {
  88. @apply inline-flex;
  89. @apply items-center;
  90. @apply justify-center;
  91. @apply w-5;
  92. @apply h-4;
  93. @apply ml-2;
  94. @apply text-8px;
  95. @apply border border-divider;
  96. @apply rounded;
  97. @apply text-secondaryLight;
  98. }
  99. &::after {
  100. @apply absolute;
  101. @apply left-4;
  102. @apply right-4;
  103. @apply bottom-0;
  104. @apply bg-transparent;
  105. @apply z-2;
  106. @apply h-0.5;
  107. content: "";
  108. }
  109. .material-icons {
  110. @apply mr-4;
  111. }
  112. &:focus::after {
  113. @apply bg-divider;
  114. }
  115. &.active {
  116. @apply text-secondaryDark;
  117. .tab-info {
  118. @apply text-secondary;
  119. @apply border-dividerDark;
  120. }
  121. &::after {
  122. @apply bg-accent;
  123. }
  124. }
  125. }
  126. }
  127. </style>