page-selector.vue 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. <template lang="pug">
  2. v-dialog(v-model='isShown', max-width='850px')
  3. v-card.page-selector
  4. .dialog-header.is-blue
  5. v-icon.mr-3(color='white') mdi-page-next-outline
  6. .body-1 Select Page Location
  7. v-spacer
  8. v-progress-circular(
  9. indeterminate
  10. color='white'
  11. :size='20'
  12. :width='2'
  13. v-show='searchLoading'
  14. )
  15. .d-flex(style='min-height:400px;')
  16. v-flex.grey(xs4, :class='darkMode ? `darken-4` : `lighten-3`')
  17. v-toolbar(color='grey darken-3', dark, dense, flat)
  18. .body-2 Virtual Folders
  19. v-spacer
  20. v-btn(icon, tile)
  21. v-icon mdi-help-box
  22. v-treeview(
  23. :active.sync='currentNode'
  24. :open.sync='openNodes'
  25. :items='tree'
  26. :load-children='fetchFolders'
  27. dense
  28. expand-icon='mdi-menu-down-outline'
  29. item-id='path'
  30. item-text='title'
  31. activatable
  32. hoverable
  33. )
  34. template(slot='prepend', slot-scope='{ item, open, leaf }')
  35. v-icon mdi-{{ open ? 'folder-open' : 'folder' }}
  36. v-flex(xs8)
  37. v-toolbar(color='blue darken-2', dark, dense, flat)
  38. .body-2 Pages
  39. v-spacer
  40. v-btn(icon, tile): v-icon mdi-content-save-move-outline
  41. v-btn(icon, tile): v-icon mdi-trash-can-outline
  42. v-list.py-0(dense, v-if='currentPages.length > 0')
  43. v-list-item-group(
  44. v-model='currentPage'
  45. color='primary'
  46. )
  47. template(v-for='(page, idx) of currentPages')
  48. v-list-item(:key='page.id', :value='page.path')
  49. v-list-item-icon: v-icon mdi-file-document-box
  50. v-list-item-title {{page.title}}
  51. v-divider(v-if='idx < pages.length - 1')
  52. v-alert.animated.fadeIn(
  53. v-else
  54. text
  55. color='orange'
  56. prominent
  57. icon='mdi-alert'
  58. )
  59. .body-2 This folder is empty.
  60. v-card-actions.grey.pa-2(:class='darkMode ? `darken-2` : `lighten-1`')
  61. v-select(
  62. solo
  63. dark
  64. flat
  65. background-color='grey darken-3-d2'
  66. hide-details
  67. single-line
  68. :items='namespaces'
  69. style='flex: 0 0 100px; border-radius: 4px 0 0 4px;'
  70. v-model='currentLocale'
  71. )
  72. v-text-field(
  73. ref='pathIpt'
  74. solo
  75. hide-details
  76. prefix='/'
  77. v-model='currentPath'
  78. flat
  79. clearable
  80. style='border-radius: 0 4px 4px 0;'
  81. )
  82. v-card-chin
  83. v-spacer
  84. v-btn(text, @click='close') Cancel
  85. v-btn.px-4(color='primary', @click='open', :disabled='!isValidPath')
  86. v-icon(left) mdi-check
  87. span Select
  88. </template>
  89. <script>
  90. import _ from 'lodash'
  91. import { get } from 'vuex-pathify'
  92. import pageTreeQuery from 'gql/common/common-pages-query-tree.gql'
  93. /* global siteLangs, siteConfig */
  94. export default {
  95. props: {
  96. value: {
  97. type: Boolean,
  98. default: false
  99. },
  100. path: {
  101. type: String,
  102. default: 'new-page'
  103. },
  104. locale: {
  105. type: String,
  106. default: 'en'
  107. },
  108. mode: {
  109. type: String,
  110. default: 'create'
  111. },
  112. openHandler: {
  113. type: Function,
  114. default: () => {}
  115. }
  116. },
  117. data() {
  118. return {
  119. searchLoading: false,
  120. currentLocale: siteConfig.lang,
  121. currentPath: 'new-page',
  122. currentPage: null,
  123. currentNode: [0],
  124. openNodes: [0],
  125. tree: [{
  126. id: 0,
  127. title: '/ (root',
  128. children: []
  129. }],
  130. pages: [],
  131. namespaces: siteLangs.length ? siteLangs.map(ns => ns.code) : [siteConfig.lang]
  132. }
  133. },
  134. computed: {
  135. darkMode: get('site/dark'),
  136. isShown: {
  137. get() { return this.value },
  138. set(val) { this.$emit('input', val) }
  139. },
  140. currentPages () {
  141. return _.filter(this.pages, ['parent', _.head(this.currentNode) || 0])
  142. },
  143. isValidPath () {
  144. return this.currentPath && this.currentPath.length > 2
  145. }
  146. },
  147. watch: {
  148. isShown (newValue, oldValue) {
  149. if (newValue && !oldValue) {
  150. this.currentPath = this.path
  151. this.currentLocale = this.locale
  152. _.delay(() => {
  153. this.$refs.pathIpt.focus()
  154. })
  155. }
  156. },
  157. currentNode (newValue, oldValue) {
  158. if (newValue.length < 1) { // force a selection
  159. this.$nextTick(() => {
  160. this.currentNode = oldValue
  161. })
  162. }
  163. },
  164. currentPage (newValue, oldValue) {
  165. if (!_.isEmpty(newValue)) {
  166. this.currentPath = newValue
  167. }
  168. }
  169. },
  170. methods: {
  171. close() {
  172. this.isShown = false
  173. },
  174. open() {
  175. const exit = this.openHandler({
  176. locale: this.currentLocale,
  177. path: this.currentPath
  178. })
  179. if (exit !== false) {
  180. this.close()
  181. }
  182. },
  183. async fetchFolders (item) {
  184. this.searchLoading = true
  185. const resp = await this.$apollo.query({
  186. query: pageTreeQuery,
  187. fetchPolicy: 'network-only',
  188. variables: {
  189. parent: item.id,
  190. mode: 'ALL',
  191. locale: this.currentLocale
  192. }
  193. })
  194. const items = _.get(resp, 'data.pages.tree', [])
  195. const itemFolders = _.filter(items, ['isFolder', true]).map(f => ({...f, children: []}))
  196. const itemPages = _.filter(items, ['isFolder', false])
  197. if (itemFolders.length > 0) {
  198. item.children = itemFolders
  199. } else {
  200. item.children = undefined
  201. }
  202. this.pages.push(...itemPages)
  203. this.searchLoading = false
  204. }
  205. }
  206. }
  207. </script>
  208. <style lang='scss'>
  209. .page-selector {
  210. .v-treeview-node__label {
  211. font-size: 13px;
  212. }
  213. }
  214. </style>