mirror of
https://github.com/danbulant/Portfolio
synced 2026-05-27 14:02:14 +00:00
feat: refactor form components
This commit is contained in:
parent
f3fd0c04e4
commit
30c504b82e
5 changed files with 145 additions and 89 deletions
29
frontend/src/lib/components/textfield/IdField.svelte
Normal file
29
frontend/src/lib/components/textfield/IdField.svelte
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import Telephone from '../icons/Telephone.svelte';
|
||||||
|
import TextField from './TextField.svelte';
|
||||||
|
|
||||||
|
export let placeholder: string = '';
|
||||||
|
export let value: string = '';
|
||||||
|
export let error: string = '';
|
||||||
|
|
||||||
|
// Personal Id formatting
|
||||||
|
$: {
|
||||||
|
let x = value.replace(/\D/g, '').match(/(\d{0,6})(\d{0,4})/)!;
|
||||||
|
value = x[1] + (x[2] ? '/' + x[2] : '');
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<TextField
|
||||||
|
bind:error
|
||||||
|
bind:value
|
||||||
|
on:keydown
|
||||||
|
on:keyup
|
||||||
|
on:change
|
||||||
|
type="text"
|
||||||
|
{placeholder}
|
||||||
|
icon
|
||||||
|
>
|
||||||
|
<div slot="icon" class="flex items-center justify-center">
|
||||||
|
<Telephone />
|
||||||
|
</div>
|
||||||
|
</TextField>
|
||||||
14
frontend/src/lib/components/textfield/NumberField.svelte
Normal file
14
frontend/src/lib/components/textfield/NumberField.svelte
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import TextField from './TextField.svelte';
|
||||||
|
|
||||||
|
export let placeholder: string = '';
|
||||||
|
export let value: string = '';
|
||||||
|
export let error: string = '';
|
||||||
|
|
||||||
|
// Number formatting
|
||||||
|
$: {
|
||||||
|
value = value.replace(/[^0-9]/g, '');
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<TextField bind:error bind:value on:keydown on:keyup on:change type="number" {placeholder} icon />
|
||||||
30
frontend/src/lib/components/textfield/TelephoneField.svelte
Normal file
30
frontend/src/lib/components/textfield/TelephoneField.svelte
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import Telephone from '../icons/Telephone.svelte';
|
||||||
|
import TextField from './TextField.svelte';
|
||||||
|
|
||||||
|
export let placeholder: string = '';
|
||||||
|
export let value: string = '';
|
||||||
|
export let error: string = '';
|
||||||
|
|
||||||
|
// Phone Number formatting
|
||||||
|
$: {
|
||||||
|
let x = value.replace(/\D/g, '').match(/(\d{0,3})(\d{0,3})(\d{0,3})(\d{0,3})/)!;
|
||||||
|
value =
|
||||||
|
'+' + x[1] + (x[2] ? ' ' + x[2] : '') + (x[3] ? ' ' + x[3] : '') + (x[4] ? ' ' + x[4] : '');
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<TextField
|
||||||
|
bind:error
|
||||||
|
bind:value
|
||||||
|
on:keydown
|
||||||
|
on:keyup
|
||||||
|
on:change
|
||||||
|
type="tel"
|
||||||
|
{placeholder}
|
||||||
|
icon
|
||||||
|
>
|
||||||
|
<div slot="icon" class="flex items-center justify-center">
|
||||||
|
<Telephone />
|
||||||
|
</div>
|
||||||
|
</TextField>
|
||||||
|
|
@ -1,34 +1,18 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export let type: 'text' | 'number' | 'tel' | 'e-mail' | "password" = 'text';
|
export let type: 'text' | 'number' | 'tel' | 'e-mail' | 'password' = 'text';
|
||||||
export let format: 'default' | 'email' | 'tel' | 'name' | 'number' | 'birthdate' | 'personalIdNumber' = 'default';
|
|
||||||
const typeAction = (node: HTMLInputElement) => {
|
const typeAction = (node: HTMLInputElement) => {
|
||||||
node.type = type;
|
node.type = type;
|
||||||
};
|
};
|
||||||
export let placeholder: string = '';
|
export let placeholder: string = '';
|
||||||
export let value: string = '';
|
export let value: string = '';
|
||||||
|
|
||||||
$: {
|
|
||||||
if (format === 'tel') {
|
|
||||||
let x = value.replace(/\D/g, '').match(/(\d{0,3})(\d{0,3})(\d{0,3})(\d{0,3})/)!;
|
|
||||||
value = '+' + x[1] + (x[2] ? ' ' + x[2] : '') + (x[3] ? ' ' + x[3] : '') + (x[4] ? ' ' + x[4] : '');
|
|
||||||
} else if (format === 'number') {
|
|
||||||
value = value.replace(/[^0-9]/g, '');
|
|
||||||
} else if (format === 'birthdate') { // TODO: more intuitive date input
|
|
||||||
let x = value.replace(/\D/g, '').match(/(\d{0,2})(\d{0,2})(\d{0,4})/)!;
|
|
||||||
value = x[1] + (x[2] ? '.' + x[2] : '') + (x[3] ? '.' + x[3] : '');
|
|
||||||
} else if (format === 'personalIdNumber') {
|
|
||||||
let x = value.replace(/\D/g, '').match(/(\d{0,6})(\d{0,4})/)!;
|
|
||||||
value = x[1] + (x[2] ? '/' + x[2] : '');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export let icon: boolean = false;
|
export let icon: boolean = false;
|
||||||
export let error: string = "";
|
export let error: string = '';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="relative flex justify-center items-center">
|
<div class="relative flex justify-center items-center">
|
||||||
<input
|
<input
|
||||||
class:error={error}
|
class:error
|
||||||
bind:value
|
bind:value
|
||||||
on:click
|
on:click
|
||||||
on:keydown
|
on:keydown
|
||||||
|
|
@ -47,7 +31,8 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
div, input {
|
div,
|
||||||
|
input {
|
||||||
@apply w-full;
|
@apply w-full;
|
||||||
}
|
}
|
||||||
.withIcon {
|
.withIcon {
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@
|
||||||
import Home from '$lib/components/icons/Home.svelte';
|
import Home from '$lib/components/icons/Home.svelte';
|
||||||
import Telephone from '$lib/components/icons/Telephone.svelte';
|
import Telephone from '$lib/components/icons/Telephone.svelte';
|
||||||
import SplitLayout from '$lib/components/layout/SplitLayout.svelte';
|
import SplitLayout from '$lib/components/layout/SplitLayout.svelte';
|
||||||
|
import IdField from '$lib/components/textfield/IdField.svelte';
|
||||||
|
import TelephoneField from '$lib/components/textfield/TelephoneField.svelte';
|
||||||
import TextField from '$lib/components/textfield/TextField.svelte';
|
import TextField from '$lib/components/textfield/TextField.svelte';
|
||||||
|
|
||||||
import { createForm } from 'svelte-forms-lib';
|
import { createForm } from 'svelte-forms-lib';
|
||||||
|
|
@ -14,7 +16,7 @@
|
||||||
let applicationValue = '';
|
let applicationValue = '';
|
||||||
|
|
||||||
const pageCount = 3;
|
const pageCount = 3;
|
||||||
let pageIndex = 0;
|
let pageIndex = 2;
|
||||||
let pagesFilled = 0;
|
let pagesFilled = 0;
|
||||||
|
|
||||||
const formInitialValues = {
|
const formInitialValues = {
|
||||||
|
|
@ -39,7 +41,10 @@
|
||||||
validationSchema: yup.object().shape({
|
validationSchema: yup.object().shape({
|
||||||
name: yup.string().required(),
|
name: yup.string().required(),
|
||||||
email: yup.string().email().required(),
|
email: yup.string().email().required(),
|
||||||
telephone: yup.string().required().matches(/^\+\d{1,3} \d{3} \d{3} \d{3}$/),
|
telephone: yup
|
||||||
|
.string()
|
||||||
|
.required()
|
||||||
|
.matches(/^\+\d{1,3} \d{3} \d{3} \d{3}$/),
|
||||||
birthSurname: yup.string().required(),
|
birthSurname: yup.string().required(),
|
||||||
birthPlace: yup.string().required(),
|
birthPlace: yup.string().required(),
|
||||||
birthDate: yup.string().required(),
|
birthDate: yup.string().required(),
|
||||||
|
|
@ -76,12 +81,7 @@
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
if (
|
if ($errors.citizenship || $errors.personalId || $errors.study || $errors.applicationId) {
|
||||||
$errors.citizenship ||
|
|
||||||
$errors.personalId ||
|
|
||||||
$errors.study ||
|
|
||||||
$errors.applicationId
|
|
||||||
) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
@ -89,7 +89,7 @@
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<SplitLayout>
|
<SplitLayout>
|
||||||
|
|
@ -106,7 +106,7 @@
|
||||||
Lorem ipsum dolor sit amet, consectetuer adipiscing elit.<br /> Fusce suscipit libero eget
|
Lorem ipsum dolor sit amet, consectetuer adipiscing elit.<br /> Fusce suscipit libero eget
|
||||||
elit.
|
elit.
|
||||||
</p>
|
</p>
|
||||||
<div class="flex md:flex-col items-center justify-center w-full md:w-3/5">
|
<div class="flex md:flex-col items-center justify-center w-full">
|
||||||
<span class="w-full mt-8">
|
<span class="w-full mt-8">
|
||||||
<TextField
|
<TextField
|
||||||
error={$errors.name}
|
error={$errors.name}
|
||||||
|
|
@ -116,7 +116,7 @@
|
||||||
placeholder="Jméno a příjmení"
|
placeholder="Jméno a příjmení"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<span class="w-full mt-8">
|
<span class="w-full mt-8 ml-2 md:ml-0">
|
||||||
<TextField
|
<TextField
|
||||||
error={$errors.email}
|
error={$errors.email}
|
||||||
on:change={handleChange}
|
on:change={handleChange}
|
||||||
|
|
@ -131,20 +131,13 @@
|
||||||
</TextField>
|
</TextField>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-8 w-full md:w-3/5">
|
<div class="mt-8 w-full">
|
||||||
<TextField
|
<TelephoneField
|
||||||
error={$errors.telephone}
|
error={$errors.telephone}
|
||||||
on:change={handleChange}
|
on:change={handleChange}
|
||||||
bind:value={$form.telephone}
|
bind:value={$form.telephone}
|
||||||
type="tel"
|
|
||||||
format="tel"
|
|
||||||
placeholder="Telefon"
|
placeholder="Telefon"
|
||||||
icon
|
/>
|
||||||
>
|
|
||||||
<div slot="icon" class="flex items-center justify-center">
|
|
||||||
<Telephone />
|
|
||||||
</div>
|
|
||||||
</TextField>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
@ -153,7 +146,7 @@
|
||||||
<p class="block mt-8 font-light text-sspsGray text-center">
|
<p class="block mt-8 font-light text-sspsGray text-center">
|
||||||
Lorem ipsum dolor sit amet, consectetuer adipiscing elit.<br /> Fusce suscipit libero eget elit.
|
Lorem ipsum dolor sit amet, consectetuer adipiscing elit.<br /> Fusce suscipit libero eget elit.
|
||||||
</p>
|
</p>
|
||||||
<div class="flex flex-col w-full md:w-3/5">
|
<div class="flex flex-col w-full">
|
||||||
<span class="w-full mt-8">
|
<span class="w-full mt-8">
|
||||||
<TextField
|
<TextField
|
||||||
type="text"
|
type="text"
|
||||||
|
|
@ -179,22 +172,23 @@
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mt-8 flex items-center justify-center w-full md:w-3/5">
|
<div class="mt-8 flex items-center w-full">
|
||||||
<TextField
|
<TextField
|
||||||
error={$errors.birthDate}
|
error={$errors.birthDate}
|
||||||
on:change={handleChange}
|
on:change={handleChange}
|
||||||
bind:value={$form.birthDate}
|
bind:value={$form.birthDate}
|
||||||
type="text"
|
type="text"
|
||||||
format="birthdate"
|
|
||||||
placeholder="Datum narození"
|
placeholder="Datum narození"
|
||||||
/>
|
/>
|
||||||
<TextField
|
<div class="ml-2">
|
||||||
error={$errors.sex}
|
<TextField
|
||||||
on:change={handleChange}
|
error={$errors.sex}
|
||||||
bind:value={$form.sex}
|
on:change={handleChange}
|
||||||
type="text"
|
bind:value={$form.sex}
|
||||||
placeholder="Pohlaví"
|
type="text"
|
||||||
/>
|
placeholder="Pohlaví"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{#if pageIndex === 2}
|
{#if pageIndex === 2}
|
||||||
|
|
@ -202,7 +196,7 @@
|
||||||
<p class="block mt-8 font-light text-sspsGray text-center">
|
<p class="block mt-8 font-light text-sspsGray text-center">
|
||||||
Lorem ipsum dolor sit amet, consectetuer adipiscing elit.<br /> Fusce suscipit libero eget elit.
|
Lorem ipsum dolor sit amet, consectetuer adipiscing elit.<br /> Fusce suscipit libero eget elit.
|
||||||
</p>
|
</p>
|
||||||
<div class="flex flex-col w-full md:w-3/5">
|
<div class="flex flex-col w-full">
|
||||||
<span class="w-full mt-8">
|
<span class="w-full mt-8">
|
||||||
<TextField
|
<TextField
|
||||||
error={$errors.address}
|
error={$errors.address}
|
||||||
|
|
@ -212,24 +206,25 @@
|
||||||
placeholder="Adresa trvalého bydliště"
|
placeholder="Adresa trvalého bydliště"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<span class="w-full mt-8">
|
<div class="mt-8 flex flex-row items-center md:flex-col">
|
||||||
<TextField
|
<span class="w-full">
|
||||||
error={$errors.parentEmail}
|
<TextField
|
||||||
on:change={handleChange}
|
error={$errors.parentEmail}
|
||||||
bind:value={$form.parentEmail}
|
on:change={handleChange}
|
||||||
type="e-mail"
|
bind:value={$form.parentEmail}
|
||||||
placeholder="E-mail zákonného zástupce"
|
type="e-mail"
|
||||||
/>
|
placeholder="E-mail zákonného zástupce"
|
||||||
</span>
|
/>
|
||||||
<span class="w-full mt-8">
|
</span>
|
||||||
<TextField
|
<span class="w-full ml-2 md:ml-0 md:mt-8">
|
||||||
error={$errors.parentTelephone}
|
<TelephoneField
|
||||||
on:change={handleChange}
|
error={$errors.parentTelephone}
|
||||||
bind:value={$form.parentTelephone}
|
on:change={handleChange}
|
||||||
type="tel"
|
bind:value={$form.parentTelephone}
|
||||||
placeholder="Telefon zákonného zástupce"
|
placeholder="Telefon zákonného zástupce"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{#if pageIndex === 3}
|
{#if pageIndex === 3}
|
||||||
|
|
@ -237,7 +232,7 @@
|
||||||
<p class="block mt-8 font-light text-sspsGray text-center">
|
<p class="block mt-8 font-light text-sspsGray text-center">
|
||||||
Lorem ipsum dolor sit amet, consectetuer adipiscing elit.<br /> Fusce suscipit libero eget elit.
|
Lorem ipsum dolor sit amet, consectetuer adipiscing elit.<br /> Fusce suscipit libero eget elit.
|
||||||
</p>
|
</p>
|
||||||
<div class="flex flex-col w-full md:w-3/5">
|
<div class="flex flex-col w-full">
|
||||||
<span class="w-full mt-8">
|
<span class="w-full mt-8">
|
||||||
<TextField
|
<TextField
|
||||||
error={$errors.citizenship}
|
error={$errors.citizenship}
|
||||||
|
|
@ -248,13 +243,11 @@
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-8 flex items-center justify-center w-full md:w-3/5">
|
<div class="mt-8 flex items-center justify-center w-full">
|
||||||
<TextField
|
<IdField
|
||||||
error={$errors.personalId}
|
error={$errors.personalId}
|
||||||
on:change={handleChange}
|
on:change={handleChange}
|
||||||
bind:value={$form.personalId}
|
bind:value={$form.personalId}
|
||||||
type="text"
|
|
||||||
format="personalIdNumber"
|
|
||||||
placeholder="Rodné číslo"
|
placeholder="Rodné číslo"
|
||||||
/>
|
/>
|
||||||
<TextField
|
<TextField
|
||||||
|
|
@ -265,7 +258,7 @@
|
||||||
placeholder="Obor"
|
placeholder="Obor"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-8 flex flex-col w-full md:w-3/5">
|
<div class="mt-8 flex flex-col w-full">
|
||||||
<TextField
|
<TextField
|
||||||
error={$errors.applicationId}
|
error={$errors.applicationId}
|
||||||
on:change={handleChange}
|
on:change={handleChange}
|
||||||
|
|
@ -287,25 +280,30 @@
|
||||||
}
|
}
|
||||||
errors.set(formInitialValues);
|
errors.set(formInitialValues);
|
||||||
}}
|
}}
|
||||||
class="w-full mt-8 md:w-3/5 p-3 rounded-lg font-semibold text-xl transition-colors duration-300 bg-sspsBlue hover:bg-sspsBlueDark text-white hover:cursor-pointer"
|
class="w-full mt-8 p-3 rounded-lg font-semibold text-xl transition-colors duration-300 bg-sspsBlue hover:bg-sspsBlueDark text-white hover:cursor-pointer"
|
||||||
type="submit"
|
type="submit"
|
||||||
value={pageIndex === pageCount ? 'Odeslat' : 'Pokračovat'}
|
value={pageIndex === pageCount ? 'Odeslat' : 'Pokračovat'}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div class="mt-8 flex flex-row justify-center">
|
<div class="mt-8 flex flex-row justify-center">
|
||||||
{#each Array(pageCount + 1) as _, i}
|
{#each Array(pageCount + 1) as _, i}
|
||||||
<button class:dotActive={i === pageIndex} on:click={async (e) => {
|
<button
|
||||||
if (i <= pagesFilled) { // never skip unfilled or invalid pages
|
class:dotActive={i === pageIndex}
|
||||||
pageIndex = i;
|
on:click={async (e) => {
|
||||||
} else if (i == pagesFilled + 1) { // if next page is clicked, validate current page
|
if (i <= pagesFilled) {
|
||||||
await handleSubmit(e);
|
// never skip unfilled or invalid pages
|
||||||
if (isPageInvalid()) return;
|
pageIndex = i;
|
||||||
pagesFilled++;
|
} else if (i == pagesFilled + 1) {
|
||||||
pageIndex++;
|
// if next page is clicked, validate current page
|
||||||
errors.set(formInitialValues);
|
await handleSubmit(e);
|
||||||
}
|
if (isPageInvalid()) return;
|
||||||
}
|
pagesFilled++;
|
||||||
} class="dot" />
|
pageIndex++;
|
||||||
|
errors.set(formInitialValues);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
class="dot"
|
||||||
|
/>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue