Components/MotionForm

MotionForm

Fifteen controlled form interactions composed from existing TuffEx inputs, selection, file and range controls.

Since 0.6.3BETA

This component doc is in progress

This page is still being migrated. Demos and API details may change.

Usage

All fifteen effects

The demo renders every original form variant, with a filter for focused exploration. All values are editable, file selection uses files from your device, and validation uses the entered value. The submit example calculates a real local SHA-256 digest through Web Crypto: it neither uploads nor claims a server submission.

Fifteen real interactions

Input, choose, paste, drop, validate and calculate. Disable controls or motion to inspect the settled states.

Loading demo...

Best Practices

  • Keep values, validation messages and business status in the caller. validate and submit request work; the component never creates a successful result or clears an error after a delay.
  • For another rejection with the same error and status, change validationKey to replay the shake without remounting the input or losing its focus.
  • Supply options for radio, dropdown and chips. Chips are a selected list plus the existing keyboard-operated single Select for adding a choice; already selected options cannot be added twice.
  • Treat filesSelected as a selection event, not an upload-complete event. Each item retains its real File; upload it through your own application service and drive status from that result.
  • Pass a meaningful label and localized labels. The label slot customizes the visible label and remains connected through aria-labelledby; OTP inputs additionally use the label prop in their individual digit names.
  • readonly applies to textual fields and OTP. Use disabled for selection, files, range, actions, or a wholly blocked form.
  • The default action uses nativeType="button" and emits submit. Choose nativeType="submit" only when the containing native form owns submission; do not perform the same operation from both the click request and the form's submit event.

API Reference

Props

NameTypeDefaultDescription
variantMotionFormVariant'floating-label-input'One of the fifteen original IDs below.
modelValueMotionFormValueunsetControlled value; the expected shape depends on the variant.
labelstring''Visible/accessibility label; falls back to labels.field.
placeholderstring''Placeholder for text/select controls. Hidden behind an empty, resting floating label.
descriptionstring''Helper text in the live message region; error/status takes precedence.
size'xs' | 'sm' | 'md' | 'lg''md'Control height, choice size and radius. Actions map xs to the existing Button's sm.
disabledbooleanfalseBlocks edits, selections, removal, reveal and action requests.
readonlybooleanfalseMakes text, textarea and OTP read-only; password reveal remains available.
requiredbooleanfalseRequired marker and accessibility state; native text fields also receive required.
status'default' | 'loading' | 'success' | 'error''default'Caller-controlled operation/validation status. Loading blocks action requests.
errorstring''Truthy values derive error state and provide the displayed validation message.
validationKeystring | numberunsetReplays an unchanged error when the key changes.
labelsPartial<MotionFormLabels>unsetLocalizes static text, digit names and removal labels.
optionsMotionFormOption[][]Existing TxSelectOption shape: { value: string | number, label: string, disabled?: boolean, icon?: string, description?: string }.
inputType'text' | 'email' | 'number' | 'date''text'Textual control type. Password variant manages its own text/password type.
autocompletestringunsetNative textual autocomplete. OTP instead uses one-time-code on the first box.
passwordVisiblebooleanunsetOptional controlled reveal state, paired with update:passwordVisible; otherwise reveal is local.
rowsnumber2Minimum textarea rows.
maxRowsnumber8Maximum auto-grow rows, after which the field scrolls. Never below rows.
maxLengthnumberunsetNative text/textarea length limit. OTP has a fixed four-digit contract.
acceptstring'*/*'Picker/drop type filtering, delegated to FileUploader.
multiplebooleantrueMultiple-file selection; false replaces the previous selected file.
maxFilesnumber10Maximum selected files, delegated to FileUploader.
minnumber0Slider minimum.
maxnumber100Slider maximum.
stepnumber1Slider step.
formatValue(value: number) => stringunsetSlider value/tooltip formatter.
nativeType'button' | 'submit''button'Submit variant's native Button type.
motionbooleantrueEnables motion subject to shared visibility, KeepAlive and reduced-motion gating.

Model and variants

import type { FileUploaderFile } from '@talex-touch/tuffex/file-uploader'
import type { MotionFormProps, MotionFormVariant } from '@talex-touch/tuffex/motion-form'

