This commit is contained in:
Alex
2025-01-29 16:02:37 +01:00
parent f68ca9abc5
commit c2d5188765
8 changed files with 1632 additions and 1535 deletions

View File

@@ -1,304 +1,338 @@
<script>
import { createEventDispatcher } from "svelte";
import { t } from "svelte-i18n";
import { createEventDispatcher } from 'svelte';
import { t } from 'svelte-i18n';
/** @type {string} */
export let name;
/** @type {string} */
export let name;
/** @type {string} */
export let type = "text";
/** @type {string} */
export let type = 'text';
/** @type {string|Number|null} */
export let value;
/** @type {string|Number|null} */
export let value;
/** @type {string} */
export let placeholder = "";
/** @type {string} */
export let placeholder = '';
/** @type {Number} */
export let rows = 4;
/** @type {Number} */
export let rows = 4;
/** @type {Array<{value: string | number, label: string, color?:string}>} */
export let options = [];
/** @type {Array<{value: string | number, label: string, color?:string}>} */
export let options = [];
/** @type {Boolean} */
export let required = false;
/** @type {Boolean} */
export let required = false;
/** @type {string} */
export let label = "";
/** @type {string} */
export let label = '';
/** @type {string} */
export let otherPasswordValue = "";
/** @type {string} */
export let otherPasswordValue = '';
/** @type {boolean} */
export let toUpperCase = false;
/** @type {boolean} */
export let toUpperCase = false;
/** @type {boolean} */
export let checked = false;
/** @type {boolean} */
export let checked = false;
/** @type {boolean} */
export let readonly = false;
/** @type {boolean} */
export let readonly = false;
/**
* @param {Event} event - The input event
*/
function handleInput(event) {
const target = event.target;
/**
* @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;
}
}
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");
/**
* 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;
}
}
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 : "";
$: 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' : ''}">
{#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;"
/>
{:else if type != "checkbox"}
<input
{name}
{type}
{placeholder}
{readonly}
{value}
{required}
on:input={handleInput}
on:blur={handleInput}
class="input {readonly ? 'readonly' : ''}"
/>
{/if}
</div>
{#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: #6bff55;
--form-control-disabled: #959495;
}
: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;
}
.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;
}
.form-control--disabled {
color: var(--form-control-disabled);
cursor: not-allowed;
}
input[type="checkbox"] {
-webkit-appearance: none;
appearance: none;
background-color: var(--form-background);
margin: 0;
font: inherit;
color: currentColor;
width: 1.75em;
height: 1.75em;
border: 0.15em solid currentColor;
border-radius: 0.5em;
transform: translateY(-0.075em);
display: grid;
place-content: center;
transition: transform 0.2s ease-in-out;
}
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: CanvasText;
}
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']:checked::before {
transform: scale(1);
}
input[type="checkbox"]:hover {
outline: max(2px, 0.15em) solid currentColor;
outline-offset: max(2px, 0.15em);
transform: scale(1.3);
}
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: #ececec;
cursor: not-allowed;
opacity: 0.7;
color: #4f4f4f;
}
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;
}
.checkbox-container {
display: inline-flex;
align-items: center;
background-color: transparent;
margin: 0.5rem 0;
}
.checkbox-text {
font-size: 16px;
}
.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;
}
.input-error-container {
display: flex;
align-items: flex-end;
width: 100%;
max-width: 444px;
}
@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: #eb5424;
font-size: 12px;
margin-bottom: 5px;
align-self: flex-start;
}
.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.5rem;
border: 1px solid #ccc;
border-radius: 4px;
color: white;
}
textarea {
resize: vertical;
min-height: 100px;
}
.input {
width: 100%;
margin: 0.5rem 0;
}
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 {
margin: 1rem 0;
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>