aboutsummaryrefslogtreecommitdiffstats
path: root/apps/files/src/components/DragAndDropPreview.vue
diff options
context:
space:
mode:
Diffstat (limited to 'apps/files/src/components/DragAndDropPreview.vue')
-rw-r--r--apps/files/src/components/DragAndDropPreview.vue165
1 files changed, 165 insertions, 0 deletions
diff --git a/apps/files/src/components/DragAndDropPreview.vue b/apps/files/src/components/DragAndDropPreview.vue
new file mode 100644
index 00000000000..72fd98d43fb
--- /dev/null
+++ b/apps/files/src/components/DragAndDropPreview.vue
@@ -0,0 +1,165 @@
+<!--
+ - SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ - SPDX-License-Identifier: AGPL-3.0-or-later
+-->
+<template>
+ <div class="files-list-drag-image">
+ <span class="files-list-drag-image__icon">
+ <span ref="previewImg" />
+ <FolderIcon v-if="isSingleFolder" />
+ <FileMultipleIcon v-else />
+ </span>
+ <span class="files-list-drag-image__name">{{ name }}</span>
+ </div>
+</template>
+
+<script lang="ts">
+import { FileType, Node, formatFileSize } from '@nextcloud/files'
+import Vue from 'vue'
+
+import FileMultipleIcon from 'vue-material-design-icons/FileMultiple.vue'
+import FolderIcon from 'vue-material-design-icons/Folder.vue'
+
+import { getSummaryFor } from '../utils/fileUtils.ts'
+
+export default Vue.extend({
+ name: 'DragAndDropPreview',
+
+ components: {
+ FileMultipleIcon,
+ FolderIcon,
+ },
+
+ data() {
+ return {
+ nodes: [] as Node[],
+ }
+ },
+
+ computed: {
+ isSingleNode() {
+ return this.nodes.length === 1
+ },
+ isSingleFolder() {
+ return this.isSingleNode
+ && this.nodes[0].type === FileType.Folder
+ },
+
+ name() {
+ if (!this.size) {
+ return this.summary
+ }
+ return `${this.summary} – ${this.size}`
+ },
+ size() {
+ const totalSize = this.nodes.reduce((total, node) => total + node.size || 0, 0)
+ const size = parseInt(totalSize, 10) || 0
+ if (typeof size !== 'number' || size < 0) {
+ return null
+ }
+ return formatFileSize(size, true)
+ },
+ summary(): string {
+ if (this.isSingleNode) {
+ const node = this.nodes[0]
+ return node.attributes?.displayname || node.basename
+ }
+
+ return getSummaryFor(this.nodes)
+ },
+ },
+
+ methods: {
+ update(nodes: Node[]) {
+ this.nodes = nodes
+ this.$refs.previewImg.replaceChildren()
+
+ // Clone icon node from the list
+ nodes.slice(0, 3).forEach(node => {
+ const preview = document.querySelector(`[data-cy-files-list-row-fileid="${node.fileid}"] .files-list__row-icon img`)
+ if (preview) {
+ const previewElmt = this.$refs.previewImg as HTMLElement
+ previewElmt.appendChild(preview.parentNode.cloneNode(true))
+ }
+ })
+
+ this.$nextTick(() => {
+ this.$emit('loaded', this.$el)
+ })
+ },
+ },
+})
+</script>
+
+<style lang="scss">
+$size: 28px;
+$stack-shift: 6px;
+
+.files-list-drag-image {
+ position: absolute;
+ top: -9999px;
+ inset-inline-start: -9999px;
+ display: flex;
+ overflow: hidden;
+ align-items: center;
+ height: $size + $stack-shift;
+ padding: $stack-shift $stack-shift * 2;
+ background: var(--color-main-background);
+
+ &__icon,
+ .files-list__row-icon-preview-container {
+ display: flex;
+ overflow: hidden;
+ align-items: center;
+ justify-content: center;
+ width: $size - $stack-shift;
+ height: $size - $stack-shift;;
+ border-radius: var(--border-radius);
+ }
+
+ &__icon {
+ overflow: visible;
+ margin-inline-end: $stack-shift * 2;
+
+ img {
+ max-width: 100%;
+ max-height: 100%;
+ }
+
+ .material-design-icon {
+ color: var(--color-text-maxcontrast);
+ &.folder-icon {
+ color: var(--color-primary-element);
+ }
+ }
+
+ // Previews container
+ > span {
+ display: flex;
+
+ // Stack effect if more than one element
+ // Max 3 elements
+ > .files-list__row-icon-preview-container + .files-list__row-icon-preview-container {
+ margin-top: $stack-shift;
+ margin-inline-start: $stack-shift * 2 - $size;
+ & + .files-list__row-icon-preview-container {
+ margin-top: $stack-shift * 2;
+ }
+ }
+
+ // If we have manually clone the preview,
+ // let's hide any fallback icons
+ &:not(:empty) + * {
+ display: none;
+ }
+ }
+ }
+
+ &__name {
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ }
+}
+
+</style>