nav-sidebar.vue 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. <template lang="pug">
  2. div
  3. .pa-3.d-flex(v-if='navMode === `MIXED`', :class='$vuetify.theme.dark ? `grey darken-5` : `blue darken-3`')
  4. v-btn(depressed, :color='$vuetify.theme.dark ? `grey darken-4` : `blue darken-2`', style='min-width:0;', @click='goHome')
  5. v-icon(size='20') mdi-home
  6. v-btn.ml-3(v-if='currentMode === `custom`', depressed, :color='$vuetify.theme.dark ? `grey darken-4` : `blue darken-2`', style='flex: 1 1 100%;', @click='switchMode(`browse`)')
  7. v-icon(left) mdi-file-tree
  8. .body-2.text-none {{$t('common:sidebar.browse')}}
  9. v-btn.ml-3(v-else-if='currentMode === `browse`', depressed, :color='$vuetify.theme.dark ? `grey darken-4` : `blue darken-2`', style='flex: 1 1 100%;', @click='switchMode(`custom`)')
  10. v-icon(left) mdi-navigation
  11. .body-2.text-none {{$t('common:sidebar.mainMenu')}}
  12. v-divider
  13. //-> Custom Navigation
  14. v-list.py-2(v-if='currentMode === `custom`', dense, :class='color', :dark='dark')
  15. template(v-for='item of items')
  16. v-list-item(
  17. v-if='item.kind === `link`'
  18. :href='item.target'
  19. )
  20. v-list-item-avatar(size='24', tile)
  21. v-icon {{ item.icon }}
  22. v-list-item-title {{ item.label }}
  23. v-divider.my-2(v-else-if='item.kind === `divider`')
  24. v-subheader.pl-4(v-else-if='item.kind === `header`') {{ item.label }}
  25. //-> Browse
  26. v-list.py-2(v-else-if='currentMode === `browse`', dense, :class='color', :dark='dark')
  27. template(v-if='currentParent.id > 0')
  28. v-list-item(v-for='(item, idx) of parents', :key='`parent-` + item.id', @click='fetchBrowseItems(item)', style='min-height: 30px;')
  29. v-list-item-avatar(size='18', :style='`padding-left: ` + (idx * 8) + `px; width: auto; margin: 0 5px 0 0;`')
  30. v-icon(small) mdi-folder-open
  31. v-list-item-title {{ item.title }}
  32. v-divider.mt-2
  33. v-list-item.mt-2(v-if='currentParent.pageId > 0', :href='`/` + currentParent.path', :key='`directorypage-` + currentParent.id', :input-value='path === currentParent.path')
  34. v-list-item-avatar(size='24')
  35. v-icon mdi-text-box
  36. v-list-item-title {{ currentParent.title }}
  37. v-subheader.pl-4 {{$t('common:sidebar.currentDirectory')}}
  38. template(v-for='item of currentItems')
  39. v-list-item(v-if='item.isFolder', :key='`childfolder-` + item.id', @click='fetchBrowseItems(item)')
  40. v-list-item-avatar(size='24')
  41. v-icon mdi-folder
  42. v-list-item-title {{ item.title }}
  43. v-list-item(v-else, :href='`/` + item.path', :key='`childpage-` + item.id', :input-value='path === item.path')
  44. v-list-item-avatar(size='24')
  45. v-icon mdi-text-box
  46. v-list-item-title {{ item.title }}
  47. </template>
  48. <script>
  49. import _ from 'lodash'
  50. import gql from 'graphql-tag'
  51. import { get } from 'vuex-pathify'
  52. /* global siteLangs */
  53. export default {
  54. props: {
  55. color: {
  56. type: String,
  57. default: 'primary'
  58. },
  59. dark: {
  60. type: Boolean,
  61. default: true
  62. },
  63. items: {
  64. type: Array,
  65. default: () => []
  66. },
  67. navMode: {
  68. type: String,
  69. default: 'MIXED'
  70. }
  71. },
  72. data() {
  73. return {
  74. currentMode: 'custom',
  75. currentItems: [],
  76. currentParent: {
  77. id: 0,
  78. title: '/ (root)'
  79. },
  80. parents: [],
  81. loadedCache: []
  82. }
  83. },
  84. computed: {
  85. path: get('page/path'),
  86. locale: get('page/locale')
  87. },
  88. methods: {
  89. switchMode (mode) {
  90. this.currentMode = mode
  91. window.localStorage.setItem('navPref', mode)
  92. if (mode === `browse` && this.loadedCache.length < 1) {
  93. this.loadFromCurrentPath()
  94. }
  95. },
  96. async fetchBrowseItems (item) {
  97. this.$store.commit(`loadingStart`, 'browse-load')
  98. if (!item) {
  99. item = this.currentParent
  100. }
  101. if (this.loadedCache.indexOf(item.id) < 0) {
  102. this.currentItems = []
  103. }
  104. if (item.id === 0) {
  105. this.parents = []
  106. } else {
  107. const flushRightIndex = _.findIndex(this.parents, ['id', item.id])
  108. if (flushRightIndex >= 0) {
  109. this.parents = _.take(this.parents, flushRightIndex)
  110. }
  111. if (this.parents.length < 1) {
  112. this.parents.push(this.currentParent)
  113. }
  114. this.parents.push(item)
  115. }
  116. this.currentParent = item
  117. const resp = await this.$apollo.query({
  118. query: gql`
  119. query ($parent: Int, $locale: String!) {
  120. pages {
  121. tree(parent: $parent, mode: ALL, locale: $locale) {
  122. id
  123. path
  124. title
  125. isFolder
  126. pageId
  127. parent
  128. }
  129. }
  130. }
  131. `,
  132. fetchPolicy: 'cache-first',
  133. variables: {
  134. parent: item.id,
  135. locale: this.locale
  136. }
  137. })
  138. this.loadedCache = _.union(this.loadedCache, [item.id])
  139. this.currentItems = _.get(resp, 'data.pages.tree', [])
  140. this.$store.commit(`loadingStop`, 'browse-load')
  141. },
  142. async loadFromCurrentPath() {
  143. this.$store.commit(`loadingStart`, 'browse-load')
  144. const resp = await this.$apollo.query({
  145. query: gql`
  146. query ($path: String, $locale: String!) {
  147. pages {
  148. tree(path: $path, mode: ALL, locale: $locale, includeAncestors: true) {
  149. id
  150. path
  151. title
  152. isFolder
  153. pageId
  154. parent
  155. }
  156. }
  157. }
  158. `,
  159. fetchPolicy: 'cache-first',
  160. variables: {
  161. path: this.path,
  162. locale: this.locale
  163. }
  164. })
  165. const items = _.get(resp, 'data.pages.tree', [])
  166. const curPage = _.find(items, ['pageId', this.$store.get('page/id')])
  167. if (!curPage) {
  168. console.warn('Could not find current page in page tree listing!')
  169. return
  170. }
  171. let curParentId = curPage.parent
  172. let invertedAncestors = []
  173. while (curParentId) {
  174. const curParent = _.find(items, ['id', curParentId])
  175. if (!curParent) {
  176. break
  177. }
  178. invertedAncestors.push(curParent)
  179. curParentId = curParent.parent
  180. }
  181. this.parents = [this.currentParent, ...invertedAncestors.reverse()]
  182. this.currentParent = _.last(this.parents)
  183. this.loadedCache = [curPage.parent]
  184. this.currentItems = _.filter(items, ['parent', curPage.parent])
  185. this.$store.commit(`loadingStop`, 'browse-load')
  186. },
  187. goHome () {
  188. window.location.assign(siteLangs.length > 0 ? `/${this.locale}/home` : '/')
  189. }
  190. },
  191. mounted () {
  192. this.currentParent.title = `/ ${this.$t('common:sidebar.root')}`
  193. if (this.navMode === 'TREE') {
  194. this.currentMode = 'browse'
  195. } else if (this.navMode === 'STATIC') {
  196. this.currentMode = 'custom'
  197. } else {
  198. this.currentMode = window.localStorage.getItem('navPref') || 'custom'
  199. }
  200. if (this.currentMode === 'browse') {
  201. this.loadFromCurrentPath()
  202. }
  203. }
  204. }
  205. </script>