Skyroc Native UI

Cell

列表项与分组容器,承载设置项、信息展示等纵向列表

单元格(Cell)是移动端最常见的列表行:左侧图标、标题与副标题、右侧文案与箭头。传了 onPress / onLongPress 时根节点渲染为 Pressable,否则退化成纯展示的 View —— 不会给静态列表凭空加上一个可聚焦节点。CellGroup 负责把若干行收成一张卡片,并在行与行之间插入分隔线。

import { Cell, CellGroup } from '@skyroc/native-ui';

基础用法

titlesubtitletrailing 三者可以任意组合,传 string 时会自动包一层 Text 承接主题字号与颜色;传自定义节点则原样渲染。

CellBasic.tsx
Loading…
import { Cell } from '@skyroc/native-ui';
import { View } from 'react-native';

const CellBasic = () => {
  return (
    <View className="bg-muted p-4">
      <View className="overflow-hidden rounded-2xl border border-border/70 bg-background">
        <Cell title="单行标题" />
        <Cell
          subtitle="用于补充说明当前内容"
          title="带描述信息"
        />
        <Cell
          subtitle="标题、描述与右侧内容可以同时使用"
          title="完整信息"
          trailing="详情"
        />
      </View>
    </View>
  );
};

export { CellBasic };

何时使用

  • 设置页、个人中心、订单详情这类「标签 + 值」的纵向列表。
  • 需要整行可点击的入口时,直接给 CellonPress,不要在外面再套一层 Pressable
  • 只做信息展示、不需要点击时不传回调即可,组件会自动渲染为 View
  • 需要卡片式分组、分组标题和分隔线时用 CellGroup 包一层。

点击与箭头

onPressonLongPress 任一存在时,整行变成可点击的 Pressable,并默认显示右箭头。箭头的显示规则按优先级为:

条件是否显示箭头
显式传 showArrow以传入值为准
传了自定义 arrow显示
传了 onPress / onLongPress显示
以上都没有不显示
CellPress.tsx
Loading…
import { Cell } from '@skyroc/native-ui';
import { Alert, View } from 'react-native';

function handlePress(label: string) {
  Alert.alert(label, '列表项已点击');
}

const CellPress = () => {
  return (
    <View className="bg-muted p-4">
      <View className="overflow-hidden rounded-2xl border border-border/70 bg-background">
        <Cell
          title="可点击列表项"
          onPress={() => handlePress('可点击列表项')}
        />
        <Cell
          showArrow
          subtitle="也可以通过 showArrow 显式控制"
          title="显式显示箭头"
          onPress={() => handlePress('显式显示箭头')}
        />
      </View>
    </View>
  );
};

export { CellPress };

不可点击的 Cell 也可以通过 showArrow 强制显示箭头,用于「箭头只作视觉提示、点击交给外层容器」的场景。

左侧内容

leading 放图标、头像等前置内容,容器带 mr-3 的右间距并垂直居中。

CellLeading.tsx
Loading…
import { Cell, Text } from '@skyroc/native-ui';
import { View } from 'react-native';

const CellLeading = () => {
  return (
    <View className="bg-muted p-4">
      <View className="overflow-hidden rounded-2xl border border-border/70 bg-background">
        <Cell
          leading={
            <View className="size-9 items-center justify-center rounded-xl bg-primary/10">
              <Text className="text-sm font-semibold text-primary">A</Text>
            </View>
          }
          subtitle="leading 可以承载图标"
          title="图标入口"
        />
        <Cell
          leading={
            <View className="size-9 items-center justify-center rounded-full bg-success/10">
              <Text className="text-sm font-semibold text-success">林</Text>
            </View>
          }
          title="头像入口"
          trailing="在线"
        />
      </View>
    </View>
  );
};

export { CellLeading };

leading 内的文字颜色不会自动跟随 Cell:RN 的文字颜色不从父节点继承,图标组件也需要自己传 color 或使用 accent-* 类。

右侧内容与箭头方向

trailing 是右侧内容区,string 会包成弱化色的 Text(类名来自 trailingText slot)。arrowDirection 控制默认箭头的朝向,取值为 'right' | 'down' | 'up' | 'left',传了自定义 arrow 时该属性失效。

CellArrow.tsx
Loading…
import { Cell } from '@skyroc/native-ui';
import { Alert, View } from 'react-native';

function handlePress(label: string) {
  Alert.alert(label, '列表项已点击');
}

