diff options
author | Phil Hughes <me@iamphill.com> | 2018-04-11 17:12:01 +0100 |
---|---|---|
committer | Phil Hughes <me@iamphill.com> | 2018-04-16 09:48:57 +0100 |
commit | 748a2f2b542fed2cb8ab7d1792cb874ce7423a61 (patch) | |
tree | 47a281d89c47e9e1751e450429784430ff2049a7 | |
parent | c3e26860be156314e733a6dfb986c91fc55766f5 (diff) | |
download | gitlab-ce-748a2f2b542fed2cb8ab7d1792cb874ce7423a61.tar.gz |
Added fuzzy file finder to web IDE
Closes #44841
-rw-r--r-- | app/assets/javascripts/ide/components/file_finder/index.vue | 100 | ||||
-rw-r--r-- | app/assets/javascripts/ide/components/file_finder/item.vue | 36 | ||||
-rw-r--r-- | app/assets/javascripts/ide/components/ide.vue | 13 | ||||
-rw-r--r-- | app/assets/javascripts/ide/stores/getters.js | 3 | ||||
-rw-r--r-- | app/assets/javascripts/ide/stores/state.js | 1 | ||||
-rw-r--r-- | package.json | 1 | ||||
-rw-r--r-- | yarn.lock | 4 |
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" |