Skip to content

Commit 545cc3f

Browse files
author
Guillaume Chau
committed
feat(ui): folder explorer: better UX + hide hidden folders by default
1 parent d63cc0d commit 545cc3f

File tree

9 files changed

+233
-54
lines changed

9 files changed

+233
-54
lines changed

packages/@vue/cli-ui/locales/en.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@
4444
"refresh": "Refresh"
4545
},
4646
"placeholder": "Enter the full path to a folder",
47-
"empty": "No favorite folders yet."
47+
"empty": "No favorite folders yet.",
48+
"show-hidden": "Show hidden folders"
4849
}
4950
},
5051
"list-item-info": {

packages/@vue/cli-ui/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@
4141
"shortid": "^2.2.8",
4242
"terminate": "^2.1.0",
4343
"vue-cli-plugin-apollo": "^0.13.4",
44-
"watch": "^1.0.2"
44+
"watch": "^1.0.2",
45+
"winattr": "^2.0.0"
4546
},
4647
"devDependencies": {
4748
"@vue/cli-plugin-babel": "^3.0.0-rc.2",

packages/@vue/cli-ui/src/components/FolderExplorer.vue

Lines changed: 131 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
class="path-input"
2020
v-model="editedPath"
2121
:placeholder="$t('components.folder-explorer.toolbar.placeholder')"
22-
icon-left="edit"
22+
icon-right="edit"
2323
@keyup.esc="editingPath = false"
2424
@keyup.enter="submitPathEdit()"
2525
/>
@@ -29,23 +29,45 @@
2929
v-else
3030
:query="require('@/graphql/cwd.gql')"
3131
class="current-path"
32-
v-tooltip="$t('components.folder-explorer.toolbar.tooltips.edit-path')"
33-
@click.native="openPathEdit()"
32+
@dblclick.native="openPathEdit()"
3433
>
3534
<ApolloSubscribeToMore
3635
:document="require('@/graphql/cwdChanged.gql')"
3736
:update-query="cwdChangedUpdate"
3837
/>
3938

4039
<template slot-scope="{ result: { data } }">
41-
<span v-if="data">{{ data.cwd }}</span>
40+
<div
41+
v-if="data"
42+
class="path-value"
43+
>
44+
<VueButton
45+
v-for="(slice, index) of slicePath(data.cwd)"
46+
:key="index"
47+
class="path-folder flat"
48+
:icon-left="!slice.name ? 'folder' : null"
49+
:class="{
50+
'icon-button': !slice.name
51+
}"
52+
@click="openFolder(slice.path)"
53+
>
54+
{{ slice.name }}
55+
</VueButton>
56+
</div>
57+
<VueButton
58+
class="edit-path-button icon-button"
59+
icon-left="edit"
60+
v-tooltip="$t('components.folder-explorer.toolbar.tooltips.edit-path')"
61+
@click="openPathEdit()"
62+
/>
4263
</template>
4364
</ApolloQuery>
4465

45-
<VueIcon
46-
v-if="error"
47-
icon="error"
48-
class="error-icon big"
66+
<VueButton
67+
class="icon-button"
68+
icon-left="refresh"
69+
v-tooltip="$t('components.folder-explorer.toolbar.tooltips.refresh')"
70+
@click="refreshFolder"
4971
/>
5072

5173
<VueButton
@@ -80,18 +102,35 @@
80102
</div>
81103
</VueDropdown>
82104

83-
<VueButton
84-
class="icon-button"
85-
icon-left="refresh"
86-
v-tooltip="$t('components.folder-explorer.toolbar.tooltips.refresh')"
87-
@click="refreshFolder"
105+
<VueIcon
106+
v-if="error"
107+
icon="error"
108+
class="error-icon big"
109+
v-tooltip="error.message"
88110
/>
111+
112+
<VueDropdown placement="bottom-end">
113+
<VueButton
114+
slot="trigger"
115+
icon-left="more_vert"
116+
class="icon-button"
117+
/>
118+
119+
<VueSwitch
120+
icon="visibility"
121+
v-model="showHidden"
122+
class="extend-left"
123+
>
124+
{{ $t('components.folder-explorer.toolbar.show-hidden') }}
125+
</VueSwitch>
126+
</VueDropdown>
89127
</div>
90128

