Update forms page

pull/6632/head
Mert Sincan 2024-10-23 15:02:51 +01:00
parent 6b491f3804
commit bb043ac053
4 changed files with 377 additions and 129 deletions

View File

@ -63,31 +63,6 @@ export default {
),
code: {
basic: `
<Fieldset legend="Schema">
<RadioButtonGroup v-model="selectedSchema" name="schema" class="flex flex-wrap gap-4" @update:modelValue="changeResolver">
<div class="flex items-center">
<RadioButton inputId="zod" value="Zod" />
<label for="zod" class="ml-2">Zod</label>
</div>
<div class="flex items-center">
<RadioButton inputId="yup" value="Yup" />
<label for="yup" class="ml-2">Yup</label>
</div>
<div class="flex items-center">
<RadioButton inputId="valibot" value="Valibot" />
<label for="valibot" class="ml-2">Valibot</label>
</div>
<div class="flex items-center">
<RadioButton inputId="superStruct" value="SuperStruct" />
<label for="superStruct" class="ml-2">SuperStruct</label>
</div>
<div class="flex items-center">
<RadioButton inputId="custom" value="Custom" />
<label for="custom" class="ml-2">Custom</label>
</div>
</RadioButtonGroup>
</Fieldset>
<Form v-slot="$form" :initialValues :resolver @submit="onFormSubmit" class="flex flex-col gap-4 w-full sm:w-56">
<div class="flex flex-col gap-2">
<InputText name="username" type="text" placeholder="Username" fluid />
@ -98,38 +73,42 @@ export default {
`,
options: `
<template>
<Fieldset legend="Schema">
<RadioButtonGroup v-model="selectedSchema" name="schema" class="flex flex-wrap gap-4" @update:modelValue="changeResolver">
<div class="flex items-center">
<RadioButton inputId="zod" value="Zod" />
<label for="zod" class="ml-2">Zod</label>
</div>
<div class="flex items-center">
<RadioButton inputId="yup" value="Yup" />
<label for="yup" class="ml-2">Yup</label>
</div>
<div class="flex items-center">
<RadioButton inputId="valibot" value="Valibot" />
<label for="valibot" class="ml-2">Valibot</label>
</div>
<div class="flex items-center">
<RadioButton inputId="superStruct" value="SuperStruct" />
<label for="superStruct" class="ml-2">SuperStruct</label>
</div>
<div class="flex items-center">
<RadioButton inputId="custom" value="Custom" />
<label for="custom" class="ml-2">Custom</label>
</div>
</RadioButtonGroup>
</Fieldset>
<div class="card flex flex-col items-center gap-5">
<Toast />
<Form v-slot="$form" :initialValues :resolver @submit="onFormSubmit" class="flex flex-col gap-4 w-full sm:w-56">
<div class="flex flex-col gap-2">
<InputText name="username" type="text" placeholder="Username" fluid />
<Message v-if="$form.username?.invalid" severity="error">{{ $form.username.error.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
<Fieldset legend="Schema">
<RadioButtonGroup v-model="selectedSchema" name="schema" class="flex flex-wrap gap-4" @update:modelValue="changeResolver">
<div class="flex items-center">
<RadioButton inputId="zod" value="Zod" />
<label for="zod" class="ml-2">Zod</label>
</div>
<div class="flex items-center">
<RadioButton inputId="yup" value="Yup" />
<label for="yup" class="ml-2">Yup</label>
</div>
<div class="flex items-center">
<RadioButton inputId="valibot" value="Valibot" />
<label for="valibot" class="ml-2">Valibot</label>
</div>
<div class="flex items-center">
<RadioButton inputId="superStruct" value="SuperStruct" />
<label for="superStruct" class="ml-2">SuperStruct</label>
</div>
<div class="flex items-center">
<RadioButton inputId="custom" value="Custom" />
<label for="custom" class="ml-2">Custom</label>
</div>
</RadioButtonGroup>
</Fieldset>
<Form v-slot="$form" :initialValues :resolver @submit="onFormSubmit" class="flex flex-col gap-4 w-full sm:w-56">
<div class="flex flex-col gap-2">
<InputText name="username" type="text" placeholder="Username" fluid />
<Message v-if="$form.username?.invalid" severity="error">{{ $form.username.error.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
</template>
<script>
@ -204,18 +183,117 @@ export default {
`,
composition: `
<template>
<Card>
<template #title>Simple Card</template>
<template #content>
<p class="m-0">
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Inventore sed consequuntur error repudiandae numquam deserunt quisquam repellat libero asperiores earum nam nobis, culpa ratione quam perferendis esse, cupiditate neque
quas!
</p>
</template>
</Card>
<div class="card flex flex-col items-center gap-5">
<Toast />
<Fieldset legend="Schema">
<RadioButtonGroup v-model="selectedSchema" name="schema" class="flex flex-wrap gap-4" @update:modelValue="changeResolver">
<div class="flex items-center">
<RadioButton inputId="zod" value="Zod" />
<label for="zod" class="ml-2">Zod</label>
</div>
<div class="flex items-center">
<RadioButton inputId="yup" value="Yup" />
<label for="yup" class="ml-2">Yup</label>
</div>
<div class="flex items-center">
<RadioButton inputId="valibot" value="Valibot" />
<label for="valibot" class="ml-2">Valibot</label>
</div>
<div class="flex items-center">
<RadioButton inputId="superStruct" value="SuperStruct" />
<label for="superStruct" class="ml-2">SuperStruct</label>
</div>
<div class="flex items-center">
<RadioButton inputId="custom" value="Custom" />
<label for="custom" class="ml-2">Custom</label>
</div>
</RadioButtonGroup>
</Fieldset>
<Form v-slot="$form" :initialValues :resolver @submit="onFormSubmit" class="flex flex-col gap-4 w-full sm:w-56">
<div class="flex flex-col gap-2">
<InputText name="username" type="text" placeholder="Username" fluid />
<Message v-if="$form.username?.invalid" severity="error">{{ $form.username.error.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
</template>
<script setup>
import { ref, watch } from 'vue';
import { superStructResolver, valibotResolver, yupResolver, zodResolver } from '@primevue/form/resolvers';
import * as s from 'superstruct';
import * as v from 'valibot';
import * as yup from 'yup';
import { z } from 'zod';
import { useToast } from 'primevue/usetoast';
const toast = useToast();
const initialValues = ref({
username: ''
});
const selectedSchema = ref('Zod');
const resolver = ref(zodResolver(
z.object({
username: z.string().min(1, { message: 'Username is required via Zod.' })
})
));
watch(selectedSchema, (newSchema) => {
changeResolver(newSchema);
});
function changeResolver(schema) {
if (schema === 'Zod') {
resolver.value = zodResolver(
z.object({
username: z.string().min(1, { message: 'Username is required via Zod.' })
})
);
} else if (schema === 'Yup') {
resolver.value = yupResolver(
yup.object().shape({
username: yup.string().required('Username is required via Yup.')
})
);
} else if (schema === 'Valibot') {
resolver.value = valibotResolver(
v.object({
username: v.pipe(v.string(), v.minLength(1, 'Username is required via Valibot.'))
})
);
} else if (schema === 'SuperStruct') {
resolver.value = superStructResolver(
s.object({
username: s.nonempty(s.string())
})
);
} else if (schema === 'Custom') {
resolver.value = ({ values }) => {
const errors = {};
if (!values.username) {
errors.username = [{ message: 'Username is required.' }];
}
return {
errors
};
};
}
}
function handleSubmit() {
resolver.value({ values: initialValues.value }).then(({ valid }) => {
if (valid) {
toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
});
}
<\/script>
`
}

