2017-06-28 10:45:41 +00:00
|
|
|
<template>
|
2017-07-03 18:26:29 +00:00
|
|
|
<div v-if="(req.numDirs + req.numFiles) == 0">
|
|
|
|
<h2 class="message">
|
|
|
|
<i class="material-icons">sentiment_dissatisfied</i>
|
|
|
|
<span>It feels lonely here...</span>
|
|
|
|
</h2>
|
2017-07-22 12:45:50 +00:00
|
|
|
<input style="display:none" type="file" id="upload-input" @change="uploadInput($event)" value="Upload" multiple>
|
2017-07-03 18:26:29 +00:00
|
|
|
</div>
|
|
|
|
<div v-else id="listing"
|
2017-07-03 10:04:14 +00:00
|
|
|
:class="req.display"
|
2017-06-30 16:49:05 +00:00
|
|
|
@drop="drop"
|
|
|
|
@dragenter="dragEnter"
|
|
|
|
@dragend="dragEnd">
|
|
|
|
<div>
|
|
|
|
<div class="item header">
|
|
|
|
<div></div>
|
2017-06-28 10:45:41 +00:00
|
|
|
<div>
|
2017-07-03 17:49:08 +00:00
|
|
|
<p :class="{ active: nameSorted }" class="name" @click="sort('name')">
|
|
|
|
<span>Name</span>
|
|
|
|
<i class="material-icons">{{ nameIcon }}</i>
|
2017-06-30 16:49:05 +00:00
|
|
|
</p>
|
2017-06-28 10:45:41 +00:00
|
|
|
|
2017-07-29 06:57:44 +00:00
|
|
|
<p :class="{ active: sizeSorted }" class="size" @click="sort('size')">
|
2017-07-03 17:49:08 +00:00
|
|
|
<span>Size</span>
|
|
|
|
<i class="material-icons">{{ sizeIcon }}</i>
|
2017-06-30 16:49:05 +00:00
|
|
|
</p>
|
2017-07-29 06:57:44 +00:00
|
|
|
<p :class="{ active: modifiedSorted }" class="modified" @click="sort('modified')">
|
|
|
|
<span>Last modified</span>
|
|
|
|
<i class="material-icons">{{ modifiedIcon }}</i>
|
|
|
|
</p>
|
2017-06-28 10:45:41 +00:00
|
|
|
</div>
|
2017-06-30 16:49:05 +00:00
|
|
|
</div>
|
|
|
|
</div>
|
2017-06-28 10:45:41 +00:00
|
|
|
|
2017-07-03 10:04:14 +00:00
|
|
|
<h2 v-if="req.numDirs > 0">Folders</h2>
|
|
|
|
<div v-if="req.numDirs > 0">
|
|
|
|
<item v-for="(item, index) in req.items"
|
2017-06-30 16:49:05 +00:00
|
|
|
v-if="item.isDir"
|
|
|
|
:key="base64(item.name)"
|
|
|
|
v-bind:index="index"
|
|
|
|
v-bind:name="item.name"
|
|
|
|
v-bind:isDir="item.isDir"
|
|
|
|
v-bind:url="item.url"
|
|
|
|
v-bind:modified="item.modified"
|
|
|
|
v-bind:type="item.type"
|
|
|
|
v-bind:size="item.size">
|
|
|
|
</item>
|
|
|
|
</div>
|
2017-06-30 15:04:01 +00:00
|
|
|
|
2017-07-03 10:04:14 +00:00
|
|
|
<h2 v-if="req.numFiles > 0">Files</h2>
|
|
|
|
<div v-if="req.numFiles > 0">
|
|
|
|
<item v-for="(item, index) in req.items"
|
2017-06-30 16:49:05 +00:00
|
|
|
v-if="!item.isDir"
|
|
|
|
:key="base64(item.name)"
|
|
|
|
v-bind:index="index"
|
|
|
|
v-bind:name="item.name"
|
|
|
|
v-bind:isDir="item.isDir"
|
|
|
|
v-bind:url="item.url"
|
|
|
|
v-bind:modified="item.modified"
|
|
|
|
v-bind:type="item.type"
|
|
|
|
v-bind:size="item.size">
|
|
|
|
</item>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<input style="display:none" type="file" id="upload-input" @change="uploadInput($event)" value="Upload" multiple>
|
|
|
|
|
|
|
|
<div v-show="$store.state.multiple" :class="{ active: $store.state.multiple }" id="multiple-selection">
|
|
|
|
<p>Multiple selection enabled</p>
|
|
|
|
<div @click="$store.commit('multiple', false)" tabindex="0" role="button" title="Clear" aria-label="Clear" class="action">
|
|
|
|
<i class="material-icons" title="Clear">clear</i>
|
|
|
|
</div>
|
2017-06-28 10:45:41 +00:00
|
|
|
</div>
|
2017-06-30 16:49:05 +00:00
|
|
|
</div>
|
2017-06-28 10:45:41 +00:00
|
|
|
</template>
|
|
|
|
|
|
|
|
<script>
|
2017-06-30 16:49:05 +00:00
|
|
|
import {mapState} from 'vuex'
|
2017-06-28 10:45:41 +00:00
|
|
|
import Item from './ListingItem'
|
2017-07-08 11:32:25 +00:00
|
|
|
import css from '@/utils/css'
|
2017-07-03 14:19:17 +00:00
|
|
|
import api from '@/utils/api'
|
2017-07-04 17:04:00 +00:00
|
|
|
import buttons from '@/utils/buttons'
|
2017-06-28 10:45:41 +00:00
|
|
|
|
|
|
|
export default {
|
2017-06-29 13:32:46 +00:00
|
|
|
name: 'listing',
|
2017-06-28 15:05:30 +00:00
|
|
|
components: { Item },
|
2017-07-03 17:49:08 +00:00
|
|
|
computed: {
|
2017-07-26 14:55:39 +00:00
|
|
|
...mapState(['req', 'selected']),
|
2017-07-03 17:49:08 +00:00
|
|
|
nameSorted () {
|
|
|
|
return (this.req.sort === 'name')
|
|
|
|
},
|
2017-07-29 06:57:44 +00:00
|
|
|
sizeSorted () {
|
|
|
|
return (this.req.sort === 'size')
|
|
|
|
},
|
|
|
|
modifiedSorted () {
|
|
|
|
return (this.req.sort === 'modified')
|
|
|
|
},
|
2017-07-03 17:49:08 +00:00
|
|
|
ascOrdered () {
|
|
|
|
return (this.req.order === 'asc')
|
|
|
|
},
|
|
|
|
nameIcon () {
|
|
|
|
if (this.nameSorted && !this.ascOrdered) {
|
|
|
|
return 'arrow_upward'
|
|
|
|
}
|
|
|
|
|
|
|
|
return 'arrow_downward'
|
|
|
|
},
|
|
|
|
sizeIcon () {
|
2017-07-29 06:57:44 +00:00
|
|
|
if (this.sizeSorted && this.ascOrdered) {
|
|
|
|
return 'arrow_downward'
|
|
|
|
}
|
|
|
|
|
|
|
|
return 'arrow_upward'
|
|
|
|
},
|
|
|
|
modifiedIcon () {
|
|
|
|
if (this.modifiedSorted && this.ascOrdered) {
|
2017-07-03 17:49:08 +00:00
|
|
|
return 'arrow_downward'
|
|
|
|
}
|
|
|
|
|
|
|
|
return 'arrow_upward'
|
|
|
|
}
|
|
|
|
},
|
2017-06-28 15:05:30 +00:00
|
|
|
mounted: function () {
|
2017-07-08 11:32:25 +00:00
|
|
|
// Check the columns size for the first time.
|
|
|
|
this.resizeEvent()
|
2017-06-28 15:05:30 +00:00
|
|
|
|
2017-07-08 11:32:25 +00:00
|
|
|
// Add the needed event listeners to the window and document.
|
2017-07-25 14:20:20 +00:00
|
|
|
window.addEventListener('keydown', this.keyEvent)
|
2017-07-08 11:32:25 +00:00
|
|
|
window.addEventListener('resize', this.resizeEvent)
|
|
|
|
document.addEventListener('dragover', this.preventDefault)
|
|
|
|
document.addEventListener('drop', this.drop)
|
|
|
|
},
|
|
|
|
beforeDestroy () {
|
|
|
|
// Remove event listeners before destroying this page.
|
2017-07-25 14:20:20 +00:00
|
|
|
window.removeEventListener('keydown', this.keyEvent)
|
2017-07-08 11:32:25 +00:00
|
|
|
window.removeEventListener('resize', this.resizeEvent)
|
|
|
|
document.removeEventListener('dragover', this.preventDefault)
|
|
|
|
document.removeEventListener('drop', this.drop)
|
2017-06-28 15:05:30 +00:00
|
|
|
},
|
2017-06-28 10:45:41 +00:00
|
|
|
methods: {
|
|
|
|
base64: function (name) {
|
2017-07-01 08:10:31 +00:00
|
|
|
return window.btoa(unescape(encodeURIComponent(name)))
|
2017-06-28 15:05:30 +00:00
|
|
|
},
|
2017-07-25 14:20:20 +00:00
|
|
|
keyEvent (event) {
|
|
|
|
if (!event.ctrlKey && !event.metaKey) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-07-26 14:55:39 +00:00
|
|
|
let key = String.fromCharCode(event.which).toLowerCase()
|
2017-07-25 14:20:20 +00:00
|
|
|
|
2017-07-26 14:55:39 +00:00
|
|
|
switch (key) {
|
|
|
|
case 'f':
|
|
|
|
event.preventDefault()
|
|
|
|
this.$store.commit('showHover', 'search')
|
|
|
|
break
|
|
|
|
case 'c':
|
|
|
|
case 'x':
|
|
|
|
this.copyCut(event, key)
|
|
|
|
break
|
|
|
|
case 'v':
|
|
|
|
this.paste(event)
|
|
|
|
break
|
|
|
|
}
|
2017-07-25 14:20:20 +00:00
|
|
|
},
|
2017-07-08 11:32:25 +00:00
|
|
|
preventDefault (event) {
|
|
|
|
// Wrapper around prevent default.
|
|
|
|
event.preventDefault()
|
|
|
|
},
|
2017-07-26 14:55:39 +00:00
|
|
|
copyCut (event, key) {
|
|
|
|
event.preventDefault()
|
|
|
|
let items = []
|
|
|
|
|
|
|
|
for (let i of this.selected) {
|
|
|
|
items.push({
|
|
|
|
from: this.req.items[i].url,
|
|
|
|
name: encodeURIComponent(this.req.items[i].name)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
this.$store.commit('updateClipboard', {
|
|
|
|
key: key,
|
|
|
|
items: items
|
|
|
|
})
|
|
|
|
},
|
|
|
|
paste (event) {
|
|
|
|
event.preventDefault()
|
|
|
|
|
|
|
|
let items = []
|
|
|
|
|
|
|
|
for (let item of this.$store.state.clipboard.items) {
|
|
|
|
items.push({
|
|
|
|
from: item.from,
|
|
|
|
to: this.$route.path + item.name
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.$store.state.clipboard.key === 'x') {
|
|
|
|
api.move(items).then(() => {
|
|
|
|
this.$store.commit('setReload', true)
|
|
|
|
}).catch(error => {
|
|
|
|
this.$store.commit('showError', error)
|
|
|
|
})
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
api.copy(items).then(() => {
|
|
|
|
this.$store.commit('setReload', true)
|
|
|
|
}).catch(error => {
|
|
|
|
this.$store.commit('showError', error)
|
|
|
|
})
|
|
|
|
},
|
2017-07-08 11:32:25 +00:00
|
|
|
resizeEvent () {
|
|
|
|
// Update the columns size based on the window width.
|
|
|
|
let columns = Math.floor(document.querySelector('main').offsetWidth / 300)
|
|
|
|
let items = css(['#listing.mosaic .item', '.mosaic#listing .item'])
|
|
|
|
if (columns === 0) columns = 1
|
|
|
|
items.style.width = `calc(${100 / columns}% - 1em)`
|
|
|
|
},
|
2017-06-28 15:05:30 +00:00
|
|
|
dragEnter: function (event) {
|
2017-07-08 11:32:25 +00:00
|
|
|
// When the user starts dragging an item, put every
|
|
|
|
// file on the listing with 50% opacity.
|
2017-06-28 15:05:30 +00:00
|
|
|
let items = document.getElementsByClassName('item')
|
|
|
|
|
|
|
|
Array.from(items).forEach(file => {
|
|
|
|
file.style.opacity = 0.5
|
|
|
|
})
|
|
|
|
},
|
|
|
|
dragEnd: function (event) {
|
2017-06-30 16:49:05 +00:00
|
|
|
this.resetOpacity()
|
2017-06-28 15:05:30 +00:00
|
|
|
},
|
|
|
|
drop: function (event) {
|
|
|
|
event.preventDefault()
|
|
|
|
|
|
|
|
let dt = event.dataTransfer
|
|
|
|
let files = dt.files
|
|
|
|
let el = event.target
|
|
|
|
|
|
|
|
for (let i = 0; i < 5; i++) {
|
|
|
|
if (el !== null && !el.classList.contains('item')) {
|
|
|
|
el = el.parentElement
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (files.length > 0) {
|
|
|
|
if (el !== null && el.classList.contains('item') && el.dataset.dir === 'true') {
|
|
|
|
this.handleFiles(files, el.querySelector('.name').innerHTML + '/')
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
this.handleFiles(files, '')
|
|
|
|
} else {
|
2017-06-30 16:49:05 +00:00
|
|
|
this.resetOpacity()
|
2017-06-28 15:05:30 +00:00
|
|
|
}
|
|
|
|
},
|
2017-06-29 08:11:46 +00:00
|
|
|
uploadInput: function (event) {
|
|
|
|
this.handleFiles(event.currentTarget.files, '')
|
|
|
|
},
|
2017-06-30 16:49:05 +00:00
|
|
|
resetOpacity: function () {
|
|
|
|
let items = document.getElementsByClassName('item')
|
|
|
|
|
|
|
|
Array.from(items).forEach(file => {
|
|
|
|
file.style.opacity = 1
|
|
|
|
})
|
|
|
|
},
|
2017-06-28 15:05:30 +00:00
|
|
|
handleFiles: function (files, base) {
|
2017-06-30 16:49:05 +00:00
|
|
|
this.resetOpacity()
|
|
|
|
|
2017-07-04 17:04:00 +00:00
|
|
|
buttons.loading('upload')
|
2017-06-28 15:05:30 +00:00
|
|
|
let promises = []
|
|
|
|
|
|
|
|
for (let file of files) {
|
2017-07-04 20:02:46 +00:00
|
|
|
promises.push(api.post(this.$route.path + base + file.name, file))
|
2017-06-28 15:05:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Promise.all(promises)
|
2017-06-30 16:49:05 +00:00
|
|
|
.then(() => {
|
2017-07-04 17:04:00 +00:00
|
|
|
buttons.done('upload')
|
2017-07-03 14:57:07 +00:00
|
|
|
this.$store.commit('setReload', true)
|
2017-06-30 16:49:05 +00:00
|
|
|
})
|
2017-07-06 13:40:06 +00:00
|
|
|
.catch(error => {
|
2017-07-04 17:08:42 +00:00
|
|
|
buttons.done('upload')
|
2017-07-06 13:40:06 +00:00
|
|
|
this.$store.commit('showError', error)
|
2017-06-30 16:49:05 +00:00
|
|
|
})
|
2017-06-28 15:05:30 +00:00
|
|
|
|
|
|
|
return false
|
2017-07-03 17:49:08 +00:00
|
|
|
},
|
|
|
|
sort (sort) {
|
|
|
|
let order = 'desc'
|
|
|
|
|
|
|
|
if (sort === 'name') {
|
|
|
|
if (this.nameIcon === 'arrow_upward') {
|
|
|
|
order = 'asc'
|
|
|
|
}
|
2017-07-29 06:57:44 +00:00
|
|
|
} else if (sort === 'size') {
|
2017-07-03 17:49:08 +00:00
|
|
|
if (this.sizeIcon === 'arrow_upward') {
|
|
|
|
order = 'asc'
|
|
|
|
}
|
2017-07-29 06:57:44 +00:00
|
|
|
} else if (sort === 'modified') {
|
|
|
|
if (this.modifiedIcon === 'arrow_upward') {
|
|
|
|
order = 'asc'
|
|
|
|
}
|
2017-07-03 17:49:08 +00:00
|
|
|
}
|
|
|
|
|
2017-07-20 18:00:51 +00:00
|
|
|
let path = this.$store.state.baseURL
|
|
|
|
if (path === '') path = '/'
|
|
|
|
document.cookie = `sort=${sort}; max-age=31536000; path=${path}`
|
|
|
|
document.cookie = `order=${order}; max-age=31536000; path=${path}`
|
2017-07-03 17:49:08 +00:00
|
|
|
this.$store.commit('setReload', true)
|
2017-06-28 10:45:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
</script>
|