const CellArrow = () => {
  return (
    <View className="bg-muted p-4">
      <View className="overflow-hidden rounded-2xl border border-border/70 bg-background">
        <Cell
          showArrow
          title="右箭头"
          onPress={() => handlePress('右箭头')}
        />
        <Cell
          showArrow
          arrowDirection="down"
          title="下箭头"
          trailing="展开"
          onPress={() => handlePress('下箭头')}
        />
        <Cell
          showArrow
          arrowDirection="up"
          title="上箭头"
          trailing="收起"
          onPress={() => handlePress('上箭头')}
        />
      </View>
    </View>
  );
};

export { CellArrow };

默认箭头是 @expo/vector-icons 的 AntDesign 图标,颜色取自 arrowIcon slot 的 accent-muted-foreground,跟随主题 token 而不是硬编码灰值;尺寸随 size 缩放(见下表)。该 slot 不在 classNames 里开放,需要换色请传自定义 arrow

分组

CellGroup 把多个 Cell 收成一张卡片:根节点为 overflow-hidden rounded-xltitle 作为分组标题渲染在卡片上方,border(默认 true)在相邻子项之间插入 h-px bg-border 的分隔线。

CellGroupBasic.tsx
Loading…
import { Cell, CellGroup } from '@skyroc/native-ui';
import { Alert, View } from 'react-native';

function handlePress(label: string) {
  Alert.alert(label, '列表项已点击');
}

const CellGroupBasic = () => {
  return (
    <View className="bg-muted p-4">
      <CellGroup
        classNames={{ root: 'border border-border/70 bg-background' }}
        title="账户设置"
      >
        <Cell
          title="个人资料"
          trailing="已完善"
        />
        <Cell
          title="安全设置"
          trailing="正常"
        />
        <Cell
          showArrow
          title="更多设置"
          onPress={() => handlePress('更多设置')}
        />
      </CellGroup>
    </View>
  );
};

export { CellGroupBasic };

两点值得注意:

  • 分隔线是容器插入的独立节点,不是往子元素上注入类名,所以子项可以是 Cell 之外的任何组件;需要缩进时给 classNames.dividerml-*
  • CellGroup 的根节点只有圆角和裁剪,没有背景色。底色来自每个 Cell 自带的 bg-background;要给卡片本身加底色或描边,通过 classNames.root 传。

内嵌分组

inset 给卡片加上 mx-4 的左右留边,形成 iOS 设置页那种悬浮卡片效果。分组标题保持 px-4,与卡片内容左对齐。

CellGroupInset.tsx
Loading…
import { Cell, CellGroup } from '@skyroc/native-ui';
import { Alert, View } from 'react-native';

function handlePress(label: string) {
  Alert.alert(label, '列表项已点击');
}

const CellGroupInset = () => {
  return (
    <View className="bg-muted py-4">
      <CellGroup
        inset
        classNames={{ root: 'border border-border/70 bg-background' }}
        title="通知设置"
      >
        <Cell
          title="系统通知"
          trailing="已开启"
        />
        <Cell
          title="活动提醒"
          trailing="仅重要"
        />
        <Cell
          showArrow
          title="通知偏好"
          onPress={() => handlePress('通知偏好')}
        />
      </CellGroup>
    </View>
  );
};

export { CellGroupInset };

尺寸

size 同时决定行高、内边距、标题与副标题字号,以及默认箭头的像素大小:

尺寸最小行高横向内边距纵向内边距标题字号副标题字号右侧文字字号箭头大小
sm4012814101211
md48161216121412
lg56161418141614
CellSize.tsx
Loading…
import { Cell } from '@skyroc/native-ui';
import { View } from 'react-native';

const CellSize = () => {
  return (
    <View className="bg-muted p-4">
      <View className="overflow-hidden rounded-2xl border border-border/70 bg-background">
        <Cell
          size="sm"
          subtitle="Small"
          title="紧凑尺寸"
        />
        <Cell
          size="md"
          subtitle="Medium"
          title="默认尺寸"
        />
        <Cell
          size="lg"
          subtitle="Large"
          title="宽松尺寸"
        />
      </View>
    </View>
  );
};

export { CellSize };

行高是 min-h-* 而不是固定高度,内容多行时会自然撑高。

对齐方式

center 默认为 trueleading / 内容区 / trailing / 箭头整体垂直居中。副标题较长导致内容换行时,传 center={false} 让各区域顶部对齐。

CellCenter.tsx
Loading…
import { Cell } from '@skyroc/native-ui';
import { View } from 'react-native';

const CellCenter = () => {
  return (
    <View className="gap-3 bg-muted p-4">
      <View className="overflow-hidden rounded-2xl border border-border/70 bg-background">
        <Cell
          subtitle="默认 center 为 true,标题与右侧内容整体垂直居中,副标题较长时右侧文字会落在中间。"
          title="居中对齐"
          trailing="默认"
        />
      </View>

      <View className="overflow-hidden rounded-2xl border border-border/70 bg-background">
        <Cell
          center={false}
          subtitle="center 传 false 后各区域顶部对齐,多行副标题的场景下右侧内容与标题在同一行。"
          title="顶部对齐"
          trailing="center=false"
        />
      </View>
    </View>
  );
};

