131 lines
3.2 KiB
Svelte
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} <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>
|