123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329 |
- <template lang="pug">
- v-dialog(
- v-model='isShown'
- max-width='850px'
- overlay-color='blue darken-4'
- overlay-opacity='.7'
- )
- v-card.page-selector
- .dialog-header.is-blue
- v-icon.mr-3(color='white') mdi-page-next-outline
- .body-1(v-if='mode === `create`') {{$t('common:pageSelector.createTitle')}}
- .body-1(v-else-if='mode === `move`') {{$t('common:pageSelector.moveTitle')}}
- .body-1(v-else-if='mode === `select`') {{$t('common:pageSelector.selectTitle')}}
- v-spacer
- v-progress-circular(
- indeterminate
- color='white'
- :size='20'
- :width='2'
- v-show='searchLoading'
- )
- .d-flex
- v-flex.grey(xs5, :class='$vuetify.theme.dark ? `darken-4` : `lighten-3`')
- v-toolbar(color='grey darken-3', dark, dense, flat)
- .body-2 {{$t('common:pageSelector.virtualFolders')}}
- v-spacer
- v-btn(icon, tile, href='https://docs.requarks.io/guide/pages#folders', target='_blank')
- v-icon mdi-help-box
- div(style='height:400px;')
- vue-scroll(:ops='scrollStyle')
- v-treeview(
- :key='`pageTree-` + treeViewCacheId'
- :active.sync='currentNode'
- :open.sync='openNodes'
- :items='tree'
- :load-children='fetchFolders'
- dense
- expand-icon='mdi-menu-down-outline'
- item-id='path'
- item-text='title'
- activatable
- hoverable
- )
- template(slot='prepend', slot-scope='{ item, open, leaf }')
- v-icon mdi-{{ open ? 'folder-open' : 'folder' }}
- v-flex(xs7)
- v-toolbar(color='blue darken-2', dark, dense, flat)
- .body-2 {{$t('common:pageSelector.pages')}}
- //- v-spacer
- //- v-btn(icon, tile, disabled): v-icon mdi-content-save-move-outline
- //- v-btn(icon, tile, disabled): v-icon mdi-trash-can-outline
- div(v-if='currentPages.length > 0', style='height:400px;')
- vue-scroll(:ops='scrollStyle')
- v-list.py-0(dense)
- v-list-item-group(
- v-model='currentPage'
- color='primary'
- )
- template(v-for='(page, idx) of currentPages')
- v-list-item(:key='`page-` + page.id', :value='page')
- v-list-item-icon: v-icon mdi-text-box
- v-list-item-title {{page.title}}
- v-divider(v-if='idx < pages.length - 1')
- v-alert.animated.fadeIn(
- v-else
- text
- color='orange'
- prominent
- icon='mdi-alert'
- )
- .body-2 {{$t('common:pageSelector.folderEmptyWarning')}}
- v-card-actions.grey.pa-2(:class='$vuetify.theme.dark ? `darken-2` : `lighten-1`', v-if='!mustExist')
- v-select(
- solo
- dark
- flat
- background-color='grey darken-3-d2'
- hide-details
- single-line
- :items='namespaces'
- style='flex: 0 0 100px; border-radius: 4px 0 0 4px;'
- v-model='currentLocale'
- )
- v-text-field(
- ref='pathIpt'
- solo
- hide-details
- prefix='/'
- v-model='currentPath'
- flat
- clearable
- style='border-radius: 0 4px 4px 0;'
- )
- v-card-chin
- v-spacer
- v-btn(text, @click='close') {{$t('common:actions.cancel')}}
- v-btn.px-4(color='primary', @click='open', :disabled='!isValidPath')
- v-icon(left) mdi-check
- span {{$t('common:actions.select')}}
- </template>
- <script>
- import _ from 'lodash'
- import gql from 'graphql-tag'
- const localeSegmentRegex = /^[A-Z]{2}(-[A-Z]{2})?$/i
- /* global siteLangs, siteConfig */
- export default {
- props: {
- value: {
- type: Boolean,
- default: false
- },
- path: {
- type: String,
- default: 'new-page'
- },
- locale: {
- type: String,
- default: 'en'
- },
- mode: {
- type: String,
- default: 'create'
- },
- openHandler: {
- type: Function,
- default: () => {}
- },
- mustExist: {
- type: Boolean,
- default: false
- }
- },
- data() {
- return {
- treeViewCacheId: 0,
- searchLoading: false,
- currentLocale: siteConfig.lang,
- currentFolderPath: '',
- currentPath: 'new-page',
- currentPage: null,
- currentNode: [0],
- openNodes: [0],
- tree: [
- {
- id: 0,
- title: '/ (root)',
- children: []
- }
- ],
- pages: [],
- all: [],
- namespaces: siteLangs.length ? siteLangs.map(ns => ns.code) : [siteConfig.lang],
- scrollStyle: {
- vuescroll: {},
- scrollPanel: {
- initialScrollX: 0.01, // fix scrollbar not disappearing on load
- scrollingX: false,
- speed: 50
- },
- rail: {
- gutterOfEnds: '2px'
- },
- bar: {
- onlyShowBarOnScroll: false,
- background: '#999',
- hoverStyle: {
- background: '#64B5F6'
- }
- }
- }
- }
- },
- computed: {
- isShown: {
- get() { return this.value },
- set(val) { this.$emit('input', val) }
- },
- currentPages () {
- return _.sortBy(_.filter(this.pages, ['parent', _.head(this.currentNode) || 0]), ['title', 'path'])
- },
- isValidPath () {
- if (!this.currentPath) {
- return false
- }
- if (this.mustExist && !this.currentPage) {
- return false
- }
- const firstSection = _.head(this.currentPath.split('/'))
- if (firstSection.length <= 1) {
- return false
- } else if (localeSegmentRegex.test(firstSection)) {
- return false
- } else if (
- _.some(['login', 'logout', 'register', 'verify', 'favicons', 'fonts', 'img', 'js', 'svg'], p => {
- return p === firstSection
- })) {
- return false
- } else {
- return true
- }
- }
- },
- watch: {
- isShown (newValue, oldValue) {
- if (newValue && !oldValue) {
- this.currentPath = this.path
- this.currentLocale = this.locale
- _.delay(() => {
- this.$refs.pathIpt.focus()
- })
- }
- },
- currentNode (newValue, oldValue) {
- if (newValue.length < 1) { // force a selection
- this.$nextTick(() => {
- this.currentNode = oldValue
- })
- } else {
- const current = _.find(this.all, ['id', newValue[0]])
- if (this.openNodes.indexOf(newValue[0]) < 0) { // auto open and load children
- if (current) {
- if (this.openNodes.indexOf(current.parent) < 0) {
- this.$nextTick(() => {
- this.openNodes.push(current.parent)
- })
- }
- }
- this.$nextTick(() => {
- this.openNodes.push(newValue[0])
- })
- }
- this.currentPath = _.compact([_.get(current, 'path', ''), _.last(this.currentPath.split('/'))]).join('/')
- }
- },
- currentPage (newValue, oldValue) {
- if (!_.isEmpty(newValue)) {
- this.currentPath = newValue.path
- }
- },
- currentLocale (newValue, oldValue) {
- this.$nextTick(() => {
- this.tree = [
- {
- id: 0,
- title: '/ (root)',
- children: []
- }
- ]
- this.currentNode = [0]
- this.openNodes = [0]
- this.pages = []
- this.all = []
- this.treeViewCacheId += 1
- })
- }
- },
- methods: {
- close() {
- this.isShown = false
- },
- open() {
- const exit = this.openHandler({
- locale: this.currentLocale,
- path: this.currentPath,
- id: (this.mustExist && this.currentPage) ? this.currentPage.pageId : 0
- })
- if (exit !== false) {
- this.close()
- }
- },
- async fetchFolders (item) {
- this.searchLoading = true
- const resp = await this.$apollo.query({
- query: gql`
- query ($parent: Int!, $mode: PageTreeMode!, $locale: String!) {
- pages {
- tree(parent: $parent, mode: $mode, locale: $locale) {
- id
- path
- title
- isFolder
- pageId
- parent
- }
- }
- }
- `,
- fetchPolicy: 'network-only',
- variables: {
- parent: item.id,
- mode: 'ALL',
- locale: this.currentLocale
- }
- })
- const items = _.get(resp, 'data.pages.tree', [])
- const itemFolders = _.filter(items, ['isFolder', true]).map(f => ({...f, children: []}))
- const itemPages = _.filter(items, i => i.pageId > 0)
- if (itemFolders.length > 0) {
- item.children = itemFolders
- } else {
- item.children = undefined
- }
- this.pages = _.unionBy(this.pages, itemPages, 'id')
- this.all = _.unionBy(this.all, items, 'id')
- this.searchLoading = false
- }
- }
- }
- </script>
- <style lang='scss'>
- .page-selector {
- .v-treeview-node__label {
- font-size: 13px;
- }
- .v-treeview-node__content {
- cursor: pointer;
- }
- }
- </style>
|