Files
Genarrative/src/components/image-editor/ImageCanvasReferenceSlot.tsx
T
JenkenB ea29887d9c 完善快速编辑参考图与生成占位
- 快速编辑支持最多8张额外参考图,并将原图自动作为最后一张参考图提交

- 快速编辑尺寸和模型沿用原图配置,按禁用态参数胶囊展示

- 快速编辑提交前同步改写原图引用为实际图序号

- 快速编辑生成改为创建独立画布生成占位,并支持键盘删除生成中对象

- 对齐生成类面板参考图图标、规范类固定参数和相关文档测试
2026-06-18 22:50:42 +08:00

117 lines
2.9 KiB
TypeScript

import { type ReactNode, type RefObject } from 'react';
import { X } from 'lucide-react';
type ImageCanvasReferenceSlotTone =
| 'default'
| 'spec'
| 'icon'
| 'ui'
| 'character'
| 'quick-edit'
| 'video'
| 'audio';
type ImageCanvasReferenceSlotProps = {
buttonRef?: RefObject<HTMLButtonElement | null>;
tone?: ImageCanvasReferenceSlotTone;
className?: string;
icon: ReactNode;
label: string;
ariaLabel: string;
title?: string;
imageSrc?: string | null;
disabled?: boolean;
isAdd?: boolean;
onClick?: () => void;
onRemove?: () => void;
removeLabel?: string;
};
export function ImageCanvasReferenceSlot({
buttonRef,
tone = 'default',
className,
icon,
label,
ariaLabel,
title,
imageSrc,
disabled = false,
isAdd = false,
onClick,
onRemove,
removeLabel,
}: ImageCanvasReferenceSlotProps) {
const hasPreviewImage = Boolean(imageSrc);
const slotClassName = [
'image-canvas-editor__reference-chip',
`image-canvas-editor__reference-chip--${tone}`,
hasPreviewImage
? 'image-canvas-editor__reference-chip--has-image'
: 'image-canvas-editor__reference-chip--icon-only',
isAdd ? 'image-canvas-editor__reference-chip--upload' : null,
className,
]
.filter(Boolean)
.join(' ');
const slotContent = (
<>
<span className="image-canvas-editor__reference-chip-icon">
{hasPreviewImage ? (
<img src={imageSrc ?? undefined} alt="" aria-hidden="true" />
) : (
icon
)}
</span>
<span className="image-canvas-editor__reference-chip-label">{label}</span>
{title && title !== label ? (
<span className="image-canvas-editor__reference-chip-source-label">
{title}
</span>
) : null}
</>
);
return (
<span className="image-canvas-editor__reference-slot">
{onClick || buttonRef ? (
<button
ref={buttonRef}
type="button"
className={slotClassName}
disabled={disabled}
aria-label={ariaLabel}
title={title ?? ariaLabel}
onClick={onClick}
>
{slotContent}
</button>
) : (
<span
className={slotClassName}
aria-label={ariaLabel}
title={title ?? ariaLabel}
>
{slotContent}
</span>
)}
{onRemove ? (
<button
type="button"
className="image-canvas-editor__reference-chip-remove"
disabled={disabled}
aria-label={removeLabel ?? `删除${ariaLabel}`}
title="删除"
onPointerDown={(event) => event.stopPropagation()}
onClick={(event) => {
event.preventDefault();
event.stopPropagation();
onRemove();
}}
>
<X className="h-3 w-3" aria-hidden="true" />
</button>
) : null}
</span>
);
}