logo

pleroma-fe

My custom branche(s) on git.pleroma.social/pleroma/pleroma-fe git clone https://hacktivis.me/git/pleroma-fe.git

tab_switcher.jsx (4696B)


  1. // eslint-disable-next-line no-unused
  2. import { h, Fragment } from 'vue'
  3. import { mapState } from 'vuex'
  4. import { FontAwesomeIcon as FAIcon } from '@fortawesome/vue-fontawesome'
  5. import './tab_switcher.scss'
  6. const findFirstUsable = (slots) => slots.findIndex(_ => _.props)
  7. export default {
  8. name: 'TabSwitcher',
  9. props: {
  10. renderOnlyFocused: {
  11. required: false,
  12. type: Boolean,
  13. default: false
  14. },
  15. onSwitch: {
  16. required: false,
  17. type: Function,
  18. default: undefined
  19. },
  20. activeTab: {
  21. required: false,
  22. type: String,
  23. default: undefined
  24. },
  25. scrollableTabs: {
  26. required: false,
  27. type: Boolean,
  28. default: false
  29. },
  30. sideTabBar: {
  31. required: false,
  32. type: Boolean,
  33. default: false
  34. },
  35. bodyScrollLock: {
  36. required: false,
  37. type: Boolean,
  38. default: false
  39. }
  40. },
  41. data () {
  42. return {
  43. active: findFirstUsable(this.slots())
  44. }
  45. },
  46. computed: {
  47. activeIndex () {
  48. // In case of controlled component
  49. if (this.activeTab) {
  50. return this.slots().findIndex(slot => slot && slot.props && this.activeTab === slot.props.key)
  51. } else {
  52. return this.active
  53. }
  54. },
  55. isActive () {
  56. return tabName => {
  57. const isWanted = slot => slot.props && slot.props['data-tab-name'] === tabName
  58. return this.$slots.default().findIndex(isWanted) === this.activeIndex
  59. }
  60. }
  61. },
  62. beforeUpdate () {
  63. const currentSlot = this.slots()[this.active]
  64. if (!currentSlot.props) {
  65. this.active = findFirstUsable(this.slots())
  66. }
  67. },
  68. methods: {
  69. clickTab (index) {
  70. return (e) => {
  71. e.preventDefault()
  72. this.setTab(index)
  73. }
  74. },
  75. // DO NOT put it to computed, it doesn't work (caching?)
  76. slots () {
  77. if (this.$slots.default()[0].type === Fragment) {
  78. return this.$slots.default()[0].children
  79. }
  80. return this.$slots.default()
  81. },
  82. setTab (index) {
  83. if (typeof this.onSwitch === 'function') {
  84. this.onSwitch.call(null, this.slots()[index].key)
  85. }
  86. this.active = index
  87. if (this.scrollableTabs) {
  88. this.$refs.contents.scrollTop = 0
  89. }
  90. }
  91. },
  92. render () {
  93. const tabs = this.slots()
  94. .map((slot, index) => {
  95. const props = slot.props
  96. if (!props) return
  97. const classesTab = ['tab']
  98. const classesWrapper = ['tab-wrapper']
  99. if (this.activeIndex === index) {
  100. classesTab.push('active')
  101. classesWrapper.push('active')
  102. }
  103. if (props.image) {
  104. return (
  105. <div class={classesWrapper.join(' ')}>
  106. <button
  107. disabled={props.disabled}
  108. onClick={this.clickTab(index)}
  109. class={classesTab.join(' ')}
  110. type="button"
  111. role="tab"
  112. >
  113. <img src={props.image} title={props['image-tooltip']}/>
  114. {props.label ? '' : props.label}
  115. </button>
  116. </div>
  117. )
  118. }
  119. return (
  120. <div class={classesWrapper.join(' ')}>
  121. <button
  122. disabled={props.disabled}
  123. onClick={this.clickTab(index)}
  124. class={classesTab.join(' ')}
  125. type="button"
  126. role="tab"
  127. >
  128. {!props.icon ? '' : (<FAIcon class="tab-icon" size="2x" fixed-width icon={props.icon}/>)}
  129. <span class="text">
  130. {props.label}
  131. </span>
  132. </button>
  133. </div>
  134. )
  135. })
  136. const contents = this.slots().map((slot, index) => {
  137. const props = slot.props
  138. if (!props) return
  139. const active = this.activeIndex === index
  140. const classes = [ active ? 'active' : 'hidden' ]
  141. if (props.fullHeight) {
  142. classes.push('full-height')
  143. }
  144. const renderSlot = (!this.renderOnlyFocused || active)
  145. ? slot
  146. : ''
  147. return (
  148. <div class={classes}>
  149. {
  150. this.sideTabBar
  151. ? <h1 class="mobile-label">{props.label}</h1>
  152. : ''
  153. }
  154. {renderSlot}
  155. </div>
  156. )
  157. })
  158. return (
  159. <div class={'tab-switcher ' + (this.sideTabBar ? 'side-tabs' : 'top-tabs')}>
  160. <div
  161. class="tabs"
  162. role="tablist"
  163. >
  164. {tabs}
  165. </div>
  166. <div
  167. ref="contents"
  168. role="tabpanel"
  169. class={'contents' + (this.scrollableTabs ? ' scrollable-tabs' : '')}
  170. v-body-scroll-lock={this.bodyScrollLock}
  171. >
  172. {contents}
  173. </div>
  174. </div>
  175. )
  176. }
  177. }