Skip to content

Commit

Permalink
add logic to upload and preview file attachments
Browse files Browse the repository at this point in the history
  • Loading branch information
nwaughachukwuma committed Dec 6, 2024
1 parent 26b8637 commit 5441345
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 6 deletions.
19 changes: 13 additions & 6 deletions app/src/lib/components/ChatBoxAndWidget.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,17 @@
<script lang="ts">
import { Button } from '@/components/ui/button';
import { Card } from '@/components/ui/card';
import { PaperclipIcon, ArrowUpIcon } from 'lucide-svelte';
import { ArrowUpIcon } from 'lucide-svelte';
import { createEventDispatcher } from 'svelte';
import ChatBoxAttachment from './ChatBoxAttachment.svelte';
import ChatBoxAttachmentPreview from './ChatBoxAttachmentPreview.svelte';
export let searchTerm = '';
let selectedFiles: File[] = [];
const dispatch = createEventDispatcher<{
submitSearch: { value: string };
attach: void;
}>();
function handleKeyPress(ev: KeyboardEvent) {
Expand Down Expand Up @@ -58,9 +61,7 @@
<div class="flex flex-row-reverse items-center justify-between p-2 bg-zinc-800/30">
<slot name="tools">
<div class="flex items-center gap-2 px-2">
<Button variant="ghost" size="icon" class="text-zinc-400 hover:text-white" on:click>
<PaperclipIcon class="h-5 w-5" />
</Button>
<ChatBoxAttachment bind:selectedFiles on:attach />

<Button
variant="ghost"
Expand All @@ -74,6 +75,10 @@
</div>
</slot>
</div>

{#if selectedFiles.length > 0}
<ChatBoxAttachmentPreview bind:selectedFiles on:updateAttach />
{/if}
</Card>

<slot name="examples">
Expand All @@ -83,7 +88,9 @@
variant="outline"
class="bg-zinc-800/50 border-zinc-700 text-zinc-300 hover:bg-zinc-700/50 hover:text-white"
>
<span class="mr-2">{item.icon}</span>
{#if item.icon}
<span class="mr-2">{item.icon}</span>
{/if}
{item.text}
</Button>
{/each}
Expand Down
55 changes: 55 additions & 0 deletions app/src/lib/components/ChatBoxAttachment.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<script context="module">
const MAX_FILES = 5;
</script>

<script lang="ts">
import { PaperclipIcon } from 'lucide-svelte';
import { createEventDispatcher } from 'svelte';
import { Button } from './ui/button';
import { toast } from 'svelte-sonner';
export let selectedFiles: File[] = [];
const dispatch = createEventDispatcher<{
attach: { files: File[] };
}>();
let fileInput: HTMLInputElement;
function handleFileSelect(fileList: FileList | null) {
if (!fileList) {
return toast.error('No files selected');
}
const files = Array.from(fileList);
if (selectedFiles.length + files.length > MAX_FILES) {
alert(`You can only upload up to ${MAX_FILES} files`);
return;
}
selectedFiles = [...selectedFiles, ...files];
dispatch('attach', { files: selectedFiles });
// Reset input
fileInput.value = '';
}
</script>

<Button
variant="ghost"
size="icon"
class="text-zinc-400 hover:text-white"
disabled={selectedFiles.length >= MAX_FILES}
on:click={() => fileInput.click()}
>
<PaperclipIcon class="h-5 w-5" />
</Button>

<input
type="file"
accept=".pdf,.txt"
multiple
class="hidden"
bind:this={fileInput}
on:change={(e) => handleFileSelect(e.currentTarget.files)}
/>
43 changes: 43 additions & 0 deletions app/src/lib/components/ChatBoxAttachmentPreview.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<script lang="ts">
import { XIcon } from 'lucide-svelte';
import { Button } from './ui/button';
import { createEventDispatcher } from 'svelte';
export let selectedFiles: File[] = [];
const dispatch = createEventDispatcher<{ updateAttach: { files: File[] } }>();
function removeFile(index: number) {
selectedFiles = selectedFiles.filter((_, i) => i !== index);
dispatch('updateAttach', { files: selectedFiles });
}
function formatFileSize(bytes: number): string {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}
</script>

<div class="p-2 space-y-2 bg-zinc-800/30">
{#each selectedFiles as file, index}
<div class="flex items-center justify-between bg-zinc-700/30 rounded p-2">
<div class="flex-1 min-w-0">
<p class="text-sm text-white truncate">{file.name}</p>
<p class="text-xs text-zinc-400">
{formatFileSize(file.size)} • {file.type || 'text/plain'}
</p>
</div>
<Button
variant="ghost"
size="icon"
class="text-zinc-400 hover:text-white"
on:click={() => removeFile(index)}
>
<XIcon class="h-4 w-4" />
</Button>
</div>
{/each}
</div>

0 comments on commit 5441345

Please sign in to comment.