GrForm

  • ядро
  • Формы
  • API · 8 props

Берут, когда полей больше одного и они проверяются.

import { GrForm } from '@feugene/granularity/components/GrForm'

Обзор

Когда брать

  • полей больше одного и они проверяются — правила объявляются по имени поля, а не развешиваются руками;
  • поля зависят друг от друга — валидатор видит всю модель: совпадение паролей, «дата конца позже начала»;
  • отправка блокируется до валидности — вместе со скроллом и фокусом к первой ошибке;
  • форма блокируется целиком — disabled на форме гасит все контролы внутри.

Когда взять другое

НужноБерите
Поле одно и правил нетGrFormField + GrInput
Запросить одно значение окномGrPromptDialog
Только раскладка и подписи, проверка на сервереGrFormField

Контролы про форму не знают: оркестрация подключается через GrFormField по name. Поэтому любой существующий контрол попадает в валидацию без единой правки — и свой, написанный поверх контракта форм-контрола, тоже.

Playground контролы выводятся из API, а не пишутся руками

Сцена

Загружается…

0 изменено· состояние — в адресе

Примеры 3 у каждого — живое демо и его код

  • Правила, которые видят всю модель

    Правила объявлены по имени поля рядом с моделью, а validator получает всю модель — «отличается от текущего» и «совпадает с новым» становятся обычными правилами. submit приходит только от валидной формы, а invalid говорит, сколько полей её остановили, — отказ не выглядит кликом, который ничего не сделал.

    Change passwordOther devices are signed out after the change.

    12 characters or more

  • Форма редактирования со снимком

    Реквизиты приходят после монтирования, поэтому setSnapshot() вызывается, когда они на месте, — иначе «Отменить изменения» вернуло бы пустой объект. isDirty из слота включает «Сохранить» и «Отменить», resetFields возвращает к снимку, а disabled на форме гасит все контролы на время загрузки и сохранения.

  • Адрес, проверенный сервером

    validator может вернуть промис: пока идёт проверка занятости, поле занято и пишет «Проверяем…», а не держит ошибку о прежнем значении. validatingFields из слота перечисляет, что ещё проверяется, и «Создать» ждёт ответа, а не отправляет непроверенный адрес.

    Create a workspace

    Your team signs in here

API порождается из пакета · разойтись не может

Props

PropTypeпо умолчаниюОписание
modelобязательныйTModel—Реактивный объект данных формы. Поля адресуются по `name` (`GrFormField`), в т.ч. dot-path. Тип дженерик, а не `Record<string, unknown>`: моделью бывает объект чужой библиотеки (`useForm` Inertia, стор), у которого рядом с полями лежат собственные методы. Под словарь он не подходит, и потребителю пришлось бы приводить его через `as unknown as` в каждой форме.
rulesGrFormRules | undefinedundefinedПравила валидации по имени поля: `{ email: [{ required: true, type: 'email' }] }`.
validateOnBlurboolean | undefinedtrueВалидировать поле при потере фокуса.
validateOnChangeboolean | undefinedfalseВалидировать поле при каждом изменении значения.
scrollToErrorboolean | undefinedtrueСкроллить к первому невалидному полю после `validate()`.
scrollBehaviorScrollBehavior | undefined"smooth"Как прокручивать к полю — к первой ошибке и в `scrollToField`. По умолчанию `smooth`.
disabledboolean | undefinedfalseВыключить форму целиком — типично на время отправки. Доезжает до контролов через контекст поля, поэтому обходить их по одному не нужно.
reserveMessageboolean | undefinedfalseВсе поля формы держат место под строку сообщения: ошибки по submit не сдвигают форму, диалог и панель фиксированной высоты. Поле со своим `reserveMessage` решает за себя.

Слоты

