- EntryRow:头像 + 标题(可带角标)+ 单行描述,整行可点; 点击语义内置(无修饰键接管,按住 Ctrl/Cmd/Shift/Alt 保留浏览器默认) - EntryTile:上图下文白卡,由 Card padding=none hoverable + Cover 拼成 - 展示页 #cards 补两个示例与 props,COMPONENTS.md 补契约与 changelog 兼容性:纯新增;应用侧的旧类名(.app-card / .ces-card / .ces-body)随之删除
70 lines
1.9 KiB
TypeScript
70 lines
1.9 KiB
TypeScript
import type { ReactNode } from 'react'
|
|
import { Avatar } from './Avatar'
|
|
import { Text } from './Typography'
|
|
import styles from './EntryRow.module.css'
|
|
|
|
/**
|
|
* 条目行:头像 + 标题(可带角标)+ 单行描述,整行可点。
|
|
*
|
|
* 点击语义跟站点里的其它卡片一致:不带修饰键时接管点击(走 SPA 路由),
|
|
* 按住 Ctrl/Cmd/Shift/Alt 时保留浏览器默认行为(新标签打开原始链接)。
|
|
*/
|
|
export type EntryRowProps = {
|
|
title: string
|
|
description?: string
|
|
/** 不带修饰键点击时跳转的地址 */
|
|
href?: string
|
|
/** 头像图片;不传时用 fallback 首字 */
|
|
avatar?: string
|
|
fallback?: string
|
|
/** 头像右下角的认证勾 */
|
|
verified?: boolean
|
|
/** 标题右侧的角标,例如 `<ItemBadge tone="hot">HOT</ItemBadge>` */
|
|
badge?: ReactNode
|
|
/** 接管点击;不传则保持普通链接行为 */
|
|
onOpen?: () => void
|
|
className?: string
|
|
}
|
|
|
|
export function EntryRow({
|
|
title,
|
|
description,
|
|
href = '#',
|
|
avatar,
|
|
fallback,
|
|
verified = false,
|
|
badge,
|
|
onOpen,
|
|
className,
|
|
}: EntryRowProps) {
|
|
return (
|
|
<a
|
|
className={[styles.root, className].filter(Boolean).join(' ')}
|
|
href={href}
|
|
title={title}
|
|
onClick={(event) => {
|
|
if (onOpen === undefined) return
|
|
if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return
|
|
event.preventDefault()
|
|
onOpen()
|
|
}}
|
|
>
|
|
<Avatar size="lg" tone="primary" src={avatar} fallback={fallback} verified={verified} />
|
|
|
|
<span className={styles.info}>
|
|
<span className={styles.titleRow}>
|
|
<Text strong truncate className={styles.title}>
|
|
{title}
|
|
</Text>
|
|
{badge}
|
|
</span>
|
|
{description !== undefined && description !== '' && (
|
|
<Text size="xs" tone="muted" truncate>
|
|
{description}
|
|
</Text>
|
|
)}
|
|
</span>
|
|
</a>
|
|
)
|
|
}
|