Skip to content

Commit

Permalink
refactor: ♻️ Optimize file uploader
Browse files Browse the repository at this point in the history
  • Loading branch information
CPlusPatch committed Oct 31, 2024
1 parent 0ef2112 commit 7f274e7
Show file tree
Hide file tree
Showing 9 changed files with 263 additions and 203 deletions.
20 changes: 5 additions & 15 deletions components/composer/composer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ import RichTextboxInput from "../inputs/rich-textbox-input.vue";
import Note from "../social-elements/notes/note.vue";
import Button from "./button.vue";
// biome-ignore lint/style/useImportType: Biome doesn't see the Vue code
import FileUploader from "./file-uploader.vue";
import FileUploader, { type FileData } from "./uploader/uploader.vue";
const uploader = ref<InstanceType<typeof FileUploader> | undefined>(undefined);
const { Control_Enter, Command_Enter, Control_Alt } = useMagicKeys();
Expand All @@ -71,15 +71,7 @@ const openFilePicker = () => {
uploader.value?.openFilePicker();
};
const files = ref<
{
id: string;
file: File;
progress: number;
api_id?: string;
alt_text?: string;
}[]
>([]);
const files = ref<FileData[]>([]);
const handlePaste = (event: ClipboardEvent) => {
if (event.clipboardData) {
Expand All @@ -95,6 +87,7 @@ const handlePaste = (event: ClipboardEvent) => {
id: nanoid(),
file,
progress: 0,
uploading: true,
})),
);
}
Expand All @@ -109,11 +102,7 @@ watch(
files,
(newFiles) => {
// If a file is uploading, set loading to true
if (newFiles.some((file) => file.progress < 1)) {
loading.value = true;
} else {
loading.value = false;
}
loading.value = newFiles.some((file) => file.uploading);
},
{
deep: true,
Expand Down Expand Up @@ -143,6 +132,7 @@ onMounted(() => {
id: nanoid(),
file: new File([], file.url),
progress: 1,
uploading: false,
api_id: file.id,
alt_text: file.description ?? undefined,
}));
Expand Down
188 changes: 0 additions & 188 deletions components/composer/file-uploader.vue

This file was deleted.

36 changes: 36 additions & 0 deletions components/composer/uploader/alt-text-editor.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<template>
<Popover.Root :positioning="{
strategy: 'fixed',
}" @update:open="o => !o && $emit('update-alt-text', fileData.alt_text)">
<Popover.Trigger aria-hidden="true"
class="absolute top-1 left-1 p-1 bg-dark-800 ring-1 ring-white/5 text-white text-xs rounded size-6">
<iconify-icon icon="tabler:alt" width="none" class="size-4" />
</Popover.Trigger>
<Popover.Positioner class="!z-[100]">
<Popover.Content
class="p-1 bg-dark-400 rounded text-sm ring-1 ring-white/10 shadow-lg text-gray-300 !min-w-72">
<textarea :disabled="fileData.uploading" @keydown.enter.stop v-model="fileData.alt_text"
placeholder="Add alt text"
class="w-full p-2 text-sm prose prose-invert bg-dark-900 rounded focus:!ring-0 !ring-none !border-none !outline-none placeholder:text-zinc-500 appearance-none focus:!border-none focus:!outline-none" />
<Button theme="secondary" @click="$emit('update-alt-text', fileData.alt_text)" class="w-full"
:loading="fileData.uploading">
<span>Edit</span>
</Button>
</Popover.Content>
</Popover.Positioner>
</Popover.Root>
</template>

<script lang="ts" setup>
import { Popover } from "@ark-ui/vue";
import Button from "~/packages/ui/components/buttons/button.vue";
import type { FileData } from "./uploader.vue";
const props = defineProps<{
fileData: FileData;
}>();
defineEmits<{
"update-alt-text": [text?: string];
}>();
</script>
31 changes: 31 additions & 0 deletions components/composer/uploader/file-preview.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<template>
<div role="button" tabindex="0" :class="[
'size-28 bg-dark-800 rounded flex items-center relative justify-center ring-1 ring-white/20 overflow-hidden',
fileData.uploading && 'animate-pulse'
]" @keydown.enter="$emit('remove', fileData.id)">
<PreviewContent :file="fileData.file" />
<FileShadowOverlay />
<FileSize :size="fileData.file.size" :uploading="fileData.uploading" />
<RemoveButton @remove="$emit('remove', fileData.id)" />
<AltTextEditor v-if="fileData.api_id" :file-data="fileData"
@update-alt-text="(text) => $emit('update-alt-text', fileData.id, text)" />
</div>
</template>

<script lang="ts" setup>
import AltTextEditor from "./alt-text-editor.vue";
import FileShadowOverlay from "./file-shadow-overlay.vue";
import FileSize from "./file-size.vue";
import PreviewContent from "./preview-content.vue";
import RemoveButton from "./remove-button.vue";
import type { FileData } from "./uploader.vue";
defineProps<{
fileData: FileData;
}>();
defineEmits<{
remove: [id: string];
"update-alt-text": [id: string, text?: string];
}>();
</script>
3 changes: 3 additions & 0 deletions components/composer/uploader/file-shadow-overlay.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<template>
<div class="absolute inset-0 bg-black/70"></div>
</template>
26 changes: 26 additions & 0 deletions components/composer/uploader/file-size.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<template>
<div class="absolute bottom-1 right-1 p-1 bg-dark-800 text-white text-xs rounded cursor-default flex flex-row items-center gap-x-1"
aria-label="File size">
{{ formatBytes(size) }}
<iconify-icon v-if="uploading" icon="tabler:loader-2" width="none"
class="size-4 animate-spin text-primary-500" />
</div>
</template>

<script lang="ts" setup>
const props = defineProps<{
size: number;
uploading: boolean;
}>();
const formatBytes = (bytes: number) => {
if (bytes === 0) {
return "0 Bytes";
}
const k = 1000;
const dm = 2;
const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return `${Number.parseFloat((bytes / k ** i).toFixed(dm))} ${sizes[i]}`;
};
</script>
32 changes: 32 additions & 0 deletions components/composer/uploader/preview-content.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<template>
<template v-if="file.type.startsWith('image/')">
<img :src="createObjectURL(file)" class="w-full h-full object-cover cursor-default" alt="Preview of file" />
</template>
<template v-else-if="file.type.startsWith('video/')">
<video :src="createObjectURL(file)" class="w-full h-full object-cover cursor-default" />
</template>
<template v-else>
<iconify-icon :icon="getIcon(file.type)" width="none" class="size-6" />
</template>
</template>

<script lang="ts" setup>
const props = defineProps<{
file: File;
}>();
const createObjectURL = URL.createObjectURL;
const getIcon = (mimeType: string) => {
if (mimeType.startsWith("image/")) {
return "tabler:photo";
}
if (mimeType.startsWith("video/")) {
return "tabler:video";
}
if (mimeType.startsWith("audio/")) {
return "tabler:music";
}
return "tabler:file";
};
</script>
12 changes: 12 additions & 0 deletions components/composer/uploader/remove-button.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<template>
<button class="absolute top-1 right-1 p-1 bg-dark-800 text-white text-xs rounded size-6" role="button" tabindex="0"
@pointerup="$emit('remove')" @keydown.enter="$emit('remove')">
<iconify-icon icon="tabler:x" width="none" class="size-4" />
</button>
</template>

<script lang="ts" setup>
defineEmits<{
remove: [];
}>();
</script>
Loading

0 comments on commit 7f274e7

Please sign in to comment.