SlotTypeОписание
default
{ validate: () => Promise<boolean>; errors: Record<string, s{ validate: () => Promise<boolean>; errors: Record<string, string | undefined>; isDirty: boolean; isValid: boolean; validatingFields: Set<string>; resetFields: (names?: string | string[] | undefined) => void; setSnapshot: (model?: Record<string, unknown> | undefined) => void; }
Поля формы. Слот-пропы повторяют публичный API инстанса: форма в шаблоне доступна без `ref`, а значит и без `nextTick` после монтирования.

События

EventСигнатураОписание
submit(e: "submit", model: TModel): voidФорма прошла валидацию по submit. Отдаёт `model`.
validate(e: "validate", name: string, valid: boolean, message: string | undefined): voidРезультат валидации одного поля.
invalid(e: "invalid", errors: Record<string, string>): voidSubmit не прошёл валидацию. Без этого события «форма невалидна» и «ничего не произошло» выглядят для потребителя одинаково.

Типы

Все экспортируемые типы подпути, по файлам, где они объявлены. Имена, дженерики и тела читаются из файлов деклараций пакета и разойтись не могут.

Импорт
import type { GrFormContext, GrFormFileRule, GrFormProps } from '@feugene/granularity/components/GrForm'

Файлdist/types/components/GrForm/config.d.ts

grFormConfigconst
const grFormConfig: import("@feugene/granum/contract").GranumComponentDescriptor<"GrForm">;

Файлdist/types/components/shared/formContext.d.ts

GrFormContextinterface
interface GrFormContext {
    /** Реактивная карта ошибок по имени поля. */
    errors: Ref<Record<string, string | undefined>>;
    /** Имена полей, у которых в правилах есть `required` (для маркера `*`). */
    requiredFields: ComputedRef<Set<string>>;
    /**
     * Поля, у которых прямо сейчас идёт асинхронная проверка. Необязательны:
     * тип публичный, и контекст строят руками (в тестах и в своих обёртках) —
     * старая форма контекста продолжает работать.
     */
    validatingFields?: ComputedRef<Set<string>>;
    /** Форма выключена целиком: значение видно, но ничего не редактируется. */
    disabled?: ComputedRef<boolean>;
    /** Поля держат место под строку сообщения, пока ошибки нет (`reserveMessage`). */
    reserveMessage?: ComputedRef<boolean>;
    /** Есть ли для поля правила (тогда `GrFormField` берёт ошибку из формы). */
    hasField: (name: string) => boolean;
useGrFormContextfunction

Возвращает контекст формы или null, если поле вне GrForm.

function useGrFormContext(): GrFormContext | null;

Файлdist/types/components/shared/formFileRule.d.ts

GrFormFileRuleinterface
interface GrFormFileRule {
    /** W3C `accept`: `'image/*,.pdf'`. */
    accept?: string;
    /** Белый список расширений (с точкой или без). */
    extensions?: string[];
    /** Белый список MIME-типов. */
    mimeTypes?: string[];
    /** Предел размера одного файла в мегабайтах. */
    maxSizeMb?: number;
    /** Предел размера одного файла в байтах. Задан вместе с `maxSizeMb` — берётся меньший. */
    maxSizeBytes?: number;
    /** Максимум файлов в наборе. */
    maxCount?: number;
    /** Суммарный размер набора в мегабайтах. */
    maxTotalSizeMb?: number;
    /** Свои валидаторы — тот же тип, что у `GrFormFile` и `v-dropzone`. Идут после встроенных. */
    validators?: FileValidator[];
}

Файлdist/types/components/GrForm/GrForm.vue.d.ts

GrFormPropsinterface

Публичный GR-примитив «Form» — оркестрация валидации над GrFormField. Модель формы (v-model-объект приложения) + декларативные rules по имени поля. Форма прогоняет правила, раскладывает ошибки по контексту, скроллит к первой ошибке и эмитит submit только при валидной форме. Императивный API — через template ref (validate / validateField / clearValidate / resetFields / scrollToField).

interface GrFormProps<TModel extends object = Record<string, unknown>> {
    /**
     * Реактивный объект данных формы. Поля адресуются по `name` (`GrFormField`),
     * в т.ч. dot-path.
     *
     * Тип дженерик, а не `Record<string, unknown>`: моделью бывает объект чужой
     * библиотеки (`useForm` Inertia, стор), у которого рядом с полями лежат
     * собственные методы. Под словарь он не подходит, и потребителю пришлось бы
     * приводить его через `as unknown as` в каждой форме.
     */
    model: TModel;
    /** Правила валидации по имени поля: `{ email: [{ required: true, type: 'email' }] }`. */
    rules?: GrFormRules;
    /** Валидировать поле при потере фокуса. */
    validateOnBlur?: boolean;
    /** Валидировать поле при каждом изменении значения. */
    validateOnChange?: boolean;
    /** Скроллить к первому невалидному полю после `validate()`. */
    scrollToError?: boolean;
    /** Как прокручивать к полю — к первой ошибке и в `scrollToField`. По умолчанию `smooth`. */
    scrollBehavior?: ScrollBehavior;
    /**
     * Выключить форму целиком — типично на время отправки. Доезжает до контролов
     * через контекст поля, поэтому обходить их по одному не нужно.
     */
    disabled?: boolean;
    /**
     * Все поля формы держат место под строку сообщения: ошибки по submit не
     * сдвигают форму, диалог и панель фиксированной высоты. Поле со своим
     * `reserveMessage` решает за себя.
     */
    reserveMessage?: boolean;
}
GrFormEmitsinterface
interface GrFormEmits<TModel extends object = Record<string, unknown>> {
    /** Форма прошла валидацию по submit. Отдаёт `model`. */
    (e: 'submit', model: TModel): void;
    /** Результат валидации одного поля. */
    (e: 'validate', name: string, valid: boolean, message: string | undefined): void;
    /**
     * Submit не прошёл валидацию. Без этого события «форма невалидна» и «ничего
     * не произошло» выглядят для потребителя одинаково.
     */
    (e: 'invalid', errors: Record<string, string>): void;
}

Файлdist/types/components/shared/formValidation.d.ts

GrFormTriggertype
type GrFormTrigger = 'blur' | 'change' | 'submit';
GrFormValidatorResulttype

Результат кастомного валидатора: true/void — ок; false — ошибка (дефолт-текст); строка — текст ошибки.

type GrFormValidatorResult = boolean | string | void;
GrFormRuleinterface
interface GrFormRule {
    /** Значение обязательно (не пустое). */
    required?: boolean;
    /** Готовый текст ошибки — перекрывает дефолтное сообщение любой проверки этого правила. */
    message?: string;
    /** Минимум: длина для строки/массива, значение для числа. */
    min?: number;
    /** Максимум: длина для строки/массива, значение для числа. */
    max?: number;
    /** Точная длина/значение. */
    len?: number;
    /** Регулярное выражение (проверяется по `String(value)`). */
    pattern?: RegExp;
    /** Встроенный тип-валидатор. */
    type?: 'email' | 'url';
    /** Ограничения для значения `File | File[]`: тип, размер, количество. */
    file?: GrFormFileRule;
    /** Кастомный (в т.ч. async) валидатор. Второй аргумент — весь `model`. */
    validator?: (value: unknown, model: Record<string, unknown>) => GrFormValidatorResult | Promise<GrFormValidatorResult>;
GrFormRulestype
type GrFormRules = Record<string, GrFormRule | GrFormRule[]>;
GrFormRuleFailuretype

Вид сработавшей проверки — ключ для дефолтного i18n-сообщения.

type GrFormRuleFailure = 'required' | 'min' | 'max' | 'len' | 'pattern' | 'email' | 'url' | 'file' | 'invalid';
GrFormMessageResolvertype
type GrFormMessageResolver = (kind: GrFormRuleFailure, rule: GrFormRule, params: Record<string, unknown>) => string;
/** Подпись `t()` из `useGranularityTranslations()`: ключ, фолбэк, параметры. */
export type GrFormMessageTranslate = (key: string, fallback: string, params?: Record<string, unknown>) => string;
/**
 * Дефолтный резолвер сообщений: `rule.message` сильнее всего, иначе ключ
 * `gr.form.<kind>` из локали, иначе английский фолбэк.
 *
 * Вынесен из `GrForm.vue` и экспортируется наружу не «на всякий случай»:
 * `runFieldRules` публичен, но без резолвера бесполезен — любой, кто хочет
 * прогнать те же правила вне формы, обязан был написать свой.
 */
export declare function createGrFormMessageResolver(t: GrFormMessageTranslate, locale?: () => string | undefined): GrFormMessageResolver;
/** Пустое значение: `null`/`undefined`/пустая (или пробельная) строка/пустой массив. */
export declare function isEmpty(value: unknown): boolean;
/** Достаёт значение из объекта по dot-path (`address.city`). */
export declare function getByPath(obj: Record<string, unknown>, path: string): unknown;
/** Записывает значение в объект по dot-path (создаёт промежуточные объекты). */
export declare function setByPath(obj: Record<string, unknown>, path: string, value: unknown): void;
/** Нормализует правило(а) поля в массив. */
export declare function toRuleArray(rules: GrFormRule | GrFormRule[] | undefined): GrFormRule[];
/** Правила поля, применимые для данного триггера (undefined = все). */
export declare function rulesForTrigger(rules: GrFormRule[], trigger?: GrFormTrigger): GrFormRule[];
/** Есть ли среди правил `required` (для маркера обязательности в поле). */
export declare function rulesRequired(rules: GrFormRule[]): boolean;
/**
 * Прогоняет правила по порядку, возвращает первое сообщение об ошибке или `undefined`.
 * Пустое необязательное значение проходит все проверки (кроме `required`).
 */
export declare function runFieldRules(value: unknown, rules: GrFormRule[], model: Record<string, unknown>, resolveMessage: GrFormMessageResolver): Promise<string | undefined>;
GrFormMessageTranslatetype

Подпись t() из useGranularityTranslations(): ключ, фолбэк, параметры.

type GrFormMessageTranslate = (key: string, fallback: string, params?: Record<string, unknown>) => string;
/**
 * Дефолтный резолвер сообщений: `rule.message` сильнее всего, иначе ключ
 * `gr.form.<kind>` из локали, иначе английский фолбэк.
 *
 * Вынесен из `GrForm.vue` и экспортируется наружу не «на всякий случай»:
 * `runFieldRules` публичен, но без резолвера бесполезен — любой, кто хочет
 * прогнать те же правила вне формы, обязан был написать свой.
 */
export declare function createGrFormMessageResolver(t: GrFormMessageTranslate, locale?: () => string | undefined): GrFormMessageResolver;
/** Пустое значение: `null`/`undefined`/пустая (или пробельная) строка/пустой массив. */
export declare function isEmpty(value: unknown): boolean;
/** Достаёт значение из объекта по dot-path (`address.city`). */
export declare function getByPath(obj: Record<string, unknown>, path: string): unknown;
/** Записывает значение в объект по dot-path (создаёт промежуточные объекты). */
export declare function setByPath(obj: Record<string, unknown>, path: string, value: unknown): void;
/** Нормализует правило(а) поля в массив. */
export declare function toRuleArray(rules: GrFormRule | GrFormRule[] | undefined): GrFormRule[];
/** Правила поля, применимые для данного триггера (undefined = все). */
export declare function rulesForTrigger(rules: GrFormRule[], trigger?: GrFormTrigger): GrFormRule[];
/** Есть ли среди правил `required` (для маркера обязательности в поле). */
export declare function rulesRequired(rules: GrFormRule[]): boolean;
/**
 * Прогоняет правила по порядку, возвращает первое сообщение об ошибке или `undefined`.
 * Пустое необязательное значение проходит все проверки (кроме `required`).
 */
export declare function runFieldRules(value: unknown, rules: GrFormRule[], model: Record<string, unknown>, resolveMessage: GrFormMessageResolver): Promise<string | undefined>;
createGrFormMessageResolverfunction

Дефолтный резолвер сообщений: rule.message сильнее всего, иначе ключ gr.form.<kind> из локали, иначе английский фолбэк. Вынесен из GrForm.vue и экспортируется наружу не «на всякий случай»: runFieldRules публичен, но без резолвера бесполезен — любой, кто хочет прогнать те же правила вне формы, обязан был написать свой.

function createGrFormMessageResolver(t: GrFormMessageTranslate, locale?: () => string | undefined): GrFormMessageResolver;
/** Пустое значение: `null`/`undefined`/пустая (или пробельная) строка/пустой массив. */
export declare function isEmpty(value: unknown): boolean;
/** Достаёт значение из объекта по dot-path (`address.city`). */
export declare function getByPath(obj: Record<string, unknown>, path: string): unknown;
/** Записывает значение в объект по dot-path (создаёт промежуточные объекты). */
export declare function setByPath(obj: Record<string, unknown>, path: string, value: unknown): void;
/** Нормализует правило(а) поля в массив. */
export declare function toRuleArray(rules: GrFormRule | GrFormRule[] | undefined): GrFormRule[];
/** Правила поля, применимые для данного триггера (undefined = все). */
export declare function rulesForTrigger(rules: GrFormRule[], trigger?: GrFormTrigger): GrFormRule[];
/** Есть ли среди правил `required` (для маркера обязательности в поле). */
export declare function rulesRequired(rules: GrFormRule[]): boolean;
/**
 * Прогоняет правила по порядку, возвращает первое сообщение об ошибке или `undefined`.
 * Пустое необязательное значение проходит все проверки (кроме `required`).
 */
export declare function runFieldRules(value: unknown, rules: GrFormRule[], model: Record<string, unknown>, resolveMessage: GrFormMessageResolver): Promise<string | undefined>;
isEmptyfunction

Пустое значение: null/undefined/пустая (или пробельная) строка/пустой массив.

function isEmpty(value: unknown): boolean;
getByPathfunction

Достаёт значение из объекта по dot-path (address.city).

function getByPath(obj: Record<string, unknown>, path: string): unknown;
setByPathfunction

Записывает значение в объект по dot-path (создаёт промежуточные объекты).

function setByPath(obj: Record<string, unknown>, path: string, value: unknown): void;
runFieldRulesfunction

Прогоняет правила по порядку, возвращает первое сообщение об ошибке или undefined. Пустое необязательное значение проходит все проверки (кроме required).

function runFieldRules(value: unknown, rules: GrFormRule[], model: Record<string, unknown>, resolveMessage: GrFormMessageResolver): Promise<string | undefined>;

Файлdist/types/components/GrForm/index.d.ts

GrFormInstancetype
type GrFormInstance = ComponentExposed<typeof GrFormComponent>;

Использование из прозы библиотеки

Обязательность приходит из двух мест

Поле обязательно, если у него есть правило required или у самого GrFormField стоит проп required. Оба источника участвуют и в маркере *, и в валидации: раньше второй рисовал звёздочку, но submit проходил с пустым полем.

Полю без правил в этом случае применяется неявное { required: true } — с тем же сообщением из локали, что и у явного правила.

Снимок и сброс

resetFields() возвращает модель к снимку. Снимок берётся при монтировании, поэтому форме редактирования нужно переснять его после загрузки данных:

ts
const form = ref<GrFormInstance>()

const data = await api.load()
Object.assign(model, data)
form.value?.setSnapshot()

Без этого «сброс» возвращал бы пустой объект, каким модель была до ответа сервера. resetFields(names?) умеет и точечный сброс; ключ, которого в снимке нет, удаляется, а не превращается в undefined. Исключение — методы: они в снимок не попадают по построению, и удалять их значило бы ломать модель (см. «Модель может быть чужой»).

isDirty сравнивает модель со снимком, isValid означает «известных ошибок нет» — до первого validate() это ещё не «форма валидна», а «никто не проверял». Оба доступны и через ref формы, и в слот-пропах.

Выключение формы

disabled у формы уезжает в контекст поля и дальше в контролы: «выключить всё на время отправки» больше не требует обходить контролы по одному. Проп контрола и проп поля складываются с ним по «или» — как это уже сделано с readonly.

Кнопка отправки обычно стоит под полями, а не в GrFormField, поэтому GrButton type="submit" читает disabled формы сам и выключается вместе с ней — второй раз «выключенную» форму не отправить. Остальные кнопки (type="button", type="reset") не трогаются: «Отмена» и «Сбросить» остаются рабочими. Выключенная форма игнорирует и само событие submit — Enter в поле и кнопку не из GrButton.

Без сдвигов раскладки

reserveMessage у формы держит место под строку сообщения у всех её полей: ошибки по submit не сдвигают форму, диалог и панель фиксированной высоты. Поле со своим reserveMessage решает за себя. Резерв — одна строка; несколько ошибок у поля растут ниже. Подробности — GrFormField.md.

vue
<template>
  <GrForm :model="model" :rules="rules" reserve-message>
    <GrFormField label="Получатель" name="name">
      <GrInput v-model="model.name" />
    </GrFormField>
  </GrForm>
</template>
Файлы правилом формы

Ограничения файла описываются там же, где остальные правила, — правилом file:

ts
const rules: GrFormRules = {
  contract: [{
    required: true,
    file: { accept: '.pdf,application/pdf', maxSizeMb: 1 },
  }],
}
КлючЧто ограничивает
acceptW3C-строка accept ('image/*,.pdf')
extensionsбелый список расширений
mimeTypesбелый список MIME-типов
maxSizeMb / maxSizeBytesразмер одного файла; заданы оба — применяется меньший
maxCountколичество файлов в наборе
maxTotalSizeMbсуммарный размер набора
validatorsсвои FileValidator, в том числе async — идут после встроенных

Своих проверок у правила нет ни одной: оно собирает те же валидаторы из ../file-validation.md, которые GrFormFile и v-dropzone запускают на выборе и на drop. Поэтому и текст ошибки один — он приходит от валидатора и локализуется его ключом gr.fileValidation.*, а не превращается в отдельное «некорректный файл». rule.message перекрывает его, как у любого другого правила.

Проверяются значения File и File[]; всё остальное правило пропускает — придумывать вердикт за строку оно не должно. Пустоту разбирает required.

Если проблемных файлов несколько, поле показывает первую — у поля формы одна строка ошибки, и файлы тут не исключение. Подробный разбор по каждому файлу остаётся за GrFormFile.

Сам GrFormFile при этом не меняется. Ограничения на поле (accept, limit, validators) — быстрая обратная связь: плохой файл не попадает в модель вообще. Правило формы — гарантия на submit: оно видно validate(), попадает в событие invalid и участвует в скролле к первой ошибке. Дублирования на экране не возникает: то, что поле не пустило в модель, правилу формы уже не показывают. Типичное разделение — ограничения в rules, а на поле accept как фильтр диалога выбора.

Асинхронные правила

Пока правило ходит на сервер, поле помечается aria-busy и показывает строку из локали (gr.form.validating) вместо старой ошибки: ошибка относилась к прежнему значению, и держать её на экране — врать про текущее.

Список полей в проверке доступен как validatingFields — в expose и в слот-пропах.

Перекрывающиеся прогоны одного поля разруливает счётчик поколений: применяется результат последнего запуска, а вытесненный прогон отдаёт вызвавшему вердикт вытеснившего, дождавшись его. Поэтому validate() и submit не могут проскочить по значению, которое ещё никто не проверил.

clearValidate() и resetFields() летящие проверки отменяют: их ответ относится к состоянию до сброса, в очищенную форму он не попадёт, и поле перестаёт считаться проверяемым сразу, не дожидаясь сервера.

Модель может быть чужой

model типизирован дженериком, а не словарём: моделью бывает объект чужой библиотеки — useForm Inertia, стор, — у которого рядом с полями лежат собственные методы. Он заходит как есть, без as unknown as, и submit отдаёт его тем же типом.

ts
const form = useForm({ email: '', password: '' })
vue
<GrForm :model="form" :rules="rules" @submit="form.post('/login')">

Форма адресует только имена, объявленные полями (GrFormField name), поэтому всё остальное в объекте её не касается. Два следствия, которые стоит знать заранее:

  • resetFields() возвращает значения полей и не трогает методы. Снимок строится клоном, а клон отбрасывает функции — без отдельной защиты «нет в снимке» означало бы «удалить», и сброс снёс бы с объекта post, reset, errors. Собственный сброс внешней формы (form.reset()) при этом остаётся вашим: он знает про свои служебные поля, а GrForm — нет;
  • isDirty считается по всему объекту. У внешней формы рядом с полями живут её собственные реактивные поля (processing, errors), и их изменение форма тоже увидит. Нужен признак «пользователь правил данные» — берите его у самой внешней формы.
Submit асинхронный

Правила бывают промисами, поэтому validate() всегда возвращает Promise, а submit эмитится после её разрешения. В тестах одного nextTick() не хватает:

ts
await wrapper.find('form').trigger('submit')
await flushPromises() // не nextTick: правила могли уйти на сервер
expect(onSubmit).toHaveBeenCalled()
События и API
  • submit(model) — только при валидной форме;
  • invalid(errors) — карта сообщений, когда submit не прошёл. Без него «форма невалидна» и «ничего не произошло» выглядят одинаково;
  • validate(name, valid, message) — результат по одному полю.

Императивно: validate(), validateField(name, trigger?), clearValidate(), resetFields(), setSnapshot(), scrollToField(name), плюс isDirty, isValid, validatingFields.

Тип для ref — GrFormInstance, а не InstanceType<typeof GrForm>: форма дженерик, а такой компонент компилируется в функцию, у которой конструктора нет.

ts
const form = ref<GrFormInstance>()
form.value?.resetFields()

Правила и их сообщения — ../file-validation.md для файлов и GrForm/validation.ts для остального; движок публичный (runFieldRules, createGrFormMessageResolver), и GrPromptDialog пользуется тем же.