refactor: Migrate to Vue components
This commit is contained in:
@@ -1,14 +1,64 @@
|
||||
'use strict'
|
||||
|
||||
/* global alertsData */
|
||||
/* global alertsData, siteLang */
|
||||
/* eslint-disable no-new */
|
||||
|
||||
import $ from 'jquery'
|
||||
import _ from 'lodash'
|
||||
import Vue from 'vue'
|
||||
import Vuex from 'vuex'
|
||||
import io from 'socket.io-client'
|
||||
import i18next from 'i18next'
|
||||
import i18nextXHR from 'i18next-xhr-backend'
|
||||
import VueI18Next from '@panter/vue-i18next'
|
||||
import Alerts from './components/alerts.js'
|
||||
import 'jquery-smooth-scroll'
|
||||
import 'jquery-sticky'
|
||||
|
||||
// ====================================
|
||||
// Load Vue Components
|
||||
// ====================================
|
||||
|
||||
import anchorComponent from './components/anchor.vue'
|
||||
import colorPickerComponent from './components/color-picker.vue'
|
||||
import loadingSpinnerComponent from './components/loading-spinner.vue'
|
||||
import searchComponent from './components/search.vue'
|
||||
|
||||
import adminProfileComponent from './pages/admin-profile.component.js'
|
||||
import adminSettingsComponent from './pages/admin-settings.component.js'
|
||||
|
||||
// ====================================
|
||||
// Initialize i18next
|
||||
// ====================================
|
||||
|
||||
Vue.use(VueI18Next)
|
||||
|
||||
i18next
|
||||
.use(i18nextXHR)
|
||||
.init({
|
||||
backend: {
|
||||
loadPath: '/js/i18n/{{lng}}.json'
|
||||
},
|
||||
lng: siteLang,
|
||||
fallbackLng: siteLang
|
||||
})
|
||||
|
||||
// ====================================
|
||||
// Initialize Vuex
|
||||
// ====================================
|
||||
|
||||
Vue.use(Vuex)
|
||||
|
||||
const store = new Vuex.Store({
|
||||
state: {
|
||||
loading: false
|
||||
},
|
||||
mutations: {
|
||||
startLoading: state => { state.loading = true },
|
||||
stopLoading: state => { state.loading = false }
|
||||
}
|
||||
})
|
||||
|
||||
$(() => {
|
||||
// ====================================
|
||||
// Scroll
|
||||
@@ -27,28 +77,47 @@ $(() => {
|
||||
// ====================================
|
||||
|
||||
$(window).bind('beforeunload', () => {
|
||||
$('#notifload').addClass('active')
|
||||
store.commit('startLoading')
|
||||
})
|
||||
$(document).ajaxSend(() => {
|
||||
$('#notifload').addClass('active')
|
||||
store.commit('startLoading')
|
||||
}).ajaxComplete(() => {
|
||||
$('#notifload').removeClass('active')
|
||||
store.commit('stopLoading')
|
||||
})
|
||||
|
||||
var alerts = new Alerts()
|
||||
var alerts = {}
|
||||
/*var alerts = new Alerts()
|
||||
if (alertsData) {
|
||||
_.forEach(alertsData, (alertRow) => {
|
||||
alerts.push(alertRow)
|
||||
})
|
||||
}
|
||||
}*/
|
||||
|
||||
// ====================================
|
||||
// Establish WebSocket connection
|
||||
// ====================================
|
||||
|
||||
var socket = io(window.location.origin)
|
||||
let socket = io(window.location.origin)
|
||||
window.socket = socket
|
||||
|
||||
require('./components/search.js')(socket)
|
||||
// ====================================
|
||||
// Bootstrap Vue
|
||||
// ====================================
|
||||
|
||||
const i18n = new VueI18Next(i18next)
|
||||
new Vue({
|
||||
components: {
|
||||
adminProfile: adminProfileComponent,
|
||||
adminSettings: adminSettingsComponent,
|
||||
anchor: anchorComponent,
|
||||
colorPicker: colorPickerComponent,
|
||||
loadingSpinner: loadingSpinnerComponent,
|
||||
search: searchComponent
|
||||
},
|
||||
store,
|
||||
i18n,
|
||||
el: '#root'
|
||||
})
|
||||
|
||||
// ====================================
|
||||
// Pages logic
|
||||
|
17
client/js/components/anchor.vue
Normal file
17
client/js/components/anchor.vue
Normal file
@@ -0,0 +1,17 @@
|
||||
<template>
|
||||
<div>
|
||||
<p>{{ msg }}</p>
|
||||
<input type="text" v-model="msg" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'anchor',
|
||||
data () {
|
||||
return {
|
||||
msg: 'Welcome to Your Vue.js App'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
15
client/js/components/color-picker.vue
Normal file
15
client/js/components/color-picker.vue
Normal file
@@ -0,0 +1,15 @@
|
||||
<template lang="pug">
|
||||
p.control
|
||||
input.input(type='text', placeholder='#F0F0F0', v-model='color')
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'color-picker',
|
||||
data () {
|
||||
return {
|
||||
color: '000000'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
@@ -1,17 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<p>{{ msg }}</p>
|
||||
<input type="text" v-model="msg" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'app',
|
||||
data () {
|
||||
return {
|
||||
msg: 'Welcome to Your Vue.js App'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
12
client/js/components/loading-spinner.vue
Normal file
12
client/js/components/loading-spinner.vue
Normal file
@@ -0,0 +1,12 @@
|
||||
<template lang="pug">
|
||||
i.nav-item#notifload(v-bind:class='{ "is-active": loading }')
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'loading-spinner',
|
||||
computed: mapState(['loading'])
|
||||
}
|
||||
</script>
|
@@ -1,87 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
import $ from 'jquery'
|
||||
import _ from 'lodash'
|
||||
import Vue from 'vue'
|
||||
|
||||
module.exports = (socket) => {
|
||||
if ($('#search-input').length) {
|
||||
$('#search-input').focus()
|
||||
|
||||
$('.searchresults').css('display', 'block')
|
||||
|
||||
var vueHeader = new Vue({
|
||||
el: '#header-container',
|
||||
data: {
|
||||
searchq: '',
|
||||
searchres: [],
|
||||
searchsuggest: [],
|
||||
searchload: 0,
|
||||
searchactive: false,
|
||||
searchmoveidx: 0,
|
||||
searchmovekey: '',
|
||||
searchmovearr: []
|
||||
},
|
||||
watch: {
|
||||
searchq: (val, oldVal) => {
|
||||
vueHeader.searchmoveidx = 0
|
||||
if (val.length >= 3) {
|
||||
vueHeader.searchactive = true
|
||||
vueHeader.searchload++
|
||||
socket.emit('search', { terms: val }, (data) => {
|
||||
vueHeader.searchres = data.match
|
||||
vueHeader.searchsuggest = data.suggest
|
||||
vueHeader.searchmovearr = _.concat([], vueHeader.searchres, vueHeader.searchsuggest)
|
||||
if (vueHeader.searchload > 0) { vueHeader.searchload-- }
|
||||
})
|
||||
} else {
|
||||
vueHeader.searchactive = false
|
||||
vueHeader.searchres = []
|
||||
vueHeader.searchsuggest = []
|
||||
vueHeader.searchmovearr = []
|
||||
vueHeader.searchload = 0
|
||||
}
|
||||
},
|
||||
searchmoveidx: (val, oldVal) => {
|
||||
if (val > 0) {
|
||||
vueHeader.searchmovekey = (vueHeader.searchmovearr[val - 1])
|
||||
? 'res.' + vueHeader.searchmovearr[val - 1].entryPath
|
||||
: 'sug.' + vueHeader.searchmovearr[val - 1]
|
||||
} else {
|
||||
vueHeader.searchmovekey = ''
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
useSuggestion: (sug) => {
|
||||
vueHeader.searchq = sug
|
||||
},
|
||||
closeSearch: () => {
|
||||
vueHeader.searchq = ''
|
||||
},
|
||||
moveSelectSearch: () => {
|
||||
if (vueHeader.searchmoveidx < 1) { return }
|
||||
let i = vueHeader.searchmoveidx - 1
|
||||
|
||||
if (vueHeader.searchmovearr[i]) {
|
||||
window.location.assign('/' + vueHeader.searchmovearr[i].entryPath)
|
||||
} else {
|
||||
vueHeader.searchq = vueHeader.searchmovearr[i]
|
||||
}
|
||||
},
|
||||
moveDownSearch: () => {
|
||||
if (vueHeader.searchmoveidx < vueHeader.searchmovearr.length) {
|
||||
vueHeader.searchmoveidx++
|
||||
}
|
||||
},
|
||||
moveUpSearch: () => {
|
||||
if (vueHeader.searchmoveidx > 0) {
|
||||
vueHeader.searchmoveidx--
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
$('main').on('click', vueHeader.closeSearch)
|
||||
}
|
||||
}
|
101
client/js/components/search.vue
Normal file
101
client/js/components/search.vue
Normal file
@@ -0,0 +1,101 @@
|
||||
<template lang="pug">
|
||||
.nav-item
|
||||
p.control(v-bind:class='{ "is-loading": searchload > 0 }')
|
||||
input.input#search-input(type='text', v-model='searchq', autofocus, @keyup.esc='closeSearch', @keyup.down='moveDownSearch', @keyup.up='moveUpSearch', @keyup.enter='moveSelectSearch', debounce='400', v-bind:placeholder='$t("search.placeholder")')
|
||||
|
||||
transition(name='searchresults')
|
||||
.searchresults(v-show='searchactive', v-cloak)
|
||||
p.searchresults-label {{ $t('search.results') }}
|
||||
ul.searchresults-list
|
||||
li(v-if='searchres.length === 0')
|
||||
a: em {{ $t('search.nomatch') }}
|
||||
li(v-for='sres in searchres', v-bind:class='{ "is-active": searchmovekey === "res." + sres.entryPath }')
|
||||
a(v-bind:href='"/" + sres.entryPath') {{ sres.title }}
|
||||
p.searchresults-label(v-if='searchsuggest.length > 0') {{ $t('search.didyoumean') }}
|
||||
ul.searchresults-list(v-if='searchsuggest.length > 0')
|
||||
li(v-for='sug in searchsuggest', v-bind:class='{ "is-active": searchmovekey === "sug." + sug }')
|
||||
a(v-on:click='useSuggestion(sug)') {{ sug }}
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as _ from 'lodash'
|
||||
import * as $ from 'jquery'
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
searchq: '',
|
||||
searchres: [],
|
||||
searchsuggest: [],
|
||||
searchload: 0,
|
||||
searchactive: false,
|
||||
searchmoveidx: 0,
|
||||
searchmovekey: '',
|
||||
searchmovearr: []
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
searchq: function (val, oldVal) {
|
||||
let self = this
|
||||
self.searchmoveidx = 0
|
||||
if (val.length >= 3) {
|
||||
self.searchactive = true
|
||||
self.searchload++
|
||||
socket.emit('search', { terms: val }, (data) => {
|
||||
self.searchres = data.match
|
||||
self.searchsuggest = data.suggest
|
||||
self.searchmovearr = _.concat([], self.searchres, self.searchsuggest)
|
||||
if (self.searchload > 0) { self.searchload-- }
|
||||
})
|
||||
} else {
|
||||
self.searchactive = false
|
||||
self.searchres = []
|
||||
self.searchsuggest = []
|
||||
self.searchmovearr = []
|
||||
self.searchload = 0
|
||||
}
|
||||
},
|
||||
searchmoveidx: function (val, oldVal) {
|
||||
if (val > 0) {
|
||||
this.searchmovekey = (this.searchmovearr[val - 1])
|
||||
? 'res.' + this.searchmovearr[val - 1].entryPath
|
||||
: 'sug.' + this.searchmovearr[val - 1]
|
||||
} else {
|
||||
this.searchmovekey = ''
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
useSuggestion: function (sug) {
|
||||
this.searchq = sug
|
||||
},
|
||||
closeSearch: function() {
|
||||
this.searchq = ''
|
||||
},
|
||||
moveSelectSearch: function () {
|
||||
if (this.searchmoveidx < 1) { return }
|
||||
let i = this.searchmoveidx - 1
|
||||
|
||||
if (this.searchmovearr[i]) {
|
||||
window.location.assign('/' + this.searchmovearr[i].entryPath)
|
||||
} else {
|
||||
this.searchq = this.searchmovearr[i]
|
||||
}
|
||||
},
|
||||
moveDownSearch: function () {
|
||||
if (this.searchmoveidx < this.searchmovearr.length) {
|
||||
this.searchmoveidx++
|
||||
}
|
||||
},
|
||||
moveUpSearch: function () {
|
||||
if (this.searchmoveidx > 0) {
|
||||
this.searchmoveidx--
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted: function () {
|
||||
let self = this
|
||||
$('main').on('click', self.closeSearch)
|
||||
}
|
||||
}
|
||||
</script>
|
54
client/js/modals/create.vue
Normal file
54
client/js/modals/create.vue
Normal file
@@ -0,0 +1,54 @@
|
||||
<template lang="pug">
|
||||
.modal(v-if='isShown')
|
||||
.modal-background
|
||||
.modal-container
|
||||
.modal-content
|
||||
header.is-light-blue Create New Document
|
||||
section
|
||||
label.label Enter the new document path:
|
||||
p.control.is-fullwidth(v-class='{ "is-loading": isLoading }')
|
||||
input.input(type='text', placeholder='page-name', v-model='entrypath', autofocus)
|
||||
span.help.is-danger(v-show='isInvalid') This document path is invalid!
|
||||
footer
|
||||
a.button.is-grey.is-outlined(v-on:click='hide') Discard
|
||||
a.button.is-light-blue(v-on:click='create') Create
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as _ from 'lodash'
|
||||
import { makeSafePath } from '../helpers/pages'
|
||||
|
||||
export default {
|
||||
name: 'modal-create',
|
||||
data () {
|
||||
return {
|
||||
entrypath: ''
|
||||
isInvalid: false,
|
||||
isLoading: false,
|
||||
isShown: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
show: function () {
|
||||
this.isInvalid = false
|
||||
this.shown = true
|
||||
},
|
||||
hide: function () {
|
||||
this.shown = false
|
||||
},
|
||||
create: function () {
|
||||
this.isInvalid = false
|
||||
let newDocPath = makeSafePath(this.entrypath)
|
||||
if (_.isEmpty(newDocPath)) {
|
||||
this.isInvalid = true
|
||||
} else {
|
||||
$('#txt-create-prompt').parent().addClass('is-loading')
|
||||
window.location.assign('/create/' + newDocPath)
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.entrypath = currentBasePath + '/new-page'
|
||||
}
|
||||
}
|
||||
</script>
|
30
client/js/pages/admin-profile.component.js
Normal file
30
client/js/pages/admin-profile.component.js
Normal file
@@ -0,0 +1,30 @@
|
||||
'use strict'
|
||||
|
||||
import * as $ from 'jquery'
|
||||
|
||||
export default {
|
||||
name: 'admin-profile',
|
||||
props: ['email', 'name', 'provider'],
|
||||
data() {
|
||||
return {
|
||||
password: '********',
|
||||
passwordVerify: '********'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
saveUser() {
|
||||
if (this.password !== this.passwordVerify) {
|
||||
//alerts.pushError('Error', "Passwords don't match!")
|
||||
return
|
||||
}
|
||||
$.post(window.location.href, {
|
||||
password: this.password,
|
||||
name: this.name
|
||||
}).done((resp) => {
|
||||
//alerts.pushSuccess('Saved successfully', 'Changes have been applied.')
|
||||
}).fail((jqXHR, txtStatus, resp) => {
|
||||
//alerts.pushError('Error', resp)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
52
client/js/pages/admin-settings.component.js
Normal file
52
client/js/pages/admin-settings.component.js
Normal file
@@ -0,0 +1,52 @@
|
||||
'use strict'
|
||||
|
||||
import * as $ from 'jquery'
|
||||
|
||||
export default {
|
||||
name: 'admin-settings',
|
||||
data() {
|
||||
return {
|
||||
upgradeModal: {
|
||||
state: false,
|
||||
step: 'confirm',
|
||||
mode: 'upgrade',
|
||||
error: 'Something went wrong.'
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
upgrade() {
|
||||
this.upgradeModal.mode = 'upgrade'
|
||||
this.upgradeModal.step = 'confirm'
|
||||
this.upgradeModal.state = true
|
||||
},
|
||||
reinstall() {
|
||||
this.upgradeModal.mode = 're-install'
|
||||
this.upgradeModal.step = 'confirm'
|
||||
this.upgradeModal.state = true
|
||||
},
|
||||
upgradeCancel() {
|
||||
this.upgradeModal.state = false
|
||||
},
|
||||
upgradeStart() {
|
||||
this.upgradeModal.step = 'running'
|
||||
$.post('/admin/settings/install', {
|
||||
mode: this.upgradeModal.mode
|
||||
}).done((resp) => {
|
||||
// todo
|
||||
}).fail((jqXHR, txtStatus, resp) => {
|
||||
this.upgradeModal.step = 'error'
|
||||
this.upgradeModal.error = jqXHR.responseText
|
||||
})
|
||||
},
|
||||
flushcache() {
|
||||
window.alert('Coming soon!')
|
||||
},
|
||||
resetaccounts() {
|
||||
window.alert('Coming soon!')
|
||||
},
|
||||
flushsessions() {
|
||||
window.alert('Coming soon!')
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,41 +1,13 @@
|
||||
'use strict'
|
||||
|
||||
/* global usrData, usrDataName */
|
||||
/* global usrData */
|
||||
|
||||
import $ from 'jquery'
|
||||
import _ from 'lodash'
|
||||
import Vue from 'vue'
|
||||
|
||||
module.exports = (alerts) => {
|
||||
if ($('#page-type-admin-profile').length) {
|
||||
let vueProfile = new Vue({
|
||||
el: '#page-type-admin-profile',
|
||||
data: {
|
||||
password: '********',
|
||||
passwordVerify: '********',
|
||||
name: ''
|
||||
},
|
||||
methods: {
|
||||
saveUser: (ev) => {
|
||||
if (vueProfile.password !== vueProfile.passwordVerify) {
|
||||
alerts.pushError('Error', "Passwords don't match!")
|
||||
return
|
||||
}
|
||||
$.post(window.location.href, {
|
||||
password: vueProfile.password,
|
||||
name: vueProfile.name
|
||||
}).done((resp) => {
|
||||
alerts.pushSuccess('Saved successfully', 'Changes have been applied.')
|
||||
}).fail((jqXHR, txtStatus, resp) => {
|
||||
alerts.pushError('Error', resp)
|
||||
})
|
||||
}
|
||||
},
|
||||
created: function () {
|
||||
this.name = usrDataName
|
||||
}
|
||||
})
|
||||
} else if ($('#page-type-admin-users').length) {
|
||||
if ($('#page-type-admin-users').length) {
|
||||
require('../modals/admin-users-create.js')(alerts)
|
||||
} else if ($('#page-type-admin-users-edit').length) {
|
||||
let vueEditUser = new Vue({
|
||||
@@ -98,52 +70,5 @@ module.exports = (alerts) => {
|
||||
}
|
||||
})
|
||||
require('../modals/admin-users-delete.js')(alerts)
|
||||
} else if ($('#page-type-admin-settings').length) {
|
||||
let vueSettings = new Vue({ // eslint-disable-line no-unused-vars
|
||||
el: '#page-type-admin-settings',
|
||||
data: {
|
||||
upgradeModal: {
|
||||
state: false,
|
||||
step: 'confirm',
|
||||
mode: 'upgrade',
|
||||
error: 'Something went wrong.'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
upgrade: (ev) => {
|
||||
vueSettings.upgradeModal.mode = 'upgrade'
|
||||
vueSettings.upgradeModal.step = 'confirm'
|
||||
vueSettings.upgradeModal.state = true
|
||||
},
|
||||
reinstall: (ev) => {
|
||||
vueSettings.upgradeModal.mode = 're-install'
|
||||
vueSettings.upgradeModal.step = 'confirm'
|
||||
vueSettings.upgradeModal.state = true
|
||||
},
|
||||
upgradeCancel: (ev) => {
|
||||
vueSettings.upgradeModal.state = false
|
||||
},
|
||||
upgradeStart: (ev) => {
|
||||
vueSettings.upgradeModal.step = 'running'
|
||||
$.post('/admin/settings/install', {
|
||||
mode: vueSettings.upgradeModal.mode
|
||||
}).done((resp) => {
|
||||
// todo
|
||||
}).fail((jqXHR, txtStatus, resp) => {
|
||||
vueSettings.upgradeModal.step = 'error'
|
||||
vueSettings.upgradeModal.error = jqXHR.responseText
|
||||
})
|
||||
},
|
||||
flushcache: (ev) => {
|
||||
window.alert('Coming soon!')
|
||||
},
|
||||
resetaccounts: (ev) => {
|
||||
window.alert('Coming soon!')
|
||||
},
|
||||
flushsessions: (ev) => {
|
||||
window.alert('Coming soon!')
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@@ -4,7 +4,7 @@
|
||||
|
||||
import $ from 'jquery'
|
||||
import MathJax from 'mathjax'
|
||||
import * as CopyPath from '../components/copy-path.vue'
|
||||
// import * as CopyPath from '../components/copy-path.vue'
|
||||
import Vue from 'vue'
|
||||
|
||||
module.exports = (alerts) => {
|
||||
@@ -13,10 +13,10 @@ module.exports = (alerts) => {
|
||||
|
||||
// Copy Path
|
||||
|
||||
new Vue({
|
||||
el: '.modal-copypath',
|
||||
render: h => h(CopyPath)
|
||||
})
|
||||
// new Vue({
|
||||
// el: '.modal-copypath',
|
||||
// render: h => h(CopyPath)
|
||||
// })
|
||||
|
||||
// MathJax Render
|
||||
|
||||
|
Reference in New Issue
Block a user