styling
This commit is contained in:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user