Fraser's picture
monster-piclet
55b1a24
<script lang="ts">
interface Props {
onImageSelected: (image: File) => void;
isProcessing?: boolean;
}
let { onImageSelected, isProcessing = false }: Props = $props();
let fileInput: HTMLInputElement;
let dragActive = $state(false);
let preview: string | null = $state(null);
function handleFileSelect(e: Event) {
const target = e.target as HTMLInputElement;
if (target.files && target.files[0]) {
processFile(target.files[0]);
}
}
function handleDrop(e: DragEvent) {
e.preventDefault();
dragActive = false;
if (e.dataTransfer?.files && e.dataTransfer.files[0]) {
processFile(e.dataTransfer.files[0]);
}
}
function handleDragOver(e: DragEvent) {
e.preventDefault();
dragActive = true;
}
function handleDragLeave(e: DragEvent) {
e.preventDefault();
dragActive = false;
}
function processFile(file: File) {
if (!file.type.startsWith('image/')) {
alert('Please upload an image file');
return;
}
// Create preview
const reader = new FileReader();
reader.onload = (e) => {
preview = e.target?.result as string;
};
reader.readAsDataURL(file);
onImageSelected(file);
}
function triggerFileSelect() {
fileInput.click();
}
</script>
<div class="upload-container">
<h3>Upload Your Photo</h3>
<p class="subtitle">Upload a photo that will inspire your monster creation</p>
<div
class="upload-area"
class:drag-active={dragActive}
class:has-preview={preview}
ondrop={handleDrop}
ondragover={handleDragOver}
ondragleave={handleDragLeave}
onclick={triggerFileSelect}
onkeypress={(e) => e.key === 'Enter' && triggerFileSelect()}
role="button"
tabindex="0"
>
{#if preview}
<img src={preview} alt="Preview" class="preview-image" />
<div class="overlay">
<p>Click to change image</p>
</div>
{:else}
<svg class="upload-icon" width="64" height="64" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
<polyline points="17 8 12 3 7 8"></polyline>
<line x1="12" y1="3" x2="12" y2="15"></line>
</svg>
<p class="upload-text">Drop an image here or click to upload</p>
<p class="upload-hint">Supports JPG, PNG, GIF</p>
{/if}
</div>
<input
type="file"
accept="image/*"
onchange={handleFileSelect}
bind:this={fileInput}
class="hidden-input"
disabled={isProcessing}
/>
</div>
<style>
.upload-container {
max-width: 600px;
margin: 0 auto;
text-align: center;
}
h3 {
margin-bottom: 0.5rem;
color: #333;
}
.subtitle {
color: #666;
margin-bottom: 2rem;
}
.upload-area {
border: 2px dashed #ccc;
border-radius: 12px;
padding: 3rem;
background: #fafafa;
transition: all 0.3s ease;
cursor: pointer;
position: relative;
overflow: hidden;
}
.upload-area:hover {
border-color: #007bff;
background: #f0f7ff;
}
.upload-area.drag-active {
border-color: #007bff;
background: #e3f2ff;
transform: scale(1.02);
}
.upload-area.has-preview {
padding: 0;
background: #fff;
}
.upload-icon {
color: #007bff;
margin-bottom: 1rem;
}
.upload-text {
font-size: 1.1rem;
color: #333;
margin-bottom: 0.5rem;
}
.upload-hint {
font-size: 0.9rem;
color: #666;
}
.hidden-input {
display: none;
}
.preview-image {
width: 100%;
height: 400px;
object-fit: contain;
display: block;
}
.overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.7);
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
transition: opacity 0.3s ease;
}
.upload-area:hover .overlay {
opacity: 1;
}
.overlay p {
color: white;
font-size: 1.1rem;
}
</style>