Docyrus

ContactChannelsField

Backend-agnostic contact points for a record — emails, phones, messaging and social handles with a primary per kind, validation status and per-brand consent.

iOSAndroidExpo Go
Preview ContactChannelsField on your device

Scan with Expo Go

Download Expo Go, then scan the QR code to preview native components.

Installation

pnpm dlx @docyrus/cli add @docyrus/rn-contact-channels-field

The data model (ContactChannel, the three-level consent cache, validation records, brands) and the pure helpers in lib/contact-channels are the same as web, so a channel list round-trips between platforms with no mapping.

  • ContactChannelsField is a compact summary: the primary channel per kind (with a +N badge), a verified tick, a per-brand consent row (Email / Phone / WhatsApp / SMS dots) and Add channel. Details opens a bottom sheet with the full manager, or calls onDetailsClick so you can open ContactChannelsPanel instead.
  • ContactChannelsManager is the full editor. Rows are cards grouped by kind. Tapping a card opens the channel sheet.
  • ChannelDetailSheet (web: ChannelDetailPopover, kept as an alias) shows the attribute editor, validation and the consent editor, plus Make primary and Remove.
  • ConsentMatrix has one collapsible section per brand (Org-wide first). Each row is a medium × purpose with a three-way control (opted in / unknown / opted out). Transactional is medium-agnostic. Marketing and newsletter are split across the mediums the channel type allows.

Without channelActions, every edit is applied to value and reported through onChange. With channelActions, the field calls your handlers instead (for example against the Docyrus API).

Also available as a form field: DocyFieldContactChannels (@/components/docyrus-native/form-fields/docy-field-contact-channels) stores a ContactChannel[] and edits it inline with the manager.

Usage

import { ContactChannelsField, type ContactChannel } from '@/components/docyrus-native/contact-channels-field';

const [channels, setChannels] = useState<ContactChannel[]>([]);

<ContactChannelsField value={channels} onChange={setChannels} brands={brands} />
import { ContactChannelsManager } from '@/components/docyrus-native/contact-channels-field';

<ContactChannelsManager value={channels} onChange={setChannels} groupByKind enableConsent enableValidation />

API Reference

ContactChannelsField

PropTypeDefaultDescription
valueContactChannel[]-Controlled channels (required)
onChange(next: ContactChannel[]) => void-Full next list after an edit (required)
brandsContactBrand[]-Brands for the consent summary (org-wide row when omitted)
channelActionsContactChannelActionHandlers-Granular per-channel handlers (onCreate / onSave / onMakePrimary / onRemove / onRecordConsent)
enableChannelDetailsbooleantruePer-channel details sheet next to each value
titlestring'Contact Points'Heading
hideDetailsboolean-Hide the Details link
detailsLabelstring'Details'Details link label
onDetailsClick() => void-Replaces the built-in manager sheet (e.g. open ContactChannelsPanel)
showConsentbooleantrueShow the consent summary
disabledboolean-Disable editing
readOnlyboolean-Read-only
emptyTextstring'No contact points yet.'Empty placeholder
classNamestring-Container classes

ContactChannelActionHandlers

HandlerSignatureDescription
onCreate(channel: ContactChannel) => void | Promise<unknown>Persist a new channel (temporary id)
onSave(channel: ContactChannel) => void | Promise<unknown>Persist edited attributes
onMakePrimary(channel: ContactChannel) => void | Promise<unknown>Promote to primary for its kind
onRemove(channel: ContactChannel) => void | Promise<unknown>Remove / archive
onRecordConsent(channel, brandKey, medium | null, purpose, status) => void | Promise<unknown>Record one consent change instead of editing the cache

ContactChannelsManager

PropTypeDefaultDescription
valueContactChannel[]-Controlled channels (required)
onChange(next: ContactChannel[]) => void-Full next list on add / edit / remove (required)
brandsContactBrand[]-Brands for consent scoping
disabledboolean-Disable every interaction
readOnlyboolean-Hide editors and actions
enableConsentbooleantrueConsent section in the channel sheet
enableValidationbooleantrueValidation chips / section
groupByKindbooleantrueGroup under kind headers
emptyTextstring'No contact channels yet.'Empty placeholder
classNamestring-Container classes

ChannelEditor

PropTypeDefaultDescription
valueChannelDraft-Draft (required)
onChange(next: ChannelDraft) => void-Draft change (required)
disabledboolean-Disable inputs
showPrimarybooleantruePrimary switch
showVerifiedbooleantrueVerified switch
showValidationbooleantrueInline value validation

ChannelDetailSheet / ChannelDetailPopover

PropTypeDefaultDescription
channelContactChannel-Channel (required)
brandsContactBrand[]-Brands for the consent editor
lockedboolean-Read-only
onSave / onMakePrimary / onRemove(channel: ContactChannel) => void-Actions (required)
onConsentCache(channel, next: ConsentCache) => void-Cache-based consent edit
onConsentCell(channel, brandKey, medium, purpose, status) => void-Ledger consent edit (wins over onConsentCache)
triggerReactNodechevron buttonTrigger node
showValidationbooleantrueNative: validation section
showConsentbooleantrueNative: consent section

ConsentMatrix

PropTypeDefaultDescription
channelTypeChannelType-Decides the allowed mediums (required)
consentConsentCache-Current cache
brandsContactBrand[]-Extra brand sections
onChange(next: ConsentCache) => void-Rebuilt cache
onCellChange(brandKey, medium | null, purpose, status) => void-Single ledger entry
disabledboolean-Read-only
classNamestring-Container classes

Components

ComponentDescription
ContactChannelsFieldSummary + Details sheet
ContactChannelsManagerFull editor
ChannelDetailSheet (ChannelDetailPopover)Per-channel sheet
ChannelEditorAttribute editor
ConsentMatrixConsent editor
ValidationStatusBadge / ConsentStatusBadge / VerifiedBadge / ConsentStatusIconStatus visuals
AddChannelSheet / ContactChannelFormSheetNative: add button + sheet, shared sheet shell

Helpers

CHANNEL_TYPES, CHANNEL_KINDS, CHANNEL_TYPES_BY_KIND, CONSENT_MEDIUMS, CONSENT_PURPOSES, MEDIUMS_BY_CHANNEL_TYPE, allowedMediums, isMediumAllowed, validateChannelValue, normalizeChannelValue, readConsent, flattenConsent, summarizeBrandConsent, channelDisplayValue, humanizeEnum, channelKindForType, newId, draftFromChannel, emptyDraft, channelFromDraft, applyDraft, the status tone maps and the icon maps (CHANNEL_TYPE_ICONS, CHANNEL_KIND_ICONS, CONSENT_MEDIUM_ICONS, iconForType, iconForKind, iconForMedium).

Type Exports

ContactChannel, ContactBrand, ChannelKind, ChannelType, ChannelLabel, ConsentCache, ConsentCacheEntry, ConsentMediumMap, ConsentPurposeMap, ConsentRecord, ConsentAction, ConsentMedium, ConsentPurpose, ConsentStatus, ValidationCache, ValidationRecord, ValidationMethod, ValidationStatus, ChannelCreateInput, ChannelUpdateInput, ConsentEntryInput, ValidationEntryInput, ChannelDraft, FlatConsent, StatusTone, plus every component's *Props.

Differences from web

  • Popovers and dialogs are bottom sheets. The brand × medium grid is a collapsible section per brand.
  • The icon maps hold DocyrusIcon names ('fal envelope') instead of lucide components.
  • Tooltips are inline text.

On this page