Add `forms` demo page
parent
9f8003ad9d
commit
4929f978f2
|
@ -497,6 +497,12 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Forms",
|
||||
"icon": "pi pi-check-circle",
|
||||
"to": "/forms",
|
||||
"badge": "NEW"
|
||||
},
|
||||
{
|
||||
"name": "Pass Through",
|
||||
"icon": "pi pi-directions",
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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>
|
|
@ -0,0 +1,115 @@
|
|||
<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.errors[0]?.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.errors[0]?.message }}</Message>
|
||||
</div>
|
||||
<Button type="submit" severity="secondary" label="Submit" />
|
||||
</Form>
|
||||
`,
|
||||
options: `
|
||||
<template>
|
||||
<Form v-slot="$form" :initialValues :resolver @submit="onFormSubmit" class="flex flex-col gap-4 w-full sm:w-56">
|
||||
<div class="flex flex-col gap-2">
|
||||
<InputText name="username" type="text" placeholder="Username" fluid />
|
||||
<Message v-if="$form.username?.invalid" severity="error">{{ $form.username.errors[0]?.message }}</Message>
|
||||
</div>
|
||||
<Button type="submit" severity="secondary" label="Submit" />
|
||||
</Form>
|
||||
</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>
|
||||
<Card>
|
||||
<template #title>Simple Card</template>
|
||||
<template #content>
|
||||
<p class="m-0">
|
||||
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Inventore sed consequuntur error repudiandae numquam deserunt quisquam repellat libero asperiores earum nam nobis, culpa ratione quam perferendis esse, cupiditate neque
|
||||
quas!
|
||||
</p>
|
||||
</template>
|
||||
</Card>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
<\/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>
|
|
@ -0,0 +1,267 @@
|
|||
<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" 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" />
|
||||
</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,
|
||||
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" 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>
|
||||
<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" 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>
|
||||
</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,
|
||||
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>
|
||||
<Card>
|
||||
<template #title>Simple Card</template>
|
||||
<template #content>
|
||||
<p class="m-0">
|
||||
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Inventore sed consequuntur error repudiandae numquam deserunt quisquam repellat libero asperiores earum nam nobis, culpa ratione quam perferendis esse, cupiditate neque
|
||||
quas!
|
||||
</p>
|
||||
</template>
|
||||
</Card>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
<\/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>
|
|
@ -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>
|
|
@ -0,0 +1,125 @@
|
|||
<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 name="username" type="text" placeholder="Username" v-bind="$form.register('username')" />
|
||||
<Message v-if="$form.username?.invalid" severity="error">{{ $form.username.errors[0]?.message }}</Message>
|
||||
</div>
|
||||
<Button type="submit" severity="secondary" label="Submit" />
|
||||
</Form>
|
||||
</div>
|
||||
<DocSectionCode :code="code" />
|
||||
</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 name="username" type="text" placeholder="Username" v-bind="$form.register('username')" />
|
||||
<Message v-if="$form.username?.invalid" severity="error">{{ $form.username.errors[0]?.message }}</Message>
|
||||
</div>
|
||||
<Button type="submit" severity="secondary" label="Submit" />
|
||||
</Form>
|
||||
`,
|
||||
options: `
|
||||
<template>
|
||||
<Form v-slot="$form" :initialValues :resolver @submit="onFormSubmit" class="flex flex-col gap-4 w-full sm:w-56">
|
||||
<div class="flex flex-col gap-2">
|
||||
<input name="username" type="text" placeholder="Username" v-bind="$form.register('username')" />
|
||||
<Message v-if="$form.username?.invalid" severity="error">{{ $form.username.errors[0]?.message }}</Message>
|
||||
</div>
|
||||
<Button type="submit" severity="secondary" label="Submit" />
|
||||
</Form>
|
||||
</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>
|
||||
<Card>
|
||||
<template #title>Simple Card</template>
|
||||
<template #content>
|
||||
<p class="m-0">
|
||||
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Inventore sed consequuntur error repudiandae numquam deserunt quisquam repellat libero asperiores earum nam nobis, culpa ratione quam perferendis esse, cupiditate neque
|
||||
quas!
|
||||
</p>
|
||||
</template>
|
||||
</Card>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
<\/script>
|
||||
`
|
||||
}
|
||||
};
|
||||
},
|
||||
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>
|
|
@ -0,0 +1,271 @@
|
|||
<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.errors[0]?.message }}</Message>
|
||||
</div>
|
||||
<Button type="submit" severity="secondary" label="Submit" />
|
||||
</Form>
|
||||
</div>
|
||||
<DocSectionCode :code="code" />
|
||||
</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: `
|
||||
<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.errors[0]?.message }}</Message>
|
||||
</div>
|
||||
<Button type="submit" severity="secondary" label="Submit" />
|
||||
</Form>
|
||||
`,
|
||||
options: `
|
||||
<template>
|
||||
<Fieldset legend="Schema">
|
||||
<RadioButtonGroup v-model="selectedSchema" name="schema" class="flex flex-wrap gap-4" @update:modelValue="changeResolver">
|
||||
<div class="flex items-center">
|
||||
<RadioButton inputId="zod" value="Zod" />
|
||||
<label for="zod" class="ml-2">Zod</label>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<RadioButton inputId="yup" value="Yup" />
|
||||
<label for="yup" class="ml-2">Yup</label>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<RadioButton inputId="valibot" value="Valibot" />
|
||||
<label for="valibot" class="ml-2">Valibot</label>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<RadioButton inputId="superStruct" value="SuperStruct" />
|
||||
<label for="superStruct" class="ml-2">SuperStruct</label>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<RadioButton inputId="custom" value="Custom" />
|
||||
<label for="custom" class="ml-2">Custom</label>
|
||||
</div>
|
||||
</RadioButtonGroup>
|
||||
</Fieldset>
|
||||
|
||||
<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.errors[0]?.message }}</Message>
|
||||
</div>
|
||||
<Button type="submit" severity="secondary" label="Submit" />
|
||||
</Form>
|
||||
</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>
|
||||
<Card>
|
||||
<template #title>Simple Card</template>
|
||||
<template #content>
|
||||
<p class="m-0">
|
||||
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Inventore sed consequuntur error repudiandae numquam deserunt quisquam repellat libero asperiores earum nam nobis, culpa ratione quam perferendis esse, cupiditate neque
|
||||
quas!
|
||||
</p>
|
||||
</template>
|
||||
</Card>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
<\/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>
|
|
@ -0,0 +1,119 @@
|
|||
<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="initialValues" :resolver="resolver" @submit="onFormSubmit" class="grid md:grid-cols-2 gap-4 w-full">
|
||||
<div class="flex flex-col justify-center items-center gap-4">
|
||||
<InputText name="username" type="text" placeholder="Username" />
|
||||
<Button type="submit" severity="secondary" label="Submit" />
|
||||
</div>
|
||||
<Fieldset legend="Form States">
|
||||
<pre>{{ $form }}</pre>
|
||||
</Fieldset>
|
||||
</Form>
|
||||
</div>
|
||||
<DocSectionCode :code="code" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
initialValues: {
|
||||
username: ''
|
||||
},
|
||||
code: {
|
||||
basic: `
|
||||
<Form v-slot="$form" :initialValues :resolver @submit="onFormSubmit" class="grid md:grid-cols-2 gap-4 w-full">
|
||||
<div class="flex flex-col justify-center items-center gap-4">
|
||||
<InputText name="username" type="text" placeholder="Username" />
|
||||
<Button type="submit" severity="secondary" label="Submit" />
|
||||
</div>
|
||||
<Fieldset legend="Form States">
|
||||
<pre>{{ $form }}</pre>
|
||||
</Fieldset>
|
||||
</Form>
|
||||
`,
|
||||
options: `
|
||||
<template>
|
||||
<Form v-slot="$form" :initialValues :resolver @submit="onFormSubmit" class="grid md:grid-cols-2 gap-4 w-full">
|
||||
<div class="flex flex-col justify-center items-center gap-4">
|
||||
<InputText name="username" type="text" placeholder="Username" />
|
||||
<Button type="submit" severity="secondary" label="Submit" />
|
||||
</div>
|
||||
<Fieldset legend="Form States">
|
||||
<pre>{{ $form }}</pre>
|
||||
</Fieldset>
|
||||
</Form>
|
||||
</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>
|
||||
<Card>
|
||||
<template #title>Simple Card</template>
|
||||
<template #content>
|
||||
<p class="m-0">
|
||||
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Inventore sed consequuntur error repudiandae numquam deserunt quisquam repellat libero asperiores earum nam nobis, culpa ratione quam perferendis esse, cupiditate neque
|
||||
quas!
|
||||
</p>
|
||||
</template>
|
||||
</Card>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
<\/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>
|
|
@ -0,0 +1,169 @@
|
|||
<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-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.errors[0]?.message }}</Message>
|
||||
</div>
|
||||
<div class="flex flex-col gap-2">
|
||||
<Password name="password" placeholder="Password" :feedback="false" fluid />
|
||||
<Message v-if="$form.password?.invalid" severity="error">
|
||||
<ul class="mx-1 px-3">
|
||||
<li v-for="(error, index) of $form.password.errors" :key="index" class="py-1">{{ error.message }}</li>
|
||||
</ul>
|
||||
</Message>
|
||||
</div>
|
||||
<Button type="submit" severity="secondary" label="Submit" />
|
||||
</Form>
|
||||
</div>
|
||||
<DocSectionCode :code="code" />
|
||||
</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: 'Password must be at least 3 characters long.' })
|
||||
.max(8, { message: 'Password must not exceed 8 characters.' })
|
||||
.refine((value) => /[a-z]/.test(value), {
|
||||
message: 'Password must contain at least one lowercase letter.'
|
||||
})
|
||||
.refine((value) => /[A-Z]/.test(value), {
|
||||
message: 'Password must contain at least one uppercase letter.'
|
||||
})
|
||||
.refine((value) => /\d/.test(value), {
|
||||
message: 'Password must contain at least one number.'
|
||||
})
|
||||
})
|
||||
),
|
||||
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.errors[0]?.message }}</Message>
|
||||
</div>
|
||||
<div class="flex flex-col gap-2">
|
||||
<Password name="password" placeholder="Password" :feedback="false" fluid />
|
||||
<Message v-if="$form.password?.invalid" severity="error">
|
||||
<ul class="mx-1 px-3">
|
||||
<li v-for="(error, index) of $form.password.errors" :key="index" class="py-1">{{ error.message }}</li>
|
||||
</ul>
|
||||
</Message>
|
||||
</div>
|
||||
<Button type="submit" severity="secondary" label="Submit" />
|
||||
</Form>
|
||||
`,
|
||||
options: `
|
||||
<template>
|
||||
<Form v-slot="$form" :initialValues :resolver @submit="onFormSubmit" class="flex flex-col gap-4 w-full sm:w-56">
|
||||
<div class="flex flex-col gap-2">
|
||||
<InputText name="username" type="text" placeholder="Username" fluid />
|
||||
<Message v-if="$form.username?.invalid" severity="error">{{ $form.username.errors[0]?.message }}</Message>
|
||||
</div>
|
||||
<div class="flex flex-col gap-2">
|
||||
<Password name="password" placeholder="Password" :feedback="false" fluid />
|
||||
<Message v-if="$form.password?.invalid" severity="error">
|
||||
<ul class="mx-1 px-3">
|
||||
<li v-for="(error, index) of $form.password.errors" :key="index" class="py-1">{{ error.message }}</li>
|
||||
</ul>
|
||||
</Message>
|
||||
</div>
|
||||
<Button type="submit" severity="secondary" label="Submit" />
|
||||
</Form>
|
||||
</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: 'Password must be at least 3 characters long.' })
|
||||
.max(8, { message: 'Password must not exceed 8 characters.' })
|
||||
.refine((value) => /[a-z]/.test(value), {
|
||||
message: 'Password must contain at least one lowercase letter.'
|
||||
})
|
||||
.refine((value) => /[A-Z]/.test(value), {
|
||||
message: 'Password must contain at least one uppercase letter.'
|
||||
})
|
||||
.refine((value) => /\d/.test(value), {
|
||||
message: 'Password must contain at least one 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.
|
||||
if (e.valid) {
|
||||
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
<\/script>
|
||||
`,
|
||||
composition: `
|
||||
<template>
|
||||
<Card>
|
||||
<template #title>Simple Card</template>
|
||||
<template #content>
|
||||
<p class="m-0">
|
||||
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Inventore sed consequuntur error repudiandae numquam deserunt quisquam repellat libero asperiores earum nam nobis, culpa ratione quam perferendis esse, cupiditate neque
|
||||
quas!
|
||||
</p>
|
||||
</template>
|
||||
</Card>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
<\/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.
|
||||
if (e.valid) {
|
||||
this.$toast.add({ severity: 'success', summary: 'Form is submitted.', life: 3000 });
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
|
@ -0,0 +1,65 @@
|
|||
<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>
|
||||
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';
|
||||
|
||||
export default {
|
||||
name: 'DynamicForm',
|
||||
emits: ['submit'],
|
||||
props: {
|
||||
fields: Object
|
||||
},
|
||||
provide() {
|
||||
return {
|
||||
$fcDynamicForm: this
|
||||
};
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
defaultValues: {},
|
||||
schemas: {}
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
addField(name, schema, defaultValue) {
|
||||
schema && (this.schemas[name] = schema);
|
||||
this.defaultValues[name] = defaultValue;
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
resolver() {
|
||||
return isNotEmpty(this.schemas) ? zodResolver(z.object(this.schemas)) : undefined;
|
||||
},
|
||||
initialValues() {
|
||||
return this.defaultValues;
|
||||
}
|
||||
},
|
||||
components: {
|
||||
DynamicFormControl,
|
||||
DynamicFormField,
|
||||
DynamicFormLabel,
|
||||
DynamicFormMessage,
|
||||
DynamicFormSubmit
|
||||
}
|
||||
};
|
||||
</script>
|
|
@ -0,0 +1,43 @@
|
|||
<template>
|
||||
<component :is="component" :id :name class="w-full" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as PrimeVue from 'primevue/primevue';
|
||||
|
||||
export default {
|
||||
name: 'DynamicFormControl',
|
||||
props: {
|
||||
as: {
|
||||
type: String,
|
||||
default: 'InputText'
|
||||
},
|
||||
schema: null,
|
||||
defaultValue: {
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
inject: {
|
||||
$fcDynamicForm: {
|
||||
default: undefined
|
||||
},
|
||||
$fcDynamicFormField: {
|
||||
default: undefined
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.$fcDynamicForm?.addField(this.name, this.schema, this.defaultValue);
|
||||
},
|
||||
computed: {
|
||||
id() {
|
||||
return this.$fcDynamicFormField?.$props.groupId;
|
||||
},
|
||||
name() {
|
||||
return this.$fcDynamicFormField?.$props.name;
|
||||
},
|
||||
component() {
|
||||
return PrimeVue[this.as] ?? this.as;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
|
@ -0,0 +1,26 @@
|
|||
<template>
|
||||
<div class="flex flex-col gap-2">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'DynamicFormField',
|
||||
props: {
|
||||
groupId: {
|
||||
type: String,
|
||||
default: undefined
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
default: undefined
|
||||
}
|
||||
},
|
||||
provide() {
|
||||
return {
|
||||
$fcDynamicFormField: this
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
|
@ -0,0 +1,21 @@
|
|||
<template>
|
||||
<label :for="htmlFor">
|
||||
<slot />
|
||||
</label>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'DynamicFormLabel',
|
||||
inject: {
|
||||
$fcDynamicFormField: {
|
||||
default: undefined
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
htmlFor() {
|
||||
return this.$fcDynamicFormField?.$props.groupId;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
|
@ -0,0 +1,54 @@
|
|||
<template>
|
||||
<Message v-if="visible" :severity>
|
||||
<slot>{{ message }}</slot>
|
||||
</Message>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { isNotEmpty } from '@primeuix/utils';
|
||||
|
||||
export default {
|
||||
name: 'DynamicFormLabel',
|
||||
props: {
|
||||
errorType: {
|
||||
type: String,
|
||||
default: undefined
|
||||
},
|
||||
severity: {
|
||||
type: String,
|
||||
default: 'error'
|
||||
}
|
||||
},
|
||||
inject: {
|
||||
$pcForm: {
|
||||
default: undefined
|
||||
},
|
||||
$fcDynamicFormField: {
|
||||
default: undefined
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
visible() {
|
||||
return this.invalid && (this.error || this.errorType === undefined);
|
||||
},
|
||||
message() {
|
||||
return this.errorType ? this.error?.message : this.errors?.[0]?.message;
|
||||
},
|
||||
error() {
|
||||
return this.errors?.find((error) => error.errorType === this.errorType || isNotEmpty(error[this.errorType]));
|
||||
},
|
||||
fieldName() {
|
||||
return this.$fcDynamicFormField?.$props.name;
|
||||
},
|
||||
state() {
|
||||
return this.$pcForm?.states?.[this.fieldName];
|
||||
},
|
||||
errors() {
|
||||
return this.state?.errors;
|
||||
},
|
||||
invalid() {
|
||||
return this.state?.invalid;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
|
@ -0,0 +1,19 @@
|
|||
<template>
|
||||
<Button type="submit" :severity :label />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'DynamicFormSubmit',
|
||||
props: {
|
||||
severity: {
|
||||
type: String,
|
||||
default: 'secondary'
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
default: 'Submit'
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
|
@ -0,0 +1,37 @@
|
|||
<template>
|
||||
<DocPTViewer :docs="docs">
|
||||
<Card style="width: 25rem">
|
||||
<template #title>Advanced Card</template>
|
||||
<template #subtitle>Card subtitle</template>
|
||||
<template #content>
|
||||
<p class="m-0">
|
||||
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Inventore sed consequuntur error repudiandae numquam deserunt quisquam repellat libero asperiores earum nam nobis, culpa ratione quam perferendis esse, cupiditate neque
|
||||
quas!
|
||||
</p>
|
||||
</template>
|
||||
<template #footer>
|
||||
<div class="flex gap-4 mt-1">
|
||||
<Button label="Cancel" severity="secondary" outlined class="w-full" />
|
||||
<Button label="Save" class="w-full" />
|
||||
</div>
|
||||
</template>
|
||||
</Card>
|
||||
</DocPTViewer>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getPTOptions } from '@/components/doc/helpers';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
docs: [
|
||||
{
|
||||
data: getPTOptions('Card'),
|
||||
key: 'Card'
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
|
@ -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>
|
|
@ -0,0 +1,8 @@
|
|||
<template>
|
||||
<DocSectionText v-bind="$attrs">
|
||||
<p>
|
||||
Visit <a href="https://github.com/primefaces/primevue-tailwind" target="_blank" rel="noopener noreferrer" class="doc-link">Tailwind Presets</a> project for detailed documentation, examples and ready-to-use presets about how to style
|
||||
PrimeVue components with Tailwind CSS.
|
||||
</p>
|
||||
</DocSectionText>
|
||||
</template>
|
|
@ -0,0 +1,49 @@
|
|||
<template>
|
||||
<div class="doc-main">
|
||||
<div class="doc-intro">
|
||||
<h1>Form Theming</h1>
|
||||
</div>
|
||||
<DocSections :docs="docs" />
|
||||
</div>
|
||||
<DocSectionNav :docs="docs" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import DocApiTable from '@/components/doc/DocApiTable.vue';
|
||||
import { getStyleOptions } from '@/components/doc/helpers';
|
||||
import TailwindDoc from './TailwindDoc.vue';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
docs: [
|
||||
{
|
||||
id: 'theming.styled',
|
||||
label: 'Styled',
|
||||
children: [
|
||||
{
|
||||
id: 'theming.classes',
|
||||
label: 'CSS Classes',
|
||||
description: 'List of class names used in the styled mode.',
|
||||
component: DocApiTable,
|
||||
data: getStyleOptions('Form')
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'theming.unstyled',
|
||||
label: 'Unstyled',
|
||||
description: 'Theming is implemented with the pass through properties in unstyled mode.',
|
||||
children: [
|
||||
{
|
||||
id: 'theming.tailwind',
|
||||
label: 'Tailwind',
|
||||
component: TailwindDoc
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
|
@ -0,0 +1,67 @@
|
|||
<template>
|
||||
<DocComponent title="Vue Form Component" header="Form" 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 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: '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>
|
|
@ -15,6 +15,6 @@
|
|||
"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"],
|
||||
"exclude": ["node_modules", "**/node_modules", "**/dist"]
|
||||
}
|
||||
|
|
|
@ -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"]
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue