Merge pull request #6632 from primefaces/v4.2.0-form

PrimeVue Forms
pull/6637/head
Tuğçe Küçükoğlu 2024-10-24 09:19:00 +03:00 committed by GitHub
commit d33ed122e4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
220 changed files with 11796 additions and 2022 deletions

View File

@ -497,6 +497,12 @@
}
]
},
{
"name": "Forms",
"icon": "pi pi-check-circle",
"to": "/forms",
"badge": "NEW"
},
{
"name": "Pass Through",
"icon": "pi pi-directions",

View File

@ -69,7 +69,7 @@ export default {
interfaces = this.findOtherInterfaces(values, docName);
}
const types = APIDocs[moduleName]['types'];
const types = APIDocs[moduleName]?.['types'];
const services = modelValues; // (TerminalService && ConfirmationService && ToastService)

View File

@ -16,6 +16,7 @@ const core_dependencies = {
primevue: pkg.version || PrimeVue.version || 'latest',
'@primevue/themes': pkg.version || PrimeVue.version || 'latest',
'@primevue/auto-import-resolver': pkg.version || PrimeVue.version || 'latest',
'@primevue/form': pkg.version || PrimeVue.version || 'latest',
primeicons: app_dependencies['primeicons'] || 'latest',
tailwindcss: app_dependencies['tailwindcss'] || 'latest',
autoprefixer: app_dependencies['autoprefixer'] || 'latest',

View File

@ -248,7 +248,6 @@ export default {
selectedRows: []
};
},
methods: {
displayPopover(event) {
this.hidePopover();

View File

@ -0,0 +1,202 @@
<template>
<DocSectionText v-bind="$attrs">
<p>AutoComplete can be used with the <NuxtLink to="/forms">PrimeVue Forms</NuxtLink> library.</p>
</DocSectionText>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex justify-center flex-col gap-4 w-full md:w-56">
<div class="flex flex-col gap-2">
<AutoComplete name="country" optionLabel="name" :suggestions="filteredCountries" @complete="search" fluid />
<Message v-if="$form.country?.invalid" severity="error">{{ $form.country.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
<DocSectionCode :code="code" :dependencies="{ zod: '3.23.8' }" />
</template>
<script>
import { CountryService } from '@/service/CountryService';
import { zodResolver } from '@primevue/form/resolvers';
import { z } from 'zod';
export default {
data() {
return {
initialValues: {
country: { name: '' }
},
countries: null,
filteredCountries: null,
resolver: zodResolver(
z.object({
country: z.union([
z.object({
name: z.string().min(1, 'Country required.')
}),
z.any().refine((val) => false, { message: 'Country required.' })
])
})
),
code: {
basic: `
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex justify-center flex-col gap-4 w-full md:w-56">
<div class="flex flex-col gap-2">
<AutoComplete name="country" optionLabel="name" :suggestions="filteredCountries" @complete="search" />
<Message v-if="$form.country?.invalid" severity="error">{{ $form.country.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
`,
options: `
<template>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex justify-center flex-col gap-4 w-full md:w-56">
<div class="flex flex-col gap-2">
<AutoComplete name="country" optionLabel="name" :suggestions="filteredCountries" @complete="search" />
<Message v-if="$form.country?.invalid" severity="error">{{ $form.country.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
<Toast />
</div>
</template>
<script>
import { CountryService } from '@/service/CountryService';
import { zodResolver } from '@primevue/form/resolvers';
import { z } from 'zod';
export default {
data() {
return {
initialValues: {
country: { name: '' }
},
countries: null,
filteredCountries: null,
resolver: zodResolver(
z.object({
country: z.union([
z.object({
name: z.string().min(1, 'Country required.')
}),
z.any().refine((val) => false, { message: 'Country required.' })
])
})
)
}
},
mounted() {
CountryService.getCountries().then((data) => (this.countries = data));
},
methods: {
search(event) {
setTimeout(() => {
if (!event.query.trim().length) {
this.filteredCountries = [...this.countries];
} else {
this.filteredCountries = this.countries.filter((country) => {
return country.name.toLowerCase().startsWith(event.query.toLowerCase());
});
}
}, 250);
},
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
}
<\/script>
`,
composition: `
<template>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex justify-center flex-col gap-4 w-full md:w-56">
<div class="flex flex-col gap-2">
<AutoComplete name="country" optionLabel="name" :suggestions="filteredCountries" @complete="search" />
<Message v-if="$form.country?.invalid" severity="error">{{ $form.country.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
<Toast />
</div>
</template>
<script setup>
import { ref, onMounted } from "vue";
import { zodResolver } from '@primevue/form/resolvers';
import { useToast } from "primevue/usetoast";
import { z } from 'zod';
import { CountryService } from "@/service/CountryService";
onMounted(() => {
CountryService.getCountries().then((data) => (countries.value = data));
});
const initialValues = ref({
country: { name: '' }
});
const resolver = ref(
zodResolver(
z.object({
country: z.union([
z.object({
name: z.string().min(1, 'Country required.')
}),
z.any().refine((val) => false, { message: 'Country required.' })
])
})
));
const countries = ref();
const selectedCountry = ref();
const filteredCountries = ref();
const toast = useToast();
const search = (event) => {
setTimeout(() => {
if (!event.query.trim().length) {
filteredCountries.value = [...countries.value];
} else {
filteredCountries.value = countries.value.filter((country) => {
return country.name.toLowerCase().startsWith(event.query.toLowerCase());
});
}
}, 250);
};
const onFormSubmit = ({ valid }) => {
if (valid) {
toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
};
<\/script>
`
}
};
},
mounted() {
CountryService.getCountries().then((data) => (this.countries = data));
},
methods: {
search(event) {
setTimeout(() => {
if (!event.query.trim().length) {
this.filteredCountries = [...this.countries];
} else {
this.filteredCountries = this.countries.filter((country) => {
return country.name.toLowerCase().startsWith(event.query.toLowerCase());
});
}
}, 250);
},
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
};
</script>

View File

@ -0,0 +1,366 @@
<template>
<DocSectionText v-bind="$attrs">
<p>CascadeSelect can be used with the <NuxtLink to="/forms">PrimeVue Forms</NuxtLink> library.</p>
</DocSectionText>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex justify-center flex-col gap-4">
<div class="flex flex-col gap-2">
<CascadeSelect name="city" :options="countries" optionLabel="cname" optionGroupLabel="name" :optionGroupChildren="['states', 'cities']" class="w-56" placeholder="Select a City" />
<Message v-if="$form.city?.invalid" severity="error">{{ $form.city.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
<DocSectionCode :code="code" :dependencies="{ zod: '3.23.8' }" />
</template>
<script>
import { zodResolver } from '@primevue/form/resolvers';
import { z } from 'zod';
export default {
data() {
return {
initialValues: {
city: null
},
resolver: zodResolver(
z.object({
city: z.union([
z.object({
cname: z.string().min(1, 'City required.')
}),
z.any().refine((val) => false, { message: 'City required.' })
])
})
),
countries: [
{
name: 'Australia',
code: 'AU',
states: [
{
name: 'New South Wales',
cities: [
{ cname: 'Sydney', code: 'A-SY' },
{ cname: 'Newcastle', code: 'A-NE' },
{ cname: 'Wollongong', code: 'A-WO' }
]
},
{
name: 'Queensland',
cities: [
{ cname: 'Brisbane', code: 'A-BR' },
{ cname: 'Townsville', code: 'A-TO' }
]
}
]
},
{
name: 'Canada',
code: 'CA',
states: [
{
name: 'Quebec',
cities: [
{ cname: 'Montreal', code: 'C-MO' },
{ cname: 'Quebec City', code: 'C-QU' }
]
},
{
name: 'Ontario',
cities: [
{ cname: 'Ottawa', code: 'C-OT' },
{ cname: 'Toronto', code: 'C-TO' }
]
}
]
},
{
name: 'United States',
code: 'US',
states: [
{
name: 'California',
cities: [
{ cname: 'Los Angeles', code: 'US-LA' },
{ cname: 'San Diego', code: 'US-SD' },
{ cname: 'San Francisco', code: 'US-SF' }
]
},
{
name: 'Florida',
cities: [
{ cname: 'Jacksonville', code: 'US-JA' },
{ cname: 'Miami', code: 'US-MI' },
{ cname: 'Tampa', code: 'US-TA' },
{ cname: 'Orlando', code: 'US-OR' }
]
},
{
name: 'Texas',
cities: [
{ cname: 'Austin', code: 'US-AU' },
{ cname: 'Dallas', code: 'US-DA' },
{ cname: 'Houston', code: 'US-HO' }
]
}
]
}
],
code: {
basic: `
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex justify-center flex-col gap-4">
<div class="flex flex-col gap-2">
<CascadeSelect name="city" :options="countries" optionLabel="cname" optionGroupLabel="name" :optionGroupChildren="['states', 'cities']" class="w-56" placeholder="Select a City" />
<Message v-if="$form.city?.invalid" severity="error">{{ $form.city.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
`,
options: `
<template>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex justify-center flex-col gap-4">
<div class="flex flex-col gap-2">
<CascadeSelect name="city" :options="countries" optionLabel="cname" optionGroupLabel="name" :optionGroupChildren="['states', 'cities']" class="w-56" placeholder="Select a City" />
<Message v-if="$form.city?.invalid" severity="error">{{ $form.city.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
<Toast />
</div>
</template>
<script>
export default {
data() {
return {
initialValues: {
city: null
},
resolver: zodResolver(
z.object({
city: z.object({
cname: z.string().min(1, 'City is required.')
})
})
),
countries: [
{
name: 'Australia',
code: 'AU',
states: [
{
name: 'New South Wales',
cities: [
{ cname: 'Sydney', code: 'A-SY' },
{ cname: 'Newcastle', code: 'A-NE' },
{ cname: 'Wollongong', code: 'A-WO' }
]
},
{
name: 'Queensland',
cities: [
{ cname: 'Brisbane', code: 'A-BR' },
{ cname: 'Townsville', code: 'A-TO' }
]
}
]
},
{
name: 'Canada',
code: 'CA',
states: [
{
name: 'Quebec',
cities: [
{ cname: 'Montreal', code: 'C-MO' },
{ cname: 'Quebec City', code: 'C-QU' }
]
},
{
name: 'Ontario',
cities: [
{ cname: 'Ottawa', code: 'C-OT' },
{ cname: 'Toronto', code: 'C-TO' }
]
}
]
},
{
name: 'United States',
code: 'US',
states: [
{
name: 'California',
cities: [
{ cname: 'Los Angeles', code: 'US-LA' },
{ cname: 'San Diego', code: 'US-SD' },
{ cname: 'San Francisco', code: 'US-SF' }
]
},
{
name: 'Florida',
cities: [
{ cname: 'Jacksonville', code: 'US-JA' },
{ cname: 'Miami', code: 'US-MI' },
{ cname: 'Tampa', code: 'US-TA' },
{ cname: 'Orlando', code: 'US-OR' }
]
},
{
name: 'Texas',
cities: [
{ cname: 'Austin', code: 'US-AU' },
{ cname: 'Dallas', code: 'US-DA' },
{ cname: 'Houston', code: 'US-HO' }
]
}
]
}
]
};
},
methods: {
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
};
<\/script>
`,
composition: `
<template>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex justify-center flex-col gap-4">
<div class="flex flex-col gap-2">
<CascadeSelect name="city" :options="countries" optionLabel="cname" optionGroupLabel="name" :optionGroupChildren="['states', 'cities']" class="w-56" placeholder="Select a City" />
<Message v-if="$form.city?.invalid" severity="error">{{ $form.city.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
<Toast />
</div>
</template>
<script setup>
import { ref } from "vue";
import { zodResolver } from '@primevue/form/resolvers';
import { useToast } from "primevue/usetoast";
import { z } from 'zod';
const toast = useToast();
const initialValues = ref({
city: null
});
const resolver = ref(zodResolver(
z.object({
city: z.object({
cname: z.string().min(1, 'City is required.')
})
})
));
const resolver = ref(zodResolver(
z.object({
city: z.object({
cname: z.string().min(1, 'City is required.')
})
})
));
const countries = ref([
{
name: 'Australia',
code: 'AU',
states: [
{
name: 'New South Wales',
cities: [
{ cname: 'Sydney', code: 'A-SY' },
{ cname: 'Newcastle', code: 'A-NE' },
{ cname: 'Wollongong', code: 'A-WO' }
]
},
{
name: 'Queensland',
cities: [
{ cname: 'Brisbane', code: 'A-BR' },
{ cname: 'Townsville', code: 'A-TO' }
]
}
]
},
{
name: 'Canada',
code: 'CA',
states: [
{
name: 'Quebec',
cities: [
{ cname: 'Montreal', code: 'C-MO' },
{ cname: 'Quebec City', code: 'C-QU' }
]
},
{
name: 'Ontario',
cities: [
{ cname: 'Ottawa', code: 'C-OT' },
{ cname: 'Toronto', code: 'C-TO' }
]
}
]
},
{
name: 'United States',
code: 'US',
states: [
{
name: 'California',
cities: [
{ cname: 'Los Angeles', code: 'US-LA' },
{ cname: 'San Diego', code: 'US-SD' },
{ cname: 'San Francisco', code: 'US-SF' }
]
},
{
name: 'Florida',
cities: [
{ cname: 'Jacksonville', code: 'US-JA' },
{ cname: 'Miami', code: 'US-MI' },
{ cname: 'Tampa', code: 'US-TA' },
{ cname: 'Orlando', code: 'US-OR' }
]
},
{
name: 'Texas',
cities: [
{ cname: 'Austin', code: 'US-AU' },
{ cname: 'Dallas', code: 'US-DA' },
{ cname: 'Houston', code: 'US-HO' }
]
}
]
}
]);
const onFormSubmit = ({ valid }) => {
if (valid) {
toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
};
<\/script>
`
}
};
},
methods: {
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
};
</script>

View File

@ -0,0 +1,204 @@
<template>
<DocSectionText v-bind="$attrs">
<p>Checkbox can be used with the <NuxtLink to="/forms">PrimeVue Forms</NuxtLink> library.</p>
</DocSectionText>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex justify-center flex-col gap-4">
<div class="flex flex-col gap-2">
<CheckboxGroup name="checkbox" class="flex flex-wrap gap-4">
<div class="flex items-center">
<Checkbox inputId="ingredient1" value="Cheese" />
<label for="ingredient1" class="ml-2"> Cheese </label>
</div>
<div class="flex items-center">
<Checkbox inputId="ingredient2" value="Mushroom" />
<label for="ingredient2" class="ml-2"> Mushroom </label>
</div>
<div class="flex items-center">
<Checkbox inputId="ingredient3" value="Pepper" />
<label for="ingredient3" class="ml-2"> Pepper </label>
</div>
<div class="flex items-center">
<Checkbox inputId="ingredient4" value="Onion" />
<label for="ingredient4" class="ml-2"> Onion </label>
</div>
</CheckboxGroup>
<Message v-if="$form.checkbox?.invalid" severity="error">{{ $form.checkbox.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
<DocSectionCode :code="code" :dependencies="{ zod: '3.23.8' }" />
</template>
<script>
import { zodResolver } from '@primevue/form/resolvers';
import { z } from 'zod';
export default {
data() {
return {
initialValues: {
checkbox: []
},
resolver: zodResolver(
z.object({
checkbox: z.array(z.string()).min(1, { message: 'At least one checkbox must be selected.' })
})
),
code: {
basic: `
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex justify-center flex-col gap-4">
<div class="flex flex-col gap-2">
<CheckboxGroup name="checkbox" class="flex flex-wrap gap-4">
<div class="flex items-center">
<Checkbox inputId="ingredient1" value="Cheese" />
<label for="ingredient1" class="ml-2"> Cheese </label>
</div>
<div class="flex items-center">
<Checkbox inputId="ingredient2" value="Mushroom" />
<label for="ingredient2" class="ml-2"> Mushroom </label>
</div>
<div class="flex items-center">
<Checkbox inputId="ingredient3" value="Pepper" />
<label for="ingredient3" class="ml-2"> Pepper </label>
</div>
<div class="flex items-center">
<Checkbox inputId="ingredient4" value="Onion" />
<label for="ingredient4" class="ml-2"> Onion </label>
</div>
</CheckboxGroup>
<Message v-if="$form.checkbox?.invalid" severity="error">{{ $form.checkbox.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
`,
options: `
<template>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex justify-center flex-col gap-4">
<div class="flex flex-col gap-2">
<CheckboxGroup name="checkbox" class="flex flex-wrap gap-4">
<div class="flex items-center">
<Checkbox inputId="ingredient1" value="Cheese" />
<label for="ingredient1" class="ml-2"> Cheese </label>
</div>
<div class="flex items-center">
<Checkbox inputId="ingredient2" value="Mushroom" />
<label for="ingredient2" class="ml-2"> Mushroom </label>
</div>
<div class="flex items-center">
<Checkbox inputId="ingredient3" value="Pepper" />
<label for="ingredient3" class="ml-2"> Pepper </label>
</div>
<div class="flex items-center">
<Checkbox inputId="ingredient4" value="Onion" />
<label for="ingredient4" class="ml-2"> Onion </label>
</div>
</CheckboxGroup>
<Message v-if="$form.checkbox?.invalid" severity="error">{{ $form.checkbox.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
<Toast />
</template>
<script>
import { zodResolver } from '@primevue/form/resolvers';
import { z } from 'zod';
export default {
data() {
return {
initialValues: {
checkbox: []
},
resolver: zodResolver(
z.object({
checkbox: z.array(z.string()).min(1, { message: 'At least one checkbox must be selected.' })
})
),
}
},
methods: {
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
}
<\/script>
`,
composition: `
<template>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex justify-center flex-col gap-4">
<div class="flex flex-col gap-2">
<CheckboxGroup name="checkbox" class="flex flex-wrap gap-4">
<div class="flex items-center">
<Checkbox inputId="ingredient1" value="Cheese" />
<label for="ingredient1" class="ml-2"> Cheese </label>
</div>
<div class="flex items-center">
<Checkbox inputId="ingredient2" value="Mushroom" />
<label for="ingredient2" class="ml-2"> Mushroom </label>
</div>
<div class="flex items-center">
<Checkbox inputId="ingredient3" value="Pepper" />
<label for="ingredient3" class="ml-2"> Pepper </label>
</div>
<div class="flex items-center">
<Checkbox inputId="ingredient4" value="Onion" />
<label for="ingredient4" class="ml-2"> Onion </label>
</div>
</CheckboxGroup>
<Message v-if="$form.checkbox?.invalid" severity="error">{{ $form.checkbox.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
<Toast />
</template>
<script setup>
import { ref } from 'vue';
import { zodResolver } from '@primevue/form/resolvers';
import { useToast } from "primevue/usetoast";
import { z } from 'zod';
const toast = useToast();
const initialValues = ref({
checkbox: []
});
const resolver = ref(zodResolver(
z.object({
checkbox: z.array(z.string()).min(1, { message: 'At least one checkbox must be selected.' })
})
));
const onFormSubmit = ({ valid }) => {
if (valid) {
toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
};
<\/script>
`
}
};
},
methods: {
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
};
</script>

View File

@ -0,0 +1,130 @@
<template>
<DocSectionText v-bind="$attrs">
<p>ColorPicker can be used with the <NuxtLink to="/forms">PrimeVue Forms</NuxtLink> library.</p>
</DocSectionText>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4">
<div class="flex flex-col items-center gap-2">
<ColorPicker name="colorpicker" />
<Message v-if="$form.colorpicker?.invalid" severity="error">{{ $form.colorpicker.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
<DocSectionCode :code="code" :dependencies="{ zod: '3.23.8' }" />
</template>
<script>
import { zodResolver } from '@primevue/form/resolvers';
import { z } from 'zod';
export default {
data() {
return {
initialValues: {
colorpicker: null
},
resolver: zodResolver(
z.object({
colorpicker: z.union([z.string(), z.literal(null)]).refine((value) => value !== null, { message: 'ColorPicker is required.' })
})
),
code: {
basic: `
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4">
<div class="flex flex-col items-center gap-2">
<ColorPicker name="colorpicker" />
<Message v-if="$form.colorpicker?.invalid" severity="error">{{ $form.colorpicker.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
`,
options: `
<template>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4">
<div class="flex flex-col items-center gap-2">
<ColorPicker name="colorpicker" />
<Message v-if="$form.colorpicker?.invalid" severity="error">{{ $form.colorpicker.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
</template>
<script>
import { zodResolver } from '@primevue/form/resolvers';
import { z } from 'zod';
export default {
data() {
return {
initialValues: {
colorpicker: null
},
resolver: zodResolver(
z.object({
colorpicker: z.union([z.string(), z.literal(null)]).refine((value) => value !== null, { message: 'ColorPicker is required.' })
})
)
}
},
methods: {
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
}
<\/script>
`,
composition: `
<template>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4">
<div class="flex flex-col items-center gap-2">
<ColorPicker name="colorpicker" />
<Message v-if="$form.colorpicker?.invalid" severity="error">{{ $form.colorpicker.error?.message }}</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 { useToast } from "primevue/usetoast";
import { z } from 'zod';
const toast = useToast();
const initialValues = ref({
colorpicker: null
});
const resolver = ref(zodResolver(
z.object({
colorpicker: z.union([z.string(), z.literal(null)]).refine((value) => value !== null, { message: 'ColorPicker is required.' })
})
));
const onFormSubmit = ({ valid }) => {
if (valid) {
toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
};
<\/script>
`
}
};
},
methods: {
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
};
</script>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,148 @@
<template>
<DocSectionText v-bind="$attrs">
<p>DatePicker can be used with the <NuxtLink to="/forms">PrimeVue Forms</NuxtLink> library.</p>
</DocSectionText>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4">
<div class="flex flex-col gap-2">
<DatePicker name="date" fluid />
<Message v-if="$form.date?.invalid" severity="error">{{ $form.date.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
<DocSectionCode :code="code" :dependencies="{ zod: '3.23.8' }" />
</template>
<script>
import { zodResolver } from '@primevue/form/resolvers';
import { z } from 'zod';
export default {
data() {
return {
initialValues: {
date: ''
},
resolver: zodResolver(
z.object({
date: z.preprocess((val) => {
if (val === '' || val === null) {
return null;
}
return new Date(val);
}, z.union([z.date(), z.null().refine((val) => val !== null, { message: 'Date is required.' })]))
})
),
code: {
basic: `
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4">
<div class="flex flex-col gap-2">
<DatePicker name="date" fluid />
<Message v-if="$form.date?.invalid" severity="error">{{ $form.date.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
`,
options: `
<template>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4">
<div class="flex flex-col gap-2">
<DatePicker name="date" fluid />
<Message v-if="$form.date?.invalid" severity="error">{{ $form.date.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
</template>
<script>
import { zodResolver } from '@primevue/form/resolvers';
import { z } from 'zod';
export default {
data() {
return {
initialValues: {
date: ''
},
resolver: zodResolver(
z.object({
date: z.preprocess((val) => {
if (val === '' || val === null) {
return null;
}
return new Date(val);
}, z.union([z.date(), z.null().refine((val) => val !== null, { message: 'Date is required.' })]))
})
)
}
},
methods: {
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
}
<\/script>
`,
composition: `
<template>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4">
<div class="flex flex-col gap-2">
<DatePicker name="date" fluid />
<Message v-if="$form.date?.invalid" severity="error">{{ $form.date.error?.message }}</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 { useToast } from "primevue/usetoast";
import { z } from 'zod';
const toast = useToast();
const initialValues = ref({
date: ''
});
const resolver = ref(zodResolver(
z.object({
date: z.preprocess((val) => {
if (val === '' || val === null) {
return null;
}
return new Date(val);
}, z.union([z.date(), z.null().refine((val) => val !== null, { message: 'Date is required.' })]))
})
));
const onFormSubmit = ({ valid }) => {
if (valid) {
toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
};
<\/script>
`
}
};
},
methods: {
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
};
</script>

View File

@ -0,0 +1,130 @@
<template>
<DocSectionText v-bind="$attrs">
<p>Editor can be used with the <NuxtLink to="/forms">PrimeVue Forms</NuxtLink> library.</p>
</DocSectionText>
<div class="card">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4">
<div class="flex flex-col gap-2">
<Editor name="editor" editorStyle="height: 320px" />
<Message v-if="$form.editor?.invalid" severity="error">{{ $form.editor.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
<DocSectionCode :code="code" :dependencies="{ zod: '3.23.8' }" />
</template>
<script>
import { zodResolver } from '@primevue/form/resolvers';
import { z } from 'zod';
export default {
data() {
return {
initialValues: {
editor: ''
},
resolver: zodResolver(
z.object({
editor: z.string().min(1, { message: 'Editor is required.' })
})
),
code: {
basic: `
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4">
<div class="flex flex-col gap-2">
<Editor name="editor" editorStyle="height: 320px" />
<Message v-if="$form.editor?.invalid" severity="error">{{ $form.editor.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
`,
options: `
<template>
<div class="card">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4">
<div class="flex flex-col gap-2">
<Editor name="editor" editorStyle="height: 320px" />
<Message v-if="$form.editor?.invalid" severity="error">{{ $form.editor.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
</template>
<script>
import { zodResolver } from '@primevue/form/resolvers';
import { z } from 'zod';
export default {
data() {
return {
initialValues: {
editor: ''
},
resolver: zodResolver(
z.object({
editor: z.string().min(1, { message: 'Editor is required.' })
})
)
}
},
methods: {
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
}
<\/script>
`,
composition: `
<template>
<div class="card">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4">
<div class="flex flex-col gap-2">
<Editor name="editor" editorStyle="height: 320px" />
<Message v-if="$form.editor?.invalid" severity="error">{{ $form.editor.error?.message }}</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 { useToast } from "primevue/usetoast";
import { z } from 'zod';
const toast = useToast();
const initialValues = ref({
editor: '
});
const resolver = ref(zodResolver(
z.object({
editor: z.string().min(1, { message: 'Editor is required.' })
})
));
const onFormSubmit = ({ valid }) => {
if (valid) {
toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
};
<\/script>
`
}
};
},
methods: {
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
};
</script>

View File

@ -0,0 +1,9 @@
<template>
<DocSectionText id="accessibility" label="Accessibility" v-bind="$attrs">
<h3>Screen Reader</h3>
<p>Form does not require any roles and attributes.</p>
<h3>Keyboard Support</h3>
<p>Component does not include any interactive elements.</p>
</DocSectionText>
</template>

View File

@ -0,0 +1,151 @@
<template>
<DocSectionText v-bind="$attrs">
<p>
Form is compatible with PrimeVue components, enabling smooth integration and functionality. Each component is linked to Form via a <i>name</i> property, which the form uses to create a state object for tracking values, errors and actions.
</p>
</DocSectionText>
<div class="card flex justify-center">
<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>
<DocSectionCode :code="code" />
</template>
<script>
export default {
data() {
return {
initialValues: {
username: ''
},
code: {
basic: `
<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>
`,
options: `
<template>
<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>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
</template>
<script>
export default {
data() {
return {
initialValues: {
username: ''
}
};
},
methods: {
resolver: ({ values }) => {
const errors = {};
if (!values.username) {
errors.username = [{ message: 'Username is required.' }];
}
return {
errors
};
},
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
};
<\/script>
`,
composition: `
<template>
<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>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
</template>
<script setup>
import { reactive } from 'vue';
import { useToast } from 'primevue/usetoast';
const toast = useToast();
const initialValues = reactive({
username: ''
});
const resolver = ({ values }) => {
const errors = {};
if (!values.username) {
errors.username = [{ message: 'Username is required.' }];
}
return {
errors
};
};
const onFormSubmit = ({ valid }) => {
if (valid) {
toast.add({
severity: 'success',
summary: 'Form is submitted.',
life: 3000
});
}
};
<\/script>
`
}
};
},
methods: {
resolver: ({ values }) => {
const errors = {};
if (!values.username) {
errors.username = [{ message: 'Username is required.' }];
}
return {
errors
};
},
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
};
</script>

View File

@ -0,0 +1,357 @@
<template>
<DocSectionText v-bind="$attrs">
<p>This section demonstrates how to create a dynamic form using a custom Form component. It showcases an example where form fields are generated dynamically based on the provided configuration, allowing for flexible form structures.</p>
</DocSectionText>
<div class="card grid md:grid-cols-2 gap-4 w-full">
<Fieldset legend="Form 1" pt:content:class="flex justify-center">
<DynamicForm @submit="onFormSubmit('Form 1', $event)">
<DynamicFormField groupId="userId_1" name="username">
<DynamicFormLabel>Username</DynamicFormLabel>
<DynamicFormControl defaultValue="PrimeVue" fluid :schema="userNameSchema" />
<DynamicFormMessage />
</DynamicFormField>
<DynamicFormField groupId="passId_1" name="password">
<DynamicFormLabel>Password</DynamicFormLabel>
<DynamicFormControl as="Password" :feedback="false" toggleMask fluid :schema="passwordSchema" />
<DynamicFormMessage errorType="minimum" />
<DynamicFormMessage errorType="maximum" />
<DynamicFormMessage errorType="uppercase" severity="warn" />
<DynamicFormMessage errorType="lowercase" severity="warn" />
<DynamicFormMessage errorType="number" severity="secondary" />
</DynamicFormField>
<DynamicFormSubmit />
</DynamicForm>
</Fieldset>
<Fieldset legend="Form 2" pt:content:class="flex justify-center">
<DynamicForm :fields @submit="onFormSubmit('Form 2', $event)" />
</Fieldset>
</div>
<DocSectionCode :code="code" :dependencies="{ zod: '3.23.8' }" />
</template>
<script>
import { markRaw } from 'vue';
import { z } from 'zod';
import DynamicForm from './dynamic/DynamicForm.vue';
import DynamicFormControl from './dynamic/DynamicFormControl.vue';
import DynamicFormField from './dynamic/DynamicFormField.vue';
import DynamicFormLabel from './dynamic/DynamicFormLabel.vue';
import DynamicFormMessage from './dynamic/DynamicFormMessage.vue';
import DynamicFormSubmit from './dynamic/DynamicFormSubmit.vue';
export default {
data() {
return {
userNameSchema: z.string().min(1, { message: 'Username is required.' }),
passwordSchema: z
.string()
.min(3, { message: 'Password must be at least 3 characters long.' })
.max(8, { message: 'Password must not exceed 8 characters.' })
.refine((value) => /[a-z]/.test(value), {
errorType: 'lowercase',
message: 'Password must contain at least one lowercase letter.'
})
.refine((value) => /[A-Z]/.test(value), {
errorType: 'uppercase',
message: 'Password must contain at least one uppercase letter.'
})
.refine((value) => /\d/.test(value), {
errorType: 'number',
message: 'Password must contain at least one number.'
}),
fields: {
username: {
groupId: 'userId_2',
label: 'Username',
defaultValue: 'PrimeVue',
fluid: true,
schema: z.string().min(1, { message: 'Username is required.' })
},
password: {
groupId: 'passId_2',
label: 'Password',
as: 'Password',
feedback: false,
toggleMask: true,
fluid: true,
messages: [{ errorType: 'minimum' }, { errorType: 'maximum' }, { errorType: 'uppercase', severity: 'warn' }, { errorType: 'lowercase', severity: 'warn' }, { errorType: 'number', severity: 'secondary' }],
schema: z
.string()
.min(3, { message: 'Password must be at least 3 characters long.' })
.max(8, { message: 'Password must not exceed 8 characters.' })
.refine((value) => /[a-z]/.test(value), {
errorType: 'lowercase',
message: 'Password must contain at least one lowercase letter.'
})
.refine((value) => /[A-Z]/.test(value), {
errorType: 'uppercase',
message: 'Password must contain at least one uppercase letter.'
})
.refine((value) => /\d/.test(value), {
errorType: 'number',
message: 'Password must contain at least one number.'
})
}
},
code: {
basic: `
<Fieldset legend="Form 1" pt:content:class="flex justify-center">
<DynamicForm @submit="onFormSubmit('Form 1', $event)">
<DynamicFormField groupId="userId_1" name="username">
<DynamicFormLabel>Username</DynamicFormLabel>
<DynamicFormControl defaultValue="PrimeVue" fluid :schema="userNameSchema" />
<DynamicFormMessage />
</DynamicFormField>
<DynamicFormField groupId="passId_1" name="password">
<DynamicFormLabel>Password</DynamicFormLabel>
<DynamicFormControl as="Password" :feedback="false" toggleMask fluid :schema="passwordSchema" />
<DynamicFormMessage errorType="minimum" />
<DynamicFormMessage errorType="maximum" />
<DynamicFormMessage errorType="uppercase" severity="warn" />
<DynamicFormMessage errorType="lowercase" severity="warn" />
<DynamicFormMessage errorType="number" severity="secondary" />
</DynamicFormField>
<DynamicFormSubmit />
</DynamicForm>
</Fieldset>
<Fieldset legend="Form 2" pt:content:class="flex justify-center">
<DynamicForm :fields @submit="onFormSubmit('Form 2', $event)" />
</Fieldset>
`,
options: `
<template>
<div class="card grid md:grid-cols-2 gap-4 w-full">
<Toast />
<Fieldset legend="Form 1" pt:content:class="flex justify-center">
<DynamicForm @submit="onFormSubmit('Form 1', $event)">
<DynamicFormField groupId="userId_1" name="username">
<DynamicFormLabel>Username</DynamicFormLabel>
<DynamicFormControl defaultValue="PrimeVue" fluid :schema="userNameSchema" />
<DynamicFormMessage />
</DynamicFormField>
<DynamicFormField groupId="passId_1" name="password">
<DynamicFormLabel>Password</DynamicFormLabel>
<DynamicFormControl as="Password" :feedback="false" toggleMask fluid :schema="passwordSchema" />
<DynamicFormMessage errorType="minimum" />
<DynamicFormMessage errorType="maximum" />
<DynamicFormMessage errorType="uppercase" severity="warn" />
<DynamicFormMessage errorType="lowercase" severity="warn" />
<DynamicFormMessage errorType="number" severity="secondary" />
</DynamicFormField>
<DynamicFormSubmit />
</DynamicForm>
</Fieldset>
<Fieldset legend="Form 2" pt:content:class="flex justify-center">
<DynamicForm :fields @submit="onFormSubmit('Form 2', $event)" />
</Fieldset>
</div>
</template>
<script>
import { markRaw } from 'vue';
import { z } from 'zod';
import DynamicForm from './dynamic/DynamicForm.vue';
import DynamicFormControl from './dynamic/DynamicFormControl.vue';
import DynamicFormField from './dynamic/DynamicFormField.vue';
import DynamicFormLabel from './dynamic/DynamicFormLabel.vue';
import DynamicFormMessage from './dynamic/DynamicFormMessage.vue';
import DynamicFormSubmit from './dynamic/DynamicFormSubmit.vue';
export default {
data() {
return {
userNameSchema: z.string().min(1, { message: 'Username is required.' }),
passwordSchema: z
.string()
.min(3, { message: 'Password must be at least 3 characters long.' })
.max(8, { message: 'Password must not exceed 8 characters.' })
.refine((value) => /[a-z]/.test(value), {
errorType: 'lowercase',
message: 'Password must contain at least one lowercase letter.'
})
.refine((value) => /[A-Z]/.test(value), {
errorType: 'uppercase',
message: 'Password must contain at least one uppercase letter.'
})
.refine((value) => /\d/.test(value), {
errorType: 'number',
message: 'Password must contain at least one number.'
}),
fields: {
username: {
groupId: 'userId_2',
label: 'Username',
defaultValue: 'PrimeVue',
fluid: true,
schema: z.string().min(1, { message: 'Username is required.' })
},
password: {
groupId: 'passId_2',
label: 'Password',
as: 'Password',
feedback: false,
toggleMask: true,
fluid: true,
messages: [{ errorType: 'minimum' }, { errorType: 'maximum' }, { errorType: 'uppercase', severity: 'warn' }, { errorType: 'lowercase', severity: 'warn' }, { errorType: 'number', severity: 'secondary' }],
schema: z
.string()
.min(3, { message: 'Password must be at least 3 characters long.' })
.max(8, { message: 'Password must not exceed 8 characters.' })
.refine((value) => /[a-z]/.test(value), {
errorType: 'lowercase',
message: 'Password must contain at least one lowercase letter.'
})
.refine((value) => /[A-Z]/.test(value), {
errorType: 'uppercase',
message: 'Password must contain at least one uppercase letter.'
})
.refine((value) => /\d/.test(value), {
errorType: 'number',
message: 'Password must contain at least one number.'
})
}
}
};
},
methods: {
onFormSubmit(text, { valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: \`\${text} is submitted.\`, life: 3000 });
}
}
},
components: {
DynamicForm: markRaw(DynamicForm),
DynamicFormControl: markRaw(DynamicFormControl),
DynamicFormField: markRaw(DynamicFormField),
DynamicFormLabel: markRaw(DynamicFormLabel),
DynamicFormMessage: markRaw(DynamicFormMessage),
DynamicFormSubmit: markRaw(DynamicFormSubmit)
}
};
<\/script>
`,
composition: `
<template>
<div class="card grid md:grid-cols-2 gap-4 w-full">
<Toast />
<Fieldset legend="Form 1" pt:content:class="flex justify-center">
<DynamicForm @submit="onFormSubmit('Form 1', $event)">
<DynamicFormField groupId="userId_1" name="username">
<DynamicFormLabel>Username</DynamicFormLabel>
<DynamicFormControl defaultValue="PrimeVue" fluid :schema="userNameSchema" />
<DynamicFormMessage />
</DynamicFormField>
<DynamicFormField groupId="passId_1" name="password">
<DynamicFormLabel>Password</DynamicFormLabel>
<DynamicFormControl as="Password" :feedback="false" toggleMask fluid :schema="passwordSchema" />
<DynamicFormMessage errorType="minimum" />
<DynamicFormMessage errorType="maximum" />
<DynamicFormMessage errorType="uppercase" severity="warn" />
<DynamicFormMessage errorType="lowercase" severity="warn" />
<DynamicFormMessage errorType="number" severity="secondary" />
</DynamicFormField>
<DynamicFormSubmit />
</DynamicForm>
</Fieldset>
<Fieldset legend="Form 2" pt:content:class="flex justify-center">
<DynamicForm :fields @submit="onFormSubmit('Form 2', $event)" />
</Fieldset>
</div>
</template>
<script setup>
import { reactive } from 'vue';
import { z } from 'zod';
import { useToast } from 'primevue/usetoast';
import DynamicForm from './dynamic/DynamicForm.vue';
import DynamicFormControl from './dynamic/DynamicFormControl.vue';
import DynamicFormField from './dynamic/DynamicFormField.vue';
import DynamicFormLabel from './dynamic/DynamicFormLabel.vue';
import DynamicFormMessage from './dynamic/DynamicFormMessage.vue';
import DynamicFormSubmit from './dynamic/DynamicFormSubmit.vue';
const toast = useToast();
const userNameSchema = z.string().min(1, { message: 'Username is required.' });
const passwordSchema = z
.string()
.min(3, { message: 'Password must be at least 3 characters long.' })
.max(8, { message: 'Password must not exceed 8 characters.' })
.refine((value) => /[a-z]/.test(value), {
errorType: 'lowercase',
message: 'Password must contain at least one lowercase letter.'
})
.refine((value) => /[A-Z]/.test(value), {
errorType: 'uppercase',
message: 'Password must contain at least one uppercase letter.'
})
.refine((value) => /\d/.test(value), {
errorType: 'number',
message: 'Password must contain at least one number.'
});
const fields = reactive({
username: {
groupId: 'userId_2',
label: 'Username',
defaultValue: 'PrimeVue',
fluid: true,
schema: userNameSchema
},
password: {
groupId: 'passId_2',
label: 'Password',
as: 'Password',
feedback: false,
toggleMask: true,
fluid: true,
messages: [
{ errorType: 'minimum' },
{ errorType: 'maximum' },
{ errorType: 'uppercase', severity: 'warn' },
{ errorType: 'lowercase', severity: 'warn' },
{ errorType: 'number', severity: 'secondary' }
],
schema: passwordSchema
}
});
const onFormSubmit = (text, { valid }) => {
if (valid) {
toast.add({
severity: 'success',
summary: \`\${text} is submitted.\`,
life: 3000
});
}
};
<\/script>
`
}
};
},
methods: {
onFormSubmit(text, { valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: `${text} is submitted.`, life: 3000 });
}
}
},
components: {
DynamicForm: markRaw(DynamicForm),
DynamicFormControl: markRaw(DynamicFormControl),
DynamicFormField: markRaw(DynamicFormField),
DynamicFormLabel: markRaw(DynamicFormLabel),
DynamicFormMessage: markRaw(DynamicFormMessage),
DynamicFormSubmit: markRaw(DynamicFormSubmit)
}
};
</script>

View File

@ -0,0 +1,18 @@
<template>
<DocSectionText v-bind="$attrs" />
<DocSectionCode :code="code" hideToggleCode importCode hideStackBlitz />
</template>
<script>
export default {
data() {
return {
code: {
basic: `
import Form from '@primevue/form';
`
}
};
}
};
</script>

View File

@ -0,0 +1,159 @@
<template>
<DocSectionText v-bind="$attrs">
<p>
The <i>register</i> callback allows non-PrimeVue components to be registered within Form component. This enables custom elements to participate in the form's validation and value tracking, ensuring they work alongside PrimeVue components.
</p>
</DocSectionText>
<div class="card flex justify-center">
<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">
<input type="text" placeholder="Username" v-bind="$form.register('username')" />
<Message v-if="$form.username?.invalid" severity="error">{{ $form.username.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
<DocSectionCode :code="code" :dependencies="{ zod: '3.23.8' }" />
</template>
<script>
import { zodResolver } from '@primevue/form/resolvers';
import { z } from 'zod';
export default {
data() {
return {
initialValues: {
username: ''
},
resolver: zodResolver(
z.object({
username: z.string().min(1, { message: 'Username is required.' })
})
),
code: {
basic: `
<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">
<input type="text" placeholder="Username" v-bind="$form.register('username')" />
<Message v-if="$form.username?.invalid" severity="error">{{ $form.username.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
`,
options: `
<template>
<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">
<input type="text" placeholder="Username" v-bind="$form.register('username')" />
<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>
import { zodResolver } from '@primevue/form/resolvers';
import { z } from 'zod';
export default {
data() {
return {
initialValues: {
username: ''
},
resolver: zodResolver(
z.object({
username: z.string().min(1, { message: 'Username is required.' })
})
)
};
},
methods: {
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
};
<\/script>
<style scoped>
input {
width: 100%;
color: var(--p-inputtext-color);
background: var(--p-inputtext-background);
border: 1px solid var(--p-inputtext-border-color);
}
<\/style>
`,
composition: `
<template>
<div class="card flex justify-center">
<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">
<input type="text" placeholder="Username" v-bind="$form.register('username')" />
<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 { reactive } from 'vue';
import { zodResolver } from '@primevue/form/resolvers';
import { z } from 'zod';
import { useToast } from 'primevue/usetoast';
const toast = useToast();
const initialValues = reactive({
username: ''
});
const resolver = zodResolver(
z.object({
username: z.string().min(1, { message: 'Username is required.' })
})
);
const onFormSubmit = ({ valid }) => {
if (valid) {
toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
};
<\/script>
<style scoped>
input {
width: 100%;
color: var(--p-inputtext-color);
background: var(--p-inputtext-background);
border: 1px solid var(--p-inputtext-border-color);
}
<\/style>
`
}
};
},
methods: {
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
};
</script>
<style scoped>
input {
width: 100%;
color: var(--p-inputtext-color);
background: var(--p-inputtext-background);
border: 1px solid var(--p-inputtext-border-color);
}
</style>

View File

@ -0,0 +1,347 @@
<template>
<DocSectionText v-bind="$attrs">
<p>
It can be integrated with with schema libraries such as <a href="https://zod.dev/">Zod</a>, <a href="https://github.com/jquense/yup">Yup</a>, <a href="https://joi.dev/">Joi</a>, <a href="https://valibot.dev/">Valibot</a>,
<a href="https://docs.superstructjs.org/">Superstruct</a> or custom validation logic is possible using <i>resolver</i> property, with available resolvers from <i>@primevue/form/resolvers</i> for each schema.
</p>
</DocSectionText>
<div class="card flex flex-col items-center gap-5">
<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>
<DocSectionCode :code="code" :dependencies="{ zod: '3.23.8' }" />
</template>
<script>
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';
export default {
data() {
return {
initialValues: {
username: ''
},
selectedSchema: 'Zod',
resolver: zodResolver(
z.object({
username: z.string().min(1, { message: 'Username is required via Zod.' })
})
),
code: {
basic: `
<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>
`,
options: `
<template>
<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>
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';
export default {
data() {
return {
initialValues: {
username: ''
},
selectedSchema: 'Zod',
resolver: zodResolver(
z.object({
username: z.string().min(1, { message: 'Username is required via Zod.' })
})
)
};
},
methods: {
changeResolver(schema) {
if (schema === 'Zod') {
this.resolver = zodResolver(
z.object({
username: z.string().min(1, { message: 'Username is required via Zod.' })
})
);
} else if (schema === 'Yup') {
this.resolver = yupResolver(
yup.object().shape({
username: yup.string().required('Username is required via Yup.')
})
);
} else if (schema === 'Valibot') {
this.resolver = valibotResolver(
v.object({
username: v.pipe(v.string(), v.minLength(1, 'Username is required via Valibot.'))
})
);
} else if (schema === 'SuperStruct') {
this.resolver = superStructResolver(
s.object({
username: s.nonempty(s.string())
})
);
} else if (schema === 'Custom') {
this.resolver = ({ values }) => {
const errors = {};
if (!values.username) {
errors.username = [{ message: 'Username is required.' }];
}
return {
errors
};
};
}
},
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
};
<\/script>
`,
composition: `
<template>
<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);
});
const 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
};
};
}
}
const onFormSubmit = ({ valid }) => {
if (valid) {
toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
<\/script>
`
}
};
},
methods: {
changeResolver(schema) {
if (schema === 'Zod') {
this.resolver = zodResolver(
z.object({
username: z.string().min(1, { message: 'Username is required via Zod.' })
})
);
} else if (schema === 'Yup') {
this.resolver = yupResolver(
yup.object().shape({
username: yup.string().required('Username is required via Yup.')
})
);
} else if (schema === 'Valibot') {
this.resolver = valibotResolver(
v.object({
username: v.pipe(v.string(), v.minLength(1, 'Username is required via Valibot.'))
})
);
} else if (schema === 'SuperStruct') {
this.resolver = superStructResolver(
s.object({
username: s.nonempty(s.string())
})
);
} else if (schema === 'Custom') {
this.resolver = ({ values }) => {
const errors = {};
if (!values.username) {
errors.username = [{ message: 'Username is required.' }];
}
return {
errors
};
};
}
},
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
};
</script>

View File

@ -0,0 +1,165 @@
<template>
<DocSectionText v-bind="$attrs">
<p>Form uses the <i>name</i> property to create a state object for tracking values, errors, and actions.</p>
</DocSectionText>
<div class="card flex justify-center">
<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>
<DocSectionCode :code="code" />
</template>
<script>
export default {
data() {
return {
initialValues: {
username: ''
},
code: {
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" 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>
`,
options: `
<template>
<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>
export default {
data() {
return {
initialValues: {
username: ''
},
};
},
methods: {
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
};
},
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
};
<\/script>
`,
composition: `
<template>
<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>
`
}
};
},
methods: {
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
};
},
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
};
</script>

View File

@ -0,0 +1,230 @@
<template>
<DocSectionText v-bind="$attrs">
<p>The <i>submit</i> callback provides an object containing the form's validity, all errors, and current states. This offers access to the form values as well as validation status and any existing errors during submission.</p>
</DocSectionText>
<div class="card flex justify-center">
<Form v-slot="$form" :initialValues :resolver @submit="onFormSubmit" class="flex flex-col gap-4 w-full sm:w-60">
<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" toggleMask 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>
<DocSectionCode :code="code" :dependencies="{ zod: '3.23.8' }" />
</template>
<script>
import { zodResolver } from '@primevue/form/resolvers';
import { z } from 'zod';
export default {
data() {
return {
initialValues: {
username: '',
password: ''
},
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 a uppercase letter.'
})
.refine((value) => /\d/.test(value), {
message: 'Must have a number.'
})
})
),
code: {
basic: `
<Form v-slot="$form" :initialValues :resolver @submit="onFormSubmit" class="flex flex-col gap-4 w-full sm:w-60">
<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" toggleMask 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>
`,
options: `
<template>
<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-60">
<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" toggleMask 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>
import { zodResolver } from '@primevue/form/resolvers';
import { z } from 'zod';
export default {
data() {
return {
initialValues: {
username: '',
password: ''
},
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 a uppercase letter.'
})
.refine((value) => /\d/.test(value), {
message: 'Must have a number.'
})
})
)
};
},
methods: {
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) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
};
<\/script>
`,
composition: `
<template>
<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-60">
<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" toggleMask 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>
`
}
};
},
methods: {
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) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
};
</script>

View File

@ -0,0 +1,210 @@
<template>
<DocSectionText v-bind="$attrs">
<p>
Form component supports flexible validation triggers, allowing validation on value updates, blur events, form mount, or submission, with options to apply this behavior globally or to specific fields via <i>validateOnValueUpdate</i>,
<i>validateOnBlur</i>, <i>validateOnMount</i>, and <i>validateOnSubmit</i>, which can also be set in PrimeVue components using <i>formControl</i> property.
</p>
</DocSectionText>
<div class="card flex justify-center">
<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 :formControl="{ validateOnValueUpdate: true }" />
<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>
<DocSectionCode :code="code" />
</template>
<script>
export default {
data() {
return {
initialValues: {
username: '',
name: '',
surname: ''
},
code: {
basic: `
<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 :formControl="{ validateOnValueUpdate: true }" />
<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>
`,
options: `
<template>
<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 :formControl="{ validateOnValueUpdate: true }"/>
<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>
export default {
data() {
return {
initialValues: {
username: '',
name: '',
surname: ''
}
};
},
methods: {
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
};
},
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
};
<\/script>
`,
composition: `
<template>
<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 :formControl="{ validateOnValueUpdate: true }" />
<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>
`
}
};
},
methods: {
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
};
},
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
};
</script>

View File

@ -0,0 +1,46 @@
<template>
<Form v-slot="$form" :initialValues :resolver @submit="$emit('submit', $event)" class="flex flex-col gap-4 w-full sm:w-56">
<slot v-bind="$form">
<template v-for="({ groupId, label, messages, ...rest }, name) in fields" :key="name">
<DynamicFormField :groupId :name>
<DynamicFormLabel>{{ label }}</DynamicFormLabel>
<DynamicFormControl v-bind="rest" />
<DynamicFormMessage v-for="(message, index) in messages || [{}]" :key="index" v-bind="message" />
</DynamicFormField>
</template>
<DynamicFormSubmit />
</slot>
</Form>
</template>
<script setup>
import { isNotEmpty } from '@primeuix/utils';
import { zodResolver } from '@primevue/form/resolvers';
import { z } from 'zod';
import DynamicFormControl from './DynamicFormControl.vue';
import DynamicFormField from './DynamicFormField.vue';
import DynamicFormLabel from './DynamicFormLabel.vue';
import DynamicFormMessage from './DynamicFormMessage.vue';
import DynamicFormSubmit from './DynamicFormSubmit.vue';
const props = defineProps({
fields: Object
});
const emit = defineEmits(['submit']);
const defaultValues = ref({});
const schemas = ref({});
const resolver = computed(() => (isNotEmpty(schemas.value) ? zodResolver(z.object(schemas.value)) : undefined));
const initialValues = computed(() => defaultValues.value);
const addField = (name, schema, defaultValue) => {
schema && (schemas.value[name] = schema);
defaultValues.value[name] = defaultValue;
};
provide('$fcDynamicForm', {
addField
});
</script>

View File

@ -0,0 +1,28 @@
<template>
<component :is="component" :id :name class="w-full" />
</template>
<script setup>
import * as PrimeVue from 'primevue/primevue';
import { computed, inject } from 'vue';
const props = defineProps({
as: {
type: String,
default: 'InputText'
},
schema: null,
defaultValue: {
default: ''
}
});
const $fcDynamicForm = inject('$fcDynamicForm', undefined);
const $fcDynamicFormField = inject('$fcDynamicFormField', undefined);
const id = computed(() => $fcDynamicFormField?.groupId);
const name = computed(() => $fcDynamicFormField?.name);
const component = computed(() => PrimeVue[props.as] ?? props.as);
$fcDynamicForm?.addField(name.value, props.schema, props.defaultValue);
</script>

View File

@ -0,0 +1,25 @@
<template>
<div class="flex flex-col gap-2">
<slot />
</div>
</template>
<script setup>
import { provide } from 'vue';
const props = defineProps({
groupId: {
type: String,
default: undefined
},
name: {
type: String,
default: undefined
}
});
provide('$fcDynamicFormField', {
groupId: props.groupId,
name: props.name
});
</script>

View File

@ -0,0 +1,13 @@
<template>
<label :for="htmlFor">
<slot />
</label>
</template>
<script setup>
import { computed, inject } from 'vue';
const $fcDynamicFormField = inject('$fcDynamicFormField', undefined);
const htmlFor = computed(() => $fcDynamicFormField?.groupId);
</script>

View File

@ -0,0 +1,32 @@
<template>
<Message v-if="visible" :severity>
<slot>{{ message }}</slot>
</Message>
</template>
<script setup>
import { isNotEmpty } from '@primeuix/utils';
import { computed, inject } from 'vue';
const props = defineProps({
errorType: {
type: String,
default: undefined
},
severity: {
type: String,
default: 'error'
}
});
const $pcForm = inject('$pcForm', undefined); // Inject PrimeVue Form component
const $fcDynamicFormField = inject('$fcDynamicFormField', undefined);
const fieldName = computed(() => $fcDynamicFormField?.name);
const state = computed(() => $pcForm?.states?.[fieldName.value]);
const errors = computed(() => state.value?.errors);
const invalid = computed(() => state.value?.invalid);
const error = computed(() => errors.value?.find((error) => props.errorType === error.errorType || isNotEmpty(error[props.errorType])));
const message = computed(() => (props.errorType ? error.value?.message : errors.value?.[0]?.message));
const visible = computed(() => invalid.value && (error.value || props.errorType === undefined));
</script>

View File

@ -0,0 +1,18 @@
<template>
<Button type="submit" :severity :label />
</template>
<script setup>
import Button from 'primevue/button';
const props = defineProps({
severity: {
type: String,
default: 'secondary'
},
label: {
type: String,
default: 'Submit'
}
});
</script>

View File

@ -0,0 +1,45 @@
<template>
<DocPTViewer :docs="docs">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @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>
</DocPTViewer>
</template>
<script>
import { getPTOptions } from '@/components/doc/helpers';
import { zodResolver } from '@primevue/form/resolvers';
import { z } from 'zod';
export default {
data() {
return {
docs: [
{
data: getPTOptions('Form'),
key: 'Form'
}
],
initialValues: {
username: ''
},
resolver: zodResolver(
z.object({
username: z.string().min(1, { message: 'Username is required.' })
})
)
};
},
methods: {
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
};
</script>

View File

@ -0,0 +1,35 @@
<template>
<div class="doc-main">
<div class="doc-intro">
<h1>Form Pass Through</h1>
</div>
<DocSections :docs="docs" />
</div>
<DocSectionNav :docs="docs" />
</template>
<script>
import DocApiTable from '@/components/doc/DocApiTable.vue';
import { getPTOptions } from '@/components/doc/helpers';
import PTViewer from './PTViewer.vue';
export default {
data() {
return {
docs: [
{
id: 'pt.viewer',
label: 'Viewer',
component: PTViewer
},
{
id: 'pt.doc.form',
label: 'Form PT Options',
component: DocApiTable,
data: getPTOptions('Form')
}
]
};
}
};
</script>

View File

@ -0,0 +1,5 @@
<template>
<DocSectionText v-bind="$attrs">
<p>Component does not apply any styling.</p>
</DocSectionText>
</template>

View File

@ -0,0 +1,27 @@
<template>
<div class="doc-main">
<div class="doc-intro">
<h1>Form Theming</h1>
</div>
<DocSections :docs="docs" />
</div>
<DocSectionNav :docs="docs" />
</template>
<script>
import StyledDoc from './StyledDoc.vue';
export default {
data() {
return {
docs: [
{
id: 'theming.styled',
label: 'Styled',
component: StyledDoc
}
]
};
}
};
</script>

View File

@ -2,10 +2,10 @@
<DocPTViewer :docs="docs">
<ImageCompare style="width: 728px; height: 410px">
<template #left>
<img src="~/assets/images/island1.jpg" />
<img src="https://primefaces.org/cdn/primevue/images/compare/island1.jpg" />
</template>
<template #right>
<img src="~/assets/images/island2.jpg" />
<img src="https://primefaces.org/cdn/primevue/images/compare/island2.jpg" />
</template>
</ImageCompare>
</DocPTViewer>

View File

@ -33,7 +33,6 @@ export default {
}
}
<\/script>
`,
composition: `
<template>

View File

@ -0,0 +1,130 @@
<template>
<DocSectionText v-bind="$attrs">
<p>InputMask can be used with the <NuxtLink to="/forms">PrimeVue Forms</NuxtLink> library.</p>
</DocSectionText>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4 w-full sm:w-56">
<div class="flex flex-col gap-2">
<InputMask name="mask" mask="99-999999" placeholder="99-999999" fluid />
<Message v-if="$form.mask?.invalid" severity="error">{{ $form.mask.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
<DocSectionCode :code="code" :dependencies="{ zod: '3.23.8' }" />
</template>
<script>
import { zodResolver } from '@primevue/form/resolvers';
import { z } from 'zod';
export default {
data() {
return {
initialValues: {
mask: ''
},
resolver: zodResolver(
z.object({
mask: z.string().min(1, { message: 'Mask is required.' })
})
),
code: {
basic: `
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4 w-full sm:w-56">
<div class="flex flex-col gap-2">
<InputMask name="mask" mask="99-999999" placeholder="99-999999" fluid />
<Message v-if="$form.mask?.invalid" severity="error">{{ $form.mask.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
`,
options: `
<template>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4 w-full sm:w-56">
<div class="flex flex-col gap-2">
<InputMask name="mask" mask="99-999999" placeholder="99-999999" fluid />
<Message v-if="$form.mask?.invalid" severity="error">{{ $form.mask.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
</template>
<script>
import { zodResolver } from '@primevue/form/resolvers';
import { z } from 'zod';
export default {
data() {
return {
initialValues: {
mask: ''
},
resolver: zodResolver(
z.object({
mask: z.string().min(1, { message: 'Mask is required.' })
})
)
}
},
methods: {
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
}
<\/script>
`,
composition: `
<template>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4 w-full sm:w-56">
<div class="flex flex-col gap-2">
<InputMask name="mask" mask="99-999999" placeholder="99-999999" fluid />
<Message v-if="$form.mask?.invalid" severity="error">{{ $form.mask.error?.message }}</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 { useToast } from "primevue/usetoast";
import { z } from 'zod';
const toast = useToast();
const initialValues = ref({
mask: ''
});
const resolver = ref(zodResolver(
z.object({
mask: z.string().min(1, { message: 'Mask is required.' })
})
));
const onFormSubmit = ({ valid }) => {
if (valid) {
toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
};
<\/script>
`
}
};
},
methods: {
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
};
</script>

View File

@ -0,0 +1,130 @@
<template>
<DocSectionText v-bind="$attrs">
<p>InputNumber can be used with the <NuxtLink to="/forms">PrimeVue Forms</NuxtLink> library.</p>
</DocSectionText>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4 w-full sm:w-56">
<div class="flex flex-col gap-2">
<InputNumber name="number" fluid />
<Message v-if="$form.number?.invalid" severity="error">{{ $form.number.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
<DocSectionCode :code="code" :dependencies="{ zod: '3.23.8' }" />
</template>
<script>
import { zodResolver } from '@primevue/form/resolvers';
import { z } from 'zod';
export default {
data() {
return {
initialValues: {
number: 20
},
resolver: zodResolver(
z.object({
number: z.number().gt(0, { message: 'Must be greater than 0.' }).lt(10, { message: 'Must be less than 10.' })
})
),
code: {
basic: `
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4 w-full sm:w-56">
<div class="flex flex-col gap-2">
<InputNumber name="number" fluid />
<Message v-if="$form.number?.invalid" severity="error">{{ $form.number.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
`,
options: `
<template>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4 w-full sm:w-56">
<div class="flex flex-col gap-2">
<InputNumber name="number" fluid />
<Message v-if="$form.number?.invalid" severity="error">{{ $form.number.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
</template>
<script>
import { zodResolver } from '@primevue/form/resolvers';
import { z } from 'zod';
export default {
data() {
return {
initialValues: {
number: 20
},
resolver: zodResolver(
z.object({
number: z.number().gt(0, { message: 'Must be greater than 0.' }).lt(10, { message: 'Must be less than 10.' })
})
)
}
},
methods: {
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
}
<\/script>
`,
composition: `
<template>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4 w-full sm:w-56">
<div class="flex flex-col gap-2">
<InputNumber name="number" fluid />
<Message v-if="$form.number?.invalid" severity="error">{{ $form.number.error?.message }}</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 { useToast } from "primevue/usetoast";
import { z } from 'zod';
const toast = useToast();
const initialValues = ref({
number: 20
});
const resolver = ref(zodResolver(
z.object({
number: z.number().gt(0, { message: 'Must be greater than 0.' }).lt(10, { message: 'Must be less than 10.' })
})
));
const onFormSubmit = ({ valid }) => {
if (valid) {
toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
};
<\/script>
`
}
};
},
methods: {
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
};
</script>

View File

@ -0,0 +1,122 @@
<template>
<DocSectionText v-bind="$attrs">
<p>InputOtp can be used with the <NuxtLink to="/forms">PrimeVue Forms</NuxtLink> library.</p>
</DocSectionText>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4">
<InputOtp name="otp" />
<Message v-if="$form.otp?.invalid" severity="error" icon="pi pi-times-circle">{{ $form.otp.error?.message }}</Message>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
<DocSectionCode :code="code" :dependencies="{ zod: '3.23.8' }" />
</template>
<script>
import { zodResolver } from '@primevue/form/resolvers';
import { z } from 'zod';
export default {
data() {
return {
initialValues: {
otp: ''
},
resolver: zodResolver(
z.object({
otp: z.string().min(1, { message: 'Otp is required.' })
})
),
code: {
basic: `
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4">
<InputOtp name="otp" />
<Message v-if="$form.otp?.invalid" severity="error" icon="pi pi-times-circle">{{ $form.otp.error?.message }}</Message>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
`,
options: `
<template>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4">
<InputOtp name="otp" />
<Message v-if="$form.otp?.invalid" severity="error" icon="pi pi-times-circle">{{ $form.otp.error?.message }}</Message>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
</template>
<script>
import { zodResolver } from '@primevue/form/resolvers';
import { z } from 'zod';
export default {
data() {
return {
initialValues: {
otp: ''
},
resolver: zodResolver(
z.object({
otp: z.string().min(1, { message: 'Otp is required.' })
})
)
}
},
methods: {
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
}
<\/script>
`,
composition: `
<template>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4">
<InputOtp name="otp" />
<Message v-if="$form.otp?.invalid" severity="error" icon="pi pi-times-circle">{{ $form.otp.error?.message }}</Message>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
</template>
<script setup>
import { ref } from 'vue';
import { zodResolver } from '@primevue/form/resolvers';
import { useToast } from "primevue/usetoast";
import { z } from 'zod';
const toast = useToast();
const initialValues = ref({
otp: ''
});
const resolver = ref(zodResolver(
z.object({
otp: z.string().min(1, { message: 'Otp is required.' })
})
));
const onFormSubmit = ({ valid }) => {
if (valid) {
toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
};
<\/script>
`
}
};
},
methods: {
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
};
</script>

View File

@ -0,0 +1,152 @@
<template>
<DocSectionText v-bind="$attrs">
<p>InputText can be used with the <NuxtLink to="/forms">PrimeVue Forms</NuxtLink> library.</p>
</DocSectionText>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @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="email" type="text" placeholder="Email" fluid />
<Message v-if="$form.email?.invalid" severity="error">{{ $form.email.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
<DocSectionCode :code="code" :dependencies="{ zod: '3.23.8' }" />
</template>
<script>
import { zodResolver } from '@primevue/form/resolvers';
import { z } from 'zod';
export default {
data() {
return {
initialValues: {
username: '',
email: ''
},
resolver: zodResolver(
z.object({
username: z.string().min(1, { message: 'Username is required.' }),
email: z.string().min(1, { message: 'Email is required.' }).email({ message: 'Email must be valid.' })
})
),
code: {
basic: `
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex justify-center flex-col gap-4">
<div class="flex flex-col gap-2">
<InputText name="username" type="text" placeholder="Username" />
<Message v-if="$form.username?.invalid" severity="error">{{ $form.username.error?.message }}</Message>
</div>
<div class="flex flex-col gap-2">
<InputText name="email" type="text" placeholder="Email" />
<Message v-if="$form.email?.invalid" severity="error">{{ $form.email.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
`,
options: `
<template>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @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="email" type="text" placeholder="Email" fluid />
<Message v-if="$form.email?.invalid" severity="error">{{ $form.email.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
</template>
<script>
import { zodResolver } from '@primevue/form/resolvers';
import { z } from 'zod';
export default {
data() {
return {
initialValues: {
username: '',
email: ''
},
resolver: zodResolver(
z.object({
username: z.string().min(1, { message: 'Username is required.' }),
email: z.string().min(1, { message: 'Email is required.' }).email({ message: 'Email must be valid.' })
})
)
}
},
methods: {
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
}
<\/script>
`,
composition: `
<template>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @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="email" type="text" placeholder="Email" fluid />
<Message v-if="$form.email?.invalid" severity="error">{{ $form.email.error?.message }}</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 { useToast } from "primevue/usetoast";
import { z } from 'zod';
const toast = useToast();
const initialValues = ref({
username: '',
email: ''
});
const resolver = ref(zodResolver(
z.object({
username: z.string().min(1, { message: 'Username is required.' }),
email: z.string().min(1, { message: 'Email is required.' }).email({ message: 'Email must be valid.' })
})
));
const onFormSubmit = ({ valid }) => {
if (valid) {
toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
};
<\/script>
`
}
};
},
methods: {
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
};
</script>

View File

@ -0,0 +1,133 @@
<template>
<DocSectionText v-bind="$attrs">
<p>Knob can be used with the <NuxtLink to="/forms">PrimeVue Forms</NuxtLink> library.</p>
</DocSectionText>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4">
<div class="flex flex-col items-center gap-2">
<Knob name="knob" />
<Message v-if="$form.knob?.invalid" severity="error">{{ $form.knob.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
<DocSectionCode :code="code" :dependencies="{ zod: '3.23.8' }" />
</template>
<script>
import { zodResolver } from '@primevue/form/resolvers';
import { z } from 'zod';
export default {
data() {
return {
initialValues: {
knob: 15
},
resolver: zodResolver(
z.object({
knob: z.number().gt(25, { message: 'Must be greater than 25.' }).lt(75, { message: 'Must be less than 75.' })
})
),
code: {
basic: `
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4">
<div class="flex flex-col items-center gap-2">
<Knob name="knob" />
<Message v-if="$form.knob?.invalid" severity="error">{{ $form.knob.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
`,
options: `
<template>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4">
<div class="flex flex-col items-center gap-2">
<Knob name="knob" />
<Message v-if="$form.knob?.invalid" severity="error">{{ $form.knob.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
<Toast />
</template>
<script>
import { zodResolver } from '@primevue/form/resolvers';
import { z } from 'zod';
export default {
data() {
return {
initialValues: {
knob: 15
},
resolver: zodResolver(
z.object({
knob: z.number().gt(25, { message: 'Must be greater than 25.' }).lt(75, { message: 'Must be less than 75.' })
})
)
}
},
methods: {
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
}
<\/script>
`,
composition: `
<template>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4">
<div class="flex flex-col items-center gap-2">
<Knob name="knob" />
<Message v-if="$form.knob?.invalid" severity="error">{{ $form.knob.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
<Toast />
</template>
<script setup>
import { ref } from 'vue';
import { zodResolver } from '@primevue/form/resolvers';
import { useToast } from "primevue/usetoast";
import { z } from 'zod';
const toast = useToast();
const initialValues = ref({
knob: 15
});
const resolver = ref(zodResolver(
z.object({
knob: z.number().gt(25, { message: 'Must be greater than 25.' }).lt(75, { message: 'Must be less than 75.' })
})
));
const onFormSubmit = ({ valid }) => {
if (valid) {
toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
};
<\/script>
`
}
};
},
methods: {
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
};
</script>

View File

@ -0,0 +1,159 @@
<template>
<DocSectionText v-bind="$attrs">
<p>Listbox can be used with the <NuxtLink to="/forms">PrimeVue Forms</NuxtLink> library.</p>
</DocSectionText>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4 w-full sm:w-56">
<div class="flex flex-col gap-2">
<Listbox name="city" :options="cities" optionLabel="name" fluid />
<Message v-if="$form.city?.invalid" severity="error">{{ $form.city.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
<DocSectionCode :code="code" :dependencies="{ zod: '3.23.8' }" />
</template>
<script>
import { zodResolver } from '@primevue/form/resolvers';
import { z } from 'zod';
export default {
data() {
return {
initialValues: {
city: { name: '' }
},
resolver: zodResolver(
z.object({
city: z.union([
z.object({
name: z.string().min(1, 'City required.')
}),
z.any().refine((val) => false, { message: 'City required.' })
])
})
),
cities: [
{ name: 'New York', code: 'NY' },
{ name: 'Rome', code: 'RM' },
{ name: 'London', code: 'LDN' },
{ name: 'Istanbul', code: 'IST' },
{ name: 'Paris', code: 'PRS' }
],
code: {
basic: `
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4 w-full sm:w-56">
<div class="flex flex-col gap-2">
<Listbox name="city" :options="cities" optionLabel="name" fluid />
<Message v-if="$form.city?.invalid" severity="error">{{ $form.city.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
`,
options: `
<template>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4 w-full sm:w-56">
<div class="flex flex-col gap-2">
<Listbox name="city" :options="cities" optionLabel="name" fluid />
<Message v-if="$form.city?.invalid" severity="error">{{ $form.city.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
</template>
<script>
import { zodResolver } from '@primevue/form/resolvers';
import { z } from 'zod';
export default {
data() {
return {
initialValues: {
city: { name: '' }
},
resolver: zodResolver(
z.object({
city: z.union([
z.object({
name: z.string().min(1, 'City required.')
}),
z.any().refine((val) => false, { message: 'City required.' })
])
})
),
cities: [
{ name: 'New York', code: 'NY' },
{ name: 'Rome', code: 'RM' },
{ name: 'London', code: 'LDN' },
{ name: 'Istanbul', code: 'IST' },
{ name: 'Paris', code: 'PRS' }
]
}
},
methods: {
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
}
<\/script>
`,
composition: `
<template>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4 w-full sm:w-56">
<div class="flex flex-col gap-2">
<Listbox name="city" :options="cities" optionLabel="name" fluid />
<Message v-if="$form.city?.invalid" severity="error">{{ $form.city.error?.message }}</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 { useToast } from "primevue/usetoast";
import { z } from 'zod';
const toast = useToast();
const initialValues = ref({
city: { name: '' }
});
const resolver = ref(zodResolver(
z.object({
city: z.union([
z.object({
name: z.string().min(1, 'City required.')
}),
z.any().refine((val) => false, { message: 'City required.' })
])
})
));
const onFormSubmit = ({ valid }) => {
if (valid) {
toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
};
<\/script>
`
}
};
},
methods: {
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
};
</script>

View File

@ -0,0 +1,167 @@
<template>
<DocSectionText v-bind="$attrs"> </DocSectionText>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex justify-center flex-col gap-4 w-full md:w-80">
<div class="flex flex-col gap-2">
<MultiSelect name="city" :options="cities" optionLabel="name" filter placeholder="Select Cities" :maxSelectedLabels="3" fluid />
<Message v-if="$form.city?.invalid" severity="error">{{ $form.city.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
<DocSectionCode :code="code" :dependencies="{ zod: '3.23.8' }" />
</template>
<script>
import { zodResolver } from '@primevue/form/resolvers';
import { z } from 'zod';
export default {
data() {
return {
initialValues: {
city: []
},
resolver: zodResolver(
z.object({
city: z
.array(
z.object({
name: z.string().min(1, 'City required.')
})
)
.min(1, 'City required.')
})
),
cities: [
{ name: 'New York', code: 'NY' },
{ name: 'Rome', code: 'RM' },
{ name: 'London', code: 'LDN' },
{ name: 'Istanbul', code: 'IST' },
{ name: 'Paris', code: 'PRS' }
],
code: {
basic: `
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex justify-center flex-col gap-4">
<div class="flex flex-col gap-2">
<MultiSelect name="city" :options="cities" optionLabel="name" filter placeholder="Select Cities" :maxSelectedLabels="3" class="w-full md:w-80" />
<Message v-if="$form.city?.invalid" severity="error">{{ $form.city.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
`,
options: `
<template>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex justify-center flex-col gap-4">
<div class="flex flex-col gap-2">
<MultiSelect name="city" :options="cities" optionLabel="name" filter placeholder="Select Cities" :maxSelectedLabels="3" class="w-full md:w-80" />
<Message v-if="$form.city?.invalid" severity="error">{{ $form.city.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
</template>
<script>
import { zodResolver } from '@primevue/form/resolvers';
import { z } from 'zod';
export default {
data() {
return {
initialValues: {
city: []
},
resolver: zodResolver(
z.object({
city: z
.array(
z.object({
name: z.string().min(1, 'City required.')
})
)
.min(1, 'City required.')
})
),
cities: [
{ name: 'New York', code: 'NY' },
{ name: 'Rome', code: 'RM' },
{ name: 'London', code: 'LDN' },
{ name: 'Istanbul', code: 'IST' },
{ name: 'Paris', code: 'PRS' }
]
}
},
methods: {
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
}
<\/script>
`,
composition: `
<template>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex justify-center flex-col gap-4">
<div class="flex flex-col gap-2">
<MultiSelect name="city" :options="cities" optionLabel="name" filter placeholder="Select Cities" :maxSelectedLabels="3" class="w-full md:w-80" />
<Message v-if="$form.city?.invalid" severity="error">{{ $form.city.error?.message }}</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 { useToast } from "primevue/usetoast";
import { z } from 'zod';
const toast = useToast();
const initialValues = ref({
city: []
});
const resolver = ref(zodResolver(
z.object({
city: z
.array(
z.object({
name: z.string().min(1, 'City required.')
})
)
.min(1, 'City required.')
})
));
const cities = ref([
{ name: 'New York', code: 'NY' },
{ name: 'Rome', code: 'RM' },
{ name: 'London', code: 'LDN' },
{ name: 'Istanbul', code: 'IST' },
{ name: 'Paris', code: 'PRS' }
]);
const onFormSubmit = ({ valid }) => {
if (valid) {
toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
};
<\/script>
`
}
};
},
methods: {
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
};
</script>

View File

@ -0,0 +1,174 @@
<template>
<DocSectionText v-bind="$attrs">
<p>InputText is used with the <i>v-model</i> property.</p>
</DocSectionText>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4 w-full sm:w-80">
<div class="flex flex-col gap-2">
<Password name="password" placeholder="Password" :feedback="false" fluid />
<template v-if="$form.password?.invalid">
<Message v-for="(error, index) of $form.password.errors" :key="index" severity="error">{{ error.message }}</Message>
</template>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
<DocSectionCode :code="code" :dependencies="{ zod: '3.23.8' }" />
</template>
<script>
import { zodResolver } from '@primevue/form/resolvers';
import { z } from 'zod';
export default {
data() {
return {
initialValues: {
password: ''
},
resolver: zodResolver(
z.object({
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 a uppercase letter.'
})
.refine((value) => /\d/.test(value), {
message: 'Must have a number.'
})
})
),
code: {
basic: `
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4 w-full sm:w-80">
<div class="flex flex-col gap-2">
<Password name="password" placeholder="Password" :feedback="false" fluid />
<template v-if="$form.password?.invalid">
<Message v-for="(error, index) of $form.password.errors" :key="index" severity="error">{{ error.message }}</Message>
</template>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
`,
options: `
<template>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4 w-full sm:w-80">
<div class="flex flex-col gap-2">
<Password name="password" placeholder="Password" :feedback="false" fluid />
<template v-if="$form.password?.invalid">
<Message v-for="(error, index) of $form.password.errors" :key="index" severity="error">{{ error.message }}</Message>
</template>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
</template>
<script>
import { zodResolver } from '@primevue/form/resolvers';
import { z } from 'zod';
export default {
data() {
return {
initialValues: {
password: ''
},
resolver: zodResolver(
z.object({
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 a uppercase letter.'
})
.refine((value) => /\d/.test(value), {
message: 'Must have a number.'
})
})
)
}
},
methods: {
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
}
<\/script>
`,
composition: `
<template>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4 w-full sm:w-80">
<div class="flex flex-col gap-2">
<Password name="password" placeholder="Password" :feedback="false" fluid />
<template v-if="$form.password?.invalid">
<Message v-for="(error, index) of $form.password.errors" :key="index" severity="error">{{ error.message }}</Message>
</template>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
</template>
<script setup>
import { ref } from 'vue';
import { zodResolver } from '@primevue/form/resolvers';
import { useToast } from "primevue/usetoast";
import { z } from 'zod';
const toast = useToast();
const initialValues = ref({
password: ''
});
const resolver = ref(zodResolver(
z.object({
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 a uppercase letter.'
})
.refine((value) => /\d/.test(value), {
message: 'Must have a number.'
})
})
));
const onFormSubmit = ({ valid }) => {
if (valid) {
toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
};
<\/script>
`
}
};
},
methods: {
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
};
</script>

View File

@ -0,0 +1,190 @@
<template>
<DocSectionText v-bind="$attrs">
<p>RadioButton can be used with the <NuxtLink to="/forms">PrimeVue Forms</NuxtLink> library.</p>
</DocSectionText>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4">
<RadioButtonGroup name="radiobutton" class="flex flex-wrap gap-4">
<div class="flex items-center">
<RadioButton inputId="ingredient1" value="Cheese" />
<label for="ingredient1" class="ml-2">Cheese</label>
</div>
<div class="flex items-center">
<RadioButton inputId="ingredient2" value="Mushroom" />
<label for="ingredient2" class="ml-2">Mushroom</label>
</div>
<div class="flex items-center">
<RadioButton inputId="ingredient3" value="Pepper" />
<label for="ingredient3" class="ml-2">Pepper</label>
</div>
<div class="flex items-center">
<RadioButton inputId="ingredient4" value="Onion" />
<label for="ingredient4" class="ml-2">Onion</label>
</div>
</RadioButtonGroup>
<Message v-if="$form.radiobutton?.invalid" severity="error" icon="pi pi-times-circle">{{ $form.radiobutton.error?.message }}</Message>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
<DocSectionCode :code="code" :dependencies="{ zod: '3.23.8' }" />
</template>
<script>
import { zodResolver } from '@primevue/form/resolvers';
import { z } from 'zod';
export default {
data() {
return {
initialValues: {
radiobutton: ''
},
resolver: zodResolver(
z.object({
radiobutton: z.string().min(1, { message: 'Selection is required.' })
})
),
code: {
basic: `
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4">
<RadioButtonGroup name="radiobutton" class="flex flex-wrap gap-4">
<div class="flex items-center">
<RadioButton inputId="ingredient1" value="Cheese" />
<label for="ingredient1" class="ml-2">Cheese</label>
</div>
<div class="flex items-center">
<RadioButton inputId="ingredient2" value="Mushroom" />
<label for="ingredient2" class="ml-2">Mushroom</label>
</div>
<div class="flex items-center">
<RadioButton inputId="ingredient3" value="Pepper" />
<label for="ingredient3" class="ml-2">Pepper</label>
</div>
<div class="flex items-center">
<RadioButton inputId="ingredient4" value="Onion" />
<label for="ingredient4" class="ml-2">Onion</label>
</div>
</RadioButtonGroup>
<Message v-if="$form.radiobutton?.invalid" severity="error" icon="pi pi-times-circle">{{ $form.radiobutton.error?.message }}</Message>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
`,
options: `
<template>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4">
<RadioButtonGroup name="radiobutton" class="flex flex-wrap gap-4">
<div class="flex items-center">
<RadioButton inputId="ingredient1" value="Cheese" />
<label for="ingredient1" class="ml-2">Cheese</label>
</div>
<div class="flex items-center">
<RadioButton inputId="ingredient2" value="Mushroom" />
<label for="ingredient2" class="ml-2">Mushroom</label>
</div>
<div class="flex items-center">
<RadioButton inputId="ingredient3" value="Pepper" />
<label for="ingredient3" class="ml-2">Pepper</label>
</div>
<div class="flex items-center">
<RadioButton inputId="ingredient4" value="Onion" />
<label for="ingredient4" class="ml-2">Onion</label>
</div>
</RadioButtonGroup>
<Message v-if="$form.radiobutton?.invalid" severity="error" icon="pi pi-times-circle">{{ $form.radiobutton.error?.message }}</Message>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
</template>
<script>
import { zodResolver } from '@primevue/form/resolvers';
import { z } from 'zod';
export default {
data() {
return {
initialValues: {
radiobutton: ''
},
resolver: zodResolver(
z.object({
radiobutton: z.string().min(1, { message: 'Selection is required.' })
})
)
}
},
methods: {
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
}
<\/script>
`,
composition: `
<template>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4">
<RadioButtonGroup name="radiobutton" class="flex flex-wrap gap-4">
<div class="flex items-center">
<RadioButton inputId="ingredient1" value="Cheese" />
<label for="ingredient1" class="ml-2">Cheese</label>
</div>
<div class="flex items-center">
<RadioButton inputId="ingredient2" value="Mushroom" />
<label for="ingredient2" class="ml-2">Mushroom</label>
</div>
<div class="flex items-center">
<RadioButton inputId="ingredient3" value="Pepper" />
<label for="ingredient3" class="ml-2">Pepper</label>
</div>
<div class="flex items-center">
<RadioButton inputId="ingredient4" value="Onion" />
<label for="ingredient4" class="ml-2">Onion</label>
</div>
</RadioButtonGroup>
<Message v-if="$form.radiobutton?.invalid" severity="error" icon="pi pi-times-circle">{{ $form.radiobutton.error?.message }}</Message>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
</template>
<script setup>
import { ref } from 'vue';
import { zodResolver } from '@primevue/form/resolvers';
import { useToast } from "primevue/usetoast";
import { z } from 'zod';
const toast = useToast();
const initialValues = ref({
radiobutton: ''
});
const resolver = ref(zodResolver(
z.object({
radiobutton: z.string().min(1, { message: 'Selection is required.' })
})
));
const onFormSubmit = ({ valid }) => {
if (valid) {
toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
};
<\/script>
`
}
};
},
methods: {
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
};
</script>

View File

@ -0,0 +1,130 @@
<template>
<DocSectionText v-bind="$attrs">
<p>Rating can be used with the <NuxtLink to="/forms">PrimeVue Forms</NuxtLink> library.</p>
</DocSectionText>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4">
<div class="flex flex-col items-center gap-2">
<Rating name="rating" />
<Message v-if="$form.rating?.invalid" severity="error">{{ $form.rating.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
<DocSectionCode :code="code" :dependencies="{ zod: '3.23.8' }" />
</template>
<script>
import { zodResolver } from '@primevue/form/resolvers';
import { z } from 'zod';
export default {
data() {
return {
initialValues: {
rating: null
},
resolver: zodResolver(
z.object({
rating: z.union([z.number(), z.literal(null)]).refine((value) => value !== null, { message: 'Rating is required.' })
})
),
code: {
basic: `
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4">
<div class="flex flex-col items-center gap-2">
<Rating name="rating" />
<Message v-if="$form.rating?.invalid" severity="error">{{ $form.rating.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
`,
options: `
<template>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4">
<div class="flex flex-col items-center gap-2">
<Rating name="rating" />
<Message v-if="$form.rating?.invalid" severity="error">{{ $form.rating.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
</template>
<script>
import { zodResolver } from '@primevue/form/resolvers';
import { z } from 'zod';
export default {
data() {
return {
initialValues: {
rating: null
},
resolver: zodResolver(
z.object({
rating: z.union([z.number(), z.literal(null)]).refine((value) => value !== null, { message: 'Rating is required.' })
})
)
}
},
methods: {
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
}
<\/script>
`,
composition: `
<template>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4">
<div class="flex flex-col items-center gap-2">
<Rating name="rating" />
<Message v-if="$form.rating?.invalid" severity="error">{{ $form.rating.error?.message }}</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 { useToast } from "primevue/usetoast";
import { z } from 'zod';
const toast = useToast();
const initialValues = ref({
rating: null
});
const resolver = ref(zodResolver(
z.object({
rating: z.union([z.number(), z.literal(null)]).refine((value) => value !== null, { message: 'Rating is required.' })
})
));
const onFormSubmit = ({ valid }) => {
if (valid) {
toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
};
<\/script>
`
}
};
},
methods: {
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
};
</script>

View File

@ -0,0 +1,163 @@
<template>
<DocSectionText v-bind="$attrs"> </DocSectionText>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4 w-full md:w-56">
<div class="flex flex-col gap-2">
<Select name="city" :options="cities" optionLabel="name" placeholder="Select a City" fluid />
<Message v-if="$form.city?.invalid" severity="error">{{ $form.city.error.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
<DocSectionCode :code="code" :dependencies="{ zod: '3.23.8' }" />
</template>
<script>
import { zodResolver } from '@primevue/form/resolvers';
import { z } from 'zod';
export default {
data() {
return {
initialValues: {
city: { name: '' }
},
resolver: zodResolver(
z.object({
city: z.union([
z.object({
name: z.string().min(1, 'City required.')
}),
z.any().refine((val) => false, { message: 'City required.' })
])
})
),
cities: [
{ name: 'New York', code: 'NY' },
{ name: 'Rome', code: 'RM' },
{ name: 'London', code: 'LDN' },
{ name: 'Istanbul', code: 'IST' },
{ name: 'Paris', code: 'PRS' }
],
code: {
basic: `
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4 w-full md:w-56">
<div class="flex flex-col gap-2">
<Select name="city" :options="cities" optionLabel="name" placeholder="Select a City" fluid />
<Message v-if="$form.city?.invalid" severity="error">{{ $form.city.error.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
`,
options: `
<template>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4 w-full md:w-56">
<div class="flex flex-col gap-2">
<Select name="city" :options="cities" optionLabel="name" placeholder="Select a City" fluid />
<Message v-if="$form.city?.invalid" severity="error">{{ $form.city.error.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
</template>
<script>
import { zodResolver } from '@primevue/form/resolvers';
import { z } from 'zod';
export default {
data() {
return {
initialValues: {
city: { name: '' }
},
resolver: zodResolver(
z.object({
city: z.union([
z.object({
name: z.string().min(1, 'City required.')
}),
z.any().refine((val) => false, { message: 'City required.' })
])
})
),
cities: [
{ name: 'New York', code: 'NY' },
{ name: 'Rome', code: 'RM' },
{ name: 'London', code: 'LDN' },
{ name: 'Istanbul', code: 'IST' },
{ name: 'Paris', code: 'PRS' }
],
}
},
methods: {
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
}
<\/script>
`,
composition: `
<template>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4 w-full md:w-56">
<div class="flex flex-col gap-2">
<Select name="city" :options="cities" optionLabel="name" placeholder="Select a City" fluid />
<Message v-if="$form.city?.invalid" severity="error">{{ $form.city.error.message }}</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 { useToast } from "primevue/usetoast";
import { z } from 'zod';
const toast = useToast();
const initialValues = ref({
city: { name: '' }
});
const resolver = ref(zodResolver(
z.object({
city: z.union([
z.object({
name: z.string().min(1, 'City required.')
}),
z.any().refine((val) => false, { message: 'City required.' })
])
})
));
const cities = ref([
{ name: 'New York', code: 'NY' },
{ name: 'Rome', code: 'RM' },
{ name: 'London', code: 'LDN' },
{ name: 'Istanbul', code: 'IST' },
{ name: 'Paris', code: 'PRS' }
]);
const onFormSubmit = ({ valid }) => {
if (valid) {
toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
};
<\/script>
`
}
};
},
methods: {
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
};
</script>

View File

@ -0,0 +1,132 @@
<template>
<DocSectionText v-bind="$attrs">
<p>SelectButton can be used with the <NuxtLink to="/forms">PrimeVue Forms</NuxtLink> library.</p>
</DocSectionText>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4">
<div class="flex items-center flex-col gap-2">
<SelectButton name="selectbutton" :options="options" />
<Message v-if="$form.selectbutton?.invalid" severity="error">{{ $form.selectbutton.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
<DocSectionCode :code="code" :dependencies="{ zod: '3.23.8' }" />
</template>
<script>
import { zodResolver } from '@primevue/form/resolvers';
import { z } from 'zod';
export default {
data() {
return {
initialValues: {
selectbutton: ''
},
resolver: zodResolver(
z.object({
selectbutton: z.preprocess((val) => (val === null ? '' : val), z.string().min(1, { message: 'SelectButton is required' }))
})
),
options: ['One-Way', 'Return'],
code: {
basic: `
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4">
<div class="flex flex-col gap-2">
<SelectButton name="selectbutton" :options="options" />
<Message v-if="$form.selectbutton?.invalid" severity="error">{{ $form.selectbutton.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
`,
options: `
<template>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4">
<div class="flex flex-col gap-2">
<SelectButton name="selectbutton" :options="options" />
<Message v-if="$form.selectbutton?.invalid" severity="error">{{ $form.selectbutton.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
</template>
<script>
import { zodResolver } from '@primevue/form/resolvers';
import { z } from 'zod';
export default {
data() {
return {
initialValues: {
selectbutton: ''
},
resolver: zodResolver(
z.object({
selectbutton: z.preprocess((val) => (val === null ? '' : val), z.string().min(1, { message: 'SelectButton is required' }))
})
),
options: ['One-Way', 'Return'],
}
},
methods: {
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
}
<\/script>
`,
composition: `
<template>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4">
<div class="flex flex-col gap-2">
<SelectButton name="selectbutton" :options="options" />
<Message v-if="$form.selectbutton?.invalid" severity="error">{{ $form.selectbutton.error?.message }}</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 { useToast } from "primevue/usetoast";
import { z } from 'zod';
const toast = useToast();
const initialValues = ref({
selectbutton: ''
});
const resolver = ref(zodResolver(
z.object({
selectbutton: z.preprocess((val) => (val === null ? '' : val), z.string().min(1, { message: 'SelectButton is required' }))
})
));
const onFormSubmit = ({ valid }) => {
if (valid) {
toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
};
<\/script>
`
}
};
},
methods: {
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
};
</script>

View File

@ -0,0 +1,130 @@
<template>
<DocSectionText v-bind="$attrs">
<p>Slider can be used with the <NuxtLink to="/forms">PrimeVue Forms</NuxtLink> library.</p>
</DocSectionText>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4 w-full sm:w-56">
<div class="flex flex-col gap-2">
<Slider name="slider" />
<Message v-if="$form.slider?.invalid" severity="error">{{ $form.slider.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
<DocSectionCode :code="code" :dependencies="{ zod: '3.23.8' }" />
</template>
<script>
import { zodResolver } from '@primevue/form/resolvers';
import { z } from 'zod';
export default {
data() {
return {
initialValues: {
slider: 0
},
resolver: zodResolver(
z.object({
slider: z.number().gt(25, { message: 'Must be greater than 25.' }).lt(75, { message: 'Must be less than 75.' })
})
),
code: {
basic: `
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4 w-full sm:w-56">
<div class="flex flex-col gap-2">
<Slider name="slider" />
<Message v-if="$form.slider?.invalid" severity="error">{{ $form.slider.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
`,
options: `
<template>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4 w-full sm:w-56">
<div class="flex flex-col gap-2">
<Slider name="slider" />
<Message v-if="$form.slider?.invalid" severity="error">{{ $form.slider.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
</template>
<script>
import { zodResolver } from '@primevue/form/resolvers';
import { z } from 'zod';
export default {
data() {
return {
initialValues: {
slider: 0
},
resolver: zodResolver(
z.object({
slider: z.number().gt(25, { message: 'Must be greater than 25.' }).lt(75, { message: 'Must be less than 75.' })
})
),
}
},
methods: {
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
}
<\/script>
`,
composition: `
<template>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4 w-full sm:w-56">
<div class="flex flex-col gap-2">
<Slider name="slider" />
<Message v-if="$form.slider?.invalid" severity="error">{{ $form.slider.error?.message }}</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 { useToast } from "primevue/usetoast";
import { z } from 'zod';
const toast = useToast();
const initialValues = ref({
slider: 0
});
const resolver = ref(zodResolver(
z.object({
slider: z.number().gt(25, { message: 'Must be greater than 25.' }).lt(75, { message: 'Must be less than 75.' })
})
));
const onFormSubmit = ({ valid }) => {
if (valid) {
toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
};
<\/script>
`
}
};
},
methods: {
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
};
</script>

View File

@ -0,0 +1,130 @@
<template>
<DocSectionText v-bind="$attrs">
<p>Textarea can be used with the <NuxtLink to="/forms">PrimeVue Forms</NuxtLink> library.</p>
</DocSectionText>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4">
<div class="flex flex-col gap-2">
<Textarea name="textarea" rows="5" cols="30" style="resize: none" />
<Message v-if="$form.textarea?.invalid" severity="error">{{ $form.textarea.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
<DocSectionCode :code="code" :dependencies="{ zod: '3.23.8' }" />
</template>
<script>
import { zodResolver } from '@primevue/form/resolvers';
import { z } from 'zod';
export default {
data() {
return {
initialValues: {
textarea: ''
},
resolver: zodResolver(
z.object({
textarea: z.string().min(1, { message: 'Textarea is required.' })
})
),
code: {
basic: `
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4">
<div class="flex flex-col gap-2">
<Textarea name="textarea" rows="5" cols="30" style="resize: none" />
<Message v-if="$form.textarea?.invalid" severity="error">{{ $form.textarea.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
`,
options: `
<template>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4">
<div class="flex flex-col gap-2">
<Textarea name="textarea" rows="5" cols="30" style="resize: none" />
<Message v-if="$form.textarea?.invalid" severity="error">{{ $form.textarea.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
</template>
<script>
import { zodResolver } from '@primevue/form/resolvers';
import { z } from 'zod';
export default {
data() {
return {
initialValues: {
textarea: ''
},
resolver: zodResolver(
z.object({
textarea: z.string().min(1, { message: 'Textarea is required.' })
})
),
}
},
methods: {
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
}
<\/script>
`,
composition: `
<template>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4">
<div class="flex flex-col gap-2">
<Textarea name="textarea" rows="5" cols="30" style="resize: none" />
<Message v-if="$form.textarea?.invalid" severity="error">{{ $form.textarea.error?.message }}</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 { useToast } from "primevue/usetoast";
import { z } from 'zod';
const toast = useToast();
const initialValues = ref({
textarea: ''
});
const resolver = ref(zodResolver(
z.object({
textarea: z.string().min(1, { message: 'Textarea is required.' })
})
));
const onFormSubmit = ({ valid }) => {
if (valid) {
toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
};
<\/script>
`
}
};
},
methods: {
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
};
</script>

View File

@ -0,0 +1,130 @@
<template>
<DocSectionText v-bind="$attrs">
<p>ToggleButton can be used with the <NuxtLink to="/forms">PrimeVue Forms</NuxtLink> library.</p>
</DocSectionText>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4">
<div class="flex flex-col items-center gap-2">
<ToggleButton name="togglebutton" class="w-24" onLabel="On" offLabel="Off" />
<Message v-if="$form.togglebutton?.invalid" severity="error">{{ $form.togglebutton.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
<DocSectionCode :code="code" :dependencies="{ zod: '3.23.8' }" />
</template>
<script>
import { zodResolver } from '@primevue/form/resolvers';
import { z } from 'zod';
export default {
data() {
return {
initialValues: {
togglebutton: false
},
resolver: zodResolver(
z.object({
togglebutton: z.boolean().refine((val) => val === true, { message: 'ToggleButton is required.' })
})
),
code: {
basic: `
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4">
<div class="flex flex-col items-center gap-2">
<ToggleButton name="togglebutton" class="w-24" onLabel="On" offLabel="Off" />
<Message v-if="$form.togglebutton?.invalid" severity="error">{{ $form.togglebutton.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
`,
options: `
<template>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4">
<div class="flex flex-col items-center gap-2">
<ToggleButton name="togglebutton" class="w-24" onLabel="On" offLabel="Off" />
<Message v-if="$form.togglebutton?.invalid" severity="error">{{ $form.togglebutton.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
</template>
<script>
import { zodResolver } from '@primevue/form/resolvers';
import { z } from 'zod';
export default {
data() {
return {
initialValues: {
togglebutton: false
},
resolver: zodResolver(
z.object({
togglebutton: z.boolean().refine((val) => val === true, { message: 'ToggleButton is required.' })
})
),
}
},
methods: {
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
}
<\/script>
`,
composition: `
<template>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4">
<div class="flex flex-col items-center gap-2">
<ToggleButton name="togglebutton" class="w-24" onLabel="On" offLabel="Off" />
<Message v-if="$form.togglebutton?.invalid" severity="error">{{ $form.togglebutton.error?.message }}</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 { useToast } from "primevue/usetoast";
import { z } from 'zod';
const toast = useToast();
const initialValues = ref({
togglebutton: false
});
const resolver = ref(zodResolver(
z.object({
togglebutton: z.boolean().refine((val) => val === true, { message: 'ToggleButton is required.' })
})
));
const onFormSubmit = ({ valid }) => {
if (valid) {
toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
};
<\/script>
`
}
};
},
methods: {
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
};
</script>

View File

@ -0,0 +1,124 @@
<template>
<DocSectionText v-bind="$attrs">
<p>ToggleSwitch can be used with the <NuxtLink to="/forms">PrimeVue Forms</NuxtLink> library.</p>
</DocSectionText>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4">
<div class="flex flex-col items-center gap-2">
<ToggleSwitch name="toggleswitch" />
<Message v-if="$form.toggleswitch?.invalid" severity="error">{{ $form.toggleswitch.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
<DocSectionCode :code="code" :dependencies="{ zod: '3.23.8' }" />
</template>
<script>
import { zodResolver } from '@primevue/form/resolvers';
import { z } from 'zod';
export default {
data() {
return {
initialValues: {
toggleswitch: false
},
resolver: zodResolver(
z.object({
toggleswitch: z.boolean().refine((val) => val === true, { message: 'ToggleSwitch is required.' })
})
),
code: {
basic: `
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4">
<div class="flex flex-col items-center gap-2">
<ToggleSwitch name="toggleswitch" />
<Message v-if="$form.toggleswitch?.invalid" severity="error">{{ $form.toggleswitch.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
`,
options: `
<template>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4">
<div class="flex flex-col items-center gap-2">
<ToggleSwitch name="toggleswitch" />
<Message v-if="$form.toggleswitch?.invalid" severity="error">{{ $form.toggleswitch.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
</template>
<script>
import { zodResolver } from '@primevue/form/resolvers';
import { z } from 'zod';
export default {
data() {
return {
initialValues: {
toggleswitch: false
},
resolver: zodResolver(
z.object({
toggleswitch: z.boolean().refine((val) => val === true, { message: 'ToggleSwitch is required.' })
})
),
}
},
methods: {
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
}
<\/script>
`,
composition: `
<template>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4">
<div class="flex flex-col items-center gap-2">
<ToggleSwitch name="toggleswitch" />
<Message v-if="$form.toggleswitch?.invalid" severity="error">{{ $form.toggleswitch.error?.message }}</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 { useToast } from "primevue/usetoast";
import { z } from 'zod';
const toast = useToast();
const initialValues = ref({
toggleswitch: false
});
const resolver = ref(zodResolver(
z.object({
toggleswitch: z.boolean().refine((val) => val === true, { message: 'ToggleSwitch is required.' })
})
));
<\/script>
`
}
};
},
methods: {
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
};
</script>

View File

@ -0,0 +1,138 @@
<template>
<DocSectionText v-bind="$attrs">
<p>TreeSelect is used with the <i>v-model</i> property.</p>
</DocSectionText>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4 w-full md:w-80">
<div class="flex flex-col gap-2">
<TreeSelect name="node" :options="nodes" placeholder="Select Item" fluid />
<Message v-if="$form.node?.invalid" severity="error">{{ $form.node.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
<DocSectionCode :code="code" :dependencies="{ zod: '3.23.8' }" />
</template>
<script>
import { zodResolver } from '@primevue/form/resolvers';
import { z } from 'zod';
import { NodeService } from '/service/NodeService';
export default {
data() {
return {
initialValues: {
node: null
},
resolver: zodResolver(
z.object({
node: z.union([z.record(z.boolean()), z.literal(null)]).refine((obj) => obj !== null && Object.keys(obj).length > 0, { message: 'TreeSelect is required.' })
})
),
nodes: null,
code: {
basic: `
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4 w-full md:w-80">
<div class="flex flex-col gap-2">
<TreeSelect name="node" :options="nodes" placeholder="Select Item" fluid />
<Message v-if="$form.node?.invalid" severity="error">{{ $form.node.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
`,
options: `
<template>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4 w-full md:w-80">
<div class="flex flex-col gap-2">
<TreeSelect name="node" :options="nodes" placeholder="Select Item" fluid />
<Message v-if="$form.node?.invalid" severity="error">{{ $form.node.error?.message }}</Message>
</div>
<Button type="submit" severity="secondary" label="Submit" />
</Form>
</div>
</template>
<script>
import { zodResolver } from '@primevue/form/resolvers';
import { z } from 'zod';
import { NodeService } from '/service/NodeService';
export default {
data() {
return {
initialValues: {
node: null
},
resolver: zodResolver(
z.object({
node: z.union([z.record(z.boolean()), z.literal(null)]).refine((obj) => obj !== null && Object.keys(obj).length > 0, { message: 'TreeSelect is required.' })
})
),
nodes: null,
}
},
methods: {
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
}
<\/script>
`,
composition: `
<template>
<div class="card flex justify-center">
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit" class="flex flex-col gap-4 w-full md:w-80">
<div class="flex flex-col gap-2">
<TreeSelect name="node" :options="nodes" placeholder="Select Item" fluid />
<Message v-if="$form.node?.invalid" severity="error">{{ $form.node.error?.message }}</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 { useToast } from "primevue/usetoast";
import { z } from 'zod';
import { NodeService } from '/service/NodeService';
const toast = useToast();
const initialValues = ref({
node: null
});
const resolver = ref(zodResolver(
z.object({
node: z.union([z.record(z.boolean()), z.literal(null)]).refine((obj) => obj !== null && Object.keys(obj).length > 0, { message: 'TreeSelect is required.' })
})
));
const onFormSubmit = ({ valid }) => {
if (valid) {
toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
};
<\/script>
`
}
};
},
mounted() {
NodeService.getTreeNodes().then((data) => (this.nodes = data));
},
methods: {
onFormSubmit({ valid }) {
if (valid) {
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
}
}
}
};
</script>

View File

@ -123,7 +123,6 @@ nodes.value = files;
}
};
},
mounted() {},
methods: {
loadDemoData() {
let files = [];

View File

@ -47,15 +47,20 @@
"prettier": "2.7.1",
"primeicons": "^7.0.0",
"quill": "^1.3.7",
"sass": "^1.45.0",
"sass-loader": "^8.0.2",
"sass": "1.45.0",
"sass-loader": "8.0.2",
"tailwindcss": "^3.4.1",
"tailwindcss-primeui": "^0.3.2",
"typedoc": "0.23.23",
"primevue": "workspace:*",
"@primevue/core": "workspace:*",
"@primevue/nuxt-module": "workspace:*",
"@primevue/themes": "workspace:*"
"@primevue/themes": "workspace:*",
"@primevue/form": "workspace:*",
"yup": "1.4.0",
"zod": "3.23.8",
"valibot": "^0.42.1",
"superstruct": "^2.0.2"
},
"dependencies": {
"@docsearch/js": "^3.3.3",

View File

@ -18,6 +18,7 @@ import DropdownDoc from '@/doc/autocomplete/DropdownDoc.vue';
import FilledDoc from '@/doc/autocomplete/FilledDoc.vue';
import FloatLabelDoc from '@/doc/autocomplete/FloatLabelDoc.vue';
import ForceSelectionDoc from '@/doc/autocomplete/ForceSelectionDoc.vue';
import FormsDoc from '@/doc/autocomplete/FormsDoc.vue';
import GroupDoc from '@/doc/autocomplete/GroupDoc.vue';
import IftaLabelDoc from '@/doc/autocomplete/IftaLabelDoc.vue';
import ImportDoc from '@/doc/autocomplete/ImportDoc.vue';
@ -43,6 +44,11 @@ export default {
label: 'Basic',
component: BasicDoc
},
{
id: 'forms',
label: 'Forms',
component: FormsDoc
},
{
id: 'dropdown',
label: 'Dropdown',

View File

@ -16,6 +16,7 @@ import BasicDoc from '@/doc/cascadeselect/BasicDoc.vue';
import DisabledDoc from '@/doc/cascadeselect/DisabledDoc.vue';
import FilledDoc from '@/doc/cascadeselect/FilledDoc.vue';
import FloatLabelDoc from '@/doc/cascadeselect/FloatLabelDoc.vue';
import FormsDoc from '@/doc/cascadeselect/FormsDoc.vue';
import IftaLabelDoc from '@/doc/cascadeselect/IftaLabelDoc.vue';
import ImportDoc from '@/doc/cascadeselect/ImportDoc.vue';
import InvalidDoc from '@/doc/cascadeselect/InvalidDoc.vue';
@ -38,6 +39,11 @@ export default {
label: 'Basic',
component: BasicDoc
},
{
id: 'forms',
label: 'Forms',
component: FormsDoc
},
{
id: 'template',
label: 'Template',

View File

@ -16,6 +16,7 @@ import BasicDoc from '@/doc/checkbox/BasicDoc.vue';
import DisabledDoc from '@/doc/checkbox/DisabledDoc.vue';
import DynamicDoc from '@/doc/checkbox/DynamicDoc.vue';
import FilledDoc from '@/doc/checkbox/FilledDoc.vue';
import FormsDoc from '@/doc/checkbox/FormsDoc.vue';
import GroupDoc from '@/doc/checkbox/GroupDoc.vue';
import ImportDoc from '@/doc/checkbox/ImportDoc.vue';
import IndeterminateDoc from '@/doc/checkbox/IndeterminateDoc.vue';
@ -37,6 +38,11 @@ export default {
label: 'Basic',
component: BasicDoc
},
{
id: 'forms',
label: 'Forms',
component: FormsDoc
},
{
id: 'indeterminate',
label: 'Indeterminate',

View File

@ -7,6 +7,7 @@ import AccessibilityDoc from '@/doc/colorpicker/AccessibilityDoc.vue';
import BasicDoc from '@/doc/colorpicker/BasicDoc.vue';
import DisabledDoc from '@/doc/colorpicker/DisabledDoc.vue';
import FormatDoc from '@/doc/colorpicker/FormatDoc.vue';
import FormsDoc from '@/doc/colorpicker/FormsDoc.vue';
import ImportDoc from '@/doc/colorpicker/ImportDoc.vue';
import InlineDoc from '@/doc/colorpicker/InlineDoc.vue';
import PTComponent from '@/doc/colorpicker/pt/index.vue';
@ -26,6 +27,11 @@ export default {
label: 'Basic',
component: BasicDoc
},
{
id: 'forms',
label: 'Forms',
component: FormsDoc
},
{
id: 'inline',
label: 'Inline',

View File

@ -11,6 +11,7 @@ import DisabledDoc from '@/doc/datepicker/DisabledDoc.vue';
import FilledDoc from '@/doc/datepicker/FilledDoc.vue';
import FloatLabelDoc from '@/doc/datepicker/FloatLabelDoc.vue';
import FormatDoc from '@/doc/datepicker/FormatDoc.vue';
import FormsDoc from '@/doc/datepicker/FormsDoc.vue';
import IconDoc from '@/doc/datepicker/IconDoc.vue';
import IftaLabelDoc from '@/doc/datepicker/IftaLabelDoc.vue';
import ImportDoc from '@/doc/datepicker/ImportDoc.vue';
@ -41,6 +42,11 @@ export default {
label: 'Basic',
component: BasicDoc
},
{
id: 'forms',
label: 'Forms',
component: FormsDoc
},
{
id: 'format',
label: 'Format',

View File

@ -5,6 +5,7 @@
<script>
import AccessibilityDoc from '@/doc/editor/AccessibilityDoc.vue';
import BasicDoc from '@/doc/editor/BasicDoc.vue';
import FormsDoc from '@/doc/editor/FormsDoc.vue';
import ImportDoc from '@/doc/editor/ImportDoc.vue';
import QuillDoc from '@/doc/editor/QuillDoc.vue';
import ReadOnlyDoc from '@/doc/editor/ReadOnlyDoc.vue';
@ -31,6 +32,11 @@ export default {
label: 'Basic',
component: BasicDoc
},
{
id: 'forms',
label: 'Forms',
component: FormsDoc
},
{
id: 'readonly',
label: 'ReadOnly',

View File

@ -0,0 +1,73 @@
<template>
<DocComponent title="Vue Form Component" header="Forms" description="Form provides validation functionality and manages form state." :componentDocs="docs" :apiDocs="['Form']" :ptTabComponent="ptComponent" :themingDocs="themingDoc" />
</template>
<script>
import AccessibilityDoc from '@/doc/forms/AccessibilityDoc.vue';
import BasicDoc from '@/doc/forms/BasicDoc.vue';
import DynamicDoc from '@/doc/forms/DynamicDoc.vue';
import ImportDoc from '@/doc/forms/ImportDoc.vue';
import RegisterDoc from '@/doc/forms/RegisterDoc.vue';
import ResolversDoc from '@/doc/forms/ResolversDoc.vue';
import StatesDoc from '@/doc/forms/StatesDoc.vue';
import SubmitDoc from '@/doc/forms/SubmitDoc.vue';
import ValidateOnDoc from '@/doc/forms/ValidateOnDoc.vue';
import PTComponent from '@/doc/forms/pt/index.vue';
import ThemingDoc from '@/doc/forms/theming/index.vue';
export default {
data() {
return {
docs: [
{
id: 'import',
label: 'Import',
component: ImportDoc
},
{
id: 'basic',
label: 'Basic',
component: BasicDoc
},
{
id: 'states',
label: 'States',
component: StatesDoc
},
{
id: 'resolvers',
label: 'Resolvers',
component: ResolversDoc
},
{
id: 'validateon',
label: 'ValidateOn',
component: ValidateOnDoc
},
{
id: 'register',
label: 'Register',
component: RegisterDoc
},
{
id: 'submit',
label: 'Submit',
component: SubmitDoc
},
{
id: 'dynamic',
label: 'Dynamic',
component: DynamicDoc
},
{
id: 'accessibility',
label: 'Accessibility',
component: AccessibilityDoc
}
],
ptComponent: PTComponent,
themingDoc: ThemingDoc
};
}
};
</script>

View File

@ -16,6 +16,7 @@ import BasicDoc from '@/doc/inputmask/BasicDoc.vue';
import DisabledDoc from '@/doc/inputmask/DisabledDoc.vue';
import FilledDoc from '@/doc/inputmask/FilledDoc.vue';
import FloatLabelDoc from '@/doc/inputmask/FloatLabelDoc.vue';
import FormsDoc from '@/doc/inputmask/FormsDoc.vue';
import IftaLabelDoc from '@/doc/inputmask/IftaLabelDoc.vue';
import ImportDoc from '@/doc/inputmask/ImportDoc.vue';
import InvalidDoc from '@/doc/inputmask/InvalidDoc.vue';
@ -39,6 +40,11 @@ export default {
label: 'Basic',
component: BasicDoc
},
{
id: 'forms',
label: 'Forms',
component: FormsDoc
},
{
id: 'mask',
label: 'Mask',

View File

@ -17,6 +17,7 @@ import CurrencyDoc from '@/doc/inputnumber/CurrencyDoc.vue';
import DisabledDoc from '@/doc/inputnumber/DisabledDoc.vue';
import FilledDoc from '@/doc/inputnumber/FilledDoc.vue';
import FloatLabelDoc from '@/doc/inputnumber/FloatLabelDoc.vue';
import FormsDoc from '@/doc/inputnumber/FormsDoc.vue';
import IftaLabelDoc from '@/doc/inputnumber/IftaLabelDoc.vue';
import ImportDoc from '@/doc/inputnumber/ImportDoc.vue';
import InvalidDoc from '@/doc/inputnumber/InvalidDoc.vue';
@ -41,6 +42,11 @@ export default {
label: 'Numerals',
component: NumeralsDoc
},
{
id: 'forms',
label: 'Forms',
component: FormsDoc
},
{
id: 'locale',
label: 'Locale',

View File

@ -5,11 +5,12 @@
<script>
import AccessibilityDoc from '@/doc/inputotp/AccessibilityDoc.vue';
import BasicDoc from '@/doc/inputotp/BasicDoc.vue';
import FilledDoc from '@/doc/inputotp/FilledDoc.vue';
import FormsDoc from '@/doc/inputotp/FormsDoc.vue';
import ImportDoc from '@/doc/inputotp/ImportDoc.vue';
import IntegerOnlyDoc from '@/doc/inputotp/IntegerOnlyDoc.vue';
import MaskDoc from '@/doc/inputotp/MaskDoc.vue';
import SampleDoc from '@/doc/inputotp/SampleDoc.vue';
import FilledDoc from '@/doc/inputotp/FilledDoc.vue';
import TemplateDoc from '@/doc/inputotp/TemplateDoc.vue';
import PTComponent from '@/doc/inputotp/pt/index.vue';
import ThemingDoc from '@/doc/inputotp/theming/index.vue';
@ -28,6 +29,11 @@ export default {
label: 'Basic',
component: BasicDoc
},
{
id: 'forms',
label: 'Forms',
component: FormsDoc
},
{
id: 'mask',
label: 'Mask',

View File

@ -8,6 +8,7 @@ import BasicDoc from '@/doc/inputtext/BasicDoc.vue';
import DisabledDoc from '@/doc/inputtext/DisabledDoc.vue';
import FilledDoc from '@/doc/inputtext/FilledDoc.vue';
import FloatLabelDoc from '@/doc/inputtext/FloatLabelDoc.vue';
import FormsDoc from '@/doc/inputtext/FormsDoc.vue';
import HelpTextDoc from '@/doc/inputtext/HelpTextDoc.vue';
import IftaLabelDoc from '@/doc/inputtext/IftaLabelDoc.vue';
import ImportDoc from '@/doc/inputtext/ImportDoc.vue';
@ -30,6 +31,11 @@ export default {
label: 'Basic',
component: BasicDoc
},
{
id: 'forms',
label: 'Forms',
component: FormsDoc
},
{
id: 'filled',
label: 'Filled',

View File

@ -7,6 +7,7 @@ import AccessibilityDoc from '@/doc/knob/AccessibilityDoc.vue';
import BasicDoc from '@/doc/knob/BasicDoc.vue';
import ColorDoc from '@/doc/knob/ColorDoc.vue';
import DisabledDoc from '@/doc/knob/DisabledDoc.vue';
import FormsDoc from '@/doc/knob/FormsDoc.vue';
import ImportDoc from '@/doc/knob/ImportDoc.vue';
import MinMaxDoc from '@/doc/knob/MinMaxDoc.vue';
import ReactiveDoc from '@/doc/knob/ReactiveDoc.vue';
@ -32,6 +33,11 @@ export default {
label: 'Basic',
component: BasicDoc
},
{
id: 'forms',
label: 'Forms',
component: FormsDoc
},
{
id: 'minmax',
label: 'Min/Max',

View File

@ -8,6 +8,7 @@ import BasicDoc from '@/doc/listbox/BasicDoc.vue';
import CheckmarkDoc from '@/doc/listbox/CheckmarkDoc.vue';
import DisabledDoc from '@/doc/listbox/DisabledDoc.vue';
import FilterDoc from '@/doc/listbox/FilterDoc.vue';
import FormsDoc from '@/doc/listbox/FormsDoc.vue';
import GroupDoc from '@/doc/listbox/GroupDoc.vue';
import ImportDoc from '@/doc/listbox/ImportDoc.vue';
import InvalidDoc from '@/doc/listbox/InvalidDoc.vue';
@ -31,6 +32,11 @@ export default {
label: 'Basic',
component: BasicDoc
},
{
id: 'forms',
label: 'Forms',
component: FormsDoc
},
{
id: 'checkmark',
label: 'Checkmark',

View File

@ -7,7 +7,7 @@ import AccessibilityDoc from '@/doc/message/AccessibilityDoc.vue';
import BasicDoc from '@/doc/message/BasicDoc.vue';
import ClosabledDoc from '@/doc/message/ClosableDoc.vue';
import DynamicDoc from '@/doc/message/DynamicDoc.vue';
import FormDoc from '@/doc/message/FormDoc.vue';
import FormsDoc from '@/doc/message/FormsDoc.vue';
import IconDoc from '@/doc/message/IconDoc.vue';
import ImportDoc from '@/doc/message/ImportDoc.vue';
import LifeDoc from '@/doc/message/LifeDoc.vue';
@ -40,9 +40,9 @@ export default {
component: IconDoc
},
{
id: 'form',
label: 'Form',
component: FormDoc
id: 'forms',
label: 'Forms',
component: FormsDoc
},
{
id: 'dynamic',

View File

@ -18,6 +18,7 @@ import DisabledDoc from '@/doc/multiselect/DisabledDoc.vue';
import FilledDoc from '@/doc/multiselect/FilledDoc.vue';
import FilterDoc from '@/doc/multiselect/FilterDoc.vue';
import FloatLabelDoc from '@/doc/multiselect/FloatLabelDoc.vue';
import FormsDoc from '@/doc/multiselect/FormsDoc.vue';
import GroupDoc from '@/doc/multiselect/GroupDoc.vue';
import IftaLabelDoc from '@/doc/multiselect/IftaLabelDoc.vue';
import ImportDoc from '@/doc/multiselect/ImportDoc.vue';
@ -42,6 +43,11 @@ export default {
label: 'Basic',
component: BasicDoc
},
{
id: 'forms',
label: 'Forms',
component: FormsDoc
},
{
id: 'chips',
label: 'Chips',

View File

@ -8,6 +8,7 @@ import BasicDoc from '@/doc/password/BasicDoc.vue';
import DisabledDoc from '@/doc/password/DisabledDoc.vue';
import FilledDoc from '@/doc/password/FilledDoc.vue';
import FloatLabelDoc from '@/doc/password/FloatLabelDoc.vue';
import FormsDoc from '@/doc/password/FormsDoc.vue';
import IftaLabelDoc from '@/doc/password/IftaLabelDoc.vue';
import ImportDoc from '@/doc/password/ImportDoc.vue';
import InvalidDoc from '@/doc/password/InvalidDoc.vue';
@ -32,6 +33,11 @@ export default {
label: 'Basic',
component: BasicDoc
},
{
id: 'forms',
label: 'Forms',
component: FormsDoc
},
{
id: 'meter',
label: 'Meter',

View File

@ -15,6 +15,7 @@ import AccessibilityDoc from '@/doc/radiobutton/AccessibilityDoc.vue';
import DisabledDoc from '@/doc/radiobutton/DisabledDoc.vue';
import DynamicDoc from '@/doc/radiobutton/DynamicDoc.vue';
import FilledDoc from '@/doc/radiobutton/FilledDoc.vue';
import FormsDoc from '@/doc/radiobutton/FormsDoc.vue';
import GroupDoc from '@/doc/radiobutton/GroupDoc.vue';
import ImportDoc from '@/doc/radiobutton/ImportDoc.vue';
import InvalidDoc from '@/doc/radiobutton/InvalidDoc.vue';
@ -35,6 +36,11 @@ export default {
label: 'Group',
component: GroupDoc
},
{
id: 'forms',
label: 'Forms',
component: FormsDoc
},
{
id: 'dynamic',
label: 'Dynamic',

View File

@ -6,6 +6,7 @@
import AccessibilityDoc from '@/doc/rating/AccessibilityDoc.vue';
import BasicDoc from '@/doc/rating/BasicDoc.vue';
import DisabledDoc from '@/doc/rating/DisabledDoc.vue';
import FormsDoc from '@/doc/rating/FormsDoc.vue';
import ImportDoc from '@/doc/rating/ImportDoc.vue';
import NumberOfStarsDoc from '@/doc/rating/NumberOfStarsDoc.vue';
import ReadOnlyDoc from '@/doc/rating/ReadOnlyDoc.vue';
@ -27,6 +28,11 @@ export default {
label: 'Basic',
component: BasicDoc
},
{
id: 'forms',
label: 'Forms',
component: FormsDoc
},
{
id: 'numberofstars',
label: 'Number of Stars',

View File

@ -12,6 +12,7 @@ import EditableDoc from '@/doc/select/EditableDoc.vue';
import FilledDoc from '@/doc/select/FilledDoc.vue';
import FilterDoc from '@/doc/select/FilterDoc.vue';
import FloatLabelDoc from '@/doc/select/FloatLabelDoc.vue';
import FormsDoc from '@/doc/select/FormsDoc.vue';
import GroupDoc from '@/doc/select/GroupDoc.vue';
import IftaLabelDoc from '@/doc/select/IftaLabelDoc.vue';
import ImportDoc from '@/doc/select/ImportDoc.vue';
@ -37,6 +38,11 @@ export default {
label: 'Basic',
component: BasicDoc
},
{
id: 'forms',
label: 'Forms',
component: FormsDoc
},
{
id: 'checkmark',
label: 'Checkmark',

View File

@ -14,6 +14,7 @@
import AccessibilityDoc from '@/doc/selectbutton/AccessibilityDoc.vue';
import BasicDoc from '@/doc/selectbutton/BasicDoc.vue';
import DisabledDoc from '@/doc/selectbutton/DisabledDoc.vue';
import FormsDoc from '@/doc/selectbutton/FormsDoc.vue';
import ImportDoc from '@/doc/selectbutton/ImportDoc.vue';
import InvalidDoc from '@/doc/selectbutton/InvalidDoc.vue';
import MultipleDoc from '@/doc/selectbutton/MultipleDoc.vue';
@ -35,6 +36,11 @@ export default {
label: 'Basic',
component: BasicDoc
},
{
id: 'forms',
label: 'Forms',
component: FormsDoc
},
{
id: 'multiple',
label: 'Multiple',

View File

@ -6,6 +6,7 @@
import AccessibilityDoc from '@/doc/slider/AccessibilityDoc.vue';
import BasicDoc from '@/doc/slider/BasicDoc.vue';
import FilterDoc from '@/doc/slider/FilterDoc.vue';
import FormsDoc from '@/doc/slider/FormsDoc.vue';
import ImportDoc from '@/doc/slider/ImportDoc.vue';
import InputDoc from '@/doc/slider/InputDoc.vue';
import RangeDoc from '@/doc/slider/RangeDoc.vue';
@ -28,6 +29,11 @@ export default {
label: 'Basic',
component: BasicDoc
},
{
id: 'forms',
label: 'Forms',
component: FormsDoc
},
{
id: 'input',
label: 'Input',

View File

@ -17,6 +17,7 @@ import BasicDoc from '@/doc/textarea/BasicDoc.vue';
import DisabledDoc from '@/doc/textarea/DisabledDoc.vue';
import FilledDoc from '@/doc/textarea/FilledDoc.vue';
import FloatLabelDoc from '@/doc/textarea/FloatLabelDoc.vue';
import FormsDoc from '@/doc/textarea/FormsDoc.vue';
import IftaLabelDoc from '@/doc/textarea/IftaLabelDoc.vue';
import ImportDoc from '@/doc/textarea/ImportDoc.vue';
import InvalidDoc from '@/doc/textarea/InvalidDoc.vue';
@ -37,6 +38,11 @@ export default {
label: 'Basic',
component: BasicDoc
},
{
id: 'forms',
label: 'Forms',
component: FormsDoc
},
{
id: 'auto-resize',
label: 'Auto Resize',

View File

@ -15,6 +15,7 @@ import AccessibilityDoc from '@/doc/togglebutton/AccessibilityDoc.vue';
import BasicDoc from '@/doc/togglebutton/BasicDoc.vue';
import CustomizedDoc from '@/doc/togglebutton/CustomizedDoc.vue';
import DisabledDoc from '@/doc/togglebutton/DisabledDoc.vue';
import FormsDoc from '@/doc/togglebutton/FormsDoc.vue';
import ImportDoc from '@/doc/togglebutton/ImportDoc.vue';
import InvalidDoc from '@/doc/togglebutton/InvalidDoc.vue';
import PTComponent from '@/doc/togglebutton/pt/index.vue';
@ -34,6 +35,11 @@ export default {
label: 'Basic',
component: BasicDoc
},
{
id: 'forms',
label: 'Forms',
component: FormsDoc
},
{
id: 'customized',
label: 'Customized',

View File

@ -6,6 +6,7 @@
import AccessibilityDoc from '@/doc/toggleswitch/AccessibilityDoc.vue';
import BasicDoc from '@/doc/toggleswitch/BasicDoc.vue';
import DisabledDoc from '@/doc/toggleswitch/DisabledDoc.vue';
import FormsDoc from '@/doc/toggleswitch/FormsDoc.vue';
import ImportDoc from '@/doc/toggleswitch/ImportDoc.vue';
import InvalidDoc from '@/doc/toggleswitch/InvalidDoc.vue';
import PreselectionDoc from '@/doc/toggleswitch/PreselectionDoc.vue';
@ -27,6 +28,11 @@ export default {
label: 'Basic',
component: BasicDoc
},
{
id: 'forms',
label: 'Forms',
component: FormsDoc
},
{
id: 'preselection',
label: 'Preselection',

View File

@ -18,6 +18,7 @@ import DisabledDoc from '@/doc/treeselect/DisabledDoc.vue';
import FilledDoc from '@/doc/treeselect/FilledDoc.vue';
import FilterDoc from '@/doc/treeselect/FilterDoc.vue';
import FloatLabelDoc from '@/doc/treeselect/FloatLabelDoc.vue';
import FormsDoc from '@/doc/treeselect/FormsDoc.vue';
import IftaLabelDoc from '@/doc/treeselect/IftaLabelDoc.vue';
import ImportDoc from '@/doc/treeselect/ImportDoc.vue';
import InvalidDoc from '@/doc/treeselect/InvalidDoc.vue';
@ -41,6 +42,11 @@ export default {
label: 'Basic',
component: BasicDoc
},
{
id: 'forms',
label: 'Forms',
component: FormsDoc
},
{
id: 'multiple',
label: 'Multiple',

View File

@ -15,6 +15,14 @@
"jsx": "preserve",
"incremental": true
},
"include": ["../../packages/primevue/src/**/*.d.ts", "../../packages/primevue/src/**/style/*.d.ts", "../../packages/themes/types", "../packages/themes/src/**/**/index.js"],
"include": [
"../../packages/primevue/src/**/*.d.ts",
"../../packages/primevue/src/**/style/*.d.ts",
"../../packages/themes/types",
"../packages/themes/src/**/**/index.js",
"../packages/form/src/**/*.d.ts",
"../../packages/form/src/**/*.d.ts",
"../../packages/form/src/**/style/*.d.ts"
],
"exclude": ["node_modules", "**/node_modules", "**/dist"]
}

View File

@ -1,5 +1,5 @@
{
"$schema": "https://typedoc.org/schema.json",
"entryPoints": ["../../packages/primevue", "../../packages/themes"],
"entryPoints": ["../../packages/primevue", "../../packages/themes", "../../packages/form"],
"sort": ["source-order"]
}

View File

@ -19,7 +19,7 @@
"release:rc": "pnpm run build && pnpm recursive publish --filter './packages/*' --no-git-checks --report-summary --tag rc",
"build": "NODE_ENV=production pnpm run build:check && pnpm run build:packages",
"build:check": "pnpm run format:check && pnpm run security:check",
"build:packages": "pnpm run build:metadata && pnpm run build:resolver && pnpm run build:core && pnpm run build:lib && pnpm run build:module && pnpm run build:themes && pnpm run build:icons && pnpm run build:showcase",
"build:packages": "pnpm run build:metadata && pnpm run build:resolver && pnpm run build:core && pnpm run build:lib && pnpm run build:module && pnpm run build:themes && pnpm run build:icons && pnpm run build:form && pnpm run build:showcase",
"build:resolver": "pnpm --filter auto-import-resolver build",
"build:core": "pnpm --filter core build",
"build:metadata": "pnpm --filter metadata build",
@ -27,6 +27,7 @@
"build:lib": "pnpm --filter primevue build",
"build:themes": "pnpm --filter themes build",
"build:icons": "pnpm --filter icons build",
"build:form": "pnpm --filter form build",
"build:showcase": "pnpm --filter showcase build",
"build:apidoc": "pnpm --filter themes build:tokens && pnpm --filter showcase build:apidoc",
"dev": "pnpm --filter showcase dev",

View File

@ -22,6 +22,8 @@
"./basecomponent/style": "./src/basecomponent/style/BaseComponentStyle.js",
"./basecomponent": "./src/basecomponent/BaseComponent.vue",
"./basedirective": "./src/basedirective/BaseDirective.js",
"./baseeditableholder": "./src/baseeditableholder/BaseEditableHolder.vue",
"./baseinput": "./src/baseinput/BaseInput.vue",
"./config": "./src/config/PrimeVue.js",
"./service": "./src/service/PrimeVueService.js",
"./usestyle": "./src/usestyle/UseStyle.js",

View File

@ -0,0 +1,48 @@
/**
*
* [Live Demo](https://primevue.org/)
*
* @module baseeditableholder
*
*/
export interface BaseEditableHolderProps {
/**
* Value of the component.
*/
modelValue?: any;
/**
* The default value for the input when not controlled by `modelValue`.
*/
defaultValue?: any;
/**
* The name attribute for the element, typically used in form submissions.
*/
name?: string | undefined;
/**
* When present, it specifies that the component should have invalid state style.
* @defaultValue false
*/
invalid?: boolean | undefined;
/**
* Disables the element, making it non-interactive and unclickable.
* @defaultValue false
*/
disabled?: boolean | undefined;
/**
* Form control object, typically used for handling validation and form state.
*/
formControl?: Record<string, any> | undefined;
}
export interface BaseEditableHolderEmitsOptions {
/**
* Emitted when the value changes in controlled mode.
* @param {*} value - New value.
*/
'update:modelValue'(value: any): void;
/**
* Emitted when the value changes in uncontrolled mode.
* @param {*} value - New value.
*/
'value-change'(value: any): void;
}

View File

@ -0,0 +1,109 @@
<script>
import { isNotEmpty } from '@primeuix/utils';
import BaseComponent from '@primevue/core/basecomponent';
export default {
name: 'BaseEditableHolder',
extends: BaseComponent,
emits: ['update:modelValue', 'value-change'],
props: {
modelValue: {
type: null,
default: undefined
},
defaultValue: {
type: null,
default: undefined
},
name: {
type: String,
default: undefined
},
invalid: {
type: Boolean,
default: undefined
},
disabled: {
type: Boolean,
default: false
},
formControl: {
type: Object,
default: undefined
}
},
inject: {
$parentInstance: {
default: undefined
},
$pcForm: {
default: undefined
}
},
data() {
return {
d_value: this.defaultValue || this.modelValue
};
},
watch: {
modelValue(newValue) {
this.d_value = newValue;
},
defaultValue(newValue) {
this.d_value = newValue;
},
formControl: {
immediate: true,
handler(newValue) {
this.formField = this.$pcForm?.register?.(this.$formName, newValue) || {};
}
},
$formName: {
immediate: true,
handler(newValue) {
this.formField = this.$pcForm?.register?.(newValue, this.formControl) || {};
}
},
$formDefaultValue: {
immediate: true,
handler(newValue) {
this.d_value !== newValue && (this.d_value = newValue);
}
}
},
formField: {},
methods: {
writeValue(value, event) {
if (this.controlled) {
this.d_value = value;
this.$emit('update:modelValue', value);
}
this.$emit('value-change', value);
this.formField.onChange?.({ originalEvent: event, value });
}
},
computed: {
$filled() {
return isNotEmpty(this.d_value);
},
$invalid() {
return this.invalid ?? this.$pcForm?.states?.[this.$formName]?.invalid;
},
$formName() {
return this.formControl?.name || this.name;
},
$formDefaultValue() {
return this.d_value ?? this.$pcForm?.initialValues?.[this.$formName];
},
controlled() {
return this.defaultValue === undefined;
},
// @deprecated use $filled instead
filled() {
return this.$filled;
}
}
};
</script>

View File

@ -0,0 +1,11 @@
{
"main": "./BaseEditableHolder.vue",
"module": "./BaseEditableHolder.vue",
"types": "./BaseEditableHolder.d.ts",
"browser": {
"./sfc": "./BaseEditableHolder.vue"
},
"sideEffects": [
"*.vue"
]
}

View File

@ -0,0 +1,26 @@
/**
*
* [Live Demo](https://primevue.org/)
*
* @module baseinput
*
*/
import { BaseEditableHolderEmitsOptions, BaseEditableHolderProps } from '@primevue/core/baseeditableholder';
export interface BaseInputProps extends BaseEditableHolderProps {
/**
* Defines the size of the component.
*/
size?: 'small' | 'large' | undefined;
/**
* Spans 100% width of the container when enabled.
*/
fluid?: boolean | undefined;
/**
* Specifies the input variant of the component.
* @defaultValue outlined
*/
variant?: 'outlined' | 'filled' | undefined;
}
export interface BaseInputEmitsOptions extends BaseEditableHolderEmitsOptions {}

View File

@ -0,0 +1,42 @@
<script>
import BaseEditableHolder from '@primevue/core/baseeditableholder';
export default {
name: 'BaseInput',
extends: BaseEditableHolder,
props: {
size: {
type: String,
default: null
},
fluid: {
type: Boolean,
default: null
},
variant: {
type: String,
default: null
}
},
inject: {
$parentInstance: {
default: undefined
},
$pcFluid: {
default: undefined
}
},
computed: {
$variant() {
return this.variant ?? (this.$primevue.config.inputStyle || this.$primevue.config.inputVariant);
},
$fluid() {
return this.fluid ?? !!this.$pcFluid;
},
// @deprecated use $fluid instead
hasFluid() {
return this.$fluid;
}
}
};
</script>

View File

@ -0,0 +1,11 @@
{
"main": "./BaseInput.vue",
"module": "./BaseInput.vue",
"types": "./BaseInput.d.ts",
"browser": {
"./sfc": "./BaseInput.vue"
},
"sideEffects": [
"*.vue"
]
}

View File

@ -47,6 +47,8 @@ export * from '@primevue/core/base/style';
export * from '@primevue/core/basecomponent';
export * from '@primevue/core/basecomponent/style';
export * from '@primevue/core/basedirective';
export * from '@primevue/core/baseeditableholder';
export * from '@primevue/core/baseinput';
export * from '@primevue/core/config';
export { default as PrimeVue } from '@primevue/core/config';
export * from '@primevue/core/service';

View File

@ -12,6 +12,12 @@ export { default as BaseComponentStyle } from '@primevue/core/basecomponent/styl
// BaseDirective
export { default as BaseDirective } from '@primevue/core/basedirective';
// BaseEditableHolder
export { default as BaseEditableHolder } from '@primevue/core/baseeditableholder';
// BaseInput
export { default as BaseInput } from '@primevue/core/baseinput';
// PrimeVue
export * from '@primevue/core/config';
export { default as PrimeVue } from '@primevue/core/config';

1
packages/form/README.md Normal file
View File

@ -0,0 +1 @@
# PrimeVue Form

View File

@ -0,0 +1,60 @@
{
"name": "@primevue/form",
"version": "4.1.1",
"author": "PrimeTek Informatics",
"description": "",
"homepage": "https://primevue.org/",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/primefaces/primevue.git",
"directory": "packages/form"
},
"bugs": {
"url": "https://github.com/primefaces/primevue/issues"
},
"main": "./src/form/Form.vue",
"types": "./src/form/Form.d.ts",
"exports": {
".": "./src/form/Form.vue",
"./style": "./src/form/style/FormStyle.js",
"./*": "./src/*/index.js"
},
"publishConfig": {
"main": "./form/index.mjs",
"module": "./form/index.mjs",
"types": "./form/index.d.ts",
"exports": {
".": {
"types": "./form/index.d.ts",
"import": "./form/index.mjs"
},
"./style": {
"types": "./form/style/index.d.ts",
"import": "./form/style/index.mjs"
},
"./*": {
"types": "./*/index.d.ts",
"import": "./*/index.mjs"
}
},
"directory": "dist",
"linkDirectory": false,
"access": "public"
},
"scripts": {
"build": "NODE_ENV=production INPUT_DIR=src/ OUTPUT_DIR=dist/ pnpm run build:package",
"build:package": "pnpm run build:prebuild && rollup -c && pnpm run build:postbuild",
"build:prebuild": "node ./scripts/prebuild.mjs",
"build:postbuild": "node ./scripts/postbuild.mjs",
"dev:link": "pnpm link --global && npm link"
},
"dependencies": {
"@primeuix/utils": "catalog:",
"@primeuix/form": "catalog:",
"@primevue/core": "workspace:*"
},
"engines": {
"node": ">=12.11.0"
}
}

View File

@ -0,0 +1,226 @@
import alias from '@rollup/plugin-alias';
import { babel } from '@rollup/plugin-babel';
import resolve from '@rollup/plugin-node-resolve';
import terser from '@rollup/plugin-terser';
import postcss from 'rollup-plugin-postcss';
import vue from 'rollup-plugin-vue';
import fs from 'fs-extra';
import path, { dirname } from 'path';
import { fileURLToPath } from 'url';
// @todo - Remove
const __dirname = dirname(fileURLToPath(import.meta.url));
// globals
const GLOBALS = {
vue: 'Vue'
};
// externals
const GLOBAL_EXTERNALS = ['vue'];
const INLINE_EXTERNALS = [/@primevue\/core\/.*/, /@primeuix\/.*/];
const EXTERNALS = [...GLOBAL_EXTERNALS, ...INLINE_EXTERNALS];
// alias
const ALIAS_ENTRIES = [
{
find: /^@primevue\/form\/(.*)$/,
replacement: path.resolve(__dirname, './src/$1'),
customResolver(source, importer) {
const basedir = path.dirname(importer);
const folderPath = path.resolve(basedir, source);
const folderName = path.basename(folderPath);
const fName = folderName === 'style' ? `${path.basename(path.dirname(folderPath))}Style` : folderName;
const files = fs.readdirSync(folderPath);
const targetFile = files.find((file) => {
const ext = path.extname(file);
return ['.vue', '.js'].includes(ext) && path.basename(file, ext).toLowerCase() === fName.toLowerCase();
});
return targetFile ? path.join(folderPath, targetFile) : null;
}
},
{ find: '@primevue/form/resolvers', replacement: path.resolve(__dirname, './src/resolvers/index.js') },
{ find: '@primevue/form/useform', replacement: path.resolve(__dirname, './src/useform/index.js') }
];
// plugins
const BABEL_PLUGIN_OPTIONS = {
extensions: ['.js', '.vue'],
exclude: 'node_modules/**',
presets: ['@babel/preset-env'],
plugins: [],
skipPreflightCheck: true,
babelHelpers: 'runtime',
babelrc: false
};
const ALIAS_PLUGIN_OPTIONS = {
entries: ALIAS_ENTRIES
};
const POSTCSS_PLUGIN_OPTIONS = {
sourceMap: false
};
const TERSER_PLUGIN_OPTIONS = {
compress: {
keep_infinity: true,
pure_getters: true,
reduce_funcs: true
},
mangle: {
reserved: ['theme', 'css']
}
};
const PLUGINS = [vue(), postcss(POSTCSS_PLUGIN_OPTIONS), babel(BABEL_PLUGIN_OPTIONS)];
const ENTRY = {
entries: [],
onwarn(warning) {
if (warning.code === 'CIRCULAR_DEPENDENCY') {
//console.error(`(!) ${warning.message}`);
return;
}
},
format: {
cjs_es(options) {
return ENTRY.format.cjs(options).es(options);
},
cjs({ input, output, minify }) {
ENTRY.entries.push({
onwarn: ENTRY.onwarn,
input,
plugins: [...PLUGINS, minify && terser(TERSER_PLUGIN_OPTIONS)],
external: EXTERNALS,
inlineDynamicImports: true,
output: [
{
format: 'cjs',
file: `${output}${minify ? '.min' : ''}.cjs`,
sourcemap: true,
exports: 'auto'
}
]
});
ENTRY.update.packageJson({ input, output, options: { main: `${output}.cjs` } });
return ENTRY.format;
},
es({ input, output, minify }) {
ENTRY.entries.push({
onwarn: ENTRY.onwarn,
input,
plugins: [...PLUGINS, minify && terser(TERSER_PLUGIN_OPTIONS)],
external: EXTERNALS,
inlineDynamicImports: true,
output: [
{
format: 'es',
file: `${output}${minify ? '.min' : ''}.mjs`,
sourcemap: true,
exports: 'auto'
}
]
});
ENTRY.update.packageJson({ input, output, options: { main: `${output}.mjs`, module: `${output}.mjs` } });
return ENTRY.format;
},
umd({ name, input, output, minify }) {
ENTRY.entries.push({
onwarn: ENTRY.onwarn,
input,
plugins: [alias(ALIAS_PLUGIN_OPTIONS), resolve(), ...PLUGINS, minify && terser(TERSER_PLUGIN_OPTIONS)],
external: GLOBAL_EXTERNALS,
inlineDynamicImports: true,
output: [
{
format: 'umd',
name: name ?? 'PrimeVue',
file: `${output}${minify ? '.min' : ''}.js`,
globals: GLOBALS,
exports: 'auto'
}
]
});
return ENTRY.format;
}
},
update: {
packageJson({ input, output, options }) {
try {
const inputDir = path.resolve(__dirname, path.dirname(input));
const outputDir = path.resolve(__dirname, path.dirname(output));
const packageJson = path.resolve(outputDir, 'package.json');
!fs.existsSync(packageJson) && fs.copySync(path.resolve(inputDir, './package.json'), packageJson);
const pkg = JSON.parse(fs.readFileSync(packageJson, { encoding: 'utf8', flag: 'r' }));
!pkg?.main?.includes('.cjs') && (pkg.main = path.basename(options?.main) ? `./${path.basename(options.main)}` : pkg.main);
pkg.module = path.basename(options?.module) ? `./${path.basename(options.module)}` : packageJson.module;
pkg.types && (pkg.types = './index.d.ts');
fs.writeFileSync(packageJson, JSON.stringify(pkg, null, 4));
} catch {}
}
}
};
function addFile() {
fs.readdirSync(path.resolve(__dirname, process.env.INPUT_DIR), { withFileTypes: true })
.filter((dir) => dir.isDirectory())
.forEach(({ name: folderName }) => {
fs.readdirSync(path.resolve(__dirname, process.env.INPUT_DIR + folderName)).forEach((file) => {
let name = file.split(/(.vue)$|(.js)$/)[0].toLowerCase();
if (name === folderName) {
const input = process.env.INPUT_DIR + folderName + '/' + file;
const output = process.env.OUTPUT_DIR + folderName + '/index';
ENTRY.format.es({ input, output });
}
});
});
}
function addStyle() {
fs.readdirSync(path.resolve(__dirname, process.env.INPUT_DIR), { withFileTypes: true })
.filter((dir) => dir.isDirectory())
.forEach(({ name: folderName }) => {
try {
fs.readdirSync(path.resolve(__dirname, process.env.INPUT_DIR + folderName + '/style')).forEach((file) => {
if (/\.js$/.test(file)) {
const name = file.split(/(.js)$/)[0].toLowerCase();
const input = process.env.INPUT_DIR + folderName + '/style/' + file;
const output = process.env.OUTPUT_DIR + folderName + '/style/index';
ENTRY.format.es({ input, output });
}
});
} catch {}
});
}
function addResolvers() {
ENTRY.format.es({ input: process.env.INPUT_DIR + 'resolvers/index.js', output: process.env.OUTPUT_DIR + 'resolvers/index' });
}
function addUseForm() {
ENTRY.format.es({ input: process.env.INPUT_DIR + 'useform/index.js', output: process.env.OUTPUT_DIR + 'useform/index' });
}
addFile();
addStyle();
addResolvers();
addUseForm();
export default ENTRY.entries;

View File

@ -0,0 +1,14 @@
import fs from 'fs-extra';
import path from 'path';
import { clearPackageJson, copyDependencies, renameDTSFile, resolvePath } from '../../../scripts/build-helper.mjs';
const { __dirname, __workspace, INPUT_DIR, OUTPUT_DIR } = resolvePath(import.meta.url);
copyDependencies(INPUT_DIR, OUTPUT_DIR, '/style');
renameDTSFile(OUTPUT_DIR, 'index');
fs.copySync(path.resolve(__dirname, '../package.json'), `${OUTPUT_DIR}/package.json`);
fs.copySync(path.resolve(__dirname, '../README.md'), `${OUTPUT_DIR}/README.md`);
fs.copySync(path.resolve(__workspace, './LICENSE.md'), `${OUTPUT_DIR}/LICENSE.md`);
clearPackageJson(path.resolve(__dirname, `../${OUTPUT_DIR}/package.json`));

View File

@ -0,0 +1,5 @@
import path from 'path';
import { removeBuild, resolvePath, updatePackageJson } from '../../../scripts/build-helper.mjs';
removeBuild(import.meta.url);
updatePackageJson(path.resolve(resolvePath(import.meta.url).__dirname, '../package.json'));

View File

@ -0,0 +1,42 @@
<script>
import BaseComponent from '@primevue/core/basecomponent';
import FormStyle from '@primevue/form/style';
export default {
name: 'BaseForm',
extends: BaseComponent,
style: FormStyle,
props: {
resolver: {
type: Function,
default: null
},
initialValues: {
type: Object,
default: null
},
validateOnValueUpdate: {
type: [Boolean, Array],
default: true
},
validateOnBlur: {
type: [Boolean, Array],
default: false
},
validateOnMount: {
type: [Boolean, Array],
default: false
},
validateOnSubmit: {
type: [Boolean, Array],
default: true
}
},
provide() {
return {
$pcForm: this,
$parentInstance: this
};
}
};
</script>

281
packages/form/src/form/Form.d.ts vendored Normal file
View File

@ -0,0 +1,281 @@
/**
*
* Form provides validation functionality and manages form state.
*
* [Live Demo](https://www.primevue.org/form/)
*
* @module form
*
*/
import type { DefineComponent, DesignToken, EmitFn, PassThrough } from '@primevue/core';
import type { ComponentHooks } from '@primevue/core/basecomponent';
import { VNode } from 'vue';
/**
* From primevue/passthrough/index.d.ts
*/
export declare type PassThroughMergePropsType = ((...args: any) => object | undefined) | boolean | undefined;
export interface PassThroughOptions {
mergeSections?: boolean | undefined;
mergeProps?: PassThroughMergePropsType;
}
export declare type FormPassThroughOptionType = FormPassThroughAttributes | ((options: FormPassThroughMethodOptions) => FormPassThroughAttributes | string) | string | null | undefined;
/**
* Custom passthrough(pt) option method.
*/
export interface FormPassThroughMethodOptions {
/**
* Defines instance.
*/
instance: any;
/**
* Defines valid properties.
*/
props: FormProps;
/**
* Defines valid attributes.
*/
attrs: any;
/**
* Defines parent options.
*/
parent: any;
/**
* Defines passthrough(pt) options in global config.
*/
global: object | undefined;
}
/**
* Custom passthrough(pt) options.
* @see {@link FormProps.pt}
*/
export interface FormPassThroughOptions {
/**
* Used to pass attributes to the root's DOM element.
*/
root?: FormPassThroughOptionType;
/**
* Used to manage all lifecycle hooks.
* @see {@link BaseComponent.ComponentHooks}
*/
hooks?: ComponentHooks;
}
/**
* Custom passthrough attributes for each DOM elements
*/
export interface FormPassThroughAttributes {
[key: string]: any;
}
/**
* Resolver options for Form component.
*/
export interface FormResolverOptions {
/**
* The values of the form fields.
*/
values: Record<string, any>;
/**
* The names of the form fields.
*/
names: string[] | undefined;
}
/**
* Submit events
*/
export interface FormSubmitEvent {
/**
* The original DOM event.
*/
originalEvent: Event;
/**
* The form values.
*/
values: Record<string, any>;
/**
* The form state.
*/
states: Record<string, FormFieldState>;
/**
* Whether the form is valid.
*/
valid: boolean;
/**
* The form errors.
*/
errors: any[];
/**
* Resets the form.
*/
reset: () => void;
}
/**
* The state of a form field.
*/
export interface FormFieldState {
/**
* The value of the form field.
*/
value: any;
/**
* Whether the form field has been touched.
* @defaultValue false
*/
touched: boolean;
/**
* Whether the form field has been modified.
* @defaultValue false
*/
dirty: boolean;
/**
* Whether the form field has not been modified.
* @defaultValue true
*/
pristine: boolean;
/**
* Whether the form field is valid.
* @defaultValue true
*/
valid: boolean;
/**
* Whether the form field is invalid.
* @defaultValue false
*/
invalid: boolean;
/**
* The first error message of the form field.
*/
error: any;
/**
* All error messages of the form field.
* @defaultValue []
*/
errors: any[];
}
/**
* Defines valid properties in Form component.
*/
export interface FormProps {
/**
* A function that resolves validation logic.
* @param {FormResolverOptions} e - Resolver options
*/
resolver?: (e: FormResolverOptions) => Promise<Record<string, any>> | Record<string, any> | undefined;
/**
* The initial values for the form fields.
*/
initialValues?: Record<string, any> | undefined;
/**
* Whether to validate the form fields when the values change.
* @defaultValue true
*/
validateOnValueUpdate?: boolean | string[] | undefined;
/**
* Whether to validate the form fields when they lose focus (on blur).
* @defaultValue false
*/
validateOnBlur?: boolean | string[] | undefined;
/**
* Whether to validate the form fields immediately after the form is mounted.
* @defaultValue false
*/
validateOnMount?: boolean | string[] | undefined;
/**
* Whether to validate the form fields when the form is submitted.
* @defaultValue true
*/
validateOnSubmit?: boolean | string[] | undefined;
/**
* It generates scoped CSS variables using design tokens for the component.
*/
dt?: DesignToken<any>;
/**
* Used to pass attributes to DOM elements inside the component.
* @type {FormPassThroughOptions}
*/
pt?: PassThrough<FormPassThroughOptions>;
/**
* Used to configure passthrough(pt) options of the component.
* @type {PassThroughOptions}
*/
ptOptions?: PassThroughOptions;
/**
* When enabled, it removes component related styles in the core.
* @defaultValue false
*/
unstyled?: boolean;
}
/**
* Defines valid slots in Form component.
*/
export interface FormSlots {
/**
* Default content slot.
* @param {Object} scope - default slot's params.
*/
default: (scope: {
/**
* Registers a form field for validation and tracking.
* @param field - The name of the form field to register.
* @param options - Configuration options for the field, such as validation rules.
* @returns - Returns an object or value representing the registered field.
*/
register: (field: string, options: any) => any;
/**
* Resets the entire form state, clearing values and validation statuses.
*/
reset: () => void;
/**
* Indicates whether the form is valid, returning `true` if all fields pass validation.
*/
valid: boolean;
/**
* Stores the state of each form field, with the field name as the key and its state as the value.
*/
states: Record<string, FormFieldState>;
}) => VNode[];
}
/**
* Defines valid emits in Form component.
*/
export interface FormEmitsOptions {
/**
* Emitted when the form is submitted.
* @param {Event} event - Original DOM event.
*/
submit: (event: Event) => void;
}
export declare type FormEmits = EmitFn<FormEmitsOptions>;
/**
* **PrimeVue - Form**
*
* _Form provides validation functionality and manages form state._
*
* [Live Demo](https://www.primevue.org/form/)
* --- ---
* ![PrimeVue](https://primefaces.org/cdn/primevue/images/logo-100.png)
*
* @group Component
*
*/
declare const Form: DefineComponent<FormProps, FormSlots, FormEmits>;
declare module 'vue' {
export interface GlobalComponents {
Form: DefineComponent<FormProps, FormSlots, FormEmits>;
}
}
export default Form;

View File

@ -0,0 +1,37 @@
<template>
<form @submit.prevent="onSubmit" v-bind="ptmi('root')">
<slot :register :valid :reset v-bind="states" />
</form>
</template>
<script>
import { omit } from '@primeuix/utils';
import { useForm } from '@primevue/form/useform';
import BaseForm from './BaseForm.vue';
export default {
name: 'Form',
extends: BaseForm,
inheritAttrs: false,
emits: ['submit'],
setup(props, { emit }) {
const $form = useForm(props);
const register = (field, options) => {
const [, fieldProps] = $form.defineField(field, options);
return fieldProps;
};
const onSubmit = $form.handleSubmit((e) => {
emit('submit', e);
});
return {
register,
onSubmit,
...omit($form, ['defineField', 'handleSubmit'])
};
}
};
</script>

View File

@ -0,0 +1,11 @@
{
"main": "./Form.vue",
"module": "./Form.vue",
"types": "./Form.d.ts",
"browser": {
"./sfc": "./Form.vue"
},
"sideEffects": [
"*.vue"
]
}

View File

@ -0,0 +1,10 @@
/**
*
* [Live Demo](https://www.primevue.org/form/)
*
* @module formstyle
*
*/
import type { BaseStyle } from '@primevue/core/base/style';
export interface FormStyle extends BaseStyle {}

View File

@ -0,0 +1,5 @@
import BaseStyle from '@primevue/core/base/style';
export default BaseStyle.extend({
name: 'form'
});

Some files were not shown because too many files have changed in this diff Show More