iOS Android
Preview EnumOptionEditor on your device
Download Expo Go, then scan the QR code to preview native components.
pnpm dlx @docyrus/cli add @docyrus/rn-enum-option-editor
Required Packages (6 packages) pnpm add @tanstack/react-query @docyrus/api-client @docyrus/app-utils react-native-gesture-handler react-native-reanimated tailwind-variants
The module exports four building blocks, API-aligned with the web @docyrus/ui enum option editor:
Export Kind Use it for EnumOptionEditorComponent Pure, backend-agnostic inline editor (settings screens, tests) EnumOptionEditorDialogComponent The same editor inside a large bottom sheet (web: Dialog) with a trigger DocyrusEnumOptionEditorComponent The sheet wired to the Docyrus enum admin API for one field useDocyrusEnumEditorPermissionHook Whether the signed-in user may manage options (ADMIN / ARCHITECT role)
import { useState } from 'react' ;
import {
EnumOptionEditor ,
type EnumEditorOption
} from '@/components/docyrus-native/enum-option-editor' ;
const INITIAL : EnumEditorOption [] = [
{ id : 's-1' , name : 'Open' , color : 'green-500' , sort_order : 10 , origin : 'inherited' },
{ id : 's-2' , name : 'Doing' , color : 'orange-500' , sort_order : 20 , origin : 'inherited' , isCustomized : true },
{ id : 's-3' , name : 'On hold' , color : '#8b5cf6' , icon : 'fal pause' , sort_order : 30 , origin : 'custom' }
];
export function StatusOptions () {
const [ options , setOptions ] = useState ( INITIAL );
return (
< EnumOptionEditor
options = { options }
onCreateOption = { input => ({ id : `n- ${ Date . now () } ` , origin : 'custom' , ... input }) }
onUpdateOption = { ( id , patch ) => setOptions ( prev => prev . map ( o => ( o . id === id ? { ... o , ... patch } : o ))) }
onDeleteOption = { id => setOptions ( prev => prev . filter ( o => o . id !== id )) }
onReorder = { ids => setOptions ( prev => ids . map ( id => prev . find ( o => o . id === id ) ! )) } />
);
}
import { EnumOptionEditorDialog } from '@/components/docyrus-native/enum-option-editor' ;
< EnumOptionEditorDialog
options = { options }
title = "Manage “Status” options"
onUpdateOption = { updateOption }
trigger = { ({ onPress }) => < Button variant = "ghost" onPress = { onPress } >Manage options</ Button > } />
import {
DocyrusEnumOptionEditor ,
useDocyrusEnumEditorPermission
} from '@/components/docyrus-native/enum-option-editor' ;
export function ManageStatusOptions ({ client , field , dataSource }) {
const { canManage } = useDocyrusEnumEditorPermission ( client );
if ( ! canManage ) return null ;
return (
< DocyrusEnumOptionEditor
client = { client }
appId = { dataSource . tenant_app_id }
dataSourceId = { dataSource . id }
fieldId = { field . id }
fieldName = { field . name }
onChanged = { () => refetchSchema () } />
);
}
Gesture Result Tap the icon / colour tile Opens the icon sheet (Font Awesome / Huge Icons, featured + search) or the colour sheet (Tailwind families at tone 500, stored as blue-500, plus a free token / hex input) Edit the name Committed on blur or the keyboard's Done key — not per keystroke. Duplicate names show an inline error Active switch Retires / re-activates the option (active) "⋯" Action sheet: More settings (slug, description, is_final_option, force_description, force_followup_date, active), Move up / Move down , Delete (custom) or Revert to default (inherited, enabled only when customized) Long-press the grip + drag Reorders the list; neighbours slide out of the way and onReorder(orderedIds) fires on drop
Every change is applied optimistically; if the matching callback rejects, the row rolls back and shows the error message.
Prop Type Default Description optionsEnumEditorOption[]— Required. Initial options, rendered by sort_order (nulls last) then name. The editor reseeds when the incoming options changeonCreateOption(input: EnumOptionInput) => Promise<EnumEditorOption> | EnumEditorOption— Persist a new option; must resolve to the created option. Omitted → a local tmp_<n> row is added onUpdateOption(id: string, patch: Partial<EnumOptionInput>) => Promise<void> | void— Persist an edit (name on blur / submit, pickers, switches, advanced fields) onDeleteOption(id: string) => Promise<void> | void— Permanently delete a custom option onRevertOption(id: string) => Promise<void> | void— Drop the workspace override of an inherited option onReorder(orderedIds: string[]) => Promise<void> | void— Persist the new order after a drag or Move up / down readOnlybooleanfalseRender everything read-only (e.g. options managed via a shared enum set) allowCreatebooleantrueShow the add-option row allowDeletebooleantrueOffer Delete / Revert in the row menu allowReorderbooleantrueEnable the drag grip and the Move up / Move down menu items noticeTextstring— Info banner (Alert) above the list emptyTextstring'No options yet.'Empty-state copy classNamestring— Container className
Accepts every EnumOptionEditor prop plus:
Prop Type Default Description triggerReactNode | ((opts: { onPress: () => void }) => ReactNode) | null"Manage options" button Custom trigger. A node is wrapped in a Pressable; a function receives onPress; null renders no trigger (fully controlled) openboolean— Controlled open state onOpenChange(open: boolean) => void— Open state callback titlestring'Manage options'Sheet title descriptionstring'Add, edit, reorder and retire the selectable options for this field.'Sheet message disabledboolean— Disable the default trigger
Prop Type Default Description clientRestApiClient— Required. Authenticated Docyrus REST clientappIdstring— Required. App id (tenant_app_id) or slug — the {appId} path segmentdataSourceIdstring— Required. Data-source idfieldIdstring— Required. Field id whose options are managedfieldNamestring— Used as the sheet title when title is omitted triggerReactNode | ((opts: { onPress: () => void }) => ReactNode) | null"Manage options" button Same as EnumOptionEditorDialog openboolean— Controlled open state onOpenChange(open: boolean) => void— Open state callback onChanged() => void— Called after every successful write (refresh the owning form's schema) disabledboolean— Disable the default trigger titlestringfieldNameSheet title
Endpoints (all under /v1/dev/apps/{appId}/data-sources/{dataSourceId}/fields/{fieldId}/enums): GET (options + enumSetId), POST create, PATCH update / reorder (sortOrder = (i + 1) * 10), DELETE (enumIds), DELETE …/{id}/customization revert. Ownership (custom vs inherited) comes from GET /v1/dev/data-sources/enums?fieldId=…. A field on a shared enum set opens read-only with a notice. Options are fetched only while the sheet is open (query key ['docyrus-enum-options', dataSourceId, fieldId]); every write invalidates it and the shared inventory's data sources (getSharedDocyrusInventory(client).invalidateDataSources()). Requires the Architect.ReadWrite.All scope plus the ADMIN or ARCHITECT role.
useDocyrusEnumEditorPermission (
client : RestApiClient | undefined ,
options ?: UseDocyrusEnumEditorPermissionOptions
): { canManage : boolean ; isLoading : boolean }
Option Type Default Description enabledbooleantrueGate the profile fetch adminRoleIdsstring[][ADMIN_ROLE_ID, ARCHITECT_ROLE_ID]Role ids allowed to manage options meEndpointstring'/v1/users/me'Profile endpoint (query key ['docyrus-me', meEndpoint], 5-minute stale time)
Constants: ADMIN_ROLE_ID = 'e37ea658-8cfa-11ed-a098-7703de2a84b1', ARCHITECT_ROLE_ID = '61672102-b5c8-11ee-b08a-23d1fbdaaa6a'.
Component Description EnumOptionEditorInline optimistic option list editor EnumOptionEditorDialogBottom-sheet launcher for the editor DocyrusEnumOptionEditorLauncher wired to the Docyrus enum admin API
Type Description EnumEditorOptionEnumOption + origin?: EnumOptionOrigin + isCustomized?: booleanEnumOptionOrigin'custom' | 'inherited'EnumOptionInputEditable attributes: name, slug?, color?, icon?, description?, sort_order?, active?, is_final_option?, force_description?, force_followup_date?, parent? EnumOptionEditorPropsProps for EnumOptionEditor EnumOptionEditorDialogPropsProps for EnumOptionEditorDialog EnumOptionEditorTriggerNative trigger type (ReactNode | (({ onPress }) => ReactNode)) DocyrusEnumOptionEditorPropsProps for DocyrusEnumOptionEditor UseDocyrusEnumEditorPermissionOptionsOptions for useDocyrusEnumEditorPermission
EnumOptionEditorDialog / DocyrusEnumOptionEditor render a large bottom sheet instead of a Dialog; trigger additionally accepts ({ onPress }) => ReactNode.
Row actions live in a "⋯" action sheet; Move up / Move down are added as an accessible alternative to drag.
The Active switch is shown on every row (web keeps it in the advanced popover; native has it in both places).
Copy is read through useUiTranslation() with the web keys: ui.enumEditor.* (manageOptions, title, dialogDescription, color, colorPlaceholder, icon, iconFontAwesome, iconHugeIcons, iconSearch, noIcons, clear, slug, slugAuto, description, active, activeHint, finalOption, finalOptionHint, forceDescription, forceFollowup, moreSettings, reorder, revert, delete, inherited, overridden, empty, addPlaceholder, add, duplicateName, saveFailed, deleteFailed, revertFailed, createFailed, sharedSetNotice) plus native-only ui.enumEditor.name, ui.enumEditor.reorderHint, ui.enumEditor.moveUp, ui.enumEditor.moveDown, and the shared ui.common.done / ui.common.loading.