Slave/ui polishing #13

Merged
nobswebdev merged 14 commits from slave/ui-polishing into master 2026-10-30 19:49:55 +00:00
Showing only changes of commit 26899fdf66 - Show all commits
@@ -38,6 +38,16 @@
:metadata-error-count="metadataErrorCount" :metadata-error-count="metadataErrorCount"
@upload="onUploadFromModal" @upload="onUploadFromModal"
/> />
<Teleport to="body">
<div v-if="showFileDragOverlay" class="pointer-events-none fixed inset-0 z-70 bg-background/70">
<div
class="absolute inset-[5%] flex items-center justify-center rounded-lg border-2 border-dashed border-primary"
>
<p class="text-2xl font-medium text-foreground sm:text-3xl">Drop files</p>
</div>
</div>
</Teleport>
</div> </div>
</template> </template>
@@ -77,19 +87,28 @@ const emit = defineEmits<{
const fileInputRef = ref<HTMLInputElement | null>(null); const fileInputRef = ref<HTMLInputElement | null>(null);
const configureModalOpen = ref(false); const configureModalOpen = ref(false);
const fileDragDepth = ref(0);
onMounted(() => { onMounted(() => {
window.addEventListener('dragenter', onWindowDragEnter);
window.addEventListener('dragleave', onWindowDragLeave);
window.addEventListener('dragover', onWindowDragOver); window.addEventListener('dragover', onWindowDragOver);
window.addEventListener('drop', onWindowDrop); window.addEventListener('drop', onWindowDrop);
window.addEventListener('dragend', onWindowDragEnd);
window.addEventListener('paste', onWindowPaste); window.addEventListener('paste', onWindowPaste);
}); });
onBeforeUnmount(() => { onBeforeUnmount(() => {
window.removeEventListener('dragenter', onWindowDragEnter);
window.removeEventListener('dragleave', onWindowDragLeave);
window.removeEventListener('dragover', onWindowDragOver); window.removeEventListener('dragover', onWindowDragOver);
window.removeEventListener('drop', onWindowDrop); window.removeEventListener('drop', onWindowDrop);
window.removeEventListener('dragend', onWindowDragEnd);
window.removeEventListener('paste', onWindowPaste); window.removeEventListener('paste', onWindowPaste);
}); });
const showFileDragOverlay = computed(() => fileDragDepth.value > 0 && !props.disabled);
const maxImageSizeLabel = computed(() => prettyBytes(maxImageBytes, { binary: true, maximumFractionDigits: 1 })); const maxImageSizeLabel = computed(() => prettyBytes(maxImageBytes, { binary: true, maximumFractionDigits: 1 }));
const emitPicked = (picked: File[]): void => { const emitPicked = (picked: File[]): void => {
@@ -117,15 +136,37 @@ const isFileDrag = (event: DragEvent): boolean => {
return Boolean(event.dataTransfer?.types.includes('Files')); return Boolean(event.dataTransfer?.types.includes('Files'));
}; };
const onWindowDragEnter = (event: DragEvent): void => {
if (props.disabled || !isFileDrag(event)) {
return;
}
fileDragDepth.value += 1;
};
const onWindowDragLeave = (event: DragEvent): void => {
if (!isFileDrag(event)) {
return;
}
fileDragDepth.value = Math.max(0, fileDragDepth.value - 1);
};
const onWindowDragOver = (event: DragEvent): void => { const onWindowDragOver = (event: DragEvent): void => {
if (props.disabled || !isFileDrag(event)) { if (props.disabled || !isFileDrag(event)) {
return; return;
} }
event.preventDefault(); event.preventDefault();
if (fileDragDepth.value === 0) {
fileDragDepth.value = 1;
}
}; };
const onWindowDrop = (event: DragEvent): void => { const onWindowDrop = (event: DragEvent): void => {
resetFileDragDepth();
if (props.disabled) { if (props.disabled) {
return; return;
} }
@@ -137,6 +178,10 @@ const onWindowDrop = (event: DragEvent): void => {
emitPicked(picked); emitPicked(picked);
}; };
const onWindowDragEnd = (): void => {
resetFileDragDepth();
};
const isPasswordInputTarget = (target: EventTarget | null): boolean => { const isPasswordInputTarget = (target: EventTarget | null): boolean => {
if (!(target instanceof HTMLElement)) { if (!(target instanceof HTMLElement)) {
return false; return false;
@@ -175,5 +220,9 @@ const closeConfigureModal = (): void => {
configureModalOpen.value = false; configureModalOpen.value = false;
}; };
const resetFileDragDepth = (): void => {
fileDragDepth.value = 0;
};
defineExpose({ clearInput, closeConfigureModal }); defineExpose({ clearInput, closeConfigureModal });
</script> </script>