UNPKG

svelte-formup

Version:
660 lines (656 loc) 22.9 kB
/** Callback to inform of a value updates. */ declare type Subscriber<T> = (value: T) => void; /** Unsubscribes from value updates. */ declare type Unsubscriber = () => void; /** Callback to update a value. */ declare type Updater<T> = (value: T) => T; /** Cleanup logic callback. */ declare type Invalidator<T> = (value?: T) => void; /** * Svelte readable store contract. */ interface Readable<T> { /** * Subscribe on value changes. * @param run - subscription callback * @param invalidate - cleanup callback * @returns function to unsubscribe */ subscribe(run: Subscriber<T>, invalidate?: Invalidator<T>): Unsubscriber; } /** * Svelte writable store contract. */ interface Writable<T> extends Readable<T> { /** * Set value and inform subscribers. * @param value - to set */ set(value: T): void; /** * Update value using callback and inform subscribers. * @param updater - callback */ update(updater: Updater<T>): void; } interface SvelteActionResult<P> { update?: (parameters?: P) => void; destroy?: () => void; } /** * A [svelte action](https://svelte.dev/docs#use_action) to be applied on a element */ declare type SvelteAction<P> = (node: Element, parameters?: P) => SvelteActionResult<P>; /** * A DOM event. Common events are: * * - [change](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/change_event) * - [input](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/input_event) * - [focus](https://developer.mozilla.org/en-US/docs/Web/API/Element/focus_event) * - [blur](https://developer.mozilla.org/en-US/docs/Web/API/Element/blur_event) */ declare type EventName = keyof GlobalEventHandlersEventMap | string; /** * Options for {@link FormupSchema.validate} and {@link FormupSchema.validateAt}. */ interface ValidateOptions<Values = Record<string, unknown>, State = Record<string, unknown>> { /** * Only validate the input, and skip and coercion or transformation. * * @defaultvalue true */ strict?: boolean; /** * Return from validation methods on the first error rather than after all validations run. * * @defaultvalue true */ abortEarly?: boolean; /** * Remove unspecified keys from objects. * * @defaultvalue true */ stripUnknown?: boolean; /** * When false validations will not descend into nested schema (relevant for objects or arrays). * * @defaultvalue true */ recursive?: boolean; /** * Any context needed for validating schema conditions. */ context: ValidateContext<Values, State>; } /** * Type of {@link ValidateOptions.context}. */ interface ValidateContext<Values = Record<string, unknown>, State = Record<string, unknown>> { /** * The formup context. */ formup: FormupContext<Values, State>; /** * To be notified if the validation is longer needed. */ signal: AbortSignal; /** * The event that caused the validation. */ event?: Event; } /** * A [yup](https://www.npmjs.com/package/yup) like schema to perform validation. */ interface FormupSchema<Values = Record<string, unknown>, State = Record<string, unknown>> { /** * Returns the value (a cast value if `options.strict` is false) if the value is valid, * and throws the errors otherwise. * * This method is asynchronous and returns a Promise object, that is fulfilled with the value, or rejected with a ValidationError. * * @param value - the data to validate * @param options - an object hash containing any schema options you may want to override (or specify for the first time). * @throws ValidationError */ validate(value: unknown, options?: ValidateOptions<Values, State>): Promise<Values>; /** * Validate a deeply nested path within the schema. Similar to how reach works, but uses the resulting schema as the subject for validation. * * @param path - to validate * @param value - the root value relative to the starting schema, not the value at the nested path. * @param options - an object hash containing any schema options you may want to override (or specify for the first time). * @throws ValidationError */ validateAt(path: string, value: Values, options?: ValidateOptions<Values, State>): Promise<Values>; } /** * CSS class name mapping used {@link FormupContext.validity}. * * These can be overriden using {@link FormupOptions.classes} when invoking {@link formup} * or {@link FormupContext.validity}. * * - on form: `.is-{dirty,pristine,error,validating,submitting,submitted}` * - on form elements (controls & fieldsets): * - `.is-{dirty,pristine,error,success,validating}` * - `:valid` & `:invalid` * - other elements: `has-{dirty,pristine,error,success,validating}` * * - dirty: field or one child dirty - `$dirty.has(field)` * - pristine: field or all childs pristine - `!$dirty.has(field)` * - error: field or one child - `$dirty.has(field) && !$validating.has(field) && $errors.has(field)` * - success: field or all child - `$dirty.has(field) && !$validating.has(field) && !\$errors.has(field)` * - validating: `$validating.has(field)` */ interface ValidityCSSClasses { /** * Set on the form if it is submitting. * @defaultvalue `"is-submitting"` */ readonly ['is-submitting']?: string; /** * Set on the form if it is has been submitted. * @defaultvalue `"is-submitted"` */ readonly ['is-submitted']?: string; /** * Set on the element if it or any of its children is dirty. * @defaultvalue `"is-dirty"` */ readonly ['is-dirty']?: string; /** * Set on the element if it or all its children is pristine. * @defaultvalue `"is-pristine"` */ readonly ['is-pristine']?: string; /** * Set on the element if it or any of its children: `$dirty.has(field) && !$validating.has(field) && $errors.has(field)` * @defaultvalue `"is-error"` */ readonly ['is-error']?: string; /** * Set on the element if it or all of its children: `$dirty.has(field) && !$validating.has(field) && !$errors.has(field)` * @defaultvalue `"is-success"` */ readonly ['is-success']?: string; /** * Set on the element if it or any of its children is validating. * @defaultvalue `"is-validating"` */ readonly ['is-validating']?: string; /** * Set on non form element if it or any of its children is dirty. * @defaultvalue `"has-dirty"` */ readonly ['has-dirty']?: string; /** * Set on non form element if it or all its children is pristine. * @defaultvalue `"has-pristine"` */ readonly ['has-pristine']?: string; /** * Set on non form element if any of its children * @defaultvalue `"has-error"` */ readonly ['has-error']?: string; /** * Set on non form element if all of its children * @defaultvalue `"has-success"` */ readonly ['has-success']?: string; /** * Set on non form element if it or any of its children is validating. * @defaultvalue `"has-validating"` */ readonly ['has-validating']?: string; } /** * Provides to all form properties. */ interface FormupContext<Values = Record<string, unknown>, State = Record<string, unknown>> { /** * A [yup](https://www.npmjs.com/package/yup) like schema to perform validation. */ readonly schema: FormupSchema<Values, State>; /** * The form values as a svelte store. * * ```html * <input id="email" bind:value="{$values.email}" /> * ``` */ readonly values: Writable<Partial<NonNullable<Values>>>; /** * A top-level status object that you can use to represent form state that can't otherwise be expressed/stored with other methods. * * This is useful for capturing and passing through API responses to your inner component. */ readonly state: Writable<State>; /** * Whole form error, not associated with any field */ readonly formError: Readable<ValidationError | undefined>; /** * The form errors keyed by field path as a svelte store. * * If a validate function is provided to a field, then when it is called this map will be modified. * * ```html * {#if $errors.has(email)} $errors.get(email).message {/if} * ``` */ readonly errors: Readable<ReadonlyMap<string, ValidationError>>; /** * The dirty fields by path as a svelte store. * * This allows to show errors conditionally if the user has already visited that field. * * ```html * {#if $dirty.has(email) && $errors.has(email)} $dirty.get(email).message {/if} * ``` */ readonly dirty: Readable<ReadonlySet<string>>; /** * The currently validating fields by path as a svelte store. * * This allows to show a spinner if a field is validated. * * ```html * {#if $validating.has(email)}<Spinner />{/if} * ``` */ readonly validating: Readable<ReadonlySet<string>>; /** * The valid, meaning dirty and not validating and no error, fields by path as a svelte store. */ readonly valid: Readable<ReadonlySet<string>>; /** * The invalid, meaning dirty and not validating and error, fields by path as a svelte store. * * ```html * {#if $invalid.has(email)} $invalid.get(email).message {/if} * ``` */ readonly invalid: Readable<ReadonlyMap<string, ValidationError>>; /** * Determines if the whole form is validating (most likely because of a submit). * * This does not reflect individual field validation triggered by validateAt. * * When this becames `true` the {@link ValidityCSSClasses.validating} CSS class is added to the form. */ readonly isValidating: Readable<boolean>; /** * Determines if the form is submitting (most likely because of a submit). * * When this becames `true` the {@link ValidityCSSClasses.submitting} CSS class is added to the form. */ readonly isSubmitting: Readable<boolean>; /** * Determins if the form has been succesfully submitted. * * When this becames `true` the {@link ValidityCSSClasses.submitted} CSS class is added to the form. */ readonly isSubmitted: Readable<boolean>; /** * Number of times the form was submitted. * * Resetted to zero after a succesful {@link FormupContext.submit} or a {@link FormupContext.reset}. */ readonly submitCount: Readable<number>; /** * Boolean that is true when form is pristine. * * A form is pristine when it has not been touched && no values have been entered in any field. * * When this becames `true` the {@link ValidityCSSClasses.pristine} CSS class is added to the form. */ readonly isPristine: Readable<boolean>; /** * Boolean that is true when pristine is false * * When this becames `true` the {@link ValidityCSSClasses.dirty} CSS class is added to the form. */ readonly isDirty: Readable<boolean>; /** * Determines if the whole form is valid. * * When this becames `true` the {@link ValidityCSSClasses.valid} CSS class is added to the form. */ readonly isError: Readable<boolean>; /** * This function will submit the form and trigger some lifecycle events. * * 1. abort all active field validation * 1. call {@link FormupSchema.validate}. * 2. call {@link FormupOptions.onSubmit} if the form is valid. * * @remarks * This function can be called manually however it is also called if you * have a `<button type='submit'>` within the `<form>`. * * @remarks * Repeated invocation while there is an active submit have no effect (eg are ignored). */ readonly submit: (event?: Event) => Promise<void>; /** * Function that will reset the form to its initial state. * * This will abort all active field validation, reset all stores and call {@link FormupOptions.onReset}. * * This may have no effect (eg is ignored) if there is an active submit. */ readonly reset: (event?: Event) => void; /** * Set the form error manually. * * If `error` is falsey means deleting the path from the store. * @param error - to set */ readonly setFormError: (error?: ValidationError | undefined | null | false) => void; /** * Set the error message of a field imperatively. * * @param path - should match the key of errors you wish to update. Useful for creating custom input error handlers. * @param error - to set; falsey means deleting the path from the store. */ readonly setErrorAt: (path: string, error?: ValidationError | undefined | null | false) => void; /** * Set the dirty state of a field imperatively. * * @param path - should match the key of dirty you wish to update. Useful for creating custom input handlers. * @param dirty - to set; falsey means deleting the path from the store. */ readonly setDirtyAt: (path: string, dirty?: boolean) => void; /** * Set the validating state of a field imperatively. * * @param path - should match the key of validating you wish to update. Useful for creating custom input handlers. * @param validating - to set; falsey means deleting the path from the store. */ readonly setValidatingAt: (path: string, validating?: boolean) => void; /** * Imperatively call field's validate function if specified for given field. */ readonly validateAt: (path: string, options?: ValidateAtOptions) => void; /** * A [svelte action](https://svelte.dev/docs#use_action) to validate the element and all its form children it is applied to. * * ```html * <script> * import { formup } from 'svelte-formup' * * const { validate } = formup(options) * </script> * * <form use:validate> * <!-- .... ---> * * <input use:validate={{ on: 'input' }}> * * <!-- .... ---> * </form> * ``` * * @remarks * The {@link FormupContext.validity} action is applied automatically on that node. */ readonly validate: SvelteAction<undefined | string | ValidateActionOptions>; /** * A [svelte action](https://svelte.dev/docs#use_action) to update the validity state of * the element and all its form children it is applied to. * * That means updating `setCustomValidity`, `aria-invalid` and the {@link ValidityCSSClasses}. * * ```html * <script> * import { formup } from 'svelte-formup' * * const { validate, validity } = formup(options) * </script> * * <form use:validate> * <fieldset use:validity> * <!-- .... ---> * </fieldset> * </form> * ``` */ readonly validity: SvelteAction<undefined | string | ValiditiyActionOptions>; /** * Which events should trigger a validation. */ readonly validateOn: readonly EventName[]; /** * Which events should mark a field as dirty. */ readonly dirtyOn: readonly EventName[]; /** * Timeout in milliseconds after which a validation should start. */ readonly debounce: number; /** * The used CSS class overrides. */ readonly classes: ValidityCSSClasses; /** * Sets the `autocomplete` on the form. */ readonly autocomplete: string; } /** * Options for {@link FormupContext.validateAt}. */ interface ValidateAtOptions { /** * Timeout in milliseconds after which a validation should start. * * @defaultvalue {@link FormupContext.debounce} */ debounce?: number; } /** * Options for {@link FormupContext.validate}. */ interface ValidateActionOptions { /** * What field to validate. * * ```html * <input use:validate={{ at: 'email' }}> * ``` * * If `at` is not provided and the element is not a form it defaults to: * * - the [data attribute](https://developer.mozilla.org/en-US/docs/Learn/HTML/Howto/Use_data_attributes) `pathAt` * - the `name` attribute * - the path of the element referenced by the `for` attribute * - the `id` attribute * * If the only option is `at` it can be used directly: * * ```html * <input use:validate={'email'}> * ``` * * If no field has been found it validates itself and all its children. */ at?: string | string[]; /** * Override which events should trigger a validation and mark a field as dirty. */ on?: EventName | EventName[]; /** * Override which events should trigger a validation. * * ```html * <input use:validate={{ validateOn: ['input', 'change'] }}> * * <input use:validate={{ validateOn: 'blur' }}> * ``` * * @defaultvalue {@link ValidateActionOptions.on}, {@link FormupContext.validateOn} */ validateOn?: EventName | EventName[]; /** * Override which events should mark a field as dirty. * @defaultvalue {@link ValidateActionOptions.on}, {@link FormupContext.dirtyOn} */ dirtyOn?: EventName | EventName[]; /** * Timeout in milliseconds after which a validation should start. * @defaultvalue {@link FormupContext.debounce} */ debounce?: number; } /** * Options for {@link FormupContext.validity}. */ interface ValiditiyActionOptions { /** * For which field to track the validity status. * * If the element is a form it uses `isValid` and `isDirty` stores to determines the validity. * * ```html * <input use:validity={{ at: 'email' }}> * ``` * * If `at` is not provided and the element is not a form it defaults to: * * - the [data attribute](https://developer.mozilla.org/en-US/docs/Learn/HTML/Howto/Use_data_attributes) `pathAt` * - the `name` attribute * - the path of the element referenced by the `for` attribute * - the `id` attribute * * If the only option is `at` it can be used directly: * * ```html * <input use:validity={'email'}> * ``` * * If no field has been found it updates the validity for itself and all its children. */ at?: string | string[]; /** * Allow to override the used CSS classes. */ classes?: Partial<ValidityCSSClasses>; } /** * Represents a validation error. */ interface ValidationError extends Error { name: string; message: string; value?: unknown; /** * A string, indicating where there error was thrown. path is empty at the root level. */ path?: string; type?: unknown; /** * An array of error messages */ errors?: string[]; /** * In the case of aggregate errors, inner is an array of ValidationErrors throw earlier in the validation chain. */ inner?: ValidationError[]; params?: Record<string, unknown>; } //# sourceMappingURL=index.d.ts.map /** * Returns the FormupContext. * * ```html * <script> * import { getFormupContext } from 'svelte-formup' * const context = getFormupContext() * </script> * ``` */ declare const getFormupContext: <Values = Record<string, unknown>, State = Record<string, unknown>>() => FormupContext<Values, State>; interface FormupOptions<Values = Record<string, unknown>, State = Record<string, unknown>> { /** * A [yup](https://www.npmjs.com/package/yup) like schema to perform validation. * * The object does not have to be yup schema. This allows custom validation without using yup. */ schema: FormupSchema<Values>; /** * A function that gets called when form is submitted successfully. The function receives the values as a parameter. * * @throws An thrown error is passed to {@link FormupContext.setFormError}. */ onSubmit?: (values: Values, context: FormupContext<Values, State>, event?: Event & { submitter?: HTMLButtonElement | HTMLInputElement; }) => void | Promise<void>; /** * A function that gets called when form is resetted. */ onReset?: (context: FormupContext<Values, State>, event?: Event) => void; /** * A function called to initialize the form values on creation and reset. The default returns an empty object. * * @defaultvalue `() => Object.create(null)` */ getInitialValues?: (event?: Event) => Partial<NonNullable<Values>>; /** * Use this option to run validations each time after {@link FormupOptions.getInitialValues} has been called. * * @defaultvalue `false` */ validateInitialValues?: boolean; /** * A top-level status object that you can use to represent form state that can't otherwise be expressed/stored with other methods. * * This is useful for capturing and passing through API responses to your inner component. * * @defaultvalue `Object.create(null)` */ state?: State; /** * Which events should trigger a validation. * @defaultvalue `"change"` */ validateOn?: EventName | EventName[]; /** * Which events should mark a field as {@link FormupContext.dirty}. * @defaultvalue {@link FormupOptions.validateOn} */ dirtyOn?: EventName | EventName[]; /** * Timeout in milliseconds after which field level validation should start. * * If platform is Node.JS this defaults to `0`. * @defaultvalue `100` */ debounce?: number; /** * Allow to override the used CSS classes. */ classes?: ValidityCSSClasses; /** * Set the `autocomplete` attribute on the form. */ autocomplete?: string; } /** * Creates and registers a new {@link FormupContext} using the options and returns it. * * ```html * <script> * import { formup } from 'svelte-formup' * * const context = formup(options) * </script> * ``` * * @param options - to use */ declare const formup: <Values = Record<string, unknown>, State = Record<string, unknown>>({ schema, onSubmit, onReset, getInitialValues, validateInitialValues, state, validateOn, dirtyOn, debounce, classes, autocomplete, }: FormupOptions<Values, State>) => FormupContext<Values, State>; //# sourceMappingURL=svelte-formup.d.ts.map export { EventName, FormupContext, FormupOptions, FormupSchema, Invalidator, Readable, Subscriber, SvelteAction, SvelteActionResult, Unsubscriber, Updater, ValidateActionOptions, ValidateAtOptions, ValidateContext, ValidateOptions, ValidationError, ValiditiyActionOptions, ValidityCSSClasses, Writable, formup, getFormupContext }; //# sourceMappingURL=svelte-formup.d.ts.map