piclets / src /lib /components /Piclets /AddToRosterDialog.svelte
Fraser's picture
fix err
cf7c859
<script lang="ts">
import type { PicletInstance } from '$lib/db/schema';
import PicletCard from './PicletCard.svelte';
import { moveToRoster } from '$lib/db/piclets';
interface Props {
position: number;
availablePiclets: PicletInstance[];
onClose: () => void;
onAdded?: () => void;
}
let { position, availablePiclets, onClose, onAdded }: Props = $props();
let isAdding = $state(false);
async function handleAddToRoster(piclet: PicletInstance) {
if (!piclet.id || isAdding) return;
isAdding = true;
try {
await moveToRoster(piclet.id, position);
onAdded?.();
onClose();
} catch (err) {
console.error('Failed to add to roster:', err);
} finally {
isAdding = false;
}
}
</script>
<div class="dialog-overlay" onclick={(e) => e.target === e.currentTarget && onClose()} onkeydown={(e) => e.key === 'Escape' && onClose()} role="button" tabindex="0" aria-label="Close dialog">
<div class="dialog-content">
<header class="dialog-header">
<h2>Add to Roster</h2>
<button class="close-btn" onclick={onClose} aria-label="Close">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M6 18L18 6M6 6l12 12"></path>
</svg>
</button>
</header>
<div class="dialog-body">
{#if availablePiclets.length === 0}
<div class="empty-state">
<p>No piclets available in storage.</p>
<p class="hint">Scan objects to discover new piclets!</p>
</div>
{:else}
<p class="instruction">Select a piclet to add to position {position + 1}:</p>
<div class="piclets-grid">
{#each availablePiclets as piclet}
<button
class="piclet-option"
onclick={() => handleAddToRoster(piclet)}
disabled={isAdding}
>
<PicletCard piclet={piclet} size={100} />
</button>
{/each}
</div>
{/if}
</div>
</div>
</div>
<style>
.dialog-overlay {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
padding: 1rem;
}
.dialog-content {
background: white;
border-radius: 16px;
width: 100%;
max-width: 600px;
max-height: 80vh;
overflow: hidden;
display: flex;
flex-direction: column;
}
.dialog-header {
padding: 1rem;
border-bottom: 1px solid #e5e5ea;
position: relative;
}
.dialog-header h2 {
margin: 0;
text-align: center;
font-size: 1.25rem;
}
.close-btn {
position: absolute;
top: 1rem;
right: 1rem;
background: none;
border: none;
padding: 0;
width: 24px;
height: 24px;
cursor: pointer;
color: #8e8e93;
}
.dialog-body {
flex: 1;
overflow-y: auto;
padding: 1rem;
}
.empty-state {
text-align: center;
padding: 3rem 1rem;
color: #666;
}
.empty-state p {
margin: 0 0 0.5rem;
}
.hint {
font-size: 0.875rem;
color: #8e8e93;
}
.instruction {
margin: 0 0 1rem;
color: #666;
text-align: center;
}
.piclets-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(110px, 1fr));
gap: 1rem;
justify-items: center;
}
.piclet-option {
background: none;
border: none;
padding: 0;
cursor: pointer;
transition: transform 0.2s;
}
.piclet-option:not(:disabled):hover {
transform: scale(1.05);
}
.piclet-option:not(:disabled):active {
transform: scale(0.95);
}
.piclet-option:disabled {
opacity: 0.6;
cursor: not-allowed;
}
</style>