收口前端平台组件库能力
新增 PlatformUiKit 通用弹窗、按钮、状态、空态、媒体、表单和标签等公共组件 迁移结果页、创作工作台、认证入口、RPG 暗色面板和运行态弹窗的重复 UI chrome 补充组件测试、页面回归测试、技术文档和 Hermes 共享决策记录
This commit is contained in:
133
src/components/common/CopyCodeButton.tsx
Normal file
133
src/components/common/CopyCodeButton.tsx
Normal file
@@ -0,0 +1,133 @@
|
||||
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}
|
||||
/>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user