组件

Card

Card 是一个圆角表面,用来把相关内容归在一起。内容上方可以有页眉,下方可以有页脚。页眉、内容和页脚都是你的 JSX;卡片负责提供表面、内边距和分隔线。

ui-card.tsx 在线编辑 在新标签页打开
// Card: elevated and outlined, with a header and a footer slot.
import { mount, Scroll, signal, Text, View } from "nsh";
import { Button, Card } from "nsh/ui/mobile";

function App() {
  const likes = signal(0);
  return (
    <Scroll style={{ flex: 1, backgroundColor: "#ffffff" }}>
      <View style={{ padding: 16, paddingTop: 24, gap: 16 }}>
        <Text style={{ fontSize: 22, fontWeight: "700" }}>Card</Text>
        <Card
          header={<Text>Weekly summary</Text>}
          footer={
            <View style={{ flexDirection: "row", gap: 8 }}>
              <Button title="Reset" variant="plain" onPress={() => likes.set(0)} />
              <Button title={`Like (${likes()})`} onPress={() => likes.set(likes() + 1)} />
            </View>
          }
        >
          <Text>You walked 42 km this week, 8 km more than last week.</Text>
          <Text style={{ color: "#737373" }}>The header, this body and the footer are the app's JSX.</Text>
        </Card>
        <Card variant="outlined">
          <Text style={{ fontWeight: "600" }}>Outlined</Text>
          <Text>A border, no shadow, and no header or footer: only children.</Text>
        </Card>
        <Card
          variant="outlined"
          header={
            <View style={{ flexDirection: "row", alignItems: "center", gap: 10 }}>
              <View style={{ width: 32, height: 32, borderRadius: 16, backgroundColor: "#a78bfa" }} />
              <Text>Ada Lovelace</Text>
            </View>
          }
        >
          <Text>A header can be any row, such as an avatar and a name.</Text>
        </Card>
      </View>
    </Scroll>
  );
}

mount(App);
import { Card } from "nsh/ui/mobile";

#用法

import { mount, Text, View } from "nsh";
import { Button, Card } from "nsh/ui/mobile";

function App() {
  return (
    <View style={{ flex: 1, padding: 16, paddingTop: 48 }}>
      <Card
        header={<Text>Weekly summary</Text>}
        footer={<Button title="Details" variant="plain" onPress={() => console.log("details")} />}
      >
        <Text>You walked 42 km this week.</Text>
        <Text>That is 8 km more than last week.</Text>
      </Card>
    </View>
  );
}

mount(App);

#布局

  • 页眉:左右和顶部各有 16 pt 内边距。页眉中的文字为 semibold,字号比主题的 font.size 大 1 pt,所以一个普通的 <Text> 就能当标题用。页眉可以是任意 JSX,比如一行头像加姓名。
  • 内容:子元素纵向排列,间距 8 pt。有页眉时,内容顶部内边距为 8 pt,左右和底部为 16 pt;没有页眉时,四周都是 16 pt。
  • 页脚:靠末端对齐的一行,间距 8 pt,水平内边距 16 pt,垂直内边距 12 pt,上方有一条分隔线。操作按钮放在这里。
<Card
  variant="outlined"
  header={
    <View style={{ flexDirection: "row", alignItems: "center", gap: 10 }}>
      <View style={{ width: 32, height: 32, borderRadius: 16, backgroundColor: "#a78bfa" }} />
      <Text>Ada Lovelace</Text>
    </View>
  }
  footer={
    <View style={{ flexDirection: "row", gap: 8 }}>
      <Button title="Ignore" variant="plain" onPress={ignore} />
      <Button title="Reply" onPress={reply} />
    </View>
  }
>
  <Text>Can we move the meeting to Thursday?</Text>
</Card>

页眉和页脚只在你传入时存在:header={cond() ? <Text>…</Text> : undefined} 会添加或移除页眉,内容的内边距也会随之调整。

#变体

变体外观
elevated(默认)1 pt 边框加一层淡阴影。
outlined1 pt 边框,无阴影。
<Card>…</Card>
<Card variant="outlined">…</Card>

#Props

Prop类型默认值说明
variant"elevated" | "outlined""elevated"卡片是否带阴影。
headerJSX.Element无内容上方:标题或头像行。其中的文字为 semibold。
footerJSX.Element无内容下方、分隔线之后:操作按钮,靠末端对齐。
childrenJSX.Element无内容。
class、classList、style、testID、onLayout、ref作用于卡片的根视图。class 和 style 会覆盖卡片自身的样式。

Card 没有事件。把处理函数放在你自己的内容上。

#主题

部位主题键
表面group_box.background
文字颜色(内容会继承)group_box.foreground
边框和页脚分隔线border
圆角半径radius.lg(默认 16 pt)
页眉字号font.size + 1

参见主题。

#无障碍

卡片不添加任何角色或标签。屏幕阅读器按顺序把页眉、内容和页脚作为各自独立的元素朗读。

#相关内容

  • Group:圆角框内带分隔线的行。
  • Button:页脚中的操作。