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