Skip to content

Commit 421e4c1

Browse files
committed
feat(files): add folder icon overlay
Signed-off-by: John Molakvoæ <skjnldsv@protonmail.com>
1 parent 70e5d42 commit 421e4c1

2 files changed

Lines changed: 73 additions & 7 deletions

File tree

apps/files/src/components/FileEntry.vue

Lines changed: 64 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,12 @@
4444
<td class="files-list__row-name" data-cy-files-list-row-name>
4545
<!-- Icon or preview -->
4646
<span class="files-list__row-icon" @click="execDefaultAction">
47-
<FolderIcon v-if="source.type === 'folder'" />
47+
<template v-if="source.type === 'folder'">
48+
<FolderIcon />
49+
<OverlayIcon :is="folderOverlay"
50+
v-if="folderOverlay"
51+
class="files-list__row-icon-overlay" />
52+
</template>
4853

4954
<!-- Decorative image, should not be aria documented -->
5055
<span v-else-if="previewUrl && !backgroundFailed"
@@ -167,27 +172,33 @@
167172

168173
<script lang='ts'>
169174
import type { PropType } from 'vue'
170-
import type { Node } from '@nextcloud/files'
171175
172176
import { CancelablePromise } from 'cancelable-promise'
173177
import { debounce } from 'debounce'
174178
import { emit } from '@nextcloud/event-bus'
175179
import { extname } from 'path'
176180
import { generateUrl } from '@nextcloud/router'
177-
import { getFileActions, DefaultType, FileType, formatFileSize, Permission, Folder, File } from '@nextcloud/files'
181+
import { getFileActions, DefaultType, FileType, formatFileSize, Permission, Folder, File, Node } from '@nextcloud/files'
182+
import { Type as ShareType } from '@nextcloud/sharing'
178183
import { showError, showSuccess } from '@nextcloud/dialogs'
179184
import { translate } from '@nextcloud/l10n'
180185
import { vOnClickOutside } from '@vueuse/components'
181186
import axios from '@nextcloud/axios'
187+
import moment from '@nextcloud/moment'
188+
import Vue from 'vue'
189+
190+
import AccountGroupIcon from 'vue-material-design-icons/AccountGroup.vue'
182191
import FileIcon from 'vue-material-design-icons/File.vue'
183192
import FolderIcon from 'vue-material-design-icons/Folder.vue'
184-
import moment from '@nextcloud/moment'
193+
import KeyIcon from 'vue-material-design-icons/Key.vue'
194+
import LinkIcon from 'vue-material-design-icons/Link.vue'
195+
import NetworkIcon from 'vue-material-design-icons/Network.vue'
196+
import AccountPlusIcon from 'vue-material-design-icons/AccountPlus.vue'
185197
import NcActionButton from '@nextcloud/vue/dist/Components/NcActionButton.js'
186198
import NcActions from '@nextcloud/vue/dist/Components/NcActions.js'
187199
import NcCheckboxRadioSwitch from '@nextcloud/vue/dist/Components/NcCheckboxRadioSwitch.js'
188200
import NcLoadingIcon from '@nextcloud/vue/dist/Components/NcLoadingIcon.js'
189201
import NcTextField from '@nextcloud/vue/dist/Components/NcTextField.js'
190-
import Vue from 'vue'
191202
192203
import { action as sidebarAction } from '../actions/sidebarAction.ts'
193204
import { hashCode } from '../utils/hashUtils.ts'
@@ -212,16 +223,21 @@ export default Vue.extend({
212223
name: 'FileEntry',
213224
214225
components: {
226+
AccountGroupIcon,
227+
AccountPlusIcon,
215228
CustomElementRender,
216229
CustomSvgIconRender,
217230
FavoriteIcon,
218231
FileIcon,
219232
FolderIcon,
233+
KeyIcon,
234+
LinkIcon,
220235
NcActionButton,
221236
NcActions,
222237
NcCheckboxRadioSwitch,
223238
NcLoadingIcon,
224239
NcTextField,
240+
NetworkIcon,
225241
},
226242
227243
props: {
@@ -356,6 +372,38 @@ export default Vue.extend({
356372
return ''
357373
},
358374
375+
folderOverlay() {
376+
if (this.source.type !== FileType.Folder) {
377+
return null
378+
}
379+
380+
// Link and mail shared folders
381+
const shareTypes = Object.values(this.source?.attributes?.['share-types'] || {}).flat() as number[]
382+
if (shareTypes.some(type => type === ShareType.SHARE_TYPE_LINK || type === ShareType.SHARE_TYPE_EMAIL)) {
383+
return LinkIcon
384+
}
385+
386+
// Shared folders
387+
if (shareTypes.length > 0) {
388+
return AccountPlusIcon
389+
}
390+
391+
// Encrypted folders
392+
if (this.source?.attributes?.['is-encrypted'] === 1) {
393+
return KeyIcon
394+
}
395+
396+
switch (this.source?.attributes?.['mount-type']) {
397+
case 'external':
398+
case 'external-session':
399+
return NetworkIcon
400+
case 'group':
401+
return AccountGroupIcon
402+
}
403+
404+
return null
405+
},
406+
359407
linkTo() {
360408
if (this.source.attributes.failed) {
361409
return {
@@ -871,12 +919,21 @@ export default Vue.extend({
871919
/* Hover effect on tbody lines only */
872920
tr {
873921
&:hover,
874-
&:focus,
875-
&:visible {
922+
&:focus {
876923
background-color: var(--color-background-dark);
877924
}
878925
}
879926
927+
// Folder overlay
928+
.files-list__row-icon-overlay {
929+
position: absolute;
930+
max-height: 18px;
931+
max-width: 18px;
932+
color: var(--color-main-background);
933+
// better alignment with the folder icon
934+
margin-top: 2px;
935+
}
936+
880937
/* Preview not loaded animation effect */
881938
.files-list__row-icon-preview:not([style*='background']) {
882939
background: var(--color-loading-dark);

apps/files/src/components/FilesListVirtual.vue

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,15 @@ export default Vue.extend({
346346
width: var(--icon-preview-size);
347347
height: var(--icon-preview-size);
348348
}
349+
350+
// Slightly increase the size of the folder icon
351+
&.folder-icon {
352+
margin: -3px;
353+
svg {
354+
width: calc(var(--icon-preview-size) + 6px);
355+
height: calc(var(--icon-preview-size) + 6px);
356+
}
357+
}
349358
}
350359
351360
&-preview {

0 commit comments

Comments
 (0)