gongxiangp2/components/detail/MaterialDetailHeader.tsx

169 lines
5.9 KiB
TypeScript

'use client'
type MaterialDetailAction =
| 'favorite'
| 'share'
| 'edit'
| 'delete'
| null
export type MaterialDetailCapabilities = {
canFavorite: boolean
canEdit: boolean
canDelete: boolean
}
type MaterialDetailHeaderProps = {
title: string
materialType: string
materialId: string
isFavorited: boolean
capabilities: MaterialDetailCapabilities
busyAction?: MaterialDetailAction
onBack: () => void
onFavorite: () => void
onShare: () => void
onEdit: () => void
onDelete: () => void
}
type ActionButtonProps = {
label: string
title?: string
danger?: boolean
pressed?: boolean
disabled?: boolean
onClick: () => void
children: React.ReactNode
}
function ActionButton({
label,
title = label,
danger = false,
pressed,
disabled = false,
onClick,
children,
}: ActionButtonProps) {
return (
<button
type="button"
onClick={onClick}
disabled={disabled}
aria-label={title}
aria-pressed={pressed}
className={`inline-flex min-h-11 items-center justify-center gap-2 whitespace-nowrap rounded-[8px] border px-3 text-[13px] font-medium tracking-[-0.02em] transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#1a1c1d]/70 focus-visible:ring-offset-2 disabled:cursor-wait disabled:opacity-50 ${
danger
? 'border-[#e7e8ea] text-[#b42318] hover:bg-[#fafafa]'
: pressed
? 'border-[#1a1c1d] bg-[#1a1c1d] text-white hover:bg-[#2a2d2f]'
: 'border-[#e7e8ea] bg-white text-[#1a1c1d] hover:border-[#1a1c1d]'
}`}
>
{children}
<span>{label}</span>
</button>
)
}
export default function MaterialDetailHeader({
title,
materialType,
materialId,
isFavorited,
capabilities,
busyAction = null,
onBack,
onFavorite,
onShare,
onEdit,
onDelete,
}: MaterialDetailHeaderProps) {
const controlsDisabled = busyAction !== null
return (
<section
aria-label="素材详情操作"
className="mx-auto mb-6 max-w-[1280px] border-b border-[#e7e8ea] px-0 pb-5"
>
<div className="flex flex-col gap-3 lg:flex-row lg:items-center">
<div className="flex min-w-0 flex-1 items-center gap-3">
<button
type="button"
onClick={onBack}
aria-label="返回上一页"
className="inline-flex h-11 w-11 flex-none items-center justify-center rounded-[8px] border border-[#e7e8ea] text-[#1a1c1d] transition-colors hover:bg-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#1a1c1d]/70 focus-visible:ring-offset-2"
>
<svg className="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
</svg>
</button>
<div className="min-w-0">
<h1 className="truncate text-[20px] font-semibold tracking-[-0.03em] text-[#1a1c1d] sm:text-[22px]" title={title}>
{title}
</h1>
<p className="mt-1 flex min-w-0 items-center gap-2 text-[12px] text-[#86898e]">
<span className="flex-none">{materialType}</span>
<span aria-hidden="true">·</span>
<span className="truncate font-en tabular-nums tracking-[0.04em]" title={materialId}>ID {materialId}</span>
</p>
</div>
</div>
<div className="grid flex-none grid-cols-2 gap-2 sm:flex sm:flex-wrap sm:justify-end">
{capabilities.canFavorite && (
<ActionButton
label={isFavorited ? '已收藏' : '收藏'}
title={isFavorited ? '取消收藏' : '收藏'}
pressed={isFavorited}
disabled={controlsDisabled}
onClick={onFavorite}
>
<svg className="h-5 w-5" fill={isFavorited ? 'currentColor' : 'none'} stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={isFavorited ? 0 : 2} d="M6 4h12a1 1 0 011 1v15.382a.5.5 0 01-.777.416L12 17.5l-6.223 3.298A.5.5 0 015 20.382V5a1 1 0 011-1z" />
</svg>
</ActionButton>
)}
<ActionButton
label={busyAction === 'share' ? '复制中' : '分享'}
disabled={controlsDisabled}
onClick={onShare}
>
<svg className="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8.684 13.342A3 3 0 108.684 10.658m0 2.684l6.632 3.316m-6.632-6l6.632-3.316m0 0a3 3 0 105.367-2.684 3 3 0 00-5.367 2.684zm0 9.316a3 3 0 105.368 2.684 3 3 0 00-5.368-2.684z" />
</svg>
</ActionButton>
{capabilities.canEdit && (
<ActionButton
label="编辑"
disabled={controlsDisabled}
onClick={onEdit}
>
<svg className="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
</svg>
</ActionButton>
)}
{capabilities.canDelete && (
<ActionButton
label="删除"
danger
disabled={controlsDisabled}
onClick={onDelete}
>
<svg className="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>
</ActionButton>
)}
</div>
</div>
</section>
)
}