import { type CSSProperties, type Dispatch, type ReactNode, type RefObject, type SetStateAction, } from 'react'; import { createPortal } from 'react-dom'; import { PlatformFloatingMenu, PlatformFloatingMenuItem, } from '../common/PlatformFloatingMenu'; import { ImageCanvasBasicGenerationComposerView } from './ImageCanvasBasicGenerationComposerView'; import { ImageCanvasCharacterAnimationPanelView } from './ImageCanvasCharacterAnimationPanelView'; import { ImageCanvasCharacterGenerationComposerView } from './ImageCanvasCharacterGenerationComposerView'; import { ImageCanvasEditGenerationModalView } from './ImageCanvasEditGenerationModalView'; import { ImageCanvasIconSpritesheetComposerView } from './ImageCanvasIconSpritesheetComposerView'; import { ImageCanvasPublicationMaterialsDemoPanelView } from './ImageCanvasPublicationMaterialsDemoPanelView'; import { getPublicationMaterialsWorkflow, PUBLICATION_MATERIALS_WORKFLOWS, } from './ImageCanvasPublicationMaterialsModel'; import { ImageCanvasQuickEditPanelView } from './ImageCanvasQuickEditPanelView'; import { ImageCanvasSpecGenerationPanelView } from './ImageCanvasSpecGenerationPanelView'; import { SPEC_TYPE_LABEL } from './ImageCanvasGenerationModel'; import type { CharacterAnimationPanelState, CanvasLayer, GenerateDialogState, QuickEditPanelState, SpecFormValues, SpecGenerationType, UploadTarget, } from './ImageCanvasEditorTypes'; type ImageCanvasGenerationComposerViewProps = { specToolWrapRef: RefObject; publicationToolWrapRef: RefObject; publicationReferenceButtonRef: RefObject; characterSpecButtonRef: RefObject; characterReferenceButtonRef: RefObject; iconSpecButtonRef: RefObject; isSpecMenuOpen: boolean; isCharacterSpecMenuOpen: boolean; isCharacterReferenceMenuOpen: boolean; isIconSpecMenuOpen: boolean; isPublicationMenuOpen: boolean; isPublicationReferenceMenuOpen: boolean; isPickingCharacterSpecFromCanvas: boolean; isPickingCharacterReferenceFromCanvas: boolean; isPickingIconSpecFromCanvas: boolean; isPickingPublicationReferenceFromCanvas: boolean; generateDialog: GenerateDialogState | null; generationComposerStyle: CSSProperties | null; iconComposerStyle: CSSProperties | null; quickEditPanel: QuickEditPanelState | null; quickEditSourceLayer: CanvasLayer | null; quickEditPanelStyle: CSSProperties | null; quickEditSizeOptions: string[]; quickEditModelOptions: Array<{ label: string; value: string }>; characterAnimationPanel: CharacterAnimationPanelState | null; characterAnimationSourceLayer: CanvasLayer | null; characterAnimationPanelStyle: CSSProperties | null; characterAnimationPrice: number; setGenerateDialog: Dispatch>; setQuickEditPanel: Dispatch>; setCharacterAnimationPanel: Dispatch< SetStateAction >; setIsCharacterSpecMenuOpen: Dispatch>; setIsCharacterReferenceMenuOpen: Dispatch>; setIsIconSpecMenuOpen: Dispatch>; setIsPublicationMenuOpen: Dispatch>; setIsPublicationReferenceMenuOpen: Dispatch>; setIsPickingCharacterSpecFromCanvas: Dispatch>; setIsPickingCharacterReferenceFromCanvas: Dispatch>; setIsPickingIconSpecFromCanvas: Dispatch>; setIsPickingPublicationReferenceFromCanvas: Dispatch< SetStateAction >; onOpenSpecDialog: (specType: SpecGenerationType) => void; onOpenPublicationWorkflow: ( workflowId: NonNullable, ) => void; onRequestUpload: (target: UploadTarget) => void; onSubmitImageGeneration: (dialog: GenerateDialogState) => void; onSubmitIconSpritesheetGeneration: (dialog: GenerateDialogState) => void; onSubmitQuickEdit: () => void; onSubmitCharacterAnimation: () => void; onCloseGenerateComposer: () => void; onUpdateSpecFormValue: (key: keyof SpecFormValues, value: string) => void; onUpdateIconDescription: (index: number, value: string) => void; onAddIconDescription: () => void; onUpdateCharacterAnimationDuration: (frameCountValue: string) => void; onRememberImageModel: (model: string) => void; }; function buildPortalMenuStyle( anchor: HTMLElement | null, placement: 'above' | 'below', ): CSSProperties { const rect = anchor?.getBoundingClientRect(); if (!rect) { return { position: 'fixed', left: 0, top: 0, right: 'auto', bottom: 'auto', zIndex: 70, }; } return { position: 'fixed', left: Math.round(rect.left), top: placement === 'above' ? Math.round(rect.top) : Math.round(rect.bottom + 8), right: 'auto', bottom: 'auto', zIndex: 70, transform: placement === 'above' ? 'translateY(calc(-100% - 0.45rem))' : undefined, }; } function renderEditorPortal(node: ReactNode) { if (typeof document === 'undefined') { return node; } return createPortal(node, document.body); } export function ImageCanvasGenerationComposerView({ specToolWrapRef, publicationToolWrapRef, publicationReferenceButtonRef, characterSpecButtonRef, characterReferenceButtonRef, iconSpecButtonRef, isSpecMenuOpen, isCharacterSpecMenuOpen, isCharacterReferenceMenuOpen, isIconSpecMenuOpen, isPublicationMenuOpen, isPublicationReferenceMenuOpen, isPickingCharacterSpecFromCanvas, isPickingCharacterReferenceFromCanvas, isPickingIconSpecFromCanvas, isPickingPublicationReferenceFromCanvas, generateDialog, generationComposerStyle, iconComposerStyle, quickEditPanel, quickEditSourceLayer, quickEditPanelStyle, quickEditSizeOptions, quickEditModelOptions, characterAnimationPanel, characterAnimationSourceLayer, characterAnimationPanelStyle, characterAnimationPrice, setGenerateDialog, setQuickEditPanel, setCharacterAnimationPanel, setIsCharacterSpecMenuOpen, setIsCharacterReferenceMenuOpen, setIsIconSpecMenuOpen, setIsPublicationMenuOpen, setIsPublicationReferenceMenuOpen, setIsPickingCharacterSpecFromCanvas, setIsPickingCharacterReferenceFromCanvas, setIsPickingIconSpecFromCanvas, setIsPickingPublicationReferenceFromCanvas, onOpenSpecDialog, onOpenPublicationWorkflow, onRequestUpload, onSubmitImageGeneration, onSubmitIconSpritesheetGeneration, onSubmitQuickEdit, onSubmitCharacterAnimation, onCloseGenerateComposer, onUpdateSpecFormValue, onUpdateIconDescription, onAddIconDescription, onUpdateCharacterAnimationDuration, onRememberImageModel, }: ImageCanvasGenerationComposerViewProps) { return ( <> {isSpecMenuOpen ? renderEditorPortal( {(['character', 'ui', 'custom'] as const).map((specType) => ( onOpenSpecDialog(specType)} > {SPEC_TYPE_LABEL[specType]} ))} , ) : null} {isPublicationMenuOpen ? renderEditorPortal( {PUBLICATION_MATERIALS_WORKFLOWS.map((workflow) => ( onOpenPublicationWorkflow(workflow.id)} > {workflow.label} ))} , ) : null} {generateDialog?.mode === 'generate' && generateDialog.composerOpen !== false && generationComposerStyle ? ( ) : null} {generateDialog?.mode === 'publication' && generateDialog.composerOpen !== false && generationComposerStyle ? ( ) : null} {generateDialog?.mode === 'spec' && generateDialog.composerOpen !== false && generationComposerStyle ? ( ) : null} {generateDialog?.mode === 'character' && generationComposerStyle ? ( ) : null} {generateDialog?.mode === 'icon' && generateDialog.composerOpen !== false && iconComposerStyle ? ( ) : null} {isPickingCharacterSpecFromCanvas ? (
请选择画布中的图片作为角色形象规范,按 Esc 退出
) : null} {isPickingCharacterReferenceFromCanvas ? (
请选择画布中的图片作为常规参考图,按 Esc 退出
) : null} {isPickingIconSpecFromCanvas ? (
请选择画布中的图标素材规范,按 Esc 退出
) : null} {isPickingPublicationReferenceFromCanvas ? (
请选择画布中的图片作为宣发素材参考图,按 Esc 退出
) : null} {quickEditPanel && quickEditPanel.status !== 'generating' && quickEditSourceLayer && quickEditPanelStyle ? ( ) : null} {characterAnimationPanel && characterAnimationSourceLayer && characterAnimationPanelStyle ? ( ) : null} ); }