# rn-form-fields URL: /docs/native/docyrus/form-fields Controlled DocyField inputs plus the DocyFieldDynamic IField adapter — render any Docyrus field definition with validation tokens, companions, uploads and pickers. ## Installation ```bash pnpm dlx @docyrus/cli add @docyrus/rn-form-fields ``` **Dependencies:** - [@shopify/flash-list](https://www.npmjs.com/package/@shopify/flash-list) - [@react-native-community/datetimepicker](https://www.npmjs.com/package/@react-native-community/datetimepicker) - [expo-document-picker (optional)](https://www.npmjs.com/package/expo-document-picker) - [expo-image-picker (optional)](https://www.npmjs.com/package/expo-image-picker) - [@react-native-community/geolocation (optional)](https://www.npmjs.com/package/@react-native-community/geolocation) - [@react-querybuilder/core](https://www.npmjs.com/package/@react-querybuilder/core) ## Overview Two layers, one package: - **`DocyField*` controlled inputs** — `value` / `onChange` components that work standalone (no form library). `DocyFieldBase` renders label, help text, errors and layout for all of them. - **`DocyFieldDynamic({ field })` — the IField adapter** (web `DynamicFormField` counterpart). Pass a Docyrus `IField` and it derives label (`field.name`), placeholder (`getFieldPlaceholder`), required / read-only, inline options (`field.enums`), validation tokens (`field.validations`), companion values (money currency, phone country, status sub-status / notes / follow-up) and every value conversion the native control needs (date strings ↔ `Date`, `[start, end]` ranges, expanded `{ id, name }` records → ids + seeded options). Desktop-only editors render a read-only value instead of nothing. Optional pickers (`expo-document-picker`, `expo-image-picker`) are loaded lazily — image / file fields offer only the sources that are installed. ## Usage ```tsx import { DocyFieldDynamic } from '@/components/docyrus-native/form-fields'; const field = { id: 'budget', slug: 'budget', name: 'Budget', type: 'field-money', validations: ['required', 'min:0'] }; setRecord(prev => ({ ...prev, budget: value }))} onCompanionChange={(key, value) => setRecord(prev => ({ ...prev, [key]: value }))} /> ``` ```tsx import { DocyFieldText, DocyFieldRelation } from '@/components/docyrus-native/form-fields'; createAccount(name)} onExpand={option => router.push(`/accounts/${option.id}`)} /> ``` ## DocyFieldDynamic Two modes: - **IField mode** — `DocyrusFormFieldProps` (below). - **Legacy mode** — `fieldType` + the controlled `DocyField*` props (`DocyFieldDynamicLegacyProps`). Kept for existing code. ### DocyrusFormFieldProps | Prop | Type | Default | Description | |------|------|---------|-------------| | `field` | `IField` | — | Field definition (required) | | `value` | `unknown` | — | Controlled value (API shape — ids, ISO strings, `StoredFileValue`, …) | | `onChange` | `(value: unknown) => void` | — | Called with the next value in the API shape | | `onBlur` / `onFocus` | `() => void` | — | Focus handlers | | `record` | `Record` | — | Companion values keyed by companion slug (`___currency`, `___country`, `___secondary`, …) — usually the whole record | | `onCompanionChange` | `(key: string, value: unknown) => void` | — | Companion change (key = full companion slug) | | `disabled` | `boolean` | — | Disable the input | | `required` | `boolean` | from `field.validations` | Label asterisk + required validation | | `readOnly` | `boolean` | from `field.readOnly` | Force read-only | | `error` | `string` | — | External error | | `description` | `string` | — | Help text below the field | | `label` | `string` | `field.name` | Label override | | `enumOptions` | `EnumOption[]` | `field.enums` | Options for select-based fields | | `appSlug` / `dataSourceSlug` | `string` | — | Dynamic enum loading context | | `onLoadMore` / `hasMore` | `() => void` / `boolean` | — | Picker pagination | | `onSearch` / `searching` | `(query) => void` / `boolean` | — | Remote search | | `onCreateRecord` | `(name: string) => Promise` | — | Relation: create from the search text | | `renderCreateForm` | `({ onCreated }) => ReactNode` | — | Relation: custom inline create form | | `itemTemplate` | `(option: EnumOption) => ReactNode` | — | Custom option rendering | | `variant` | `'dropdown' \| 'card'` | `'dropdown'` | Card list for select / radio / relation; `field-multiSelect` + `card` renders a checkbox group | | `columnCount` | `1 \| 2 \| 3 \| 4 \| 5` | — | Card columns (clamped to 2 on phones) | | `onImageUpload` / `onFileUpload` | `FieldUploadHandler` | — | `(file: NativeFile) => Promise` | | `onExpand` | `(option: EnumOption) => void` | — | Relation expand button | | `renderExpandContent` | `(option: EnumOption) => ReactNode` | — | Relation expand content (bottom sheet) | | `expandIcon` | `ReactNode` | — | Custom expand glyph | | `onComposeEmail` | `(email: string) => void` | — | Email compose button | | `onSendMessage` | `(phone: string) => void` | — | Phone send-message button | | `client` | `RestApiClient` | — | Email / phone fields: open [rn-email-compose-dialog](/docs/native/docyrus/email-compose-dialog) / [rn-instant-message-compose-dialog](/docs/native/docyrus/instant-message-compose-dialog). `onComposeEmail` / `onSendMessage` win when set | | `aiAssistant` | `boolean` | — | Reserved (not yet on native) | | `renderOptionsEditor` | `() => ReactNode` | — | Enum "Manage options" footer inside the picker sheet (`field-enum` / `field-systemEnum`; filled by `useDocyrusFormView({ enumEditor })`) | | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Field size | | `fieldVariant` | `'default' \| 'filled' \| 'outlined'` | `'default'` | Input chrome | | `labelAlignment` | `'top' \| 'left' \| 'right'` | `'top'` | Label placement | | `showLabel` | `boolean` | `true` | Render the label | | `clearable` | `boolean` | `true` | Clear affordance on optional pickers | | `autoOpen` | `boolean` | — | Open the picker on mount | | `onPickerClose` | `() => void` | — | Picker sheet closed | | `validationTokens` | `string[] \| null` | `field.validations` | Token list to enforce; `null` disables | | `validations` | `DocyFieldValidationRule[]` | — | Extra object rules | | `className` | `string` | — | Container class | ### Field type → native control | IFieldType | Control | |---|---| | `field-text` / `-textarea` / `-email` / `-url` / `-password` | `DocyFieldText` / `Textarea` / `Email` / `Url` / `Password` | | `field-code` | `DocyFieldCode` (single-line monospace) | | `field-codeEditor` / `-json` / `-jsonata` / `-handlebars` | `DocyFieldCode` (multiline, `json` parses) | | `field-markdown` | `DocyFieldMarkdown` (`SimpleMarkdownEditor`: toolbar + Write / Preview) | | `field-number` / `-percent` / `-money` / `-duration` / `-rating` | Number / Percent / Money (+ currency companion) / Duration (`options.editorOptions.increment/maxHours`) / Rating (`field.maxRating`) | | `field-checkbox` / `-switch` / `-slider` | Checkbox / Switch / Slider | | `field-phone` | Phone (`___country`, legacy `country_code`) | | `field-color` / `-icon` / `-currency` | Color / Icon / CurrencyCode | | `field-select` / `-enum` / `-systemEnum` | Select / Enum; `field.nested` → `DocyFieldTreeSelect` | | `field-radioGroup` / `-status` | RadioGroup (`RadioGroup` options, `variant: 'card'`, `columnCount` 1–5) / Status (+ sub-status, notes, follow-up) | | `field-userSelect` / `-userMultiSelect` / `-relation` | User pickers / Relation (itemMapping, nested, create, expand) | | `field-multiSelect` / `-tagSelect` | MultiSelect (`variant: 'card'` → CheckboxGroup) / TagSelect | | `field-date` / `-dateTime` / `-time` / `-dateRange` | Date (`YYYY-MM-DD`) / Datetime (ISO) / Time (`HH:mm`) / DateRange (`[start, end]`) | | `field-file` / `-image` / `-avatar` | File / Image (`options.gallery`, `options.maxImages`) / Avatar (`AvatarSelect`; reads + writes the three `field.avatarMapping` columns through `record` / `onCompanionChange`) | | `field-queryBuilder` | `DocyFieldQueryBuilder` (sheet with `QueryBuilderDocyrus`) | | `field-schemaRepeater` | `DocyFieldSchemaRepeater` (`options.schema`, `maxItems`, …) | | `field-taskList` / `-locationSelect` / `-approvalStatus` / `-upload` | TaskList / LocationSelect / ApprovalStatus / Upload | | `field-htmlEditor` / `-docEditor` / `-emailEditor` / `-jsonSchema` / `-adaptiveCard` / `-dsql` | `DocyFieldReadOnlyValue` + "edit on web" hint | | computed (`field-formula`, `-identity`, `-autonumber`, …) | `DocyFieldReadOnlyValue` | ## Base Props (DocyFieldBaseProps) | Prop | Type | Default | Description | |------|------|---------|-------------| | `name` | `string` | — | Field identifier (required) | | `label` | `string` | — | Label text | | `placeholder` | `string` | — | Placeholder | | `helpText` | `string` | — | Helper text below the field | | `required` | `boolean` | `false` | Asterisk + required validation (always enforced) | | `disabled` | `boolean` | `false` | Disable interaction | | `readOnly` | `boolean` | `false` | Prevent editing | | `hidden` | `boolean` | `false` | Render nothing | | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Size | | `variant` | `'default' \| 'filled' \| 'outlined'` | `'default'` | Input chrome | | `labelAlignment` | `'top' \| 'left' \| 'right'` | `'top'` | Label placement | | `showLabel` | `boolean` | `true` | Render the label | | `validations` | `DocyFieldValidationRule[]` | — | Object rules (`required`, `min`, `max`, `pattern`, `custom` async) | | `validationTokens` | `string[] \| null` | — | IField token DSL — see [Validation tokens](#validation-tokens) | | `clearable` | `boolean` | picker-dependent | Clear ("×") affordance when the value is set and the field is optional | | `autoOpen` | `boolean` | — | Open the picker sheet on mount (used by `EditableValue`) | | `onPickerClose` | `() => void` | — | Called when the picker sheet closes | | `error` | `string` | — | External error (shown immediately) | | `className` / `labelClassName` / `errorClassName` / `helpTextClassName` | `string` | — | Class overrides | ## Value Props (DocyFieldProps\) | Prop | Type | Description | |------|------|-------------| | `value` | `V` | Controlled value | | `onChange` | `(value: V) => void` | Change handler | | `onBlur` | `() => void` | Blur handler | | `onFocus` | `() => void` | Focus handler | ## Validation tokens `IField.validations` is a flat token list — `required`, `minLength:N`, `maxLength:N`, `pattern:RE`, `min:N`, `max:N` — enforced by the same `validateValidationTokens` module the web form engine uses (synced copy). Empty values only fail `required`; length bounds apply to strings and arrays; range bounds to numbers; unknown tokens and malformed bounds are ignored. Tokens run after the object `validations` rules. ```tsx import { validateValidationTokens, parseValidationTokens } from '@/components/docyrus-native/form-fields'; validateValidationTokens('ab', ['required', 'minLength:3'], { label: 'Code' }); // → 'Code must be at least 3 characters' ``` ## Select-Based Fields ### Select Props (DocyFieldSelectProps\) | Prop | Type | Default | Description | |------|------|---------|-------------| | `enumOptions` | `EnumOption[]` | `[]` | Options (`{ id, name, color?, icon?, parent?, description? }`) | | `searchable` | `boolean` | `true` | Search box in the sheet (shown with > 5 options or remote search) | | `searchPlaceholder` | `string` | `ui.formField.searchPlaceholder` | Search placeholder | | `onSearch` / `searching` | `(query) => void` / `boolean` | — | Remote search | | `onLoadMore` / `hasMore` | `() => void` / `boolean` | — | Pagination (end-reached) | | `itemTemplate` | `(option) => ReactNode` | — | Custom option + selected rendering | | `displayVariant` | `'dropdown' \| 'card'` | `'dropdown'` | Inline card list instead of trigger + sheet | | `columnCount` | `1 \| 2 \| 3 \| 4 \| 5` | `2` | Card columns (max 2 on phones) | Options with both `icon` and `color` render as a solid chip (`shouldRenderEnumOptionChip`) using the WCAG-contrast badge colors (`getEnumBadgeColors`). ### DocyFieldSelect / DocyFieldEnum `DocyFieldEnum` also accepts `renderOptionsEditor?: () => ReactNode` — rendered below the option list in the picker sheet. Single-select storing the option **id**. `DocyFieldEnum` also serves `field-systemEnum`. Clearing writes `''`. ### DocyFieldTreeSelect Single-select over nested options (`parent` / `nestedByProp`), indented tree in a searchable sheet. | Prop | Type | Default | Description | |------|------|---------|-------------| | `nestedByProp` | `string` | `'parent'` | Parent-reference property | ### DocyFieldMultiSelect / DocyFieldTagSelect / DocyFieldUserMultiSelect Multi-select storing `string[]` ids. Selected options render as chips; `DocyFieldMultiSelect` / `DocyFieldUserMultiSelect` show a clear affordance. ### DocyFieldCheckboxGroup Checkbox list storing `string[]` ids (web `CheckboxGroupFormField`). `displayVariant="card"` renders bordered cards with the option `description` and icon. ### DocyFieldRadioGroup | Prop | Type | Default | Description | |------|------|---------|-------------| Renders the options-driven `RadioGroup` (web `RadioGroupFormField` parity). Options map `id` → `value`, `name` → `label`, plus `description` / `icon` / `color`. Picking the selected option again no longer clears it (web parity). A custom `itemTemplate` falls back to the select-style card list. | Prop | Type | Default | Description | |------|------|---------|-------------| | `direction` | `'vertical' \| 'horizontal'` | `'vertical'` | Orientation of the default layout (when `columnCount` is 1) | | `displayVariant` | `'dropdown' \| 'card'` | — | `card` → bordered option cards; anything else → default radio list | | `columnCount` | `1 \| 2 \| 3 \| 4 \| 5` | `1` | Grid columns — 1 below 400pt window width, max 2 below 768pt | ### DocyFieldUserSelect Avatar + name rows (option `photo` / `avatar` / image `icon` and `email` shown when present). ### DocyFieldRelation | Prop | Type | Default | Description | |------|------|---------|-------------| | `nested` / `nestedByProp` | `boolean` / `string` | — / `'parent'` | Tree options | | `itemMapping` | `{ iconField?, colorField?, imageField?, descriptionField? }` | — | Map record columns onto the row visuals | | `onCreateRecord` | `(name: string) => Promise` | — | "Create \"query\"" row in the sheet | | `renderCreateForm` | `({ onCreated }) => ReactNode` | — | Custom create form inside the sheet | | `onExpand` | `(option) => void` | — | Expand button next to the selection | | `renderExpandContent` | `(option) => ReactNode` | — | Expand content in a bottom sheet | | `expandIcon` | `ReactNode` | — | Custom expand glyph | Selecting the current record again clears it (web parity). ### DocyFieldStatus | Prop | Type | Default | Description | |------|------|---------|-------------| | `companionValues` | `StatusCompanionValues` | `{}` | `{ secondary?, description?, followup_date? }` | | `onCompanionChange` | `(key: StatusCompanionKey, value: string \| null) => void` | — | Enables the companion form: sub-status (child options), notes, follow-up date; committed on **Confirm** | | `inline` | `boolean` | `false` | Status list + companion form inline (no sheet) | Options flagged `force_description` / `force_followup_date` require notes / a date before Confirm; `is_final_option` shows a "Final" tag. ### DocyFieldCurrencyCode Searchable ISO currency picker storing the code. | Prop | Type | Default | Description | |------|------|---------|-------------| | `currencies` | `{ code, name }[]` | `COMMON_CURRENCIES` | Currency list | ## Text & Code Fields ### DocyFieldText | Prop | Type | Default | Description | |------|------|---------|-------------| | `autoCapitalize` | `'none' \| 'sentences' \| 'words' \| 'characters'` | `'sentences'` | Auto-capitalization | | `autoCorrect` | `boolean` | `true` | Auto-correct | | `autoComplete` | `string` | `'off'` | Autofill hint | | `keyboardType` | `string` | `'default'` | Keyboard | | `returnKeyType` | `string` | `'done'` | Return key | | `maxLength` | `number` | — | Max characters | | `secureTextEntry` | `boolean` | `false` | Mask input | | `selectTextOnFocus` | `boolean` | `false` | Select on focus | | `clearButtonMode` | `'never' \| 'while-editing' \| 'unless-editing' \| 'always'` | `'while-editing'` | iOS clear button | | `onSubmitEditing` | `() => void` | — | Return pressed | | `leftIcon` / `rightIcon` | `ReactNode` | — | Adornments | ### DocyFieldTextarea | Prop | Type | Default | Description | |------|------|---------|-------------| | `numberOfLines` | `number` | `4` | Visible lines | | `maxLength` | `number` | — | Max characters | | `autoCapitalize` | `'none' \| 'sentences' \| 'words' \| 'characters'` | `'sentences'` | Auto-capitalization | | `autoCorrect` | `boolean` | `true` | Auto-correct | | `minHeight` / `maxHeight` | `number` | `80` / — | Height bounds | ### DocyFieldCode Monospace editor for code / JSON / JSONata / Handlebars (no syntax highlighting). | Prop | Type | Default | Description | |------|------|---------|-------------| | `language` | `'text' \| 'json' \| 'jsonata' \| 'handlebars' \| 'sql' \| 'javascript' \| 'html'` | `'text'` | `json` parses on change (invalid JSON → inline error) | | `singleLine` | `boolean` | `false` | Single-line mode | | `minHeight` / `maxHeight` | `number` | `120` / — | Height bounds | ### DocyFieldMarkdown Wraps the native `SimpleMarkdownEditor` (web `MarkdownEditorFormField` parity): formatting toolbar, Write / Preview tabs, optional stats footer. The value is the markdown string. The toolbar is hidden when the field is disabled / read-only. | Prop | Type | Default | Description | |------|------|---------|-------------| | `toolbar` | `boolean` | `true` | Show the formatting toolbar | | `showStats` | `boolean` | `false` | Words / characters footer | | `resizable` | `boolean` | — | Accepted for web parity — ignored on native | | `autoResize` | `boolean` | `false` | Grow with the content between `minHeight` and `maxHeight` | | `minHeight` | `number \| string` | `220` | Minimum editor height | | `maxHeight` | `number \| string \| null` | — | Maximum editor height | | `preview` | `boolean \| 'toggle'` | `'toggle'` | `'toggle'` → Write / Preview tabs, `true` → preview below the input, `false` → no preview | ### DocyFieldEmail | Prop | Type | Default | Description | |------|------|---------|-------------| | `onComposeEmail` | `(email: string) => void` | — | Compose button once the value is a valid address | | `client` | `RestApiClient` | — | Without `onComposeEmail`: the button opens `EmailComposeDialog` | ### DocyFieldPhone | Prop | Type | Default | Description | |------|------|---------|-------------| | `countryCode` | `string` | — | Prefix shown before the number | | `onSendMessage` | `(phone: string) => void` | — | Message button (receives country code + number) | | `client` | `RestApiClient` | — | Without `onSendMessage`: the button opens `InstantMessageComposeDialog` | ### DocyFieldPassword | Prop | Type | Default | Description | |------|------|---------|-------------| | `showStrength` | `boolean` | `false` | Strength bar | | `minLength` | `number` | — | Strength threshold | | `maxLength` | `number` | — | Max characters | ### DocyFieldUrl No extra props (`url` keyboard). ## Numeric & Boolean Fields ### DocyFieldNumber Keeps the raw text while typing (partial decimals like `12.` survive), accepts `,` or `.` as the decimal separator, clamps / rounds on blur. | Prop | Type | Default | Description | |------|------|---------|-------------| | `min` / `max` | `number` | — | Clamp on blur | | `decimalPlaces` | `number` | — | Round on blur | ### DocyFieldPercent | Prop | Type | Default | Description | |------|------|---------|-------------| | `decimalPlaces` | `number` | — | Decimal places | ### DocyFieldMoney | Prop | Type | Default | Description | |------|------|---------|-------------| | `currency` | `string` | `'USD'` | Currency code | | `onCurrencyChange` | `(currency: string) => void` | — | Enables the currency sheet | | `currencyOptions` | `{ code, name }[]` | `COMMON_CURRENCIES` | Currencies | ### DocyFieldDuration Value in seconds. | Prop | Type | Default | Description | |------|------|---------|-------------| | `showSeconds` | `boolean` | `false` | Seconds input | | `maxHours` | `number` | `23` | Hour clamp | | `increment` | `number` | — | Minute step (snaps entered minutes) | ### DocyFieldRating | Prop | Type | Default | Description | |------|------|---------|-------------| | `maxRating` | `number` | `5` | Stars | ### DocyFieldCheckbox / DocyFieldSwitch / DocyFieldSlider | Prop | Type | Default | Description | |------|------|---------|-------------| | `checkboxLabel` / `checkboxPosition` | `string` / `'left' \| 'right'` | — / `'left'` | Checkbox | | `switchLabel` / `switchPosition` | `string` / `'left' \| 'right'` | — / `'right'` | Switch | | `onColor` / `offColor` / `thumbColor` | `string` | — | Switch colors | | `min` / `max` / `step` | `number` | `0` / `100` / `1` | Slider range | | `showValue` / `showMinMax` / `showTooltip` | `boolean` | `true` / `false` / `true` | Slider display | | `formatValue` | `(value: number) => string` | — | Slider label format | | `tintColor` | `string` | — | Slider tint | ## Date & Time Fields Date / datetime / date-range triggers use the tenant format when a `DateFormatProvider` (e.g. `DocyrusTenantProvider`) is mounted, else the picker preset. ### DocyFieldDate / DocyFieldDatetime | Prop | Type | Default | Description | |------|------|---------|-------------| | `value` | `Date \| null` | `null` | Value (the IField adapter converts `YYYY-MM-DD` / ISO strings) | | `mode` | `'date' \| 'datetime' \| 'time'` | `'date'` | Picker mode (`DocyFieldDatetime` fixes `datetime`) | | `minimumDate` / `maximumDate` | `Date` | — | Bounds | | `dateFormat` | `'short' \| 'medium' \| 'long' \| 'full'` | `'medium'` | Fallback format preset | | `timeFormat` | `'12h' \| '24h'` | `'12h'` | Fallback time format | | `clearable` | `boolean` | `true` | Clear button (hidden when required / locked) | | `calendarIcon` / `clearIcon` | `ReactNode` | — | Custom icons | | `iconSize` / `iconColor` | `number` / `string` | `20` / — | Icon styling | ### DocyFieldTime Value `HH:mm` string. ### DocyFieldDateRange | Prop | Type | Default | Description | |------|------|---------|-------------| | `value` | `{ startDate: Date \| null; endDate: Date \| null }` | — | Range (adapter converts `[start, end]`) | | `minimumDate` / `maximumDate` | `Date` | — | Bounds | | `dateFormat` | `'short' \| 'medium' \| 'long' \| 'full'` | `'medium'` | Fallback format | | `separator` | `string` | `' – '` | Separator | | `clearable` | `boolean` | `true` | Clear button | | `allowSameDate` | `boolean` | `true` | Allow start = end | | `calendarIcon` / `clearIcon` / `iconSize` / `iconColor` | — | — | Icons | ## Files & Media Values follow the Docyrus `StoredFileValue` contract: `{ file_name, source, file_size, file_type, signed_url, file_data? }`. Pickers resolve a `NativeFile` (`{ uri, name, type, size? }`) that is handed to the upload handler; without one the field stores a local `StoredFileValue` pointing at the picked URI. ### DocyFieldImage | Prop | Type | Default | Description | |------|------|---------|-------------| | `value` | `StoredFileValue \| StoredFileValue[] \| null` | — | Image(s) | | `onImageUpload` | `(file: NativeFile) => Promise` | — | Upload handler | | `onImageDelete` | `(image: StoredFileValue) => Promise \| void` | — | Called on remove | | `onPickImage` | `() => Promise` | — | Custom picker | | `gallery` | `boolean` | `false` | Multiple images (horizontal strip) | | `maxImages` | `number` | — | Gallery limit | | `stencilShape` | `'rectangle' \| 'circle'` | `'rectangle'` | Thumbnail shape | | `thumbWidth` / `thumbHeight` | `number` | `112` (`88` gallery) / `88` | Thumbnail size | ### DocyFieldFile | Prop | Type | Default | Description | |------|------|---------|-------------| | `value` | `StoredFileValue \| null` | — | File | | `onFileUpload` | `FieldUploadHandler` | — | Upload handler | | `onPickFile` | `() => Promise` | — | Custom picker | | `accept` | `string[]` | — | MIME filters | ### DocyFieldAvatar | Prop | Type | Default | Description | |------|------|---------|-------------| Renders the native `AvatarSelect` (icon / emoji / color / image) — web `AvatarField` parity. | Prop | Type | Default | Description | |------|------|---------|-------------| | `value` | `AvatarFieldValue` (`{ icon, color, image: AvatarImageValue \| null }`) | — | Avatar | | `iconField` / `colorField` / `imageField` | `string \| null` | `icon` / `color` / `image` | Column mapping used for the payload | | `onPayloadChange` | `(payload: Record, value: AvatarFieldValue) => void` | — | Mapped `buildAvatarPayload` columns on every change | | `uploadImage` | `(asset: AvatarUploadAsset) => Promise` | — | Uploads the picked / cropped image | | `onImageUpload` | `FieldUploadHandler` | — | Docyrus storage upload (used when `uploadImage` is absent); the stored file becomes `value.image` | | `editorDisplay` | `'inline' \| 'popover'` | `'popover'` | Editor in an ActionSheet from the thumbnail, or in place | | `imageCrop` | `'system' \| 'editor'` | `'system'` | OS crop UI or the native `ImageEditor` | ### DocyFieldUpload Native multi-file field (`UploadedFile[]`). | Prop | Type | Default | Description | |------|------|---------|-------------| | `maxFiles` | `number` | `10` | Max files | | `maxSizeBytes` | `number` | — | Size limit | | `allowedTypes` | `string[]` | — | MIME filters | | `onUpload` | `(files: UploadedFile[]) => Promise` | — | Upload callback | | `uploadLabel` | `string` | — | Button label | ## Complex Fields ### DocyFieldApprovalStatus Approval workflow (web parity): last-response summary, optional comments, status-dependent actions (Send for approval → Approve / Reject / Request revision → Resend / Withdraw) and response history. Each action prepends a step. | Prop | Type | Default | Description | |------|------|---------|-------------| | `value` | `ApprovalValueObject \| string \| null` | — | Workflow value (bare status strings accepted) | | `canRespond` | `boolean` | `true` | Allow Approve / Reject / Request revision | | `currentUser` | `ApprovalUserSnapshot \| null` | `null` | Stamped on every step | | `commentPlaceholder` | `string` | — | Comment placeholder | ### DocyFieldQueryBuilder Summary trigger → full-height sheet with `QueryBuilderDocyrus`; commits on **Apply**. Value: react-querybuilder `RuleGroupType` (JSON strings accepted). | Prop | Type | Default | Description | |------|------|---------|-------------| | `fields` | `DocyrusQBField[]` | from `enumOptions` | Query builder fields | | `enumOptions` | `EnumOption[]` | — | One text field per option when `fields` is omitted | ### DocyFieldSchemaRepeater Repeatable group of sub-fields rendered through `DocyFieldDynamic`; key / value mode without `schema`. | Prop | Type | Default | Description | |------|------|---------|-------------| | `value` | `SchemaRepeaterRowValue[] \| null` | — | Rows (`{ id, ... }`) | | `schema` | `IField[]` | — | Sub-fields | | `schemaEnumOptions` | `Record` | — | Options per sub-field slug | | `minItems` / `maxItems` | `number` | `0` / — | Bounds | | `collapsible` / `defaultCollapsed` | `boolean` | `true` / `false` | Collapsing | | `cloneable` | `boolean` | `false` | Duplicate action | | `sortable` | `boolean` | `true` | Move up / down | | `showHeader` | `boolean` | `true` | Card header per row | | `itemLabel` | `(item, index) => string` | `#n` | Row label | | `addLabel` | `string` | `ui.schemaRepeater.add` | Add button label | ### DocyFieldReadOnlyValue Read-only fallback (`DocyValueDynamic` inside the field chrome). | Prop | Type | Default | Description | |------|------|---------|-------------| | `field` | `IField` | — | Field definition | | `record` | `Record` | — | Companion values | | `enumOptions` | `EnumOption[]` | — | Options | | `hint` | `string \| null` | "Editing is available on the web app." | Hint below the value; `null` hides | ### DocyFieldContactChannels Stores a `ContactChannel[]` on the bound field and edits it inline with the backend-agnostic `ContactChannelsManager` (web `ContactChannelsFormField`). For live API-backed management use `ContactChannelsPanel`. | Prop | Type | Default | Description | |------|------|---------|-------------| | `value` | `ContactChannel[]` | — | Channels | | `brands` | `ContactBrand[]` | — | Brands for labelling / scoping consent | | `enableConsent` | `boolean` | `true` | Per-channel consent editor | | `enableValidation` | `boolean` | `true` | Validation status chips | | `groupByKind` | `boolean` | `true` | Group channels under kind headers | ### DocyFieldColor / DocyFieldIcon / DocyFieldTaskList / DocyFieldLocationSelect / DocyFieldHidden | Component | Extra props | |---|---| | `DocyFieldColor` | — (palette sheet) | | `DocyFieldIcon` | — (featured icons + exact key) | | `DocyFieldTaskList` | value `TaskItem[]` (`{ id, title, completed }`) | | `DocyFieldLocationSelect` | `allowCurrentLocation` (`true`), `showCoordinates` (`true`), `predefinedLocations`, `requestLocationPermission`, `googleMapsApiKey` | | `DocyFieldHidden` | `name`, `value`, `onChange` | ## Utilities | Export | Description | |---|---| | `getFieldPlaceholder(field, fallback?)` | `placeholder` → `options.placeholder` → fallback | | `shouldRenderEnumOptionChip(option)` | icon + color → solid chip | | `getEnumBadgeColors(color)` | Solid background + WCAG readable text (`{ style }`) | | `getEnumColorValue(color)` | Any color format → `rgb(...)` | | `getEnumBadgeColorsNative(color, isDark, theme)` / `getEnumDotColorNative` | Theme-aware variants | | `toLocalDateString(date)` | Timezone-safe `YYYY-MM-DD` | | `parseDuration(text)` | `HH:MM:SS`, `HH:MM` (two parts = hours:minutes) or seconds | | `formatDateRange(start, end, formatDate?)` | Range text (tenant formatter optional) | | `validateValidationTokens` / `parseValidationTokens` / `readToken` / `DEFAULT_VALIDATION_MESSAGES` | Token DSL | | `canClearValue` / `ClearFieldButton` | Shared clear affordance | | `EnumOptionDisplay` | Shared option display (chip / inline) | | `extractEnumOptions` / `toIField` / `getFieldValue` / `normalizeOptionValue` / `resolveOptionName` | IField helpers | | `normalizeApprovalValue` / `toApprovalStatusCode` / `APPROVAL_STATUS` | Approval helpers | | `normalizeStoredFiles` / `toLocalStoredFile` / `getStoredFileName` | File value helpers | | `FORM_FIELD_MAP` | Field type → controlled component | | `SelectModal` | Shared option sheet | ## Components | Component | Description | |-----------|-------------| | `DocyFieldBase` | Label / error / help-text wrapper | | `DocyFieldDynamic` | IField adapter + legacy dispatcher | | `DocyFieldText`, `DocyFieldTextarea`, `DocyFieldCode`, `DocyFieldMarkdown`, `DocyFieldEmail`, `DocyFieldUrl`, `DocyFieldPassword` | Text | | `DocyFieldNumber`, `DocyFieldPercent`, `DocyFieldMoney`, `DocyFieldDuration`, `DocyFieldRating`, `DocyFieldSlider` | Numeric | | `DocyFieldCheckbox`, `DocyFieldSwitch`, `DocyFieldCheckboxGroup` | Boolean | | `DocyFieldSelect`, `DocyFieldEnum`, `DocyFieldTreeSelect`, `DocyFieldMultiSelect`, `DocyFieldTagSelect`, `DocyFieldRadioGroup`, `DocyFieldStatus`, `DocyFieldUserSelect`, `DocyFieldUserMultiSelect`, `DocyFieldRelation`, `DocyFieldCurrencyCode` | Selection | | `DocyFieldDate`, `DocyFieldDatetime`, `DocyFieldTime`, `DocyFieldDateRange` | Date / time | | `DocyFieldFile`, `DocyFieldImage`, `DocyFieldAvatar`, `DocyFieldUpload` | Files | | `DocyFieldPhone`, `DocyFieldContactChannels`, `DocyFieldColor`, `DocyFieldIcon`, `DocyFieldTaskList`, `DocyFieldLocationSelect`, `DocyFieldApprovalStatus`, `DocyFieldQueryBuilder`, `DocyFieldSchemaRepeater`, `DocyFieldReadOnlyValue`, `DocyFieldHidden` | Other | ## Type Exports | Type | Description | |------|-------------| | `IField`, `IFieldType`, `EnumOption` | Docyrus field contract (web parity) | | `DocyrusFormFieldProps` | IField adapter props | | `StoredFileValue`, `NativeFile`, `FieldUploadHandler` | Upload contract | | `CustomValidationRule`, `ComputedBooleanFormula`, `FieldAction`, `FieldActionBlock`, `FieldActionBlockItem`, `FieldActionStep`, `FieldActionCondition`, `FieldActionPropertyOverrides`, `FormAction`, `FormActionTriggerType`, `FormCustomValidationRule` | Form-engine types (web parity) | | `DocyFieldBaseProps`, `DocyFieldProps`, `DocyFieldSelectProps`, `DocyFieldMultiSelectProps`, `DocyFieldRenderProps`, `DocyFieldValidationRule`, `DocyFieldSize`, `DocyFieldVariant`, `DocyFieldLabelAlignment` | Controlled field props | | `DocyFieldDynamicProps`, `DocyFieldDynamicLegacyProps` | Dispatcher props | | `StatusCompanionKey`, `StatusCompanionValues` | Status companions | | `ApprovalStatusValue`, `ApprovalValueObject`, `ApprovalStepValue`, `ApprovalUserSnapshot`, `ApprovalWorkflowStatus` | Approval | | `DocyFieldContactChannelsProps` | Contact-channels field props | | `ImageFieldValue`, `FileFieldValue`, `AvatarFieldValue`, `SchemaRepeaterRowValue`, `DocyFieldDateRangeValue`, `TaskItem`, `LocationInfo`, `UploadedFile` | Field values | | `ValidationTokenName`, `ValidationConstraints`, `ValidationMessages`, `ValidateTokensOptions` | Token DSL | | `DocyFieldCodeLanguage`, `EnumColorResult`, `DocyrusFieldLike`, `SelectModalProps` | Misc | ## Breaking changes (web-parity release) - Enum / select values are option **ids** (already fixed in the hotfix release). - `DocyFieldFile` / `DocyFieldImage` values are `StoredFileValue` (`file_name`, `file_size`, `file_type`, `signed_url`) — the legacy `{ name, mime_type, size, uri }` shape is still read but no longer written. `onPickImage` / `onPickFile` / `onPickAvatar` now resolve a `NativeFile`. - `DocyFieldApprovalStatus` writes the web `ApprovalValueObject` with uppercase statuses (`WAITING_FOR_APPROVAL`, …); lowercase strings are read. - `DocyFieldCurrencyCode` is a picker (was a 3-letter text input). - `parseDuration('1:30')` returns 5400 (`HH:MM`), not 90. - `getEnumBadgeColorsNative` returns solid colors with WCAG text instead of pastel tints. - `DocyFieldBase` enforces `required` even without an explicit rule. - `DocyFieldAvatar` renders `AvatarSelect`; its value is the lib `AvatarFieldValue` (`image` is an `AvatarImageValue` object, not a URL string) and `onPickAvatar` was removed (use `uploadImage` / `onImageUpload`). Through `DocyFieldDynamic` it edits the three `avatarMapping` columns instead of its own slug. - `DocyFieldMarkdown` wraps `SimpleMarkdownEditor`; the textarea props (`numberOfLines`, `maxLength`, …) and `defaultPreview` were removed (use `preview`). - `DocyFieldRadioGroup` renders `RadioGroup`; re-selecting the active option no longer clears the value. - Copy moved from embedded `en` / `tr` maps to `useUiTranslation()` keys (`ui.formField.*`, `ui.formFields.*`, `ui.common.*`).