102 lines
3.8 KiB
Svelte
102 lines
3.8 KiB
Svelte
|
<script lang="ts">
|
||
|
import {locals} from '../../ts/stores.js';
|
||
|
import Message from "../components/Message.svelte";
|
||
|
import Icon from "./Icon.svelte";
|
||
|
import {getContext} from "svelte";
|
||
|
|
||
|
export let type: string;
|
||
|
export let name: string;
|
||
|
type FieldValue = string | number | Record<string, FieldValue>;
|
||
|
export let value: FieldValue | undefined = undefined;
|
||
|
export let placeholder: string | undefined = undefined;
|
||
|
export let hint: string | undefined = undefined;
|
||
|
export let extraData: string[] | undefined = undefined;
|
||
|
export let icon: string | undefined = undefined;
|
||
|
|
||
|
const formId = getContext('formId');
|
||
|
const fieldId = `${formId}-${name}-field`;
|
||
|
|
||
|
const validation = $locals.validation()?.[name];
|
||
|
const previousFormData = $locals.previousFormData() || [];
|
||
|
|
||
|
value = type !== 'hidden' && previousFormData[name] || value || validation?.value || '';
|
||
|
|
||
|
function durationValue(f: string): number {
|
||
|
if (previousFormData[name]) {
|
||
|
return value[f];
|
||
|
}
|
||
|
|
||
|
switch (f) {
|
||
|
case 's':
|
||
|
return value % 60;
|
||
|
case 'm':
|
||
|
return (value - value % 60) / 60 % 60;
|
||
|
case 'h':
|
||
|
return (value - value % 3600) / 3600;
|
||
|
default:
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function handleInput(e) {
|
||
|
// in here, you can switch on type and implement
|
||
|
// whatever behaviour you need
|
||
|
value = type.match(/^(number|range)$/)
|
||
|
? +e.target.value
|
||
|
: e.target.value;
|
||
|
}
|
||
|
</script>
|
||
|
|
||
|
{#if type === 'hidden'}
|
||
|
{#if validation}
|
||
|
<Message type="error" content={validation.message}/>
|
||
|
{/if}
|
||
|
<input type="hidden" name={name} value={value}>
|
||
|
{:else}
|
||
|
<div class="form-field" class:inline={type === 'checkbox'}>
|
||
|
<div class="control">
|
||
|
{#if icon}
|
||
|
<Icon name={icon}/>
|
||
|
{/if}
|
||
|
|
||
|
{#if type === 'duration'}
|
||
|
<div class="input-group">
|
||
|
{#each extraData as f}
|
||
|
<div class="time-input">
|
||
|
<input type="number" name="{name}[{f}]" id="{fieldId}-{f}"
|
||
|
value={durationValue(f)}
|
||
|
min="0" max={(f === 's' || f === 'm') && '60' || undefined}
|
||
|
{...$$restProps}>
|
||
|
<label for="{fieldId}-{f}">{{ f }}</label>
|
||
|
</div>
|
||
|
{/each}
|
||
|
</div>
|
||
|
{:else if type === 'select'}
|
||
|
<select name={name} id={fieldId} {...$$restProps} on:input={handleInput}>
|
||
|
{#each extraData as option}
|
||
|
<option value={(option.display === undefined || option.value !== undefined) && (option.value || option)}
|
||
|
selected={value === (option.value || option)}>{option.display || option}</option>
|
||
|
{/each}
|
||
|
</select>
|
||
|
<i data-feather="chevron-down"></i>
|
||
|
{:else if type === 'textarea'}
|
||
|
<textarea {name} id={fieldId} bind:value={value} {...$$restProps}></textarea>
|
||
|
{:else if type === 'checkbox'}
|
||
|
<input {type} {name} id={fieldId} checked={value === 'on'} {...$$restProps}>
|
||
|
{:else}
|
||
|
<input {type} {name} id={fieldId} {value} {...$$restProps} on:input={handleInput}>
|
||
|
{/if}
|
||
|
|
||
|
<label for="{fieldId}{type === 'duration' && '-' + extraData[0] || ''}">{@html placeholder || ''}<slot/></label>
|
||
|
</div>
|
||
|
|
||
|
{#if validation}
|
||
|
<div class="error"><i data-feather="x-circle"></i> {validation.message}</div>
|
||
|
{/if}
|
||
|
{#if hint}
|
||
|
<div class="hint"><i data-feather="info"></i> {hint}</div>
|
||
|
{/if}
|
||
|
</div>
|
||
|
{/if}
|
||
|
|