Refactor on forms package

pull/6705/head
Mert Sincan 2024-11-01 06:02:14 +00:00
parent 0432047fd4
commit 83225d56bc
9 changed files with 104 additions and 136 deletions

View File

@ -53,7 +53,7 @@
}, },
"dependencies": { "dependencies": {
"@primeuix/utils": "catalog:", "@primeuix/utils": "catalog:",
"@primeuix/form": "catalog:", "@primeuix/forms": "catalog:",
"@primevue/core": "workspace:*" "@primevue/core": "workspace:*"
}, },
"engines": { "engines": {

View File

@ -251,9 +251,9 @@ export interface FormSlots {
export interface FormEmitsOptions { export interface FormEmitsOptions {
/** /**
* Emitted when the form is submitted. * Emitted when the form is submitted.
* @param {Event} event - Original DOM event. * @param {FormSubmitEvent} event - Custom submit event.
*/ */
submit: (event: Event) => void; submit: (event: FormSubmitEvent) => void;
} }
export declare type FormEmits = EmitFn<FormEmitsOptions>; export declare type FormEmits = EmitFn<FormEmitsOptions>;

View File

@ -1,6 +1,6 @@
<script> <script>
import BaseComponent from '@primevue/core/basecomponent'; import BaseComponent from '@primevue/core/basecomponent';
import FormFieldStyle from '@primevue/forms/field/style'; import FormFieldStyle from '@primevue/forms/formfield/style';
export default { export default {
name: 'BaseFormField', name: 'BaseFormField',

View File

@ -1,16 +1,14 @@
/** /**
* *
* FormField is a helper component that provides validation and tracking for form fields. * FormField is a helper component that provides validation and tracking for form fields.
* It is a helper component for the Form component.
* *
* [Live Demo](https://www.primevue.org/form/) * [Live Demo](https://www.primevue.org/form/)
* *
* @module formfield * @module formfield
* @todo Add more documentation
*/ */
import type { DefineComponent, DesignToken, EmitFn, PassThrough } from '@primevue/core'; import type { DefineComponent, DesignToken, EmitFn, PassThrough } from '@primevue/core';
import type { ComponentHooks } from '@primevue/core/basecomponent'; import type { ComponentHooks } from '@primevue/core/basecomponent';
import { VNode } from 'vue'; import { Component, VNode } from 'vue';
/** /**
* From primevue/passthrough/index.d.ts * From primevue/passthrough/index.d.ts
@ -78,49 +76,88 @@ export interface FormFieldPassThroughAttributes {
*/ */
export interface FormFieldResolverOptions { export interface FormFieldResolverOptions {
/** /**
* The values of the form fields. * The value of the form field.
*/ */
values: Record<string, any>; value: any;
/** /**
* The names of the form fields. * The name of the form field.
*/ */
names: string[] | undefined; name: string | undefined;
} }
/** /**
* Submit events * Defines valid properties in Form component.
*/ */
export interface FormFieldSubmitEvent { export interface FormFieldProps {
/** /**
* The original DOM event. * A function that resolves validation logic.
* @param {FormResolverOptions} e - Resolver options
*/ */
originalEvent: Event; resolver?: (e: FormFieldResolverOptions) => any | undefined;
/** /**
* The form values. * The initial value for the form field.
*/ */
values: Record<string, any>; initialValue?: any;
/** /**
* The form state. * Whether to validate the form field when the value change.
*/ */
states: Record<string, FormFieldState>; validateOnValueUpdate?: boolean | undefined;
/** /**
* Whether the form is valid. * Whether to validate the form field when it loses focus (on blur).
*/ */
valid: boolean; validateOnBlur?: boolean | undefined;
/** /**
* The form errors. * Whether to validate the form field immediately after the form is mounted.
*/ */
errors: any[]; validateOnMount?: boolean | undefined;
/** /**
* Resets the form. * Whether to validate the form field when the form is submitted.
*/ */
reset: () => void; validateOnSubmit?: boolean | undefined;
/**
* Use to change the HTML tag of root element.
* @defaultValue DIV
*/
as?: string | Component | undefined;
/**
* When enabled, it changes the default rendered element for the one passed as a child element.
* @defaultValue false
*/
asChild?: boolean | 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<FormFieldPassThroughOptions>;
/**
* 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;
} }
/** /**
* The state of a form field. * Defines valid slots in Form component.
*/ */
export interface FormFieldState { export interface FormFieldSlots {
/**
* Default content slot.
* @param {Object} scope - default slot's params.
*/
default: (scope: {
/**
* @todo
*/
props: any;
/** /**
* The value of the form field. * The value of the form field.
*/ */
@ -159,110 +196,20 @@ export interface FormFieldState {
* @defaultValue [] * @defaultValue []
*/ */
errors: any[]; errors: any[];
}
/**
* Defines valid properties in Form component.
*/
export interface FormFieldProps {
/**
* A function that resolves validation logic.
* @param {FormResolverOptions} e - Resolver options
*/
resolver?: (e: FormFieldResolverOptions) => 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<FormFieldPassThroughOptions>;
/**
* 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[]; }) => VNode[];
} }
/** /**
* Defines valid emits in Form component. * Defines valid emits in Form component.
*/ */
export interface FormEmitsOptions { export interface FormFieldEmitsOptions {}
/**
* Emitted when the form is submitted.
* @param {Event} event - Original DOM event.
*/
submit: (event: Event) => void;
}
export declare type FormEmits = EmitFn<FormEmitsOptions>; export declare type FormEmits = EmitFn<FormFieldEmitsOptions>;
/** /**
* **PrimeVue - Form** * **PrimeVue - FormField**
* *
* _Form provides validation functionality and manages form state._ * _FormField is a helper component that provides validation and tracking for form fields._
* *
* [Live Demo](https://www.primevue.org/form/) * [Live Demo](https://www.primevue.org/form/)
* --- --- * --- ---
@ -271,11 +218,11 @@ export declare type FormEmits = EmitFn<FormEmitsOptions>;
* @group Component * @group Component
* *
*/ */
declare const FormField: DefineComponent<FormFieldProps, FormSlots, FormEmits>; declare const FormField: DefineComponent<FormFieldProps, FormFieldSlots, FormEmits>;
declare module 'vue' { declare module 'vue' {
export interface GlobalComponents { export interface GlobalComponents {
FormField: DefineComponent<FormFieldProps, FormSlots, FormEmits>; FormField: DefineComponent<FormFieldProps, FormFieldSlots, FormEmits>;
} }
} }

View File

@ -1,5 +1,11 @@
// Form
export * from '@primevue/forms/form';
export { default as Form } from '@primevue/forms/form'; export { default as Form } from '@primevue/forms/form';
export * from '@primevue/forms/form/style';
export { default as FormStyle } from '@primevue/forms/form/style'; export { default as FormStyle } from '@primevue/forms/form/style';
// FormField
export * from '@primevue/forms/formfield';
export { default as FormField } from '@primevue/forms/formfield'; export { default as FormField } from '@primevue/forms/formfield';
export * from '@primevue/forms/formfield/style';
export { default as FormFieldStyle } from '@primevue/forms/formfield/style'; export { default as FormFieldStyle } from '@primevue/forms/formfield/style';

View File

@ -1,5 +1,11 @@
// Form
export * from '@primevue/forms/form';
export { default as Form } from '@primevue/forms/form'; export { default as Form } from '@primevue/forms/form';
export * from '@primevue/forms/form/style';
export { default as FormStyle } from '@primevue/forms/form/style'; export { default as FormStyle } from '@primevue/forms/form/style';
// FormField
export * from '@primevue/forms/formfield';
export { default as FormField } from '@primevue/forms/formfield'; export { default as FormField } from '@primevue/forms/formfield';
export * from '@primevue/forms/formfield/style';
export { default as FormFieldStyle } from '@primevue/forms/formfield/style'; export { default as FormFieldStyle } from '@primevue/forms/formfield/style';

View File

@ -1 +1 @@
export * from '@primeuix/form/resolvers'; export * from '@primeuix/forms/resolvers';

View File

@ -1 +1 @@
export * from '@primeuix/form/resolvers'; export * from '@primeuix/forms/resolvers';

View File

@ -1,5 +1,5 @@
import { isArray, resolve } from '@primeuix/utils'; import { isArray, isNotEmpty, mergeKeys, resolve } from '@primeuix/utils';
import { computed, mergeProps, nextTick, onMounted, reactive, toValue, watch } from 'vue'; import { computed, getCurrentInstance, mergeProps, nextTick, onMounted, reactive, toValue, watch } from 'vue';
function tryOnMounted(fn, sync = true) { function tryOnMounted(fn, sync = true) {
if (getCurrentInstance()) onMounted(fn); if (getCurrentInstance()) onMounted(fn);
@ -34,10 +34,10 @@ export const useForm = (options = {}) => {
const validateOn = async (option, defaultValue) => { const validateOn = async (option, defaultValue) => {
let results = {}; let results = {};
isArray(options[option]) ? options[option].forEach(async (field) => (results = await validate(field))) : (options[option] ?? defaultValue) && (results = await validate()); isArray(options[option]) ? (results = await validate(options[option])) : (options[option] ?? defaultValue) && (results = await validate());
const field = Object.keys(fields).find((field) => fields[field]?.options?.[option]); const fieldArr = Object.keys(fields).filter((field) => fields[field]?.options?.[option]) || [];
field && (results = await validate(field)); isNotEmpty(fieldArr) && (results = await validate(fieldArr));
return results; return results;
}; };
@ -113,16 +113,25 @@ export const useForm = (options = {}) => {
{ names: [], values: {} } { names: [], values: {} }
); );
const result = (await options.resolver?.(resolverOptions)) ?? {}; let result = (await options.resolver?.(resolverOptions)) ?? {};
result.errors ??= {}; result.errors ??= {};
const flattenFields = [field].flat();
for (const [fieldName, fieldInst] of Object.entries(fields)) { for (const [fieldName, fieldInst] of Object.entries(fields)) {
if (flattenFields.includes(fieldName) || !field) {
const fieldResolver = fieldInst.options?.resolver; const fieldResolver = fieldInst.options?.resolver;
fieldResolver && (result.errors[fieldName] = await fieldResolver({ value: fieldInst.states.value, name: fieldName })?.errors); if (fieldResolver) {
const fieldValue = fieldInst.states.value;
const fieldResult = (await fieldResolver({ values: fieldValue, value: fieldValue, name: fieldName })) ?? {};
isArray(fieldResult.errors) && (fieldResult.errors = { [fieldName]: fieldResult.errors });
result = mergeKeys(result, fieldResult);
}
if (fieldName === field || !field) {
const errors = result.errors[fieldName] ?? []; const errors = result.errors[fieldName] ?? [];
//const value = result.values?.[fieldName] ?? states[sField].value; //const value = result.values?.[fieldName] ?? states[sField].value;