View File

@ -27,8 +27,8 @@ export default {
basic: `
<Form v-slot="$form" :initialValues :resolver @submit="onFormSubmit" class="grid lg:grid-cols-2 gap-4 w-full">
<div class="flex flex-col justify-center items-center gap-4">
<InputText name="username" type="text" placeholder="Username" />
<Button type="submit" severity="secondary" label="Submit" />
<InputText name="username" type="text" placeholder="Username" fluid class="sm:w-56" />
<Button type="submit" severity="secondary" label="Submit" fluid class="sm:w-56" />
</div>
<Fieldset legend="Form States">
<pre class="whitespace-pre-wrap">{{ $form }}</pre>
@ -37,15 +37,19 @@ export default {
`,
options: `
<template>
<Form v-slot="$form" :initialValues :resolver @submit="onFormSubmit" class="grid md:grid-cols-2 gap-4 w-full">
<div class="flex flex-col justify-center items-center gap-4">
<InputText name="username" type="text" placeholder="Username" />
<Button type="submit" severity="secondary" label="Submit" />
</div>
<Fieldset legend="Form States">
<pre>{{ $form }}</pre>
</Fieldset>
</Form>
<div class="card flex justify-center">
<Toast />
<Form v-slot="$form" :initialValues :resolver @submit="onFormSubmit" class="grid lg:grid-cols-2 gap-4 w-full">
<div class="flex flex-col justify-center items-center gap-4">
<InputText name="username" type="text" placeholder="Username" fluid class="sm:w-56" />
<Button type="submit" severity="secondary" label="Submit" fluid class="sm:w-56" />
</div>
<Fieldset legend="Form States">
<pre class="whitespace-pre-wrap">{{ $form }}</pre>
</Fieldset>
</Form>
</div>
</template>
<script>
@ -54,15 +58,19 @@ export default {
return {
initialValues: {
username: ''
}
},
};
},
methods: {
resolver: ({ values }) => {
const errors = {};
const errors = { username: [] };
if (!values.username) {
errors.username = [{ message: 'Username is required.' }];
errors.username.push({ type: 'required', message: 'Username is required.' });
}
if (values.username?.length < 3) {
errors.username.push({ type: 'minimum', message: 'Username must be at least 3 characters long.' });
}
return {
@ -80,18 +88,52 @@ export default {
`,
composition: `
<template>
<Card>
<template #title>Simple Card</template>
<template #content>
<p class="m-0">
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Inventore sed consequuntur error repudiandae numquam deserunt quisquam repellat libero asperiores earum nam nobis, culpa ratione quam perferendis esse, cupiditate neque
quas!
</p>
</template>
</Card>
<div class="card flex justify-center">
<Toast />
<Form v-slot="$form" :initialValues :resolver @submit="onFormSubmit" class="grid lg:grid-cols-2 gap-4 w-full">
<div class="flex flex-col justify-center items-center gap-4">
<InputText name="username" type="text" placeholder="Username" fluid class="sm:w-56" />
<Button type="submit" severity="secondary" label="Submit" fluid class="sm:w-56" />
</div>
<Fieldset legend="Form States">
<pre class="whitespace-pre-wrap">{{ $form }}</pre>
</Fieldset>
</Form>
</div>
</template>
<script setup>
import { ref } from 'vue';
import { useToast } from 'primevue/usetoast';
const toast = useToast();
const initialValues = ref({
username: ''
});
const resolver = ({ values }) => {
const errors = { username: [] };
if (!values.username) {
errors.username.push({ type: 'required', message: 'Username is required.' });
}
if (values.username?.length < 3) {
errors.username.push({ type: 'minimum', message: 'Username must be at least 3 characters long.' });
}
return {
errors
};
};
const onFormSubmit = ({ valid }) => {
if (valid) {
toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
<\/script>
`
}

