feat(web): components update
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-02 15:21:14
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-04 13:49:05
|
||||
* @Last Modified time: 2026-02-25 16:20:39
|
||||
*/
|
||||
/**
|
||||
* RbCard Component
|
||||
@@ -27,6 +27,7 @@ interface RbCardProps {
|
||||
headerClassName?: string;
|
||||
/** Card title (string, ReactNode, or function) */
|
||||
title?: string | ReactNode | (() => ReactNode);
|
||||
titleClassName?: string;
|
||||
/** Subtitle text displayed below title */
|
||||
subTitle?: string | ReactNode;
|
||||
/** Extra content displayed in header (top-right) */
|
||||
@@ -51,13 +52,14 @@ interface RbCardProps {
|
||||
className?: string;
|
||||
/** Click handler */
|
||||
onClick?: () => void;
|
||||
variant?: 'borderL';
|
||||
variant?: 'borderL' | 'borderless' | 'outlined';
|
||||
}
|
||||
|
||||
/** Custom card component with flexible styling and header options */
|
||||
const RbCard: FC<RbCardProps> = ({
|
||||
headerClassName,
|
||||
title,
|
||||
titleClassName,
|
||||
subTitle,
|
||||
extra,
|
||||
children,
|
||||
@@ -66,10 +68,10 @@ const RbCard: FC<RbCardProps> = ({
|
||||
bodyPadding,
|
||||
bodyClassName: bodyClassNames,
|
||||
headerType = 'border',
|
||||
bgColor = '#FBFDFF',
|
||||
bgColor = '#FFFFFF',
|
||||
height = 'auto',
|
||||
className,
|
||||
variant,
|
||||
variant = 'borderless',
|
||||
...props
|
||||
}) => {
|
||||
/** Calculate body padding based on header type and avatar presence */
|
||||
@@ -78,7 +80,7 @@ const RbCard: FC<RbCardProps> = ({
|
||||
: headerType === 'borderL'
|
||||
? 'rb:p-[0_16px_12px_16px]!'
|
||||
: avatarUrl || avatar
|
||||
? 'rb:p-[16px_20px_16px_16px]!'
|
||||
? 'rb:p-4!'
|
||||
: (headerType === 'borderless')
|
||||
? 'rb:p-[0_20px_16px_16px]!'
|
||||
: (headerType === 'border' && !avatarUrl && !avatar) || headerType === 'borderBL'
|
||||
@@ -88,15 +90,15 @@ const RbCard: FC<RbCardProps> = ({
|
||||
if (variant === 'borderL') {
|
||||
return (
|
||||
<div
|
||||
className="rb:p-[12px_16px] rb:rounded-lg rb:shadow-[inset_4px_0px_0px_0px_#155EEF] rb:border rb:border-[#DFE4ED]"
|
||||
className="rb:p-[12px_16px] rb:rounded-lg rb:shadow-[inset_4px_0px_0px_0px_#155EEF] rb-border"
|
||||
>
|
||||
<Flex justify="space-between" className={`rb:mb-3! ${headerClassName || ''}`}>
|
||||
<Flex vertical gap={4}>
|
||||
<div className="rb:font-medium rb:leading-5.5">
|
||||
{typeof title === 'function' ? title() : title ?
|
||||
<div className="rb:flex rb:items-center">
|
||||
<Flex align="center">
|
||||
{avatarUrl
|
||||
? <img src={avatarUrl} className="rb:mr-3.25 rb:w-12 rb:h-12 rb:rounded-lg" />
|
||||
? <img src={avatarUrl} alt={avatarUrl} className="rb:mr-3.25 rb:size-12 rb:rounded-lg" />
|
||||
: avatar ? avatar : null
|
||||
}
|
||||
<div className={
|
||||
@@ -107,10 +109,10 @@ const RbCard: FC<RbCardProps> = ({
|
||||
}
|
||||
)
|
||||
}>
|
||||
<div className="rb:w-full rb:text-ellipsis rb:overflow-hidden rb:whitespace-nowrap">{title}</div>
|
||||
<div className={`rb:w-full rb:text-ellipsis rb:overflow-hidden rb:whitespace-nowrap ${titleClassName}`}>{title}</div>
|
||||
{subTitle && <div className="rb:text-[#5B6167] rb:text-[12px]">{subTitle}</div>}
|
||||
</div>
|
||||
</div> : null
|
||||
</Flex> : null
|
||||
}
|
||||
</div>
|
||||
{subTitle && <div className="rb:text-[12px] rb:text-[#5B6167] rb:font-regular rb:leading-4">{subTitle}</div>}
|
||||
@@ -125,12 +127,13 @@ const RbCard: FC<RbCardProps> = ({
|
||||
}
|
||||
return (
|
||||
<Card
|
||||
variant={variant}
|
||||
{...props}
|
||||
title={typeof title === 'function' ? title() : title ?
|
||||
<div className="rb:flex rb:items-center rb:gap-2">
|
||||
<Flex align="center" gap={12}>
|
||||
{/* Avatar image or custom avatar component */}
|
||||
{avatarUrl
|
||||
? <img src={avatarUrl} className="rb:mr-3.25 rb:w-12 rb:h-12 rb:rounded-lg" />
|
||||
? <img src={avatarUrl} alt={avatarUrl} className="rb:mr-3.25 rb:size-12 rb:rounded-lg" />
|
||||
: avatar ? avatar : null
|
||||
}
|
||||
<div className={
|
||||
@@ -142,11 +145,11 @@ const RbCard: FC<RbCardProps> = ({
|
||||
)
|
||||
}>
|
||||
{/* Title with tooltip for overflow text */}
|
||||
<Tooltip title={title}><div className="rb:w-full rb:text-ellipsis rb:overflow-hidden rb:whitespace-nowrap">{title}</div></Tooltip>
|
||||
<Tooltip title={title}><div className={`rb:w-full rb:text-ellipsis rb:overflow-hidden rb:whitespace-nowrap ${titleClassName}`}>{title}</div></Tooltip>
|
||||
{/* Optional subtitle */}
|
||||
{subTitle && <div className="rb:text-[#5B6167] rb:text-[12px]">{subTitle}</div>}
|
||||
</div>
|
||||
</div> : null
|
||||
</Flex> : null
|
||||
}
|
||||
extra={extra}
|
||||
classNames={{
|
||||
@@ -154,11 +157,11 @@ const RbCard: FC<RbCardProps> = ({
|
||||
'rb:font-medium',
|
||||
{
|
||||
/** Borderless header style */
|
||||
'rb:border-[0]! rb:text-[16px] rb:p-[0_16px]!': headerType === 'borderless',
|
||||
'rb:border-[0]! rb:text-[16px] rb:p-[0_16px]! rb:min-h-10!': headerType === 'borderless',
|
||||
/** Header with avatar */
|
||||
'rb:border-[0]! rb:text-[16px] rb:p-[16px_16px_0_16px]!': avatarUrl || avatar,
|
||||
/** Standard border header */
|
||||
'rb:text-[18px] rb:p-[0]! rb:m-[0_20px]!': headerType === 'border' && !avatarUrl && !avatar,
|
||||
'rb:text-[18px] rb:p-[0]! rb:m-[0_20px]! rb:border-b-[0.5px]!': headerType === 'border' && !avatarUrl && !avatar,
|
||||
/** Border bottom-left style */
|
||||
"rb:m-[0_16px]! rb:p-[0]! rb:relative rb:before:content-[''] rb:before:w-[4px] rb:before:h-[16px] rb:before:bg-[#5B6167] rb:before:absolute rb:before:top-[50%] rb:before:left-[-16px] rb:before:translate-y-[-50%] rb:before:bg-[#5B6167]! rb:before:h-[16px]!": headerType === 'borderBL',
|
||||
/** Border left style */
|
||||
@@ -170,9 +173,13 @@ const RbCard: FC<RbCardProps> = ({
|
||||
}}
|
||||
style={{
|
||||
background: bgColor,
|
||||
height: height
|
||||
height: height,
|
||||
border: variant === 'outlined' ? '1px solid #EBEBEB' : 'none'
|
||||
}}
|
||||
className={`rb:hover:shadow-[0px_2px_4px_0px_rgba(0,0,0,0.15)] ${className}`}
|
||||
className={clsx({
|
||||
'rb:shadow-none!': variant === 'borderless' || variant === 'outlined',
|
||||
'rb:hover:shadow-[0px_2px_8px_0px_rgba(23,23,25,0.16)]!': variant !== 'borderless' && variant !== 'outlined'
|
||||
}, className)}
|
||||
>
|
||||
{children}
|
||||
</Card>
|
||||
|
||||
76
web/src/components/RbCard/index.tsx
Normal file
76
web/src/components/RbCard/index.tsx
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-02 15:21:14
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-24 14:59:53
|
||||
*/
|
||||
/**
|
||||
* RbCard Component
|
||||
*
|
||||
* A customizable card component that extends Ant Design's Card with:
|
||||
* - Multiple header styles (border, borderless, borderBL, borderL)
|
||||
* - Avatar support with image or custom component
|
||||
* - Flexible padding and styling options
|
||||
* - Tooltip support for long titles
|
||||
* - Hover effects
|
||||
*
|
||||
* @component
|
||||
*/
|
||||
|
||||
import { type FC, type ReactNode } from 'react'
|
||||
import { Card, Tooltip, Flex, type CardProps } from 'antd';
|
||||
import clsx from 'clsx';
|
||||
|
||||
/** Props interface for RbCard component */
|
||||
interface RbCardProps extends CardProps {
|
||||
children?: ReactNode;
|
||||
/** Custom avatar component */
|
||||
avatarText?: string;
|
||||
avatarClassName?: string;
|
||||
/** Avatar image URL */
|
||||
avatarUrl?: string | null;
|
||||
/** Click handler */
|
||||
onClick?: () => void;
|
||||
footer?: ReactNode;
|
||||
}
|
||||
|
||||
/** Custom card component with flexible styling and header options */
|
||||
const RbCard: FC<RbCardProps> = ({
|
||||
title,
|
||||
children,
|
||||
avatarText,
|
||||
avatarClassName,
|
||||
avatarUrl,
|
||||
footer,
|
||||
...props
|
||||
}) => {
|
||||
return (
|
||||
<Card
|
||||
variant="borderless"
|
||||
{...props}
|
||||
title={<Flex align="center" gap={12}>
|
||||
{avatarUrl
|
||||
? <img src={avatarUrl} alt={avatarUrl} className="rb:size-12 rb:rounded-lg" />
|
||||
: avatarText
|
||||
? <Flex align="center" justify="center" className={clsx(avatarClassName, "rb:size-11 rb:rounded-lg rb:text-[24px] rb:text-[#ffffff] rb:bg-[#155EEF]")}>{avatarText}</Flex> : null
|
||||
}
|
||||
<Tooltip title={title}>
|
||||
<div className="rb:flex-1 rb:leading-5.5 rb:min-w-0 rb:whitespace-break-spaces rb:wrap-break-word rb:line-clamp-2">
|
||||
{title}
|
||||
</div>
|
||||
</Tooltip>
|
||||
</Flex>}
|
||||
classNames={{
|
||||
header: 'rb:text-[16px] rb:p-[16px_16px_8px_16px]! rb:border-0!',
|
||||
body: 'rb:p-4! rb:bg-white!',
|
||||
}}
|
||||
className="rb:hover:shadow-[0px_2px_8px_0px_rgba(23,23,25,0.16)]! rb:group"
|
||||
>
|
||||
{children}
|
||||
|
||||
{footer ? <div className="rb:mt-6">{footer}</div> : null}
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export default RbCard
|
||||
Reference in New Issue
Block a user