# rn-editable-record-detail URL: /docs/native/docyrus/editable-record-detail IField-driven record editor — EditableValue rows with change tracking, conditional required / hidden, validation and a sticky save bar. ## Installation ```bash pnpm dlx @docyrus/cli add @docyrus/rn-editable-record-detail ``` ## Usage ```tsx import { EditableRecordDetail, EditableRecordDetailField, type RecordDetailField } from '@/components/docyrus-native/editable-record-detail'; const fields: RecordDetailField[] = [ { field: { id: 'title', slug: 'title', name: 'Title', type: 'field-text' }, required: true }, { field: { id: 'status', slug: 'status', name: 'Status', type: 'field-status' }, enumOptions: statusOptions }, { field: { id: 'reason', slug: 'reason', name: 'Lost reason', type: 'field-textarea' }, hidden: values => values.status !== 'lost', required: values => values.status === 'lost' } ]; {/* Every field, in order */} ``` ## API Reference ### EditableRecordDetailProps | Prop | Type | Default | Description | |------|------|---------|-------------| | `fields` | `Array` | — | Field configs (required). Legacy `{ key, label, value, type }` entries are converted automatically | | `record` | `Record` | — | Current values keyed by slug (a record with different content resets the baseline) | | `onSave` | `(changes: FieldChange[], values: Record) => void \| Promise` | — | Save handler; the baseline resets after it resolves | | `onCancel` | `() => void` | — | Pending changes discarded | | `readOnly` | `boolean` | `false` | All fields read-only | | `disabled` | `boolean` | `false` | All fields disabled | | `trackChanges` | `boolean` | `true` | Highlight changed rows | | `client` | `RestApiClient` | — | Threaded into every field | | `children` | `ReactNode` | — | `EditableRecordDetailField` composition; omitted → every field | | `isSaving` | `boolean` | `false` | External saving flag | | `fieldLayout` | `'vertical' \| 'horizontal'` | `'vertical'` | Label above / beside the value | | `saveLabel` / `cancelLabel` | `string` | `ui.editableRecordDetail.save` / `ui.common.cancel` | Action bar labels | | `emptyValueText` | `string` | `'—'` | Empty text in `renderEditor` rows | | `scrollable` | `boolean` | `true` without `children` | Wrap rows in a ScrollView | | `className` | `string` | — | Additional classes | ### RecordDetailField | Field | Type | Description | |------|------|-------------| | `field` | `IField` | Field definition | | `enumOptions` | `EnumOption[]` | Options | | `readOnly` | `boolean` | Read-only row | | `required` | `boolean \| (values) => boolean` | Required (conditional) | | `hidden` | `boolean \| (values) => boolean` | Hidden (conditional, re-evaluated on every change) | | `appSlug` / `dataSourceSlug` | `string` | Dynamic enum context | | `renderValue` | `(value) => ReactNode` | Native escape hatch: custom display | | `renderEditor` | `(value, onChange, onDone?) => ReactNode` | Native escape hatch: custom inline editor | ### EditableRecordDetailFieldProps `EditableValueProps` (minus the context-provided props) plus: | Prop | Type | Default | Description | |------|------|---------|-------------| | `slug` | `string` | — | Field slug (required) | | `label` | `ReactNode` | `field.name` | Label override | | `showLabel` | `boolean` | `true` | Label + value row | | `layout` | `'vertical' \| 'horizontal'` | parent `fieldLayout` | Row layout override | ### FieldChange | Field | Type | Description | |------|------|-------------| | `fieldSlug` | `string` | Slug | | `fieldName` | `string` | Field name | | `originalValue` | `unknown` | Baseline value | | `newValue` | `unknown` | Current value | | `fieldKey` / `fieldLabel` | `string` | Deprecated legacy aliases | ### useEditableRecordDetail() Returns `EditableRecordDetailContextValue`: `values`, `changedFields`, `validationErrors`, `setFieldValue(slug, value)`, `getChanges()`, `handleSave()`, `handleCancel()`, plus the flags (`readOnly`, `disabled`, `isSaving`, `trackChanges`, `fieldLayout`). ## Components | Component | Description | |-----------|-------------| | `EditableRecordDetail` | Provider + rows + sticky action bar (pending-changes sheet, Cancel, Save) | | `EditableRecordDetailField` | `EditableValue` bound to the context | ## Type Exports | Type | Description | |------|-------------| | `EditableRecordDetailProps`, `EditableRecordDetailFieldProps` | Props | | `RecordDetailField`, `LegacyRecordDetailField` | Field configs | | `FieldChange`, `FieldLayout` | Change + layout | | `EditableRecordDetailContextValue` | Context value | ## Breaking changes (web-parity release) - `fields` are `RecordDetailField` (`{ field: IField, … }`) with values in `record`; the legacy `{ key, label, value, type }` shape is still accepted. - `onSave(changes, values)` — changes carry `fieldSlug` / `fieldName` (legacy `fieldKey` / `fieldLabel` kept as deprecated aliases). - Rows edit through `EditableValue` (field-aware) instead of plain text inputs.