View File

@ -71,21 +71,25 @@ export default {
`,
options: `
<template>
<Form v-slot="$form" :initialValues :resolver @submit="onFormSubmit" class="flex flex-col gap-4 w-full sm:w-56">
<div class="flex flex-col gap-2">
<InputText name="username" type="text" placeholder="Username" fluid />
<Message v-if="$form.username?.invalid" severity="error">{{ $form.username.error.message }}</Message>
</div>
<div class="flex flex-col gap-2">
<Password name="password" placeholder="Password" :feedback="false" fluid />
<Message v-if="$form.password?.invalid" severity="error">
<ul class="mx-1 px-3">
<li v-for="(error, index) of $form.password.errors" :key="index" class="py-1">{{ error.message }}</li>
</ul>
</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
<div class="card flex justify-center">
<Toast />
<Form v-slot="$form" :initialValues :resolver @submit="onFormSubmit" class="flex flex-col gap-4 w-full sm:w-56">
<div class="flex flex-col gap-2">
<InputText name="username" type="text" placeholder="Username" fluid />
<Message v-if="$form.username?.invalid" severity="error">{{ $form.username.error.message }}</Message>
</div>
<div class="flex flex-col gap-2">
<Password name="password" placeholder="Password" :feedback="false" fluid />
<Message v-if="$form.password?.invalid" severity="error">
<ul class="mx-1 px-3">
<li v-for="(error, index) of $form.password.errors" :key="index" class="py-1">{{ error.message }}</li>
</ul>
</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
</template>
<script>
@ -126,6 +130,8 @@ export default {
// e.states: Contains the current state of each form field, including validity status.
// e.errors: An object that holds any validation errors for the invalid fields in the form.
// e.values: An object containing the current values of all form fields.
// e.reset: A function that resets the form to its initial state.
if (e.valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
@ -136,18 +142,71 @@ export default {
`,
composition: `
<template>
<Card>
<template #title>Simple Card</template>
<template #content>
<p class="m-0">
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Inventore sed consequuntur error repudiandae numquam deserunt quisquam repellat libero asperiores earum nam nobis, culpa ratione quam perferendis esse, cupiditate neque
quas!
</p>
</template>
</Card>
<div class="card flex justify-center">
<Toast />
<Form v-slot="$form" :initialValues :resolver @submit="onFormSubmit" class="flex flex-col gap-4 w-full sm:w-56">
<div class="flex flex-col gap-2">
<InputText name="username" type="text" placeholder="Username" fluid />
<Message v-if="$form.username?.invalid" severity="error">{{ $form.username.error.message }}</Message>
</div>
<div class="flex flex-col gap-2">
<Password name="password" placeholder="Password" :feedback="false" fluid />
<Message v-if="$form.password?.invalid" severity="error">
<ul class="mx-1 px-3">
<li v-for="(error, index) of $form.password.errors" :key="index" class="py-1">{{ error.message }}</li>
</ul>
</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
</template>
<script setup>
import { ref } from 'vue';
import { zodResolver } from '@primevue/form/resolvers';
import { z } from 'zod';
import { useToast } from 'primevue/usetoast';
const toast = useToast();
const initialValues = ref({
username: '',
password: ''
});
const resolver = zodResolver(
z.object({
username: z.string().min(1, { message: 'Username is required.' }),
password: z
.string()
.min(3, { message: 'Minimum 3 characters.' })
.max(8, { message: 'Maximum 8 characters.' })
.refine((value) => /[a-z]/.test(value), {
message: 'Must have a lowercase letter.'
})
.refine((value) => /[A-Z]/.test(value), {
message: 'Must have an uppercase letter.'
})
.refine((value) => /\d/.test(value), {
message: 'Must have a number.'
})
})
);
const onFormSubmit = (e) => {
// e.originalEvent: Represents the native form submit event.
// e.valid: A boolean that indicates whether the form is valid or not.
// e.states: Contains the current state of each form field, including validity status.
// e.errors: An object that holds any validation errors for the invalid fields in the form.
// e.values: An object containing the current values of all form fields.
// e.reset: A function that resets the form to its initial state.
if (e.valid) {
toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
};
<\/script>
`
}
@ -161,6 +220,7 @@ export default {
// e.errors: An object that holds any validation errors for the invalid fields in the form.
// e.values: An object containing the current values of all form fields.
// e.reset: A function that resets the form to its initial state.
if (e.valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}

View File

@ -54,13 +54,25 @@ export default {
`,
options: `
<template>
<Form v-slot="$form" :initialValues :resolver @submit="onFormSubmit" class="flex flex-col gap-4 w-full sm:w-56">
<div class="flex flex-col gap-2">
<InputText name="username" type="text" placeholder="Username" fluid />
<Message v-if="$form.username?.invalid" severity="error">{{ $form.username.error.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
<div class="card flex justify-center">
<Toast />
<Form v-slot="$form" :initialValues :resolver :validateOnValueUpdate="false" :validateOnBlur="true" :validateOnMount="['name']" @submit="onFormSubmit" class="flex flex-col gap-4 w-full sm:w-56">
<div class="flex flex-col gap-2">
<InputText name="username" type="text" placeholder="Username" fluid />
<Message v-if="$form.username?.invalid" severity="error">{{ $form.username.error.message }}</Message>
</div>
<div class="flex flex-col gap-2">
<InputText name="name" type="text" placeholder="Name" fluid />
<Message v-if="$form.name?.invalid" severity="error">{{ $form.name.error.message }}</Message>
</div>
<div class="flex flex-col gap-2">
<InputText name="surname" type="text" placeholder="Surname" fluid />
<Message v-if="$form.surname?.invalid" severity="error">{{ $form.surname.error.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
</template>
<script>
@ -68,7 +80,9 @@ export default {
data() {
return {
initialValues: {
username: ''
username: '',
name: '',
surname: ''
}
};
},
@ -80,6 +94,14 @@ export default {
errors.username = [{ message: 'Username is required.' }];
}
if (!values.name) {
errors.name = [{ message: 'Name is required.' }];
}
if (!values.surname) {
errors.surname = [{ message: 'Surname is required.' }];
}
return {
errors
};
@ -95,18 +117,64 @@ export default {
`,
composition: `
<template>
<Card>
<template #title>Simple Card</template>
<template #content>
<p class="m-0">
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Inventore sed consequuntur error repudiandae numquam deserunt quisquam repellat libero asperiores earum nam nobis, culpa ratione quam perferendis esse, cupiditate neque
quas!
</p>
</template>
</Card>
<div class="card flex justify-center">
<Toast />
<Form v-slot="$form" :initialValues :resolver :validateOnValueUpdate="false" :validateOnBlur="true" :validateOnMount="['name']" @submit="onFormSubmit" class="flex flex-col gap-4 w-full sm:w-56">
<div class="flex flex-col gap-2">
<InputText name="username" type="text" placeholder="Username" fluid />
<Message v-if="$form.username?.invalid" severity="error">{{ $form.username.error.message }}</Message>
</div>
<div class="flex flex-col gap-2">
<InputText name="name" type="text" placeholder="Name" fluid />
<Message v-if="$form.name?.invalid" severity="error">{{ $form.name.error.message }}</Message>
</div>
<div class="flex flex-col gap-2">
<InputText name="surname" type="text" placeholder="Surname" fluid />
<Message v-if="$form.surname?.invalid" severity="error">{{ $form.surname.error.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
</template>
<script setup>
import { ref } from 'vue';
import { useToast } from 'primevue/usetoast';
const toast = useToast();
const initialValues = ref({
username: '',
name: '',
surname: ''
});
const resolver = ({ values }) => {
const errors = {};
if (!values.username) {
errors.username = [{ message: 'Username is required.' }];
}
if (!values.name) {
errors.name = [{ message: 'Name is required.' }];
}
if (!values.surname) {
errors.surname = [{ message: 'Surname is required.' }];
}
return {
errors
};
};
const onFormSubmit = ({ valid }) => {
if (valid) {
toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
<\/script>
`
}