type MotionFormValue = string | number | boolean | (string | number)[] | FileUploaderFile[]
// FileUploaderFile: { id, name, size, type, file: File }
// MOTION_FORM_VARIANTS exports the full readonly variant tuple.

The module exports MotionFormProps, MotionFormEmits, MotionFormSlots, MotionFormLabels, MotionFormStatus, MotionFormVariant, MotionFormOption, MotionFormValue and TxMotionFormInstance. Slot scopes are declared through Vue defineSlots as well as documented below.

Original IDvariantModelTrigger and distinct effect
frm1floating-label-inputstring or numberFocus/nonempty value lifts the label by 24px and scales it to 0.85.
frm2input-focus-glowstring or numberFocus expands the halo and glow; blur retracts it.
frm3password-togglestringReveal button switches type and springs its eye rotation/scale without changing the value.
frm4search-expandstringFocus expands 160px → 240px within available width; the search icon shifts.
frm5checkbox-drawbooleanCheckbox selection draws its SVG path in 200ms and pops the box to 1.15.
frm6radio-scalestring or numberOptions select one value; inner dot uses the source's 500/25 spring.
frm7error-shakestring or numbererror/error status/key runs −10, 10, −8, 8, −4, 4, 0px over 500ms.
frm8success-checkstring or numberValidate requests caller validation; successful status springs in a 300ms drawn check.
frm9select-dropdownstring or numberExisting Select opens with origin-aware 0.95 scale, offset and blur/fade.
frm10multi-select-chips(string | number)[]Selected chips spring in/out at scale 0; native remove buttons update the array.
frm11textarea-auto-growstringActual scroll height drives bounded, spring-animated field height.
frm12otp-inputstringFour numeric boxes auto-advance, distribute paste and spring-highlight focus.
frm13file-upload-dropzoneFileUploaderFile[]Real picker/drop selection; dragging pulses the ring and lifts the upload arrow.
frm14range-slidernumberExisting Slider handles pointer/keyboard, thumb spring and floating value tooltip.
frm15form-submit-buttonoptional MotionFormValueCaller-controlled default/loading/success/error morphs text, spinner and drawn check.

An OTP string contains at most four digits. Empty middle boxes can remain while editing locally; a genuinely external string replaces all four boxes. OTP accepts paste/autofill, Left/Right, Home/End, Backspace and Delete. otpComplete fires only when all four boxes contain digits, and may fire again for an edited complete code.

Labels

All defaults are exported as MOTION_FORM_DEFAULT_LABELS. labels accepts partial overrides.

KeyDefault/type
field'Field'
showPassword, hidePassword, capsLock'Show password', 'Hide password', 'CapsLock is on'
validate, validating, verified, validationError'Validate', 'Validating…', 'Verified', 'Validation failed'
submit, submitting, submitted, submitError'Submit', 'Submitting…', 'Submitted', 'Submission failed'
chooseFiles, dropFiles, fileHint'Choose files', 'Drop files here', 'or click to browse'
searchOptions, noOptions'Search options', 'No options'
otpDigit(index: number) => string; one-based default Digit N of 4
removeOption(label: string) => string; default Remove <label>
removeFile(name: string) => string; default Remove <name>

Events

EventPayloadMeaning
update:modelValueMotionFormValueUser requests a new controlled value.
changeMotionFormValueSame accepted user edit/selection.
update:passwordVisiblebooleanUser changed the password reveal state.
focus, blurFocusEventEnter/leave the component's focus boundary, not each internal focus movement.
searchstringCurrent search-expand text changed; does not issue a network request.
validateMotionFormValueRequests caller validation of the current value.
submit[value: MotionFormValue | undefined, event: MouseEvent]Requests caller work; does not modify status.
otpCompletestringAll four OTP boxes now contain digits.
filesSelectedFileUploaderFile[]Newly selected accepted files, not the entire accumulated model and not upload completion.
fileRemove{ id: string, value: FileUploaderFile[] }Removed file ID and resulting controlled list.

Slots