91129
<div ref="folders" class="folders">
92130
<template v-if="folderCurrent.children">
93131
<FolderExplorerItem
94132
v-for="folder of folderCurrent.children"
133+
v-if="showHidden || !folder.hidden"
95134
:key="folder.name"
96135
:folder="folder"
97136
@select="openFolder(folder.path)"
@@ -109,14 +148,17 @@ import FOLDER_OPEN_PARENT from '../graphql/folderOpenParent.gql'
109148
import FOLDER_SET_FAVORITE from '../graphql/folderSetFavorite.gql'
110149
import PROJECT_CWD_RESET from '../graphql/projectCwdReset.gql'
111150
151+
const SHOW_HIDDEN = 'vue-ui.show-hidden-folders'
152+
112153
export default {
113154
data () {
114155
return {
115156
error: false,
116157
editingPath: false,
117158
editedPath: '',
118159
folderCurrent: {},
119-
foldersFavorite: []
160+
foldersFavorite: [],
161+
showHidden: localStorage.getItem(SHOW_HIDDEN) === 'true'
120162
}
121163
},
122164
@@ -133,6 +175,16 @@ export default {
133175
foldersFavorite: FOLDERS_FAVORITE
134176
},
135177
178+
watch: {
179+
showHidden (value) {
180+
if (value) {
181+
localStorage.setItem(SHOW_HIDDEN, 'true')
182+
} else {
183+
localStorage.removeItem(SHOW_HIDDEN)
184+
}
185+
}
186+
},
187+
136188
beforeRouteLeave (to, from, next) {
137189
if (to.matched.some(m => m.meta.needProject)) {
138190
this.resetProjectCwd()
@@ -143,7 +195,7 @@ export default {
143195
methods: {
144196
async openFolder (path) {
145197
this.editingPath = false
146-
this.error = false
198+
this.error = null
147199
try {
148200
await this.$apollo.mutate({
149201
mutation: FOLDER_OPEN,
@@ -155,13 +207,13 @@ export default {
155207
}
156208
})
157209
} catch (e) {
158-
this.error = true
210+
this.error = e
159211
}
160212
},
161213
162214
async openParentFolder (folder) {
163215
this.editingPath = false
164-
this.error = false
216+
this.error = null
165217
try {
166218
await this.$apollo.mutate({
167219
mutation: FOLDER_OPEN_PARENT,
@@ -170,7 +222,7 @@ export default {
170222
}
171223
})
172224
} catch (e) {
173-
this.error = true
225+
this.error = e
174226
}
175227
},
176228
@@ -223,6 +275,36 @@ export default {
223275
this.$apollo.mutate({
224276
mutation: PROJECT_CWD_RESET
225277
})
278+
},
279+
280+
slicePath (path) {
281+
const parts = []
282+
let startIndex = 0
283+
let index
284+
285+
const findSeparator = () => {
286+
index = path.indexOf('/', startIndex)
287+
if (index === -1) index = path.indexOf('\\', startIndex)
288+
return index !== -1
289+
}
290+
291+
const addPart = index => {
292+
const folder = path.substring(startIndex, index)
293+
const slice = path.substring(0, index + 1)
294+
parts.push({
295+
name: folder,
296+
path: slice
297+
})
298+
}
299+
300+
while (findSeparator()) {
301+
addPart(index)
302+
startIndex = index + 1
303+
}
304+
305+
if (startIndex < path.length) addPart(path.length)
306+
307+
return parts
226308
}
227309
}
228310
}
@@ -232,7 +314,7 @@ export default {
232314
@import "~@/style/imports"
233315
234316
.toolbar
235-
padding $padding-item
317+
padding $padding-item 0
236318
h-box()
237319
align-items center
238320
@@ -241,8 +323,36 @@ export default {
241323
242324
.current-path
243325
flex 100% 1 1
244-
ellipsis()
245-
cursor pointer
326+
h-box()
327+
align-items stretch
328+
border-radius $br
329+
background $vue-ui-color-light-neutral
330+
.vue-ui-dark-mode &
331+
background $vue-ui-color-dark
332+
333+
.path-value
334+
flex auto 1 1
335+
h-box()
336+
align-items stretch
337+
338+
.path-folder
339+
padding 0 9px
340+
&:not(:first-child)
341+
position relative
342+
&::before
343+
display block
344+
content ''
345+
position absolute
346+
top 0
347+
left -1px
348+
height 100%
349+
width 2px
350+
background $vue-ui-color-light
351+
.vue-ui-dark-mode &
352+
background $vue-ui-color-darker
353+
354+
.edit-path-button
355+
margin-left 4px
246356
247357
.path-edit
248358
flex 100% 1 1
@@ -264,8 +374,4 @@ export default {
264374
flex 100% 1 1
265375
overflow-x hidden
266376
overflow-y auto
267-
268-
&.error
269-
.current-path
270-
color $vue-ui-color-danger
271377
</style>
Lines changed: 37 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
11
<template functional>
22
<div
33
class="folder-explorer-item"
4+
:class="{
5+
hidden: props.folder.hidden
6+
}"
47
@click="listeners.select()"
58
>
6-
<VueIcon :icon="props.folder.isPackage ? 'folder' : 'folder_open'" class="folder-icon big"/>
9+
<VueIcon
10+
:icon="props.folder.isPackage ? 'folder' : 'folder_open'"
11+
class="folder-icon big"
12+
/>
713
<div class="folder-name">
814
{{ props.folder.name }}
915
<img
@@ -12,6 +18,11 @@
1218
src="~@/assets/logo.png"
1319
>
1420
</div>
21+
<VueIcon
22+
v-if="props.folder.favorite"
23+
icon="star"
24+
class="favorite-icon"
25+
/>
1526
</div>
1627
</template>
1728

@@ -29,23 +40,6 @@ export default {
2940
<style lang="stylus" scoped>
3041
@import "~@/style/imports"
3142
32-
.folder-icon
33-
margin 0 4px
34-
>>> svg
35-
fill $vue-ui-color-primary
36-
37-
.folder-name
38-
flex 100% 1 1
39-
margin-left $padding-item
40-
ellipsis()
41-
42-
.vue-ui-project-icon
43-
width 14px
44-
height @width
45-
vertical-align top
46-
position relative
47-
top 5px
48-
4943
.folder-explorer-item
5044
padding $padding-item
5145
h-box()
@@ -56,4 +50,29 @@ export default {
5650
5751
&:hover
5852
background rgba($vue-ui-color-primary, .1)
53+
54+
&.hidden
55+
opacity .5
56+
57+
.folder-icon
58+
margin 0 4px
59+
>>> svg
60+
fill $vue-ui-color-primary
61+
62+
.folder-name
63+
flex 100% 1 1
64+
margin-left $padding-item
65+
ellipsis()
66+
67+
.vue-ui-project-icon
68+
width 14px
69+
height @width
70+
vertical-align top
71+
position relative
72+
top 5px
73+
74+
.favorite-icon
75+
>>> svg
76+
fill $vue-ui-color-primary
77+
5978
</style>

packages/@vue/cli-ui/src/graphql-api/connectors/cwd.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,22 @@
11
const channels = require('../channels')
2+
const fs = require('fs')
3+
const path = require('path')
24

35
let cwd = process.cwd()
46

7+
function normalize (value) {
8+
const lastChar = value.charAt(value.length - 1)
9+
if (lastChar === path.sep) {
10+
value = value.substr(0, value.length - 1)
11+
}
12+
return value
13+
}
14+
515
module.exports = {
616
get: () => cwd,
717
set: (value, context) => {
18+
value = normalize(value)
19+
if (!fs.existsSync(value)) return
820
cwd = value
921
process.env.VUE_CLI_CONTEXT = value
1022
context.pubsub.publish(channels.CWD_CHANGED, { cwdChanged: value })

0 commit comments

Comments
 (0)