summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhil Hughes <me@iamphill.com>2018-04-11 17:12:01 +0100
committerPhil Hughes <me@iamphill.com>2018-04-16 09:48:57 +0100
commit748a2f2b542fed2cb8ab7d1792cb874ce7423a61 (patch)
tree47a281d89c47e9e1751e450429784430ff2049a7
parentc3e26860be156314e733a6dfb986c91fc55766f5 (diff)
downloadgitlab-ce-748a2f2b542fed2cb8ab7d1792cb874ce7423a61.tar.gz
Added fuzzy file finder to web IDE
Closes #44841
-rw-r--r--app/assets/javascripts/ide/components/file_finder/index.vue100
-rw-r--r--app/assets/javascripts/ide/components/file_finder/item.vue36
-rw-r--r--app/assets/javascripts/ide/components/ide.vue13
-rw-r--r--app/assets/javascripts/ide/stores/getters.js3
-rw-r--r--app/assets/javascripts/ide/stores/state.js1
-rw-r--r--package.json1
-rw-r--r--yarn.lock4
7 files changed, 157 insertions, 1 deletions
diff --git a/app/assets/javascripts/ide/components/file_finder/index.vue b/app/assets/javascripts/ide/components/file_finder/index.vue
new file mode 100644
index 00000000000..ebcd6f7592b
--- /dev/null
+++ b/app/assets/javascripts/ide/components/file_finder/index.vue
@@ -0,0 +1,100 @@
+<script>
+import { mapGetters, mapState } from 'vuex';
+import fuzzaldrinPlus from 'fuzzaldrin-plus';
+import VirtualList from 'vue-virtual-scroll-list';
+import Item from './item.vue';
+
+export default {
+ components: {
+ Item,
+ VirtualList,
+ },
+ data() {
+ return {
+ searchText: '',
+ };
+ },
+ computed: {
+ ...mapGetters(['allBlobs']),
+ ...mapState(['loading']),
+ filteredBlobs() {
+ const searchText = this.searchText.trim();
+
+ if (searchText === '') return this.allBlobs;
+
+ return fuzzaldrinPlus.filter(this.allBlobs, searchText, {
+ key: 'path',
+ });
+ },
+ listShowCount() {
+ if (this.filteredBlobs.length === 0) return 1;
+
+ return this.filteredBlobs.length > 5 ? 5 : this.filteredBlobs.length;
+ },
+ listHeight() {
+ return this.listShowCount > 1 ? 55 : 33;
+ },
+ },
+ mounted() {
+ this.$refs.searchInput.focus();
+ },
+};
+</script>
+
+<template>
+ <div class="dropdown-menu diff-file-changes ide-file-finder" style="display: block;">
+ <div class="dropdown-input">
+ <input
+ type="search"
+ class="dropdown-input-field"
+ placeholder="Search files"
+ autocomplete="off"
+ v-model="searchText"
+ ref="searchInput"
+ />
+ <i
+ aria-hidden="true"
+ class="fa fa-search dropdown-input-search"
+ ></i>
+ </div>
+ <div>
+ <virtual-list
+ :size="listHeight"
+ :remain="listShowCount"
+ wtag="ul"
+ >
+ <template v-if="filteredBlobs.length">
+ <li
+ v-for="file in filteredBlobs"
+ :key="file.key"
+ >
+ <item
+ :file="file"
+ />
+ </li>
+ </template>
+ <li
+ v-else
+ class="dropdown-menu-empty-itemhidden"
+ >
+ <a href="">
+ <template v-if="loading">
+ Loading...
+ </template>
+ <template v-else>
+ No files found.
+ </template>
+ </a>
+ </li>
+ </virtual-list>
+ </div>
+ </div>
+</template>
+
+<style>
+.ide-file-finder {
+ top: 100px;
+ left: 50%;
+ transform: translateX(-50%);
+}
+</style>
diff --git a/app/assets/javascripts/ide/components/file_finder/item.vue b/app/assets/javascripts/ide/components/file_finder/item.vue
new file mode 100644
index 00000000000..482bfb96d93
--- /dev/null
+++ b/app/assets/javascripts/ide/components/file_finder/item.vue
@@ -0,0 +1,36 @@
+<script>
+import FileIcon from '../../../vue_shared/components/file_icon.vue';
+
+export default {
+ components: {
+ FileIcon,
+ },
+ props: {
+ file: {
+ type: Object,
+ required: true,
+ },
+ },
+};
+</script>
+
+<template>
+ <a
+ href=""
+ class="diff-changed-file"
+ >
+ <file-icon
+ :file-name="file.name"
+ :size="16"
+ css-classes="diff-file-changed-icon append-right-8"
+ />
+ <span class="diff-changed-file-content append-right-8">
+ <strong class="diff-changed-file-name">
+ {{ file.name }}
+ </strong>
+ <span class="diff-changed-file-path prepend-top-5">
+ {{ file.path }}
+ </span>
+ </span>
+ </a>
+</template>
diff --git a/app/assets/javascripts/ide/components/ide.vue b/app/assets/javascripts/ide/components/ide.vue
index 1c237c0ec97..8489639e3b0 100644
--- a/app/assets/javascripts/ide/components/ide.vue
+++ b/app/assets/javascripts/ide/components/ide.vue
@@ -5,6 +5,7 @@ import ideContextbar from './ide_context_bar.vue';
import repoTabs from './repo_tabs.vue';
import ideStatusBar from './ide_status_bar.vue';
import repoEditor from './repo_editor.vue';
+import FindFile from './file_finder/index.vue';
export default {
components: {
@@ -13,6 +14,7 @@ export default {
repoTabs,
ideStatusBar,
repoEditor,
+ FindFile,
},
props: {
emptyStateSvgPath: {
@@ -29,7 +31,13 @@ export default {
},
},
computed: {
- ...mapState(['changedFiles', 'openFiles', 'viewer', 'currentMergeRequestId']),
+ ...mapState([
+ 'changedFiles',
+ 'openFiles',
+ 'viewer',
+ 'currentMergeRequestId',
+ 'fileFindVisible',
+ ]),
...mapGetters(['activeFile', 'hasChanges']),
},
mounted() {
@@ -50,6 +58,9 @@ export default {
<div
class="ide-view"
>
+ <find-file
+ v-if="fileFindVisible"
+ />
<ide-sidebar />
<div
class="multi-file-edit-pane"
diff --git a/app/assets/javascripts/ide/stores/getters.js b/app/assets/javascripts/ide/stores/getters.js
index a77cdbc13c8..6776be3b77c 100644
--- a/app/assets/javascripts/ide/stores/getters.js
+++ b/app/assets/javascripts/ide/stores/getters.js
@@ -35,3 +35,6 @@ export const currentIcon = state =>
export const hasChanges = state => !!state.changedFiles.length;
export const hasMergeRequest = state => !!state.currentMergeRequestId;
+
+export const allBlobs = state =>
+ Object.keys(state.entries).reduce((acc, key) => acc.concat(state.entries[key]), []);
diff --git a/app/assets/javascripts/ide/stores/state.js b/app/assets/javascripts/ide/stores/state.js
index e5cc8814000..e976884ae1f 100644
--- a/app/assets/javascripts/ide/stores/state.js
+++ b/app/assets/javascripts/ide/stores/state.js
@@ -17,4 +17,5 @@ export default () => ({
entries: {},
viewer: 'editor',
delayViewerUpdated: false,
+ fileFindVisible: true,
});
diff --git a/package.json b/package.json
index 45bea12fd9b..6f92600cfa5 100644
--- a/package.json
+++ b/package.json
@@ -88,6 +88,7 @@
"vue-resource": "^1.3.5",
"vue-router": "^3.0.1",
"vue-template-compiler": "^2.5.13",
+ "vue-virtual-scroll-list": "^1.2.5",
"vuex": "^3.0.1",
"webpack": "^3.11.0",
"webpack-bundle-analyzer": "^2.10.0",
diff --git a/yarn.lock b/yarn.lock
index 55a86a9a577..1718a56bac4 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -8683,6 +8683,10 @@ vue-template-es2015-compiler@^1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.6.0.tgz#dc42697133302ce3017524356a6c61b7b69b4a18"
+vue-virtual-scroll-list@^1.2.5:
+ version "1.2.5"
+ resolved "https://registry.yarnpkg.com/vue-virtual-scroll-list/-/vue-virtual-scroll-list-1.2.5.tgz#bcbd010f7cdb035eba8958ebf807c6214d9a167a"
+
vue@^2.5.13:
version "2.5.13"
resolved "https://registry.yarnpkg.com/vue/-/vue-2.5.13.tgz#95bd31e20efcf7a7f39239c9aa6787ce8cf578e1"