Every export documented here comes from @vef-framework-react/form-editor's package entry point (import { ... } from "@vef-framework-react/form-editor") — no internal module paths. For narrative explanations, see Overview, Embedding, Schema, and Linkage.
| Export | Shape |
|---|
FormEditor | Pre-composed component. Object.assign'd with .Provider, .Shell, .Workspace, .Stage, .Toolbar, .Palette, .Properties — see Embedding. |
FormEditorProvider (= FormEditor.Provider) | Context root: store + registries + preview runtime. Props: FormEditorProviderProps. |
FormEditorShell (= FormEditor.Shell) | Chrome frame (layout measurement, device context, drag-drop, shortcuts). Props: FormEditorShellProps (children only). |
FormEditorWorkspace (= FormEditor.Workspace) | Palette / stage / properties band. Props: { children: ReactNode }. |
FormEditorStage (= FormEditor.Stage) | Canvas column. No props. |
FormEditorToolbar (= FormEditor.Toolbar) | Default toolbar. Props: ToolbarProps. |
Extends FormEditorProviderProps (below) plus:
| Prop | Type | Notes |
|---|
brand | ToolbarBrand ({ name?, tag?, icon? }) | Toolbar brand mark; defaults to the framework's own name/glyph. |
onPublish | (schema: FormSchema) => void | Runs a validation pass first (errors block, warnings ask for confirmation); publish button is not rendered when omitted. |
publishText | string | Defaults to "发布". Ignored when onPublish is omitted. |
publishLoading | boolean | Loading state on the publish button. Ignored when onPublish is omitted. |
| Prop | Type | Notes |
|---|
initialSchema | FormSchema | Read once, on mount. |
registries | Partial<DeviceRegistries> | Per-device registry override; an omitted device falls back to registry, then the built-in defaults. Read once, on mount. |
registry | FormFieldRegistry | Shorthand for one registry shared by both devices. Read once, on mount. |
evaluators | LinkageEvaluators | Forwarded to the live preview — see Linkage. |
dataSourceResolver | DataSourceResolver | Forwarded to the live preview — see Embedding. |
evaluationContext | EvaluationContext | Forwarded to the live preview — see Linkage. |
contextSources | LinkageContextSource[] | Design-time pick list for the condition builder — see Linkage. |
onSchemaChange | (schema: FormSchema) => void | Fires on every committed edit. |
apiRef | Ref<FormEditorApi> | See below. |
children | ReactNode | — |
| Member | Signature |
|---|
getSchema | () => FormSchema |
setSchema | (schema: FormSchema) => void — import semantics; resets the undo timeline |
undo / redo | () => void |
canUndo / canRedo | () => boolean |
selectNode | (nodeId: string | null) => void |
setDevice | (device: EditorDeviceMode) => void |
setViewMode | (mode: EditorViewMode) => void |
| Type | Shape |
|---|
ToolbarBrand | { name?: string; tag?: string; icon?: ReactNode } |
ToolbarProps | { brand?: ToolbarBrand; onPublish?: (schema: FormSchema) => void; publishText?: string; publishLoading?: boolean } |
| Export | Shape |
|---|
FormRenderer | Runtime component. Props: FormRendererProps. |
| Prop | Type | Notes |
|---|
schema | FormSchema | Required. |
device | PresentationDevice ("pc" | "mobile") | Default "pc". |
defaultValues | Record<string, unknown> | — |
disabled | boolean | — |
fieldPermissions | Record<string, FieldPermission> | Server-resolved clamp — see Linkage. |
evaluators | LinkageEvaluators | See Linkage. |
dataSourceResolver | DataSourceResolver | See Embedding. |
evaluationContext | EvaluationContext | See Linkage. |
onSubmit | (values: Record<string, unknown>) => void | Promise<void> | Runs after schema-driven validation passes. |
apiRef | Ref<FormRendererApi> | See below. |
containOverlays | boolean | Default false. Mobile-only; pins overlay pickers to the renderer's own box. |
| Member | Signature |
|---|
submit | () => Promise<void> — same validation pipeline as a schema submit button |
reset | () => void |
getValues | () => Record<string, unknown> — the RAW live form state, including hidden fields' seeded defaults and read-only (non-writable-clamped) values |
getSubmitValues | () => Record<string, unknown> — exactly what the submit pipeline would deliver to onSubmit right now: the live values run through the same payload filtering (effectively-hidden fields dropped, non-writable-clamped keys excluded, subforms recursed per row), without running validation or the submit lifecycle. Added in v2.10.0 alongside the field-permission clamp |
Field registries
| Export | Signature |
|---|
createDefaultRegistry | () => FormFieldRegistry — PC registry: the built-in field/container definitions + the antd container chrome |
createDefaultMobileRegistry | () => FormFieldRegistry — antd-mobile registry: mobileFieldDefinitions + the antd-mobile container chrome |
registerDefaults | (registry: FormFieldRegistry) => void — applies the built-in field set + PC chrome to an existing registry |
DeviceRegistries (type) | { pc: FormFieldRegistry; mobile: FormFieldRegistry } |
| Member | Signature | Notes |
|---|
register | (definition: FieldDefinition) => void | Keyed by definition.config.type. |
unregister | (type: string) => boolean | — |
get | (type: string) => FieldDefinition | undefined | — |
has | (type: string) => boolean | — |
list | () => FieldDefinition[] | — |
registerPropertyEntry | (type: EntryType, component: FC<EntryComponentProps>) => void | Design-time property-panel renderer. |
getPropertyEntry | (type: EntryType) => FC<EntryComponentProps> | undefined | Falls back to the panel's statically-imported built-ins when undefined. |
setContainerChrome | (chrome: ContainerChromeSet) => void | — |
getContainerChrome | () => ContainerChromeSet | Throws if none installed. |
subscribe | (listener: () => void) => () => void | Notified on every mutation. |
getRevision | () => number | Monotonic; for useSyncExternalStore snapshots. |
Property entries
Built-in property-entry renderer components (each FC<EntryComponentProps>), resolvable by EntryType:
EntryType | Component |
|---|
"text" | TextEntry |
"key" | KeyEntry |
"number" | NumberEntry |
"checkbox" | CheckboxEntry |
"select" | SelectEntry |
"icon" | IconEntry |
"options-editor" | OptionsSourceEntry |
"linkage-rules" | LinkageRulesEntry |
EntryComponentProps: { entry: PropertyEntry; field: FormField; schema: FormSchema; onChange: (value: unknown) => void }. PropertyEntryTypeMap is the open interface EntryType is keyed from — augment it (declare module) to register a new entry kind alongside registerPropertyEntry.
Definition builders
| Export | Signature |
|---|
defineFieldDefinition | <TField extends FormField, TValue = unknown>(definition: { config: FieldDefinitionConfig; Component: FC<FieldComponentProps<TField, TValue>>; properties: PropertiesDescriptor }) => FieldDefinition |
defineContainerDefinition | (definition: { config: FieldDefinitionConfig }) => FieldDefinition — no Component slot (containers render structurally) |
definePropertyEntry | <TField extends FormField, TValue>(entry: PropertyEntry<TField, TValue>) => PropertyEntry — pins the entry's field/value types at its declaration site |
FieldDefinitionConfig
| Field | Type | Notes |
|---|
type | Block["type"] | Discriminator. |
name | string | Palette display name. |
group | FieldGroup ("basic-input" | "selection" | "date-file" | "container" | "action" | "presentation") | Palette category. |
keyed | boolean | Whether the field needs a unique data-binding key. |
icon | DynamicIconName | Optional; palette card / properties header icon. |
iconUrl | string | Optional. |
create | () => FieldCreateResult | Factory returning type-specific defaults; id / key are engine-minted. |
FieldComponentProps<TField, TValue>
| Field | Type |
|---|
field | TField |
value | TValue |
onChange | (value: TValue) => void |
errors | string[] | undefined |
domId | string |
disabled | boolean | undefined |
required | boolean | undefined |
labelPosition | LabelPosition | undefined |
FieldCreateResult = DistributedOmit<Block, "id" | "key">. FieldDefinition = { config: FieldDefinitionConfig; Component?: FC<FieldComponentProps>; properties: PropertiesDescriptor }.
PropertyEntry / PropertiesDescriptor
| Type | Shape |
|---|
PropertyEntry<TField, TValue> | { id, label, type: EntryType, options?: PropertyEntryOption[], visible?: (field: TField) => boolean, readOnly?, description?, placeholder?, read: (field: TField) => TValue, write: (field: TField, value: TValue) => TField } |
PropertyEntryOption | { value: string; label: string } |
PropertyGroup | { id: string; label: string; tab?: PropertyTabId; entries: PropertyEntry[] } |
PropertiesDescriptor | PropertyGroup[] |
PropertyTabId | "props" | "validation" | "linkage" | "layout" |
Editor store
| Export | Signature |
|---|
FormEditorStoreProvider | Store context provider (installed by FormEditorProvider) |
useFormEditorStore | <T>(selector: (state: FormEditorStoreState) => T) => T |
useFormEditorStoreApi | () => FormEditorStoreApi (UnboundStore<FormEditorStoreState>) |
useCurrentLayer | () => PresentationLayer — the active device's presentation |
isPaletteVisible | (state: Pick<FormEditorStoreState, "viewMode">) => boolean |
FormEditorStoreState is the store's full state + actions — most consumers read it through useFormEditorStore(selector) rather than the whole shape. Notable pieces:
| Member | Type | Notes |
|---|
schema | FormSchema | — |
selectedId | string | null | — |
viewMode | EditorViewMode ("edit" | "preview" | "json") | — |
device | EditorDeviceMode (= PresentationDevice) | — |
formConfigOpen / formConfigTab | boolean / FormConfigTabId | FormConfigTabId = "outline" | "form" | "variables" | "dataSources" | "linkage" |
past / future | HistoryEntry[] | HistoryEntry = { schema: FormSchema; device: EditorDeviceMode } |
insertField, moveNode, removeNode, duplicateNode | actions | Structural edits, each one checkpointed undo step. |
setSpan, setFlex, setColumnWidth, setStackSlot | actions | Layout edits, coalesced per node. |
editField(args, options?: EditCoalesceOptions), updateBlock(args, options?) | actions | EditCoalesceOptions = { coalesceKey?: string } — consecutive edits sharing a key fold into one undo step. |
setFieldKey | action | Sanitizes, dedupes, and renames every reference to the old key. |
patchSchema | (patch: FormSchemaPatch) => void | FormSchemaPatch = Partial<Pick<FormSchema, "dataSources" | "id" | "linkage" | "variables">> & { gap?: GapScale } |
renameVariable, removeVariable, removeDataSource | actions | Rename/remove + reference cleanup. |
undo, redo, canUndo, canRedo | actions | — |
Device and registry hooks
| Export | Signature | Notes |
|---|
DeviceProvider | (props: { device: PresentationDevice; children }) => ReactElement | Sets the active device for registry resolution. |
RegistryProvider | (props: { registries: DeviceRegistries; children }) => ReactElement | — |
useDeviceRegistries | () => DeviceRegistries | Throws without an ancestor RegistryProvider. |
useFieldRegistry | () => FormFieldRegistry | The active device's registry; re-renders on registration changes. |
useMobileScopeContainer | () => () => HTMLElement | getContainer callback for antd-mobile overlays — the containing phone-frame element when one is present (design-time preview), else document.body (real mobile runtime). |
Rendering parts
| Export | Props |
|---|
FieldShell | { children, domId, errors?, helperText?, label, labelPosition?, required? } — shared label/control/helper/error layout. |
Label | { children, htmlFor?, position?: LabelPosition, required?, title? } |
Data sources
| Export | Signature |
|---|
DataSourceProvider | (props: { children, dataSources?: FormDataSource[], resolver?: DataSourceResolver, versions?: Record<string, number> }) => ReactElement — installed by FormEditor and FormRenderer. |
useFieldOptions | (source: FieldOptionSource | undefined) => { options: FieldOption[]; loading: boolean; error: boolean } |
DataSourceResolver (type) | { resolve: (request: RemoteDataSourceRequest, mapping?: RemoteOptionMapping) => Promise<FieldOption[]> } |
noopDataSourceResolver | DataSourceResolver whose resolve always returns [] — the default. |
RemoteDataSourceRequest (type) | { resource: string; action: string; version?: string; params?: Record<string, unknown> } |
RemoteOptionMapping (type) | { labelKey?: string; valueKey?: string; disabledKey?: string; descriptionKey?: string } — all default to "label" / "value". |
Mobile
| Export | Shape |
|---|
mobileFieldDefinitions | FieldDefinition[] — antd-mobile leaf renderers (every built-in leaf type except code-editor) plus the structural containers, reusing each PC field's config / properties. |
useMobileScopeContainer | See Device and registry hooks. |
Schema types
See Schema for the narrative model. Top-level and supporting types:
| Export | Shape |
|---|
FormSchema | { id, version: 2, variables?, dataSources?, linkage?, presentations: { pc: PresentationLayer; mobile?: PresentationLayer } } |
PresentationLayer | { gap?: GapScale; children: Block[] } |
RuntimeSchema | PresentationLayer & { id: string; variables?, dataSources?, linkage? } — produced by toRuntimeSchema |
Block | FormField | ContainerNode |
FormField | FormFieldTypeMap[keyof FormFieldTypeMap] — see Leaf field interfaces |
FormFieldTypeMap | Open interface (module-augmentable) mapping type → field interface |
KeyedFormField | Extract<FormField, KeyedNode> |
ContainerNode | SectionNode | TabsNode | SubformNode | FlexNode | GridNode (closed set) |
CONTAINER_TYPES | Array<ContainerNode["type"]> |
KeyedNode | { key: string; columnType?: ColumnDataType } |
KeyedNodeUnion | Extract<Block, KeyedNode> |
Validatable | { validate?: { required?, minLength?, maxLength?, min?, max?, pattern?, message? } } |
PresentationDevice | "pc" | "mobile" |
LabelPosition | "top" | "left" | "right" |
GapScale | "small" | "medium" | "large" |
GAP_SCALES | readonly GapScale[] |
DEFAULT_GAP_SCALE | "medium" |
ROW_COLS | 24 |
FlexSlot | { grow?: number; shrink?: number; basis?: string } |
CssLength | { value: number; unit: "px" | "%" } |
StackSlot | { width?: CssLength; minWidth?: CssLength; maxWidth?: CssLength; align?: "start" | "center" | "end" } |
ColumnDataType | "string" | "text" | "integer" | "decimal" | "boolean" | "date" | "datetime" | "json" |
FieldOption | { label: string; value: string | number } |
FieldOptionSource | StaticOptionSource | RefOptionSource | RemoteOptionSource (kind: "static" | "ref" | "remote") |
FormDataSource | StaticDataSource | RemoteDataSource (each { id, name, kind, ... }) |
FormVariable | { id, name, type: "string" | "number" | "boolean" | "json", defaultValue? } |
Leaf field interfaces
Every leaf field extends the shared base: { id: string; label?: string; labelPosition?: LabelPosition; span?: number; flex?: FlexSlot; columnWidth?: number; stack?: StackSlot; linkage?: FieldLinkage }. Keyed fields additionally carry { key: string; columnType?: ColumnDataType }; Validatable fields additionally carry { validate?: {...} } (see Schema types). Only each interface's own fields are listed below.
| Interface | type | Extends | Own fields |
|---|
TextfieldField | "textfield" | Keyed, Validatable | placeholder?, helperText?, prefixIcon?: DynamicIconName, size?: "small"|"middle"|"large", allowClear?: boolean, maxLength?: number, inputType?: "text"|"password" |
CodeEditorField | "code-editor" | Keyed, Validatable | placeholder?, helperText?, language?: CodeEditorLanguage, minHeight?: number, maxHeight?: number, showLineNumbers?: boolean, showFoldGutter?: boolean, tabSize?: number |
NumberField | "number" | Keyed, Validatable | placeholder?, helperText?, min?: number, max?: number, step?: number, size?, prefix?: string, suffix?: string, precision?: number, controls?: boolean (default shown) |
SwitchField | "switch" | Keyed (not Validatable) | helperText?, onText?: string, offText?: string, size?: "default"|"small" |
SelectField | "select" | Keyed, Validatable | placeholder?, helperText?, allowClear?: boolean, size?, showSearch?: boolean, dataSource?: FieldOptionSource |
RadioField | "radio" | Keyed, Validatable | helperText?, dataSource?: FieldOptionSource, optionType?: "default"|"button", buttonStyle?: "outline"|"solid", direction?: "horizontal"|"vertical" |
CheckboxGroupField | "checkbox-group" | Keyed, Validatable | helperText?, dataSource?: FieldOptionSource, direction?: "horizontal"|"vertical" |
TextareaField | "textarea" | Keyed, Validatable | placeholder?, helperText?, rows?: number, autoSize?: boolean, maxLength?: number, showCount?: boolean, size?, allowClear?: boolean |
DateField | "date" | Keyed, Validatable | placeholder?, helperText?, allowClear?: boolean (default true) |
DatetimeField | "datetime" | Keyed, Validatable | placeholder?, helperText?, allowClear?: boolean (default true) |
DateRangeField | "daterange" | Keyed, Validatable | helperText?, allowClear?: boolean (default true) |
ButtonField | "button" | (non-keyed) | action?: "submit"|"reset"|"button" (default "submit"), buttonType?: "primary"|"default"|"dashed"|"text"|"link" (default "primary"), danger?: boolean, size?, block?: boolean, ghost?: boolean, shape?: "default"|"round", icon?: DynamicIconName |
DividerField | "divider" | (non-keyed) | title?: string, titlePlacement?: "left"|"center"|"right" (default "center"), dashed?: boolean |
AlertBlockField | "alert-block" | (non-keyed) | message?: string, description?: string, alertType?: "info"|"success"|"warning"|"error", showIcon?: boolean (default true), closable?: boolean, banner?: boolean |
ParagraphField | "paragraph" | (non-keyed) | text?: string, textType?: "secondary"|"success"|"warning"|"danger", strong?: boolean, italic?: boolean |
Container node interfaces
Every container extends the shared block base (id, span?, flex?, columnWidth?, stack?, linkage?).
| Interface | type | Own fields |
|---|
CardSection / CollapseSection (SectionNode) | "section" | variant: "card" | "collapse", title?: string, gap?: GapScale, children: Block[] — CollapseSection adds defaultCollapsed?: boolean |
TabsNode | "tabs" | gap?: GapScale, tabs: TabItem[] — TabItem = { id: string; label: string; children: Block[] } |
StackSubform / TableSubform (SubformNode) | "subform" | Keyed. variant: "stack" | "table", label?: string, template: Block[], minRows?: number, maxRows?: number, addLabel?: string — StackSubform adds gap?: GapScale; TableSubform adds size?: "small" | "middle" | "large" |
FlexNode | "flex" | children: Block[], direction?: "row" | "column" (default "row"), justify?: FlexJustify, align?: FlexAlign, wrap?: boolean (default false), gap?: number |
GridNode | "grid" | children: Block[], columns?: number (default 2), gap?: number, rowGap?: number (default = gap) |
FlexJustify = "start" | "center" | "end" | "between" | "around". FlexAlign = "start" | "center" | "end" | "stretch".
Field and container definition exports
One FieldDefinition constant per built-in type, registered by createDefaultRegistry/createDefaultMobileRegistry — see Schema → Leaf fields and Schema → Containers for the type / group mapping.
textfieldDefinition, codeEditorDefinition, numberFieldDefinition, switchFieldDefinition, selectFieldDefinition, radioFieldDefinition, checkboxGroupFieldDefinition, textareaFieldDefinition, dateFieldDefinition, datetimeFieldDefinition, dateRangeFieldDefinition, buttonDefinition, sectionDefinition, tabsDefinition, subformDefinition, flexDefinition, gridDefinition, dividerDefinition, alertBlockDefinition, paragraphDefinition.
Schema engine functions
| Export | Signature | Purpose |
|---|
createEmptySchema | () => FormSchema | Fresh document, empty pc presentation. |
createId | (prefix: IdPrefix) => string | Mint a prefixed cuid2 node id. |
generateUniqueKey | (layer, baseKey, scope?) => string | Scope-unique key allocation. |
nextUniqueKey | (used: Set<string>, baseKey: string) => string | Same, against a pre-collected set. |
sanitizeKey | (key: string) => string | Strip non-word characters. |
isKeyedNode | (node: Block) => node is KeyedNodeUnion | — |
isKeyedField | (field: FormField) => field is KeyedFormField | — |
isContainerNode | (node: Block) => node is ContainerNode | — |
isLeafField | (node: Block) => node is FormField | — |
walkNodes | (layer, visit: (node: Block, scope: readonly string[]) => void) => void | Depth-first, every node. scope is the chain of enclosing subform keys ([] at the root). |
walkFields | (layer, visit: (field: FormField, scope: readonly string[]) => void) => void | Depth-first, leaf fields only. |
findNode / findField | (layer, id: string) => Block | undefined / FormField | undefined | — |
findParentContainer | (layer, blockId: string) => ContainerNode | undefined | — |
insertBlock | (layer, block: Block, target: DropTarget) => PresentationLayer | — |
moveBlock | (layer, nodeId: string, target: DropTarget) => PresentationLayer | — |
cloneBlock | (block: Block, allocateKey: ((base: string) => string) | null) => Block | — |
setSpan | (layer, blockId: string, span: number | undefined) => PresentationLayer | — |
setFlex | (layer, blockId: string, flex: FlexSlot | undefined) => PresentationLayer | — |
editField | (layer, fieldId: string, updater: (field: FormField) => FormField) => PresentationLayer | — |
removeBlock | (layer, id: string) => PresentationLayer | — |
updateNode | (layer, id: string, updater: (node: Block) => Block) => PresentationLayer | — |
currentLayer | (schema: FormSchema, device: PresentationDevice) => PresentationLayer | — |
emptyLayer | () => PresentationLayer | — |
resolvePresentation | (schema, device) => PresentationLayer | undefined | — |
withPresentation | (schema, device, layer) => FormSchema | — |
toRuntimeSchema | (schema, device) => RuntimeSchema | undefined | — |
validateSchema | (candidate: unknown, registries: DeviceRegistries) => ValidateSchemaResult | ValidateSchemaResult = { valid: boolean; schema?: FormSchema; issues: ValidationIssue[] } |
inferColumnType | (field: FormField) => ColumnDataType | — |
toColumnDefinitions | (schema: FormSchema) => ColumnDefinition[] | ColumnDefinition = { key, columnType, maxLength?, precision? } |
toFormFieldDefinitions | (schema: FormSchema) => FormFieldDefinition[] | FormFieldDefinition = { key, kind: FieldKind, label, options? }; FieldKind = "input" | "textarea" | "select" | "number" | "date" | "upload" |
convertPresentation | (pc: PresentationLayer, rules: ConversionRegistry, target: FormFieldRegistry) => { layer: PresentationLayer; report: ConversionReport } | ConversionReport = { convertedCount: number; dropped: Array<{ sourceType, reason, label? }> } |
createDefaultConversionRules | () => ConversionRegistry | — |
ConversionRegistry (class) | .register({ type, convert }): this / .convert(block, ctx): BlockConversion | BlockConversion = { status: "converted"; block } | { status: "unwrapped"; blocks } | { status: "dropped"; sourceType; reason } |
BlockConversionRule | { type: Block["type"]; convert: (source: Block, ctx: ConversionContext) => BlockConversion } | The shape .register() takes — one rule per source block type. |
ConversionContext | { target: FormFieldRegistry; convertBlocks: (blocks: Block[]) => Block[] } | Passed to convert; convertBlocks recurses a container's body through the same rule set. |
DropTarget = { kind: "beside"; anchorId: string; side: "before" \| "after" } | { kind: "container"; containerId: string; tabIndex?: number } | { kind: "append" }.
Linkage
See Linkage for the full model. Exported surface:
| Export | Signature / Shape |
|---|
validateLinkageSchema | (layer: PresentationLayer, formLinkage?: FieldLinkage) => LinkageValidationResult ({ issues: ValidationIssue[] }) |
resolveLinkageEvaluators | (overrides?: LinkageEvaluators) => Required<LinkageEvaluators> |
defaultEvaluateExpression | (source, values, context?) => boolean |
defaultEvaluateAssignExpression | (source, values, context?) => unknown |
defaultEvaluateScriptAction | (source, values, context?) => LinkageScriptResult | void |
isStateAction / isEffectAction | (action: FieldLinkageAction) => action is StateAction / EffectAction |
isFieldEventTriggerKind | (kind: LinkageTriggerKind) => boolean |
getFieldPermission | (permissions: Record<string, FieldPermission> | undefined, key: string) => FieldPermission | undefined — own-property-guarded lookup (a field keyed like an Object prototype member never reads a prototype slot). Added in v2.10.0 |
isWritableFieldPermission | (permission: FieldPermission | undefined) => boolean — true for undefined (no clamp entry), "editable", and "required"; false for "visible" / "hidden". Added in v2.10.0 |
STATE_ACTION_TYPES | readonly StateActionType[] (show, hide, enable, disable, require, optional, assign, script) |
EFFECT_ACTION_TYPES | readonly EffectActionType[] (set_field, set_variable, refresh_data_source, alert, api_call, navigate, submit, reset) |
LINKAGE_ACTION_TYPES | readonly LinkageActionType[] — the union of the two above |
KEYED_ONLY_ACTIONS | ReadonlySet<LinkageActionType> (require, optional, assign) |
FIELD_TRIGGER_KINDS / FORM_TRIGGER_KINDS | readonly LinkageTriggerKind[] — legal triggers per rule scope |
FIELD_EVENT_TRIGGER_KINDS | readonly LinkageTriggerKind[] (change, focus, blur, click) |
LINKAGE_OPERATORS | readonly LinkageOperator[] (eq, ne, gt, lt, gte, lte, contains, empty, notEmpty) |
ALERT_LEVELS | readonly LinkageAlertLevel[] (info, success, warning, error) |
Types: FieldLinkage, FieldLinkageRule, FieldLinkageAction, FieldLinkageDefaults, LinkageTrigger, LinkageTriggerKind, LinkageCondition (LinkageConditionLeaf | LinkageConditionGroup | LinkageConditionExpression), LinkageOperator, StateAction, StateActionType, EffectAction, EffectActionType, LinkageActionType, LinkageActionValue, LinkageAlertLevel, ConditionRetrigger ("edge" | "always"), LinkageScriptResult, LinkageEvaluators, EvaluationContext, LinkageContextSource, EffectDispatchContext, FieldPermission ("hidden" | "visible" | "editable" | "required").
Validation
| Export | Signature |
|---|
formatIssueMessage | (code: ValidationIssueCode, params?) => string — every issue message flows through this |
ValidationIssue (type) | { path: string; code: ValidationIssueCode; severity: ValidationSeverity; message: string; ruleId?: string } |
ValidationIssueCode (type) | Closed union of several dozen stable machine-readable codes (schema envelope, block tree, layout, linkage, …) |
ValidationSeverity (type) | "error" | "warning" |
message is pre-rendered Chinese product copy; program against code and path, which are stable across locales.
Chrome
ContainerChromeSet is the per-device set of presentational shells for structural containers, installed via FormFieldRegistry.setContainerChrome:
| Member | Props type |
|---|
Section | SectionChromeProps = { title?: ReactNode; variant?: "card" | "collapse"; defaultCollapsed?: boolean; children: ReactNode } |
Tabs | TabsChromeProps = { items: ChromeTabItem[]; activeKey?: string; onChange?: (key: string) => void } — ChromeTabItem = { key: string; label: ReactNode; children: ReactNode } |
Subform | SubformChromeProps = { title?: ReactNode; children: ReactNode } |
SubformRow | SubformRowChromeProps = { removeControl?: ReactNode; children: ReactNode } |
AddButton | AddControlChromeProps = { label: ReactNode; onClick: () => void } |
RemoveButton | RemoveControlChromeProps = { onClick: () => void } |
flex and grid are pure CSS layout and use no chrome — identical on both devices.