feat: add extralToolbarItems support to FlowsFloatContextMenu and FlowModelRenderer

This commit is contained in:
gchust 2025-06-26 09:24:12 +08:00
parent e6fcac4627
commit 7407884bae
2 changed files with 54 additions and 3 deletions

View File

@ -42,6 +42,7 @@ import React, { Suspense, useEffect } from 'react';
import { ErrorBoundary } from 'react-error-boundary'; import { ErrorBoundary } from 'react-error-boundary';
import { useApplyAutoFlows, FlowModelProvider } from '../hooks'; import { useApplyAutoFlows, FlowModelProvider } from '../hooks';
import { FlowModel } from '../models'; import { FlowModel } from '../models';
import { ToolbarItemConfig } from '../types';
import { FlowsContextMenu } from './settings/wrappers/contextual/FlowsContextMenu'; import { FlowsContextMenu } from './settings/wrappers/contextual/FlowsContextMenu';
import { FlowsFloatContextMenu } from './settings/wrappers/contextual/FlowsFloatContextMenu'; import { FlowsFloatContextMenu } from './settings/wrappers/contextual/FlowsFloatContextMenu';
import { FlowErrorFallback } from './FlowErrorFallback'; import { FlowErrorFallback } from './FlowErrorFallback';
@ -75,6 +76,9 @@ interface FlowModelRendererProps {
/** 设置菜单层级1=仅当前模型(默认)2=包含子模型 */ /** 设置菜单层级1=仅当前模型(默认)2=包含子模型 */
settingsMenuLevel?: number; settingsMenuLevel?: number;
/** 额外的工具栏项目,仅应用于此实例 */
extralToolbarItems?: ToolbarItemConfig[];
} }
/** /**
@ -89,6 +93,7 @@ const FlowModelRendererWithAutoFlows: React.FC<{
sharedContext?: Record<string, any>; sharedContext?: Record<string, any>;
showErrorFallback?: boolean; showErrorFallback?: boolean;
settingsMenuLevel?: number; settingsMenuLevel?: number;
extralToolbarItems?: ToolbarItemConfig[];
}> = observer( }> = observer(
({ ({
model, model,
@ -99,6 +104,7 @@ const FlowModelRendererWithAutoFlows: React.FC<{
sharedContext, sharedContext,
showErrorFallback, showErrorFallback,
settingsMenuLevel, settingsMenuLevel,
extralToolbarItems,
}) => { }) => {
useApplyAutoFlows(model, extraContext); useApplyAutoFlows(model, extraContext);
@ -111,6 +117,7 @@ const FlowModelRendererWithAutoFlows: React.FC<{
hideRemoveInSettings={hideRemoveInSettings} hideRemoveInSettings={hideRemoveInSettings}
showErrorFallback={showErrorFallback} showErrorFallback={showErrorFallback}
settingsMenuLevel={settingsMenuLevel} settingsMenuLevel={settingsMenuLevel}
extralToolbarItems={extralToolbarItems}
/> />
</FlowModelProvider> </FlowModelProvider>
); );
@ -128,6 +135,7 @@ const FlowModelRendererWithoutAutoFlows: React.FC<{
sharedContext?: Record<string, any>; sharedContext?: Record<string, any>;
showErrorFallback?: boolean; showErrorFallback?: boolean;
settingsMenuLevel?: number; settingsMenuLevel?: number;
extralToolbarItems?: ToolbarItemConfig[];
}> = observer( }> = observer(
({ ({
model, model,
@ -137,6 +145,7 @@ const FlowModelRendererWithoutAutoFlows: React.FC<{
sharedContext, sharedContext,
showErrorFallback, showErrorFallback,
settingsMenuLevel, settingsMenuLevel,
extralToolbarItems,
}) => { }) => {
return ( return (
<FlowModelProvider model={model}> <FlowModelProvider model={model}>
@ -147,6 +156,7 @@ const FlowModelRendererWithoutAutoFlows: React.FC<{
hideRemoveInSettings={hideRemoveInSettings} hideRemoveInSettings={hideRemoveInSettings}
showErrorFallback={showErrorFallback} showErrorFallback={showErrorFallback}
settingsMenuLevel={settingsMenuLevel} settingsMenuLevel={settingsMenuLevel}
extralToolbarItems={extralToolbarItems}
/> />
</FlowModelProvider> </FlowModelProvider>
); );
@ -163,8 +173,17 @@ const FlowModelRendererCore: React.FC<{
hideRemoveInSettings: boolean; hideRemoveInSettings: boolean;
showErrorFallback?: boolean; showErrorFallback?: boolean;
settingsMenuLevel?: number; settingsMenuLevel?: number;
extralToolbarItems?: ToolbarItemConfig[];
}> = observer( }> = observer(
({ model, showFlowSettings, flowSettingsVariant, hideRemoveInSettings, showErrorFallback, settingsMenuLevel }) => { ({
model,
showFlowSettings,
flowSettingsVariant,
hideRemoveInSettings,
showErrorFallback,
settingsMenuLevel,
extralToolbarItems,
}) => {
// 渲染模型内容 // 渲染模型内容
const modelContent = model.render(); const modelContent = model.render();
@ -191,6 +210,7 @@ const FlowModelRendererCore: React.FC<{
showBackground={_.isObject(showFlowSettings) ? showFlowSettings.showBackground : undefined} showBackground={_.isObject(showFlowSettings) ? showFlowSettings.showBackground : undefined}
showBorder={_.isObject(showFlowSettings) ? showFlowSettings.showBorder : undefined} showBorder={_.isObject(showFlowSettings) ? showFlowSettings.showBorder : undefined}
settingsMenuLevel={settingsMenuLevel} settingsMenuLevel={settingsMenuLevel}
extralToolbarItems={extralToolbarItems}
> >
{wrapWithErrorBoundary(modelContent)} {wrapWithErrorBoundary(modelContent)}
</FlowsFloatContextMenu> </FlowsFloatContextMenu>
@ -224,6 +244,7 @@ const FlowModelRendererCore: React.FC<{
showBackground={_.isObject(showFlowSettings) ? showFlowSettings.showBackground : undefined} showBackground={_.isObject(showFlowSettings) ? showFlowSettings.showBackground : undefined}
showBorder={_.isObject(showFlowSettings) ? showFlowSettings.showBorder : undefined} showBorder={_.isObject(showFlowSettings) ? showFlowSettings.showBorder : undefined}
settingsMenuLevel={settingsMenuLevel} settingsMenuLevel={settingsMenuLevel}
extralToolbarItems={extralToolbarItems}
> >
{wrapWithErrorBoundary(modelContent)} {wrapWithErrorBoundary(modelContent)}
</FlowsFloatContextMenu> </FlowsFloatContextMenu>
@ -246,6 +267,7 @@ const FlowModelRendererCore: React.FC<{
* @param {any} props.extraContext - Extra context to pass to useApplyAutoFlows when skipApplyAutoFlows is false. * @param {any} props.extraContext - Extra context to pass to useApplyAutoFlows when skipApplyAutoFlows is false.
* @param {any} props.sharedContext - Shared context to pass to the model. * @param {any} props.sharedContext - Shared context to pass to the model.
* @param {number} props.settingsMenuLevel - Settings menu levels: 1=current model only (default), 2=include sub-models. * @param {number} props.settingsMenuLevel - Settings menu levels: 1=current model only (default), 2=include sub-models.
* @param {ToolbarItemConfig[]} props.extralToolbarItems - Extra toolbar items to add to this renderer instance.
* @returns {React.ReactNode | null} The rendered output of the model, or null if the model or its render method is invalid. * @returns {React.ReactNode | null} The rendered output of the model, or null if the model or its render method is invalid.
*/ */
export const FlowModelRenderer: React.FC<FlowModelRendererProps> = observer( export const FlowModelRenderer: React.FC<FlowModelRendererProps> = observer(
@ -260,6 +282,7 @@ export const FlowModelRenderer: React.FC<FlowModelRendererProps> = observer(
sharedContext, sharedContext,
showErrorFallback = false, showErrorFallback = false,
settingsMenuLevel, settingsMenuLevel,
extralToolbarItems,
}) => { }) => {
if (!model || typeof model.render !== 'function') { if (!model || typeof model.render !== 'function') {
// 可以选择渲染 null 或者一个错误/提示信息 // 可以选择渲染 null 或者一个错误/提示信息
@ -283,6 +306,7 @@ export const FlowModelRenderer: React.FC<FlowModelRendererProps> = observer(
sharedContext={sharedContext} sharedContext={sharedContext}
showErrorFallback={showErrorFallback} showErrorFallback={showErrorFallback}
settingsMenuLevel={settingsMenuLevel} settingsMenuLevel={settingsMenuLevel}
extralToolbarItems={extralToolbarItems}
/> />
</Suspense> </Suspense>
); );
@ -298,6 +322,7 @@ export const FlowModelRenderer: React.FC<FlowModelRendererProps> = observer(
sharedContext={sharedContext} sharedContext={sharedContext}
showErrorFallback={showErrorFallback} showErrorFallback={showErrorFallback}
settingsMenuLevel={settingsMenuLevel} settingsMenuLevel={settingsMenuLevel}
extralToolbarItems={extralToolbarItems}
/> />
</Suspense> </Suspense>
); );

View File

@ -41,10 +41,17 @@ const renderToolbarItems = (
showCopyUidButton: boolean, showCopyUidButton: boolean,
flowEngine: FlowEngine, flowEngine: FlowEngine,
settingsMenuLevel?: number, settingsMenuLevel?: number,
extralToolbarItems?: ToolbarItemConfig[],
) => { ) => {
const toolbarItems = flowEngine?.flowSettings?.getToolbarItems?.() || []; const toolbarItems = flowEngine?.flowSettings?.getToolbarItems?.() || [];
return toolbarItems // 合并额外的工具栏项目
const allToolbarItems = [...toolbarItems, ...(extralToolbarItems || [])];
// 按 sort 字段排序
allToolbarItems.sort((a, b) => (a.sort || 0) - (b.sort || 0)).reverse();
return allToolbarItems
.filter((itemConfig: ToolbarItemConfig) => { .filter((itemConfig: ToolbarItemConfig) => {
// 检查项目是否应该显示 // 检查项目是否应该显示
return itemConfig.visible ? itemConfig.visible(model) : true; return itemConfig.visible ? itemConfig.visible(model) : true;
@ -150,6 +157,10 @@ interface ModelProvidedProps {
* Settings menu levels: 1=current model only (default), 2=include sub-models * Settings menu levels: 1=current model only (default), 2=include sub-models
*/ */
settingsMenuLevel?: number; settingsMenuLevel?: number;
/**
* Extra toolbar items to add to this context menu instance
*/
extralToolbarItems?: ToolbarItemConfig[];
} }
interface ModelByIdProps { interface ModelByIdProps {
@ -173,6 +184,10 @@ interface ModelByIdProps {
* Settings menu levels: 1=current model only (default), 2=include sub-models * Settings menu levels: 1=current model only (default), 2=include sub-models
*/ */
settingsMenuLevel?: number; settingsMenuLevel?: number;
/**
* Extra toolbar items to add to this context menu instance
*/
extralToolbarItems?: ToolbarItemConfig[];
} }
type FlowsFloatContextMenuProps = ModelProvidedProps | ModelByIdProps; type FlowsFloatContextMenuProps = ModelProvidedProps | ModelByIdProps;
@ -204,6 +219,7 @@ const isModelByIdProps = (props: FlowsFloatContextMenuProps): props is ModelById
* @param props.containerStyle * @param props.containerStyle
* @param props.className * @param props.className
* @param props.settingsMenuLevel 1=()2= * @param props.settingsMenuLevel 1=()2=
* @param props.extralToolbarItems
*/ */
const FlowsFloatContextMenu: React.FC<FlowsFloatContextMenuProps> = observer((props) => { const FlowsFloatContextMenu: React.FC<FlowsFloatContextMenuProps> = observer((props) => {
const flowEngine = useFlowEngine(); const flowEngine = useFlowEngine();
@ -231,6 +247,7 @@ const FlowsFloatContextMenuWithModel: React.FC<ModelProvidedProps> = observer(
showBackground = true, showBackground = true,
showBorder = true, showBorder = true,
settingsMenuLevel, settingsMenuLevel,
extralToolbarItems,
}: ModelProvidedProps) => { }: ModelProvidedProps) => {
const [hideMenu, setHideMenu] = useState<boolean>(false); const [hideMenu, setHideMenu] = useState<boolean>(false);
const [hasButton, setHasButton] = useState<boolean>(false); const [hasButton, setHasButton] = useState<boolean>(false);
@ -305,7 +322,14 @@ const FlowsFloatContextMenuWithModel: React.FC<ModelProvidedProps> = observer(
<div className="general-schema-designer"> <div className="general-schema-designer">
<div className="general-schema-designer-icons"> <div className="general-schema-designer-icons">
<Space size={3} align="center"> <Space size={3} align="center">
{renderToolbarItems(model, showDeleteButton, showCopyUidButton, flowEngine, settingsMenuLevel)} {renderToolbarItems(
model,
showDeleteButton,
showCopyUidButton,
flowEngine,
settingsMenuLevel,
extralToolbarItems,
)}
</Space> </Space>
</div> </div>
</div> </div>
@ -326,6 +350,7 @@ const FlowsFloatContextMenuWithModelById: React.FC<ModelByIdProps> = observer(
containerStyle, containerStyle,
className, className,
settingsMenuLevel, settingsMenuLevel,
extralToolbarItems,
}) => { }) => {
const model = useFlowModelById(uid, modelClassName); const model = useFlowModelById(uid, modelClassName);
@ -342,6 +367,7 @@ const FlowsFloatContextMenuWithModelById: React.FC<ModelByIdProps> = observer(
containerStyle={containerStyle} containerStyle={containerStyle}
className={className} className={className}
settingsMenuLevel={settingsMenuLevel} settingsMenuLevel={settingsMenuLevel}
extralToolbarItems={extralToolbarItems}
> >
{children} {children}
</FlowsFloatContextMenuWithModel> </FlowsFloatContextMenuWithModel>