增加文件筛选面板

This commit is contained in:
purocean
2018-07-29 20:10:02 +08:00
parent 5428aafa31
commit 495abdaaad
4 changed files with 218 additions and 5 deletions
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

+2
View File
@@ -19,6 +19,7 @@
+ 重命名文件/目录:`ctrl + 右键文件/目录`
+ 默认快捷键参考 [vscode](https://code.visualstudio.com/)
+ 自定义编辑器快捷键
+ `Ctrl + p` 打开文件快速跳转面板
+ `Ctrl + s` 保存文档
+ `Ctrl + Shift + Up` 当前行上移
+ `Ctrl + Shift + Down` 当前行下移
@@ -80,3 +81,4 @@
![截图](./1.png)
![截图](./2.png)
![截图](./3.png)
![截图](./4.png)
+155
View File
@@ -0,0 +1,155 @@
<template>
<div @keydown.enter="chooseItem()" @keydown.up="selectItem(-1)" @keydown.down="selectItem(1)" class="filter" @click.stop="nope">
<input @keydown.up.prevent="nope" @keydown.down.prevent="nope" ref="input" v-model="searchText" type="text" class="input" autofocus>
<ul ref="result" class="result">
<li
v-for="item in list"
:key="item.path"
:class="{selected: selected === item}"
@click="chooseItem(item)">
<span>
{{item.name}}
</span>
<span class="path">
{{item.path.substr(0, item.path.lastIndexOf('/'))}}
</span>
</li>
<li v-if="list.length < 1">无结果</li>
</ul>
</div>
</template>
<script>
export default {
name: 'x-filter',
components: {},
props: {
files: Array
},
data () {
return {
selected: null,
searchText: ''
}
},
mounted () {
this.$refs.input.focus()
this.updateSelected()
},
beforeDestroy () {
},
methods: {
nope () {
// nope
},
updateSelected (item = null) {
if (item) {
this.selected = item
} else {
this.selected = this.list.length > 0 ? this.list[0] : null
}
this.$nextTick(() => {
const li = this.$refs.result.querySelector('li.selected')
if (li) {
li.scrollIntoViewIfNeeded()
}
})
},
selectItem (inc) {
if (this.list.length < 1) {
this.updateSelected()
return
}
const currentIndex = this.list.findIndex(x => this.selected === x)
let index = currentIndex + inc
if (index > this.list.length - 1) {
index = 0
} else if (index < 0) {
index = this.list.length - 1
}
this.updateSelected(this.list[index])
},
chooseItem (item = null) {
if (item) {
this.$emit('choose-item', item)
} else if (this.selected) {
this.$emit('choose-item', this.selected)
}
}
},
watch: {
list () {
this.updateSelected()
}
},
computed: {
list () {
return this.files.filter(x => x.path.toLowerCase().indexOf(this.searchText.toLowerCase()) > -1)
}
}
}
</script>
<style scoped>
.filter {
width: 600px;
background: #403e3e;
margin: auto;
padding: 10px;
}
.input {
display: block;
width: 100%;
margin: 0;
border: 0;
font-size: 18px;
padding: 6px;
box-sizing: border-box;
background: #333030;
color: #ddd;
transition: all .1s ease-in-out;
}
.input:focus {
background: #242222;
}
.result {
max-height: 300px;
overflow-y: auto;
list-style: none;
padding: 0;
margin: 6px 0;
}
.result li {
color: #999;
line-height: 1.5em;
font-size: 18px;
padding: 2px;
transition: all .1s ease-in-out;
cursor: pointer;
}
.result li.selected,
.result li:hover {
padding: 2px 6px;
background: #333030;
color: #eee;
}
.result li span {
vertical-align: middle
}
.result li span.path {
font-size: 12px;
color: #888;
padding-left: .3em;
}
</style>
+61 -5
View File
@@ -8,31 +8,43 @@
@move="onMove"
@change="change"
@select="f => file = f"
@delete="onDelete"
></TreeNode>
@delete="onDelete" />
<transition name="fade">
<div v-if="showFilter" class="filter-wrapper" @click="showFilter = false">
<XFilter @choose-item="f => { file = f; showFilter = false }" :files="files" />
</div>
</transition>
</aside>
</template>
<script>
import TreeNode from './TreeNode'
import File from '../file'
import TreeNode from './TreeNode'
import XFilter from './Filter'
export default {
name: 'tree',
components: { TreeNode },
components: { TreeNode, XFilter },
data () {
return {
files: [],
tree: [],
file: null
file: null,
showFilter: false
}
},
mounted () {
this.init()
window.addEventListener('keydown', this.keydownHandler)
},
beforeDestroy () {
window.removeEventListener('keydown', this.keydownHandler)
},
methods: {
init () {
File.tree(tree => {
this.tree = tree
this.files = this.travelFiles(tree)
})
},
onDelete (path) {
@@ -53,6 +65,32 @@ export default {
},
change (path) {
this.init()
},
keydownHandler (e) {
if (e.key === 'p' && e.ctrlKey) {
this.showFilter = true
e.preventDefault()
e.stopPropagation()
} else if (e.key === 'Escape' && this.showFilter) {
this.showFilter = false
e.preventDefault()
e.stopPropagation()
}
},
travelFiles (tree) {
let tmp = []
tree.forEach(node => {
if (node.type === 'file' && node.path.endsWith('.md')) {
tmp.push(node)
}
if (Array.isArray(node.children)) {
tmp = tmp.concat(this.travelFiles(node.children))
}
})
return tmp
}
},
watch: {
@@ -67,4 +105,22 @@ export default {
.side {
color: #ddd;
}
.filter-wrapper {
position: fixed;
top: 0;
width: 100%;
height: 100%;
background: rgba(255, 255, 255, .2);
z-index: 99999;
padding-top: 4em;
}
.fade-enter-active, .fade-leave-active {
transition: opacity .5s;
}
.fade-enter, .fade-leave-to {
opacity: 0;
}
</style>