@geode/opengeodeweb-front 10.10.2-rc.1 → 10.10.2-rc.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/app/components/DragAndDrop.vue +116 -78
- package/app/components/DragAndDropInternal/DragAndDropInline.vue +73 -0
- package/app/components/DragAndDropInternal/DragAndDropOverlay.vue +162 -0
- package/app/components/FileSelector.vue +3 -1
- package/app/components/FileUploader.vue +69 -8
- package/package.json +1 -1
- package/tests/vitest.config.js +6 -0
|
@@ -1,106 +1,144 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
-
import
|
|
2
|
+
import { onMounted, onUnmounted, ref } from "vue";
|
|
3
|
+
import DragAndDropInline from "./DragAndDropInternal/DragAndDropInline.vue";
|
|
4
|
+
import DragAndDropOverlay from "./DragAndDropInternal/DragAndDropOverlay.vue";
|
|
3
5
|
|
|
4
|
-
const { multiple, accept, loading, showExtensions } =
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
}
|
|
6
|
+
const { multiple, accept, loading, showExtensions, fullscreen, inline, showOverlay, texts } =
|
|
7
|
+
defineProps({
|
|
8
|
+
multiple: { type: Boolean, default: false },
|
|
9
|
+
accept: { type: String, default: "" },
|
|
10
|
+
loading: { type: Boolean, default: false },
|
|
11
|
+
showExtensions: { type: Boolean, default: true },
|
|
12
|
+
fullscreen: { type: Boolean, default: false },
|
|
13
|
+
inline: { type: Boolean, default: true },
|
|
14
|
+
showOverlay: { type: Boolean, default: true },
|
|
15
|
+
texts: {
|
|
16
|
+
type: Object,
|
|
17
|
+
default: () => ({
|
|
18
|
+
idle: "Click or drag and drop",
|
|
19
|
+
drop: "Drop files here",
|
|
20
|
+
loading: "Loading...",
|
|
21
|
+
}),
|
|
22
|
+
},
|
|
23
|
+
});
|
|
10
24
|
|
|
11
25
|
const emit = defineEmits(["files-selected"]);
|
|
12
26
|
|
|
13
27
|
const isDragging = ref(false);
|
|
28
|
+
const isInternalDrag = ref(false);
|
|
29
|
+
const dragCounter = ref(0);
|
|
14
30
|
const fileInput = ref(undefined);
|
|
15
31
|
|
|
16
32
|
function triggerFileDialog() {
|
|
17
33
|
fileInput.value?.click();
|
|
18
34
|
}
|
|
19
35
|
|
|
20
|
-
function
|
|
36
|
+
function onDragEnter(event) {
|
|
37
|
+
if (!isInternalDrag.value && event.dataTransfer.types.includes("Files")) {
|
|
38
|
+
dragCounter.value += 1;
|
|
39
|
+
isDragging.value = true;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function onDragLeave() {
|
|
44
|
+
dragCounter.value -= 1;
|
|
45
|
+
if (dragCounter.value <= 0) {
|
|
46
|
+
isDragging.value = false;
|
|
47
|
+
dragCounter.value = 0;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function onDragOver(event) {
|
|
52
|
+
if (!isInternalDrag.value && event.dataTransfer.types.includes("Files")) {
|
|
53
|
+
event.preventDefault();
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function onDrop(event) {
|
|
58
|
+
event.preventDefault();
|
|
59
|
+
dragCounter.value = 0;
|
|
21
60
|
isDragging.value = false;
|
|
22
61
|
const files = [...event.dataTransfer.files];
|
|
23
|
-
|
|
62
|
+
if (files.length > 0) {
|
|
63
|
+
emit("files-selected", files);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function onKeyDown(event) {
|
|
68
|
+
if (event.key === "Escape") {
|
|
69
|
+
event.preventDefault();
|
|
70
|
+
event.stopPropagation();
|
|
71
|
+
isDragging.value = false;
|
|
72
|
+
dragCounter.value = 0;
|
|
73
|
+
}
|
|
24
74
|
}
|
|
25
75
|
|
|
26
76
|
function handleFileSelect(event) {
|
|
27
77
|
const files = [...event.target.files];
|
|
28
|
-
|
|
78
|
+
if (files.length > 0) {
|
|
79
|
+
emit("files-selected", files);
|
|
80
|
+
}
|
|
29
81
|
event.target.value = "";
|
|
30
82
|
}
|
|
83
|
+
|
|
84
|
+
function onInternalDragStart() {
|
|
85
|
+
isInternalDrag.value = true;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function onInternalDragEnd() {
|
|
89
|
+
isInternalDrag.value = false;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
onMounted(() => {
|
|
93
|
+
globalThis.addEventListener("dragstart", onInternalDragStart);
|
|
94
|
+
globalThis.addEventListener("dragend", onInternalDragEnd);
|
|
95
|
+
globalThis.addEventListener("dragenter", onDragEnter);
|
|
96
|
+
globalThis.addEventListener("dragover", onDragOver);
|
|
97
|
+
globalThis.addEventListener("dragleave", onDragLeave);
|
|
98
|
+
globalThis.addEventListener("drop", onDrop);
|
|
99
|
+
globalThis.addEventListener("keydown", onKeyDown);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
onUnmounted(() => {
|
|
103
|
+
globalThis.removeEventListener("dragstart", onInternalDragStart);
|
|
104
|
+
globalThis.removeEventListener("dragend", onInternalDragEnd);
|
|
105
|
+
globalThis.removeEventListener("dragenter", onDragEnter);
|
|
106
|
+
globalThis.removeEventListener("dragover", onDragOver);
|
|
107
|
+
globalThis.removeEventListener("dragleave", onDragLeave);
|
|
108
|
+
globalThis.removeEventListener("drop", onDrop);
|
|
109
|
+
globalThis.removeEventListener("keydown", onKeyDown);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
defineExpose({ triggerFileDialog });
|
|
31
113
|
</script>
|
|
32
114
|
|
|
33
115
|
<template>
|
|
34
|
-
<
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
@dragleave.prevent="isDragging = false"
|
|
58
|
-
@drop.prevent="handleDrop"
|
|
59
|
-
>
|
|
60
|
-
<v-card-text class="pa-8">
|
|
61
|
-
<v-sheet
|
|
62
|
-
class="mx-auto mb-6 d-flex align-center justify-center"
|
|
63
|
-
:color="isHovering || isDragging ? 'white' : 'rgba(255, 255, 255, 0.1)'"
|
|
64
|
-
rounded="circle"
|
|
65
|
-
width="80"
|
|
66
|
-
height="80"
|
|
67
|
-
style="transition: all 0.3s ease"
|
|
68
|
-
>
|
|
69
|
-
<v-icon
|
|
70
|
-
:icon="loading ? 'mdi-loading' : 'mdi-cloud-upload'"
|
|
71
|
-
size="40"
|
|
72
|
-
:color="isHovering || isDragging ? 'primary' : 'white'"
|
|
73
|
-
:class="{ rotating: loading }"
|
|
74
|
-
/>
|
|
75
|
-
</v-sheet>
|
|
76
|
-
|
|
77
|
-
<v-card-title
|
|
78
|
-
class="text-h6 font-weight-bold justify-center pa-0 mb-1 text-white"
|
|
79
|
-
style="transition: color 0.3s ease"
|
|
80
|
-
>
|
|
81
|
-
{{
|
|
82
|
-
loading ? "Uploading..." : isDragging ? "Drop to upload" : "Click or Drag & Drop files"
|
|
83
|
-
}}
|
|
84
|
-
</v-card-title>
|
|
85
|
-
|
|
86
|
-
<v-card-subtitle v-if="showExtensions" class="text-body-2 pa-0">
|
|
87
|
-
{{ accept ? `(${accept} files)` : "All files allowed" }}
|
|
88
|
-
</v-card-subtitle>
|
|
89
|
-
</v-card-text>
|
|
90
|
-
|
|
91
|
-
<input
|
|
92
|
-
ref="fileInput"
|
|
93
|
-
type="file"
|
|
94
|
-
class="d-none"
|
|
95
|
-
:multiple="multiple"
|
|
96
|
-
:accept="accept"
|
|
97
|
-
@change="handleFileSelect"
|
|
98
|
-
/>
|
|
99
|
-
</GlassCard>
|
|
100
|
-
</v-hover>
|
|
116
|
+
<DragAndDropInline
|
|
117
|
+
v-if="inline"
|
|
118
|
+
:is-dragging
|
|
119
|
+
:loading
|
|
120
|
+
:texts
|
|
121
|
+
:accept
|
|
122
|
+
:show-extensions
|
|
123
|
+
@click="triggerFileDialog"
|
|
124
|
+
/>
|
|
125
|
+
|
|
126
|
+
<DragAndDropOverlay
|
|
127
|
+
v-if="isDragging && showOverlay"
|
|
128
|
+
:is-dragging
|
|
129
|
+
:show-overlay
|
|
130
|
+
:fullscreen
|
|
131
|
+
:loading
|
|
132
|
+
:texts
|
|
133
|
+
:multiple
|
|
134
|
+
:accept
|
|
135
|
+
:show-extensions
|
|
136
|
+
/>
|
|
137
|
+
|
|
138
|
+
<input ref="fileInput" type="file" class="d-none" :multiple :accept @change="handleFileSelect" />
|
|
101
139
|
</template>
|
|
102
140
|
|
|
103
|
-
<style
|
|
141
|
+
<style>
|
|
104
142
|
.rotating {
|
|
105
143
|
animation: rotate 1s linear infinite;
|
|
106
144
|
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import GlassCard from "@ogw_front/components/GlassCard";
|
|
3
|
+
|
|
4
|
+
const { isDragging, loading, texts, accept, showExtensions } = defineProps({
|
|
5
|
+
isDragging: { type: Boolean, required: true },
|
|
6
|
+
loading: { type: Boolean, required: true },
|
|
7
|
+
texts: {
|
|
8
|
+
type: Object,
|
|
9
|
+
default: () => ({
|
|
10
|
+
idle: "Click or drag and drop",
|
|
11
|
+
drop: "Drop files here",
|
|
12
|
+
loading: "Loading...",
|
|
13
|
+
}),
|
|
14
|
+
},
|
|
15
|
+
accept: { type: String, default: "" },
|
|
16
|
+
showExtensions: { type: Boolean, required: true },
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
const emit = defineEmits(["click"]);
|
|
20
|
+
</script>
|
|
21
|
+
|
|
22
|
+
<template>
|
|
23
|
+
<v-hover v-slot="{ isHovering, props: hoverProps }">
|
|
24
|
+
<GlassCard
|
|
25
|
+
v-bind="hoverProps"
|
|
26
|
+
class="drag-card-inline text-center cursor-pointer transition-swing"
|
|
27
|
+
:class="{ 'dragging-active': isDragging, 'elevation-8': isHovering }"
|
|
28
|
+
variant="ui"
|
|
29
|
+
@click="emit('click')"
|
|
30
|
+
>
|
|
31
|
+
<v-sheet
|
|
32
|
+
class="mx-auto mb-4 d-flex align-center justify-center transition-swing"
|
|
33
|
+
:color="isHovering || isDragging ? 'primary' : 'rgba(255,255,255,0.05)'"
|
|
34
|
+
rounded="circle"
|
|
35
|
+
width="64"
|
|
36
|
+
height="64"
|
|
37
|
+
>
|
|
38
|
+
<v-icon
|
|
39
|
+
:icon="loading ? 'mdi-loading' : 'mdi-cloud-upload'"
|
|
40
|
+
size="32"
|
|
41
|
+
:color="isHovering || isDragging ? 'white' : 'primary'"
|
|
42
|
+
:class="{ rotating: loading }"
|
|
43
|
+
/>
|
|
44
|
+
</v-sheet>
|
|
45
|
+
|
|
46
|
+
<v-card-text class="pa-0">
|
|
47
|
+
<v-sheet class="text-h6 font-weight-bold text-white d-block mb-1 bg-transparent">
|
|
48
|
+
{{ loading ? texts.loading : isDragging ? texts.drop : texts.idle }}
|
|
49
|
+
</v-sheet>
|
|
50
|
+
<v-sheet
|
|
51
|
+
v-if="accept && showExtensions"
|
|
52
|
+
class="text-body-2 text-white opacity-60 bg-transparent"
|
|
53
|
+
>
|
|
54
|
+
{{ accept }}
|
|
55
|
+
</v-sheet>
|
|
56
|
+
</v-card-text>
|
|
57
|
+
</GlassCard>
|
|
58
|
+
</v-hover>
|
|
59
|
+
</template>
|
|
60
|
+
|
|
61
|
+
<style scoped>
|
|
62
|
+
.drag-card-inline {
|
|
63
|
+
border: 2px dashed rgba(255, 255, 255, 0.1) !important;
|
|
64
|
+
background: rgba(255, 255, 255, 0.03) !important;
|
|
65
|
+
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.drag-card-inline:hover,
|
|
69
|
+
.drag-card-inline.dragging-active {
|
|
70
|
+
border-color: rgba(var(--v-theme-primary), 0.4) !important;
|
|
71
|
+
background: rgba(var(--v-theme-primary), 0.05) !important;
|
|
72
|
+
}
|
|
73
|
+
</style>
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
const { isDragging, showOverlay, fullscreen, loading, texts, multiple, accept, showExtensions } =
|
|
3
|
+
defineProps({
|
|
4
|
+
isDragging: { type: Boolean, required: true },
|
|
5
|
+
showOverlay: { type: Boolean, required: true },
|
|
6
|
+
fullscreen: { type: Boolean, required: true },
|
|
7
|
+
loading: { type: Boolean, required: true },
|
|
8
|
+
texts: {
|
|
9
|
+
type: Object,
|
|
10
|
+
default: () => ({
|
|
11
|
+
idle: "Click or drag and drop",
|
|
12
|
+
drop: "Drop files here",
|
|
13
|
+
loading: "Loading...",
|
|
14
|
+
}),
|
|
15
|
+
},
|
|
16
|
+
multiple: { type: Boolean, required: true },
|
|
17
|
+
accept: { type: String, default: "" },
|
|
18
|
+
showExtensions: { type: Boolean, required: true },
|
|
19
|
+
});
|
|
20
|
+
</script>
|
|
21
|
+
|
|
22
|
+
<template>
|
|
23
|
+
<Teleport to="body">
|
|
24
|
+
<v-fade-transition>
|
|
25
|
+
<v-overlay
|
|
26
|
+
v-if="isDragging && showOverlay"
|
|
27
|
+
:model-value="true"
|
|
28
|
+
:scrim="false"
|
|
29
|
+
class="drag-overlay"
|
|
30
|
+
content-class="w-100 h-100"
|
|
31
|
+
persistent
|
|
32
|
+
>
|
|
33
|
+
<v-container class="fill-height" :fluid="fullscreen">
|
|
34
|
+
<v-row align="center" justify="center">
|
|
35
|
+
<v-col
|
|
36
|
+
cols="12"
|
|
37
|
+
:md="fullscreen ? 12 : 8"
|
|
38
|
+
:lg="fullscreen ? 12 : 6"
|
|
39
|
+
class="text-center"
|
|
40
|
+
>
|
|
41
|
+
<v-sheet
|
|
42
|
+
class="mx-auto mb-8 pulse-animation d-flex align-center justify-center"
|
|
43
|
+
color="primary"
|
|
44
|
+
rounded="circle"
|
|
45
|
+
:width="fullscreen ? 140 : 120"
|
|
46
|
+
:height="fullscreen ? 140 : 120"
|
|
47
|
+
>
|
|
48
|
+
<v-icon icon="mdi-cloud-upload" :size="fullscreen ? 80 : 64" color="white" />
|
|
49
|
+
</v-sheet>
|
|
50
|
+
|
|
51
|
+
<v-sheet
|
|
52
|
+
class="font-weight-black mb-6 text-white text-glow d-block bg-transparent"
|
|
53
|
+
:class="fullscreen ? 'text-h2' : 'text-h3'"
|
|
54
|
+
>
|
|
55
|
+
{{
|
|
56
|
+
loading ? texts.loading : fullscreen ? "Drop your files here" : "Drop your files"
|
|
57
|
+
}}
|
|
58
|
+
</v-sheet>
|
|
59
|
+
|
|
60
|
+
<v-sheet
|
|
61
|
+
class="text-white opacity-70 d-block mb-8 mx-auto bg-transparent"
|
|
62
|
+
:class="fullscreen ? 'text-h5' : 'text-h6'"
|
|
63
|
+
:style="fullscreen ? 'max-width:700px' : 'max-width:500px'"
|
|
64
|
+
>
|
|
65
|
+
{{
|
|
66
|
+
multiple
|
|
67
|
+
? "Drag your files anywhere on the screen to import them."
|
|
68
|
+
: "Drag your file anywhere on the screen to import it."
|
|
69
|
+
}}
|
|
70
|
+
</v-sheet>
|
|
71
|
+
|
|
72
|
+
<v-chip
|
|
73
|
+
v-if="accept && showExtensions"
|
|
74
|
+
color="white"
|
|
75
|
+
variant="outlined"
|
|
76
|
+
:size="fullscreen ? 'x-large' : 'large'"
|
|
77
|
+
class="px-8 glow-chip"
|
|
78
|
+
>
|
|
79
|
+
Accepted formats: {{ accept }}
|
|
80
|
+
</v-chip>
|
|
81
|
+
|
|
82
|
+
<v-sheet
|
|
83
|
+
class="text-white opacity-40 d-block mt-12 bg-transparent"
|
|
84
|
+
:class="fullscreen ? 'text-h6' : 'text-body-2'"
|
|
85
|
+
>
|
|
86
|
+
Press
|
|
87
|
+
<v-kbd class="escape-kbd mx-1">Esc</v-kbd>
|
|
88
|
+
to cancel
|
|
89
|
+
</v-sheet>
|
|
90
|
+
</v-col>
|
|
91
|
+
</v-row>
|
|
92
|
+
</v-container>
|
|
93
|
+
|
|
94
|
+
<v-sheet v-if="fullscreen" class="drag-frame-border" color="transparent" />
|
|
95
|
+
</v-overlay>
|
|
96
|
+
</v-fade-transition>
|
|
97
|
+
</Teleport>
|
|
98
|
+
</template>
|
|
99
|
+
|
|
100
|
+
<style scoped>
|
|
101
|
+
.drag-overlay :deep(.v-overlay__content) {
|
|
102
|
+
background: rgba(0, 0, 0, 0.6);
|
|
103
|
+
backdrop-filter: blur(24px);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.drag-frame-border {
|
|
107
|
+
position: absolute;
|
|
108
|
+
inset: 32px;
|
|
109
|
+
border: 4px dashed rgba(255, 255, 255, 0.3);
|
|
110
|
+
border-radius: 32px;
|
|
111
|
+
pointer-events: none;
|
|
112
|
+
animation: border-glimmer 4s infinite ease-in-out;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.glow-chip {
|
|
116
|
+
background: rgba(255, 255, 255, 0.1) !important;
|
|
117
|
+
border-color: rgba(255, 255, 255, 0.4) !important;
|
|
118
|
+
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.escape-kbd {
|
|
122
|
+
padding: 6px 12px;
|
|
123
|
+
border-radius: 8px;
|
|
124
|
+
background: rgba(255, 255, 255, 0.2);
|
|
125
|
+
font-weight: bold;
|
|
126
|
+
border: 1px solid rgba(255, 255, 255, 0.4);
|
|
127
|
+
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.text-glow {
|
|
131
|
+
text-shadow: 0 0 30px rgba(255, 255, 255, 0.3);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.pulse-animation {
|
|
135
|
+
animation: pulse 2s infinite;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
@keyframes pulse {
|
|
139
|
+
0% {
|
|
140
|
+
transform: scale(1);
|
|
141
|
+
box-shadow: 0 0 0 0px rgba(var(--v-theme-primary), 0.4);
|
|
142
|
+
}
|
|
143
|
+
70% {
|
|
144
|
+
transform: scale(1.05);
|
|
145
|
+
box-shadow: 0 0 0 20px rgba(var(--v-theme-primary), 0);
|
|
146
|
+
}
|
|
147
|
+
100% {
|
|
148
|
+
transform: scale(1);
|
|
149
|
+
box-shadow: 0 0 0 0px rgba(var(--v-theme-primary), 0);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
@keyframes border-glimmer {
|
|
154
|
+
0%,
|
|
155
|
+
100% {
|
|
156
|
+
border-color: rgba(255, 255, 255, 0.2);
|
|
157
|
+
}
|
|
158
|
+
50% {
|
|
159
|
+
border-color: rgba(var(--v-theme-primary), 0.4);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
</style>
|
|
@@ -9,10 +9,11 @@ const schema = schemas.opengeodeweb_back.allowed_files;
|
|
|
9
9
|
|
|
10
10
|
const emit = defineEmits(["update_values", "increment_step", "decrement_step"]);
|
|
11
11
|
|
|
12
|
-
const { multiple, files, auto_upload } = defineProps({
|
|
12
|
+
const { multiple, files, auto_upload, show_overlay } = defineProps({
|
|
13
13
|
multiple: { type: Boolean, required: true },
|
|
14
14
|
files: { type: Array, default: () => [] },
|
|
15
15
|
auto_upload: { type: Boolean, default: true },
|
|
16
|
+
show_overlay: { type: Boolean, default: true },
|
|
16
17
|
});
|
|
17
18
|
|
|
18
19
|
const internal_files = ref(files);
|
|
@@ -63,6 +64,7 @@ await get_allowed_files();
|
|
|
63
64
|
accept,
|
|
64
65
|
files: internal_files,
|
|
65
66
|
auto_upload: internal_auto_upload,
|
|
67
|
+
show_overlay,
|
|
66
68
|
}"
|
|
67
69
|
@files_uploaded="files_uploaded_event"
|
|
68
70
|
/>
|
|
@@ -1,15 +1,25 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
-
import DragAndDrop from "@ogw_front/components/DragAndDrop";
|
|
3
2
|
import { useGeodeStore } from "@ogw_front/stores/geode";
|
|
3
|
+
import { useTemplateRef } from "vue";
|
|
4
|
+
|
|
5
|
+
import DragAndDrop from "@ogw_front/components/DragAndDrop";
|
|
4
6
|
|
|
5
7
|
const emit = defineEmits(["files_uploaded", "decrement_step", "reset_values"]);
|
|
6
8
|
|
|
7
|
-
const {
|
|
9
|
+
const {
|
|
10
|
+
multiple,
|
|
11
|
+
accept,
|
|
12
|
+
files,
|
|
13
|
+
auto_upload,
|
|
14
|
+
mini,
|
|
15
|
+
show_overlay: showOverlay,
|
|
16
|
+
} = defineProps({
|
|
8
17
|
multiple: { type: Boolean, required: true },
|
|
9
18
|
accept: { type: String, required: true },
|
|
10
19
|
files: { type: Array, required: false, default: [] },
|
|
11
20
|
auto_upload: { type: Boolean, required: false, default: false },
|
|
12
21
|
mini: { type: Boolean, required: false, default: false },
|
|
22
|
+
show_overlay: { type: Boolean, required: false, default: true },
|
|
13
23
|
});
|
|
14
24
|
|
|
15
25
|
const geodeStore = useGeodeStore();
|
|
@@ -17,6 +27,7 @@ const geodeStore = useGeodeStore();
|
|
|
17
27
|
const internal_files = ref(files);
|
|
18
28
|
const loading = ref(false);
|
|
19
29
|
const files_uploaded = ref(false);
|
|
30
|
+
const dragAndDropRef = useTemplateRef("dragAndDropRef");
|
|
20
31
|
|
|
21
32
|
const toggle_loading = useToggle(loading);
|
|
22
33
|
|
|
@@ -71,20 +82,53 @@ watch(internal_files, (value) => {
|
|
|
71
82
|
|
|
72
83
|
<template>
|
|
73
84
|
<DragAndDrop
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
:
|
|
85
|
+
v-if="!internal_files.length"
|
|
86
|
+
ref="dragAndDropRef"
|
|
87
|
+
:multiple
|
|
88
|
+
:accept
|
|
89
|
+
:loading
|
|
90
|
+
:show-extensions="false"
|
|
91
|
+
:inline="true"
|
|
92
|
+
:show-overlay="showOverlay"
|
|
93
|
+
:texts="{
|
|
94
|
+
idle: 'Select files',
|
|
95
|
+
drop: 'Drop files here',
|
|
96
|
+
loading: 'Loading...',
|
|
97
|
+
}"
|
|
98
|
+
@files-selected="processSelectedFiles"
|
|
99
|
+
/>
|
|
100
|
+
|
|
101
|
+
<DragAndDrop
|
|
102
|
+
v-else
|
|
103
|
+
ref="dragAndDropRef"
|
|
104
|
+
:multiple
|
|
105
|
+
:accept
|
|
106
|
+
:loading
|
|
77
107
|
:show-extensions="false"
|
|
108
|
+
:inline="false"
|
|
109
|
+
:show-overlay="showOverlay"
|
|
78
110
|
@files-selected="processSelectedFiles"
|
|
79
111
|
/>
|
|
80
112
|
|
|
81
|
-
<v-card-text v-if="internal_files.length" class="mt-6">
|
|
113
|
+
<v-card-text v-if="internal_files.length" class="mt-6 pa-0">
|
|
82
114
|
<v-sheet class="d-flex align-center mb-4" color="transparent">
|
|
83
115
|
<v-icon icon="mdi-file-check" class="mr-3" color="primary" size="24" />
|
|
84
|
-
<span class="text-subtitle-1 font-weight-bold text-white"> Selected
|
|
116
|
+
<span class="text-subtitle-1 font-weight-bold text-white"> Selected files </span>
|
|
85
117
|
<v-chip size="small" class="ml-3 bg-white-opacity-10" color="white" variant="flat">
|
|
86
118
|
{{ internal_files.length }}
|
|
87
119
|
</v-chip>
|
|
120
|
+
<v-spacer />
|
|
121
|
+
<v-btn
|
|
122
|
+
v-if="multiple"
|
|
123
|
+
variant="text"
|
|
124
|
+
color="white"
|
|
125
|
+
size="small"
|
|
126
|
+
class="text-none opacity-60"
|
|
127
|
+
prepend-icon="mdi-plus"
|
|
128
|
+
@click="dragAndDropRef?.triggerFileDialog"
|
|
129
|
+
>
|
|
130
|
+
Add
|
|
131
|
+
</v-btn>
|
|
88
132
|
</v-sheet>
|
|
89
133
|
|
|
90
134
|
<v-sheet class="d-flex flex-wrap ga-2" color="transparent">
|
|
@@ -108,7 +152,7 @@ watch(internal_files, (value) => {
|
|
|
108
152
|
</v-sheet>
|
|
109
153
|
</v-card-text>
|
|
110
154
|
|
|
111
|
-
<v-card-actions v-if="!auto_upload && internal_files.length" class="mt-
|
|
155
|
+
<v-card-actions v-if="!auto_upload && internal_files.length" class="mt-8 pa-0">
|
|
112
156
|
<v-btn
|
|
113
157
|
color="primary"
|
|
114
158
|
variant="flat"
|
|
@@ -124,3 +168,20 @@ watch(internal_files, (value) => {
|
|
|
124
168
|
</v-btn>
|
|
125
169
|
</v-card-actions>
|
|
126
170
|
</template>
|
|
171
|
+
|
|
172
|
+
<style scoped>
|
|
173
|
+
.border-dashed {
|
|
174
|
+
border: 2px dashed rgba(255, 255, 255, 0.1) !important;
|
|
175
|
+
transition: all 0.3s ease;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
.border-dashed:hover {
|
|
179
|
+
border-color: rgba(var(--v-theme-primary), 0.4) !important;
|
|
180
|
+
background: rgba(var(--v-theme-primary), 0.02) !important;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.custom-upload-btn {
|
|
184
|
+
letter-spacing: 0.5px;
|
|
185
|
+
box-shadow: 0 4px 15px rgba(var(--v-theme-primary), 0.3);
|
|
186
|
+
}
|
|
187
|
+
</style>
|
package/package.json
CHANGED
package/tests/vitest.config.js
CHANGED
|
@@ -7,6 +7,10 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
|
7
7
|
|
|
8
8
|
const RETRIES = 3;
|
|
9
9
|
const DEFAULT_RETRY = 0;
|
|
10
|
+
const TIMEOUTS = {
|
|
11
|
+
unit: 5000,
|
|
12
|
+
integration: 15_000,
|
|
13
|
+
};
|
|
10
14
|
|
|
11
15
|
const globalRetry = process.env.CI ? RETRIES : DEFAULT_RETRY;
|
|
12
16
|
|
|
@@ -27,6 +31,7 @@ export default defineConfig({
|
|
|
27
31
|
globals: false,
|
|
28
32
|
include: ["tests/unit/**/*.test.js"],
|
|
29
33
|
environment: "nuxt",
|
|
34
|
+
testTimeout: TIMEOUTS.unit,
|
|
30
35
|
setupFiles: [path.resolve(__dirname, "./setup_indexeddb.js")],
|
|
31
36
|
server: {
|
|
32
37
|
deps: {
|
|
@@ -43,6 +48,7 @@ export default defineConfig({
|
|
|
43
48
|
include: ["tests/integration/**/*.test.js"],
|
|
44
49
|
environment: "nuxt",
|
|
45
50
|
fileParallelism: false,
|
|
51
|
+
testTimeout: TIMEOUTS.integration,
|
|
46
52
|
setupFiles: [path.resolve(__dirname, "./setup_indexeddb.js")],
|
|
47
53
|
server: {
|
|
48
54
|
deps: {
|