export { CellCenter };

禁用

disabled 让整行降到 50% 不透明度,可点击时同时阻断 onPress / onLongPress 并把状态写进 accessibilityState。不可点击的 Celldisabled 只有视觉效果。

CellDisabled.tsx
Loading…
import { Cell } from '@skyroc/native-ui';
import { Alert, View } from 'react-native';

function handlePress(label: string) {
  Alert.alert(label, '列表项已点击');
}

const CellDisabled = () => {
  return (
    <View className="bg-muted p-4">
      <View className="overflow-hidden rounded-2xl border border-border/70 bg-background">
        <Cell
          disabled
          showArrow
          subtitle="禁用后不会触发点击事件"
          title="暂不可用"
          trailing="Disabled"
          onPress={() => handlePress('暂不可用')}
        />
      </View>
    </View>
  );
};

export { CellDisabled };

样式覆盖

Cell 没有 className,样式一律走 classNames

slot作用位置
root根节点(可点击时是 Pressable,否则是 View
leading左侧内容容器
content标题与副标题所在的中间区域
title标题文字(仅 titlestring 时生效)
subtitle副标题文字(仅 subtitlestring 时生效)
trailing右侧内容容器
trailingText右侧文字(仅 trailingstring 时生效)
arrow箭头容器

CellGroup 的 slot 为 root(卡片容器)、title(分组标题)、divider(分隔线)。

无障碍

可点击的 Cell 会设置 accessibilityRole="button",并把 disabled 映射到 accessibilityState.disabled;不可点击时不设置 role,读屏器按普通内容播报。title / subtitle 传自定义节点时读屏器只能读到节点内的文本,建议一并传 accessibilityLabel

API

Cell

属性说明类型默认值
title标题,string 自动包裹 TextReactNode-
subtitle副标题,string 自动包裹 TextReactNode-
leading左侧区域内容,通常放图标或头像ReactNode-
trailing右侧内容区域,string 自动包裹 TextReactNode-
size尺寸,同时决定行高、内边距、字号与默认箭头大小'sm' | 'md' | 'lg''md'
center内容是否垂直居中,多行内容想顶部对齐时传 falsebooleantrue
showArrow是否显示右侧箭头,缺省时由 arrow 与点击回调推导boolean-
arrow自定义箭头内容,覆盖默认图标;传入即视为需要显示箭头ReactNode-
arrowDirection默认箭头的方向,传了自定义 arrow 时无效'right' | 'down' | 'up' | 'left''right'
disabled禁用交互并整体降低透明度;可点击时同时阻断回调booleanfalse
onPress点击回调,传入后根节点渲染为 Pressable() => void-
onLongPress长按回调,同样会让整行变成可点击的 Pressable() => void-
classNames覆盖各 slot 的类名SlotClassNames<CellSlots>-
accessibilityLabel无障碍标签,未提供时读屏朗读子节点文本string-
testID测试标识string-
ref根节点的 ref,用于 measure / 滚动定位;可点击时根节点是 Pressable,实例类型同为 ViewRef<View>-

CellGroup

属性说明类型默认值
children*分组内容,通常为 Cell 组件ReactNode-
title分组标题,string 自动包裹 TextReactNode-
inset是否为卡片式内嵌样式(左右留边 mx-4)booleanfalse
border是否在相邻子项之间插入分隔线booleantrue
classNames覆盖各 slot 的类名,分隔线需要缩进时给 divider 传 ml-*SlotClassNames<CellGroupSlots>-
ref根节点的 ref,用于 measure / 滚动定位等命令式操作Ref<View>-

类型

import type { CellGroupProps, CellGroupSlots, CellProps, CellSize, CellSlots } from '@skyroc/native-ui';

CellSize

Cell 尺寸,同时决定行高、内边距、字号与默认箭头大小。

'sm' | 'md' | 'lg'

CellSlots

Cell 可通过 classNames 覆盖的 slot 名称;箭头图标取色用的 arrowIcon 不在其中。

'arrow' | 'content' | 'leading' | 'root' | 'subtitle' | 'title' | 'trailing' | 'trailingText'

CellGroupSlots

CellGroup 可通过 classNames 覆盖的 slot 名称。

'divider' | 'root' | 'title'

SlotClassNames

classNames 的取值形态:把 slot 名映射到类名,每个 slot 都可选。本页用到的 slot 见 CellSlots / CellGroupSlots。

Partial<Record<Slots, string>>