SlotScopeUse
labelnoneVisible field label; connected to controls through its stable ID.
prefix, suffixnoneInput affixes. Replacing suffix replaces the default password reveal button.
option{ option, selected }Radio/select option content.
chip{ option }Chip label content; built-in removal remains available.
file{ file, remove: () => void }File row content; built-in removal remains available.
submit{ status: MotionFormStatus, label: string }Submit label/content, alongside the state glyph.
status{ status, error, value }Content of the polite, atomic live message region.

Exposed

MethodBehavior
focus()Focuses the first available input, textarea or action control.
blur()Blurs the currently focused descendant.

CSS variables

Variablemd defaultRole
--tx-mf-height36pxBase input/action height; size classes set 26/30/36/42px.
--tx-mf-choice22pxCheckbox/radio mark size; size classes set 16/18/22/24px.
--tx-mf-radius12pxField radius; size classes set 8/10/12/12px.

Theme color comes from existing --tx-color-*, --tx-text-color-*, --tx-bg-color and --tx-border-color-* tokens. Motion duration/easing variables are generated internally by the shared spring resolver, not a second public animation API.

Overview

  • Existing Input, Select, Checkbox, Radio/RadioGroup, Textarea, FileUploader and Slider own their original editing, option, drag/drop and keyboard semantics. This component adds the effect choreography and typed controlled events rather than introducing another control stack.
  • Labels are adjacent to controls, never wrapping Select. Stable Vue useId values connect labels, helpers and OTP boxes. The password button is a named, pressed-state native button; chip/file removal is keyboard-operable.
  • Status/error content remains in a role="status" aria-live="polite" region. Error state is not a timed flash and focus is not reset to replay motion.
  • The shared useMotionActivity separates present (mounted, intersecting and document-visible) from motion active. Motion additionally respects the motion switch and reduced-motion preference. Slider receives active=present to suspend its observers, global listeners and ongoing motion when absent/KeepAlive-deactivated without disabling the control or changing its value; its decorative motion remains gated by the shared motion active. Reduced motion therefore does not shut down the native control's layout measurements or input handling. Text uses TxTextMorph, curves come from the existing liquid spring resolver, and textarea WAAPI is cancelled at suspension/unmount. No business timers are used.

Technologies

Source mapping

Fixed upstream: Amicro commit 43c29ce9cdd16459e3eab4992381b8d35b38776a, MIT, Copyright (c) 2026 SYED SUBHAN UDDIN. The Vue port changes framework/runtime, composes real existing controls, adds controlled outcomes, localized labels and lifecycle/accessibility handling.

CatalogImplementation in src/components/forms/AnimatedFormElement.tsx
src/data/formElements.ts:29-35 / frm152-79 floating label, 400/25 spring
36-42 / frm281-96 focus halo
43-49 / frm398-121 password reveal/eye transform
50-56 / frm4123-141 search width, 400/28 spring
57-63 / frm5143-182 checkbox path and pop
64-70 / frm6184-210 radio inner core, 500/25 spring
71-77 / frm7212-235 error shake trajectory
78-84 / frm8237-275 success check scale/path
85-91 / frm9277-324 dropdown scale/offset/fade
92-98 / frm10326-355 dismissible chip scale/fade
99-105 / frm11357-374 scroll-height textarea growth
106-112 / frm12375-401 four-box OTP and focus advance
113-119 / frm13403-415 dropzone/upload-arrow visual
120-126 / frm14417-432 real range value/tooltip
127-133 / frm15434-461 submit/loading/success visual

src/App.tsx was inspected in the fixed snapshot: it has no form branch or form import. Forms behavior comes from the implementation above, not a guessed inline App variant. Upstream's timed error clearing and fake submit success (31-46) are deliberately replaced by caller-owned error and status; the visual dropzone is connected to real FileUploader selection.

Local source: packages/tuffex/packages/components/src/motion-form/src/TxMotionForm.vue and src/types.ts; demo: apps/nexus/app/components/content/demos/MotionFormDemo.vue. Build/typecheck, real-browser traversal and lifecycle acceptance are run by the integration owner; this page does not claim unexecuted verification.

查看源码
packages/tuffex/packages/components/src/motion-form/index.ts