1ad25e30f8
新增 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}
|
|
/>
|
|
);
|
|
}
|