pegase-z3-frontend/src/lib/components/app/inputs/input-file.svelte
2025-08-10 12:00:35 +02:00

131 lines
3.2 KiB
Svelte

<script lang="ts">
import { Str, uniquId } from '$lib/tools'
type Props = Partial<Pick<HTMLInputElement,
| 'autocomplete'
| 'required'
| 'accept'
| 'multiple'
| 'title'
>> & {
id?: string
label?: string
helper?: string
outerClassList?: string | string[]
classList?: string | string[]
name?: string
value?: string
disabled?: boolean
errors?: string[]
validity?: string
size?:
| 'xs'
| 'sm'
| 'md'
| 'lg'
color?:
| 'ghost'
| 'primary'
| 'secondary'
| 'accent'
| 'info'
| 'success'
| 'warning'
| 'error'
}
let {
id = String(uniquId()),
outerClassList = [],
classList = [],
label,
helper,
disabled = $bindable(false),
size,
color,
errors = $bindable([]),
validity = $bindable(undefined),
...props
}: Props = $props()
let input: HTMLInputElement
let files: HTMLInputElement['files'] = $state(null)
const limitFileName = (fileName: string): string => {
const fileNameParts = fileName.split('.')
const ext = fileNameParts.indexOf('.') !== -1 ? fileNameParts.pop() : undefined
return Str.limit(fileNameParts.join('.')) + (ext ? `.${ext}` : '')
}
$effect(() => {
input.setCustomValidity(validity ?? '')
})
</script>
<div
class={[
...['flex', 'flex-col'],
...(outerClassList instanceof Array ? outerClassList : [outerClassList]),
].join(' ')}
>
{#if label}
<label
class="label"
for={id}
>
<span>{label}{#if props.required}&nbsp;<span class="text-error">*</span>{/if}</span>
</label>
{/if}
<button
class={[
...['btn btn-bordered md:hidden uppercase font-bold'],
...(classList instanceof Array ? classList : [classList]),
size ? `btn-${size}` : '',
color ? `btn-${color}` : 'btn-neutral',
].join(' ')}
onclick={(): void => { input.click() }}
type="button"
>Parcourir...</button>
<input
bind:this={input}
{id}
autocomplete={props.autocomplete}
autocorrect={props.autocomplete === 'off' ? 'off' : null}
data-form-type={props.autocomplete === 'off' ? 'other' : null}
data-lpignore={props.autocomplete === 'off' ? 'true' : null}
{disabled}
spellcheck={props.autocomplete === 'off' ? 'false' : null}
{...props}
class={[
...['file-input hidden md:inline'],
...(classList instanceof Array ? classList : [classList]),
size ? `input-${size}` : '',
color ? `input-${color}` : '',
].join(' ')}
type="file"
bind:files
/>
{#if files}
<div
class="label flex-col items-end"
class:hidden={!files.length}
>
{#each files as file, index(index)}
<span class="text-info mx-2">{limitFileName(file.name)} - <span class="font-extrabold">{Str.humanFileSize(file.size)}</span></span>
{/each}
</div>
{/if}
{#if helper}
<div class="label">
<span class="label-text-alt font-bold">{helper}</span>
</div>
{/if}
{#if errors.length}
<div class="label flex-wrap">
{#each errors as error, index(index)}
<span class="text-error">{error}</span>
{/each}
</div>
{/if}
</div>