342 lines
7.6 KiB
Svelte
342 lines
7.6 KiB
Svelte
<script>
|
|
import { t } from 'svelte-i18n';
|
|
|
|
/** @type {string} */
|
|
export let name;
|
|
|
|
/** @type {string} */
|
|
export let type = 'text';
|
|
|
|
/** @type {string|Number|null} */
|
|
export let value;
|
|
|
|
/** @type {string} */
|
|
export let placeholder = '';
|
|
|
|
/** @type {Number} */
|
|
export let rows = 4;
|
|
|
|
/** @type {Array<{value: string | number, label: string, color?:string}>} */
|
|
export let options = [];
|
|
|
|
/** @type {Boolean} */
|
|
export let required = false;
|
|
|
|
/** @type {string} */
|
|
export let label = '';
|
|
|
|
/** @type {string} */
|
|
export let otherPasswordValue = '';
|
|
|
|
/** @type {boolean} */
|
|
export let toUpperCase = false;
|
|
|
|
/** @type {boolean} */
|
|
export let checked = false;
|
|
|
|
/** @type {boolean} */
|
|
export let readonly = false;
|
|
|
|
/** @type {string} */
|
|
export let backgroundColor = '--surface0'; // New prop for background color
|
|
|
|
/**
|
|
* @param {Event} event - The input event
|
|
*/
|
|
function handleInput(event) {
|
|
const target = event.target;
|
|
|
|
if (target instanceof HTMLInputElement) {
|
|
let inputValue = target.value;
|
|
if (toUpperCase) {
|
|
inputValue = inputValue.toUpperCase();
|
|
target.value = inputValue; // Update the input field value
|
|
}
|
|
value = inputValue;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Validates the field
|
|
* @param {string} name - The name of the field
|
|
* @param {string|Number|null} value - The value of the field
|
|
* @param {Boolean} required - The requirements of the field
|
|
* @returns {string|null} The error message or null if valid
|
|
*/
|
|
function validateField(name, value, required) {
|
|
if (value === null || (typeof value === 'string' && !value.trim() && !required)) return null;
|
|
switch (name) {
|
|
case 'membership_start_date':
|
|
return typeof value === 'string' && value.trim() ? null : $t('validation.date');
|
|
case 'email':
|
|
return typeof value === 'string' && /^\S+@\S+\.\S+$/.test(value)
|
|
? null
|
|
: $t('validation.email');
|
|
case 'password':
|
|
case 'password2':
|
|
if (typeof value === 'string' && value.length < 8) {
|
|
return $t('validation.password');
|
|
}
|
|
if (otherPasswordValue && value !== otherPasswordValue) {
|
|
return $t('validation.password_match');
|
|
}
|
|
return null;
|
|
case 'phone':
|
|
return typeof value === 'string' && /^\+?[0-9\s()-]{7,}$/.test(value)
|
|
? null
|
|
: $t('validation.phone');
|
|
case 'zip_code':
|
|
return typeof value === 'string' && /^\d{5}$/.test(value)
|
|
? null
|
|
: $t('validation.zip_code');
|
|
case 'iban':
|
|
return typeof value === 'string' && /^[A-Z]{2}\d{2}[A-Z0-9]{1,30}$/.test(value)
|
|
? null
|
|
: $t('validation.iban');
|
|
case 'bic':
|
|
return typeof value === 'string' &&
|
|
/^[A-Z]{6}[A-Z2-9][A-NP-Z0-9]([A-Z0-9]{3})?$/.test(value)
|
|
? null
|
|
: $t('validation.bic');
|
|
case 'licence_number':
|
|
return typeof value === 'string' && value.length == 11 ? null : $t('validation.licence');
|
|
|
|
default:
|
|
return typeof value === 'string' && !value.trim() && required
|
|
? $t('validation.required')
|
|
: null;
|
|
}
|
|
}
|
|
|
|
$: error = validateField(name, value, required);
|
|
$: selectedOption = options.find((option) => option.value == value);
|
|
$: selectedColor = selectedOption ? selectedOption.color : '';
|
|
</script>
|
|
|
|
<div
|
|
class="input-box {type === 'checkbox' ? 'checkbox-container' : ''}"
|
|
style="background-color: var({backgroundColor});"
|
|
>
|
|
{#if type === 'checkbox'}
|
|
<label class="form-control {readonly ? 'form-control--disabled' : ''}">
|
|
<input
|
|
type="checkbox"
|
|
{name}
|
|
{value}
|
|
{checked}
|
|
{readonly}
|
|
on:change={() => (checked = !checked)}
|
|
/>
|
|
<span class="checkbox-text"> {label} </span>
|
|
</label>
|
|
{:else}
|
|
<span class="label">{label}</span>
|
|
{/if}
|
|
<div class="input-error-container">
|
|
{#if error}
|
|
<span class="error-message">{error}</span>
|
|
{/if}
|
|
{#if type === 'select'}
|
|
<select
|
|
{name}
|
|
bind:value
|
|
{required}
|
|
class="input select"
|
|
style={selectedColor ? `color: ${selectedColor};` : ''}
|
|
>
|
|
{#each options as option}
|
|
<option value={option.value}>{option.label}</option>
|
|
{/each}
|
|
</select>
|
|
{:else if type === 'textarea'}
|
|
<textarea
|
|
{name}
|
|
{placeholder}
|
|
{required}
|
|
{value}
|
|
{readonly}
|
|
{rows}
|
|
class="input textarea {readonly ? 'readonly' : ''}"
|
|
style="height:{rows * 1.5}em;"
|
|
></textarea>
|
|
{:else if type != 'checkbox'}
|
|
<input
|
|
{name}
|
|
{type}
|
|
{placeholder}
|
|
{readonly}
|
|
{value}
|
|
{required}
|
|
on:input={handleInput}
|
|
on:blur={handleInput}
|
|
class="input {readonly ? 'readonly' : ''}"
|
|
/>
|
|
{/if}
|
|
</div>
|
|
</div>
|
|
|
|
<style>
|
|
:root {
|
|
--form-control-color: var(--green); /* Changed from #6bff55 */
|
|
--form-control-disabled: var(--overlay0); /* Changed from #959495 */
|
|
}
|
|
|
|
.form-control {
|
|
font-size: 1rem;
|
|
font-weight: bold;
|
|
line-height: 1.1;
|
|
display: grid;
|
|
grid-template-columns: 1.5em auto;
|
|
gap: 0.75em;
|
|
align-items: center;
|
|
opacity: 0.8;
|
|
color: var(--text);
|
|
}
|
|
|
|
.form-control--disabled {
|
|
color: var(--form-control-disabled);
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
input[type='checkbox'] {
|
|
-webkit-appearance: none;
|
|
appearance: none;
|
|
background-color: var(--surface0);
|
|
margin: 0;
|
|
font: inherit;
|
|
color: var(--text);
|
|
width: 1.75em;
|
|
height: 1.75em;
|
|
border: 0.15em solid var(--overlay0);
|
|
border-radius: 0.5em;
|
|
transform: translateY(-0.075em);
|
|
display: grid;
|
|
place-content: center;
|
|
transition: transform 0.2s ease-in-out;
|
|
}
|
|
|
|
input[type='checkbox']::before {
|
|
content: '';
|
|
width: 1em;
|
|
height: 1em;
|
|
clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%);
|
|
transform: scale(0);
|
|
transform-origin: bottom left;
|
|
transition: 120ms transform ease-in-out;
|
|
box-shadow: inset 1em 1em var(--form-control-color);
|
|
background-color: var(--crust);
|
|
}
|
|
|
|
input[type='checkbox']:checked::before {
|
|
transform: scale(1);
|
|
}
|
|
|
|
input[type='checkbox']:hover {
|
|
outline: max(2px, 0.15em) solid var(--lavender);
|
|
outline-offset: max(2px, 0.15em);
|
|
transform: scale(1.3);
|
|
}
|
|
|
|
input[type='checkbox']:disabled {
|
|
--form-control-color: var(--form-control-disabled);
|
|
color: var(--form-control-disabled);
|
|
cursor: not-allowed;
|
|
}
|
|
.readonly {
|
|
background-color: var(--surface0);
|
|
cursor: not-allowed;
|
|
opacity: 0.7;
|
|
color: var(--overlay1);
|
|
}
|
|
|
|
.checkbox-container {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
background-color: transparent;
|
|
margin: 0.5rem 0;
|
|
}
|
|
|
|
.checkbox-text {
|
|
font-size: 16px;
|
|
color: var(--text);
|
|
}
|
|
|
|
@media (min-width: 768px) {
|
|
.checkbox-text {
|
|
flex-direction: row;
|
|
align-items: center;
|
|
}
|
|
}
|
|
.select {
|
|
padding-right: 1.5em;
|
|
background-color: var(--surface0);
|
|
}
|
|
.input-error-container {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.5rem;
|
|
width: 100%;
|
|
max-width: 444px;
|
|
}
|
|
|
|
.error-message {
|
|
color: var(--red); /* Changed from #eb5424 */
|
|
font-size: 12px;
|
|
margin-bottom: 5px;
|
|
align-self: flex-start;
|
|
}
|
|
|
|
.input {
|
|
width: 100%;
|
|
}
|
|
input,
|
|
textarea,
|
|
select {
|
|
width: 100%;
|
|
padding: 0.75rem 0;
|
|
background-color: var(--surface0);
|
|
border: 1px solid var(--overlay0);
|
|
border-radius: 6px;
|
|
color: var(--text);
|
|
transition: border-color 0.2s ease-in-out;
|
|
}
|
|
|
|
input:focus,
|
|
textarea:focus,
|
|
select:focus {
|
|
outline: none;
|
|
border-color: var(--lavender);
|
|
}
|
|
|
|
input:hover:not(:disabled),
|
|
textarea:hover:not(:disabled),
|
|
select:hover:not(:disabled) {
|
|
border-color: var(--overlay2);
|
|
}
|
|
|
|
textarea {
|
|
resize: vertical;
|
|
min-height: 100px;
|
|
}
|
|
/* Add consistent spacing between input boxes */
|
|
.input-box {
|
|
padding: 0.5rem;
|
|
background-color: var(--surface0);
|
|
border-radius: 6px;
|
|
}
|
|
|
|
.input-box .label {
|
|
display: block;
|
|
margin-bottom: 0.5rem;
|
|
color: var(--lavender);
|
|
font-weight: 500;
|
|
}
|
|
|
|
/* Style select dropdown */
|
|
select option {
|
|
background-color: var(--base);
|
|
color: var(--text);
|
|
padding: 0.5rem;
|
|
}
|
|
</style>
|