your personal website on atproto - mirror
blento.app
1<script lang="ts">
2 import { Alert, Button, Subheading } from '@foxui/core';
3 import Modal from '$lib/components/modal/Modal.svelte';
4 import type { CreationModalComponentProps } from '../../types';
5
6 let { item = $bindable(), oncreate, oncancel }: CreationModalComponentProps = $props();
7
8 let errorMessage = $state('');
9 let fileInput = $state<HTMLInputElement | undefined>(undefined);
10
11 function handleFileSelect(event: Event) {
12 const input = event.target as HTMLInputElement;
13 const file = input.files?.[0];
14
15 if (!file) return;
16
17 const extension = file.name.toLowerCase().split('.').pop();
18 if (!['gltf', 'glb', 'stl', 'fbx'].includes(extension || '')) {
19 errorMessage = 'Please select a .gltf, .glb, .stl, or .fbx file';
20 return;
21 }
22
23 errorMessage = '';
24 item.cardData.modelFile = {
25 blob: file,
26 objectUrl: URL.createObjectURL(file),
27 name: file.name,
28 type: extension
29 };
30 }
31
32 function clearFile() {
33 if (item.cardData.modelFile?.objectUrl) {
34 URL.revokeObjectURL(item.cardData.modelFile.objectUrl);
35 }
36 item.cardData.modelFile = undefined;
37 }
38
39 function canCreate() {
40 if (!item.cardData.modelFile) {
41 errorMessage = 'Please upload a file';
42 return false;
43 }
44 return true;
45 }
46</script>
47
48<Modal open={true} closeButton={false}>
49 <Subheading>Add a 3D Model</Subheading>
50
51 <div>
52 <p class="text-base-600 dark:text-base-400 mb-2 text-sm">
53 Upload a 3D model file (.glb, .stl, .fbx, or .gltf)
54 </p>
55 {#if item.cardData.modelFile}
56 <div
57 class="bg-base-100 dark:bg-base-800 flex items-center justify-between rounded-lg border p-3"
58 >
59 <span class="text-sm">{item.cardData.modelFile.name}</span>
60 <Button size="sm" variant="ghost" onclick={clearFile}>Remove</Button>
61 </div>
62 {:else}
63 <input
64 bind:this={fileInput}
65 type="file"
66 accept=".gltf,.glb,.stl,.fbx"
67 onchange={handleFileSelect}
68 class="hidden"
69 />
70 <Button variant="secondary" onclick={() => fileInput?.click()} class="w-full">
71 <svg
72 xmlns="http://www.w3.org/2000/svg"
73 fill="none"
74 viewBox="0 0 24 24"
75 stroke-width="1.5"
76 stroke="currentColor"
77 class="mr-2 size-5"
78 >
79 <path
80 stroke-linecap="round"
81 stroke-linejoin="round"
82 d="M3 16.5v2.25A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75V16.5m-13.5-9L12 3m0 0 4.5 4.5M12 3v13.5"
83 />
84 </svg>
85 Choose File
86 </Button>
87 {/if}
88 </div>
89
90 {#if errorMessage}
91 <Alert type="error" title="Error"><span>{errorMessage}</span></Alert>
92 {/if}
93
94 <div class="mt-4 flex justify-end gap-2">
95 <Button onclick={oncancel} variant="ghost">Cancel</Button>
96 <Button
97 onclick={() => {
98 if (canCreate()) oncreate();
99 }}
100 >
101 Create
102 </Button>
103 </div>
104</Modal>