新增 PlatformUiKit 通用弹窗、按钮、状态、空态、媒体、表单和标签等公共组件 迁移结果页、创作工作台、认证入口、RPG 暗色面板和运行态弹窗的重复 UI chrome 补充组件测试、页面回归测试、技术文档和 Hermes 共享决策记录
134 lines
3.3 KiB
TypeScript
134 lines
3.3 KiB
TypeScript
import { Copy } from 'lucide-react';
|
||
import type { ButtonHTMLAttributes, ReactNode } from 'react';
|
||
|
||
import {
|
||
CopyFeedbackButton,
|
||
type CopyFeedbackButtonActionAppearance,
|
||
} from './CopyFeedbackButton';
|
||
import type {
|
||
PlatformPillBadgeSize,
|
||
PlatformPillBadgeTone,
|
||
} from './platformPillBadgeModel';
|
||
import type { CopyFeedbackState } from './useCopyFeedback';
|
||
|
||
type CopyCodeButtonProps = Omit<
|
||
ButtonHTMLAttributes<HTMLButtonElement>,
|
||
'children'
|
||
> & {
|
||
state: CopyFeedbackState;
|
||
code: string;
|
||
codeLabel?: ReactNode;
|
||
copiedSuffix?: ReactNode;
|
||
failedSuffix?: ReactNode;
|
||
idleIcon?: ReactNode;
|
||
copiedIcon?: ReactNode;
|
||
failedIcon?: ReactNode;
|
||
showIcon?: boolean;
|
||
labelClassName?: string;
|
||
codeClassName?: string;
|
||
suffixClassName?: string;
|
||
accessibleLabel?: string;
|
||
title?: string;
|
||
actionAppearance?: CopyFeedbackButtonActionAppearance;
|
||
actionPillTone?: PlatformPillBadgeTone;
|
||
actionPillSize?: PlatformPillBadgeSize;
|
||
};
|
||
|
||
function resolveCodeLabelText(codeLabel: ReactNode) {
|
||
return typeof codeLabel === 'string' ? codeLabel : '内容';
|
||
}
|
||
|
||
function renderCodeLabel({
|
||
code,
|
||
codeLabel,
|
||
codeClassName,
|
||
labelClassName,
|
||
suffix,
|
||
suffixClassName,
|
||
}: {
|
||
code: string;
|
||
codeLabel: ReactNode;
|
||
codeClassName?: string;
|
||
labelClassName?: string;
|
||
suffix?: ReactNode;
|
||
suffixClassName?: string;
|
||
}) {
|
||
return (
|
||
<>
|
||
{codeLabel ? <span className={labelClassName}>{codeLabel}</span> : null}
|
||
<span className={codeClassName}>{code}</span>
|
||
{suffix ? <span className={suffixClassName}>{suffix}</span> : null}
|
||
</>
|
||
);
|
||
}
|
||
|
||
/**
|
||
* 统一代码复制按钮。
|
||
* 用于作品号、用户号等短代码 chip,收口三态文案和默认可访问名称。
|
||
*/
|
||
export function CopyCodeButton({
|
||
state,
|
||
code,
|
||
codeLabel = '作品号',
|
||
copiedSuffix = '已复制',
|
||
failedSuffix = '复制失败',
|
||
idleIcon = <Copy className="h-4 w-4" />,
|
||
copiedIcon,
|
||
failedIcon,
|
||
showIcon = true,
|
||
labelClassName,
|
||
codeClassName,
|
||
suffixClassName,
|
||
accessibleLabel,
|
||
title,
|
||
actionAppearance,
|
||
actionPillTone,
|
||
actionPillSize,
|
||
...buttonProps
|
||
}: CopyCodeButtonProps) {
|
||
const labelText = resolveCodeLabelText(codeLabel);
|
||
const defaultAccessibleLabel =
|
||
labelText === '内容' ? `复制 ${code}` : `复制${labelText} ${code}`;
|
||
const defaultTitle = labelText === '内容' ? '复制' : `复制${labelText}`;
|
||
|
||
return (
|
||
<CopyFeedbackButton
|
||
{...buttonProps}
|
||
state={state}
|
||
idleLabel={renderCodeLabel({
|
||
code,
|
||
codeLabel,
|
||
codeClassName,
|
||
labelClassName,
|
||
})}
|
||
copiedLabel={renderCodeLabel({
|
||
code,
|
||
codeLabel,
|
||
codeClassName,
|
||
labelClassName,
|
||
suffix: copiedSuffix,
|
||
suffixClassName,
|
||
})}
|
||
failedLabel={renderCodeLabel({
|
||
code,
|
||
codeLabel,
|
||
codeClassName,
|
||
labelClassName,
|
||
suffix: failedSuffix,
|
||
suffixClassName,
|
||
})}
|
||
idleIcon={idleIcon}
|
||
copiedIcon={copiedIcon ?? idleIcon}
|
||
failedIcon={failedIcon ?? idleIcon}
|
||
showIcon={showIcon}
|
||
actionAppearance={actionAppearance}
|
||
actionPillTone={actionPillTone}
|
||
actionPillSize={actionPillSize}
|
||
aria-label={
|
||
buttonProps['aria-label'] ?? accessibleLabel ?? defaultAccessibleLabel
|
||
}
|
||
title={title ?? defaultTitle}
|
||
/>
|
||
);
|
||
}
|