63 lines
1.5 KiB
Svelte
63 lines
1.5 KiB
Svelte
<script lang="ts">
|
|
import Button from '$lib/components/ui/button.svelte';
|
|
import type { ButtonVariant } from '$lib/components/ui/button.svelte';
|
|
import { isAcceptedWorkspaceFile, WORKSPACE_ACCEPT_TYPES } from '$lib/zip-selection';
|
|
import type { Snippet } from 'svelte';
|
|
|
|
type Props = {
|
|
/** Identifies the hidden input for tests, e.g. `replace-zip` or `append-zip`. */
|
|
pickerId: string;
|
|
ariaLabel: string;
|
|
onFilesSelected: (files: File[]) => void;
|
|
children: Snippet;
|
|
multiple?: boolean;
|
|
disabled?: boolean;
|
|
variant?: ButtonVariant;
|
|
class?: string;
|
|
};
|
|
|
|
let {
|
|
pickerId,
|
|
ariaLabel,
|
|
onFilesSelected,
|
|
children,
|
|
multiple = true,
|
|
disabled = false,
|
|
variant = 'primary',
|
|
class: className = ''
|
|
}: Props = $props();
|
|
|
|
let fileInput = $state<HTMLInputElement | null>(null);
|
|
|
|
const handleFileSelection = (event: Event) => {
|
|
const input = event.currentTarget as HTMLInputElement;
|
|
const files = Array.from(input.files ?? []).filter(isAcceptedWorkspaceFile);
|
|
|
|
// Reset first so selecting the same file again still fires `change`.
|
|
input.value = '';
|
|
|
|
if (files.length === 0) return;
|
|
onFilesSelected(multiple ? files : files.slice(0, 1));
|
|
};
|
|
</script>
|
|
|
|
<input
|
|
bind:this={fileInput}
|
|
class="hidden"
|
|
type="file"
|
|
accept={WORKSPACE_ACCEPT_TYPES}
|
|
{multiple}
|
|
data-zip-picker={pickerId}
|
|
onchange={handleFileSelection}
|
|
/>
|
|
|
|
<Button
|
|
{variant}
|
|
{disabled}
|
|
aria-label={ariaLabel}
|
|
class={className}
|
|
onclick={() => fileInput?.click()}
|
|
>
|
|
{@render children()}
|
|
</Button>
|