mirror of
https://gitee.com/ByteDance/flowgram.ai.git
synced 2025-07-07 17:43:29 +08:00
Merge pull request #6 from louisyoungx/feat/free-demo-node-panel
1. add node panel component 2. register node panel plugin 3. beautify minimap style 4. add node button access node panel 5. support add node through button on the line
This commit is contained in:
commit
0db1c612ed
@ -1,34 +1,22 @@
|
||||
import { useState } from 'react';
|
||||
|
||||
import { Button } from '@douyinfe/semi-ui';
|
||||
import { Popover } from '@douyinfe/semi-ui';
|
||||
import { IconPlus } from '@douyinfe/semi-icons';
|
||||
|
||||
import { NodeList } from './node-list';
|
||||
import { useAddNode } from './use-add-node';
|
||||
|
||||
export const AddNode = (props: { disabled: boolean }) => {
|
||||
const [visible, setVisible] = useState(false);
|
||||
|
||||
const addNode = useAddNode();
|
||||
return (
|
||||
<Popover
|
||||
visible={visible}
|
||||
onVisibleChange={props.disabled ? () => {} : setVisible}
|
||||
content={!props.disabled && <NodeList />}
|
||||
placement="right"
|
||||
trigger="click"
|
||||
popupAlign={{ offset: [30, 0] }}
|
||||
overlayStyle={{
|
||||
padding: 0,
|
||||
<Button
|
||||
icon={<IconPlus />}
|
||||
color="highlight"
|
||||
style={{ backgroundColor: 'rgba(171,181,255,0.3)', borderRadius: '8px' }}
|
||||
disabled={props.disabled}
|
||||
onClick={(e) => {
|
||||
const rect = e.currentTarget.getBoundingClientRect();
|
||||
addNode(rect);
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
icon={<IconPlus />}
|
||||
color="highlight"
|
||||
style={{ backgroundColor: 'rgba(171,181,255,0.3)', borderRadius: '8px' }}
|
||||
disabled={props.disabled}
|
||||
>
|
||||
Add Node
|
||||
</Button>
|
||||
</Popover>
|
||||
Add Node
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
@ -0,0 +1,72 @@
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { WorkflowNodePanelService } from '@flowgram.ai/free-node-panel-plugin';
|
||||
import {
|
||||
useService,
|
||||
WorkflowDocument,
|
||||
usePlayground,
|
||||
getAntiOverlapPosition,
|
||||
PositionSchema,
|
||||
WorkflowNodeEntity,
|
||||
WorkflowSelectService,
|
||||
} from '@flowgram.ai/free-layout-editor';
|
||||
|
||||
const useGetPanelPosition = () => {
|
||||
const playground = usePlayground();
|
||||
|
||||
return useCallback(
|
||||
(targetBoundingRect: DOMRect): PositionSchema =>
|
||||
playground.config.getPosFromMouseEvent({
|
||||
clientX: targetBoundingRect.left + 64,
|
||||
clientY: targetBoundingRect.top - 7,
|
||||
}),
|
||||
[playground]
|
||||
);
|
||||
};
|
||||
|
||||
const useSelectNode = () => {
|
||||
const selectService = useService(WorkflowSelectService);
|
||||
return useCallback(
|
||||
(node?: WorkflowNodeEntity) => {
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
selectService.selectNode(node);
|
||||
},
|
||||
[selectService]
|
||||
);
|
||||
};
|
||||
|
||||
export const useAddNode = () => {
|
||||
const workflowDocument = useService(WorkflowDocument);
|
||||
const nodePanelService = useService<WorkflowNodePanelService>(WorkflowNodePanelService);
|
||||
const playground = usePlayground();
|
||||
const getPanelPosition = useGetPanelPosition();
|
||||
const select = useSelectNode();
|
||||
|
||||
return useCallback(
|
||||
async (targetBoundingRect: DOMRect): Promise<void> => {
|
||||
const panelPosition = getPanelPosition(targetBoundingRect);
|
||||
await nodePanelService.call({
|
||||
panelPosition,
|
||||
customPosition: ({ selectPosition }) => {
|
||||
const nodeWidth = 360;
|
||||
const nodePanelOffset = 150 / playground.config.zoom;
|
||||
const customPositionX = panelPosition.x + nodeWidth / 2 + nodePanelOffset;
|
||||
const customNodePosition = getAntiOverlapPosition(workflowDocument, {
|
||||
x: customPositionX,
|
||||
y: selectPosition.y,
|
||||
});
|
||||
return {
|
||||
x: customNodePosition.x,
|
||||
y: customNodePosition.y,
|
||||
};
|
||||
},
|
||||
enableSelectPosition: true,
|
||||
enableMultiAdd: true,
|
||||
afterAddNode: select,
|
||||
});
|
||||
},
|
||||
[getPanelPosition, nodePanelService, playground.config.zoom, workflowDocument, select]
|
||||
);
|
||||
};
|
||||
@ -1 +1,3 @@
|
||||
export * from './base-node';
|
||||
export * from './line-add-button';
|
||||
export * from './node-panel';
|
||||
|
||||
@ -0,0 +1,26 @@
|
||||
export const IconPlusCircle = () => (
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="add">
|
||||
<path
|
||||
id="background"
|
||||
fill="#ffffff"
|
||||
fillRule="evenodd"
|
||||
stroke="none"
|
||||
d="M 24 12 C 24 5.372583 18.627417 0 12 0 C 5.372583 0 -0 5.372583 -0 12 C -0 18.627417 5.372583 24 12 24 C 18.627417 24 24 18.627417 24 12 Z"
|
||||
/>
|
||||
<path
|
||||
id="content"
|
||||
fill="currentColor"
|
||||
fillRule="evenodd"
|
||||
stroke="none"
|
||||
d="M 22 12.005 C 22 6.482153 17.522848 2.004999 12 2.004999 C 6.477152 2.004999 2 6.482153 2 12.005 C 2 17.527847 6.477152 22.004999 12 22.004999 C 17.522848 22.004999 22 17.527847 22 12.005 Z"
|
||||
/>
|
||||
<path
|
||||
id="cross"
|
||||
fill="#ffffff"
|
||||
stroke="none"
|
||||
d="M 11.411996 16.411797 C 11.411996 16.736704 11.675362 17 12.00023 17 C 12.325109 17 12.588474 16.736704 12.588474 16.411797 L 12.588474 12.58826 L 16.41201 12.58826 C 16.736919 12.58826 17.000216 12.324894 17.000216 12.000015 C 17.000216 11.675147 16.736919 11.411781 16.41201 11.411781 L 12.588474 11.411781 L 12.588474 7.588234 C 12.588474 7.263367 12.325109 7 12.00023 7 C 11.675362 7 11.411996 7.263367 11.411996 7.588234 L 11.411996 11.411781 L 7.588449 11.411781 C 7.263581 11.411781 7.000215 11.675147 7.000215 12.000015 C 7.000215 12.324894 7.263581 12.58826 7.588449 12.58826 L 11.411996 12.58826 L 11.411996 16.411797 Z"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
@ -0,0 +1,8 @@
|
||||
.line-add-button {
|
||||
position: absolute;
|
||||
transform: translate(-50%, -60%);
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
cursor: pointer;
|
||||
color: inherit;
|
||||
}
|
||||
@ -0,0 +1,53 @@
|
||||
import { WorkflowNodePanelService } from '@flowgram.ai/free-node-panel-plugin';
|
||||
import { LineRenderProps } from '@flowgram.ai/free-lines-plugin';
|
||||
import { useService } from '@flowgram.ai/free-layout-editor';
|
||||
|
||||
import './index.less';
|
||||
import { useVisible } from './use-visible';
|
||||
import { IconPlusCircle } from './button';
|
||||
|
||||
export const LineAddButton = (props: LineRenderProps) => {
|
||||
const { line, selected, color } = props;
|
||||
const visible = useVisible({ line, selected, color });
|
||||
const nodePanelService = useService<WorkflowNodePanelService>(WorkflowNodePanelService);
|
||||
|
||||
if (!visible) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
const { fromPort, toPort } = line;
|
||||
|
||||
return (
|
||||
<div
|
||||
className="line-add-button"
|
||||
style={{
|
||||
left: '50%',
|
||||
top: '50%',
|
||||
color,
|
||||
}}
|
||||
data-testid="sdk.workflow.canvas.line.add"
|
||||
data-line-id={line.id}
|
||||
onClick={async () => {
|
||||
const node = await nodePanelService.call({
|
||||
panelPosition: {
|
||||
x: (line.position.from.x + line.position.to.x) / 2,
|
||||
y: (line.position.from.y + line.position.to.y) / 2,
|
||||
},
|
||||
fromPort,
|
||||
toPort,
|
||||
enableBuildLine: true,
|
||||
enableAutoOffset: true,
|
||||
panelProps: {
|
||||
enableScrollClose: true,
|
||||
},
|
||||
});
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
line.dispose();
|
||||
}}
|
||||
>
|
||||
<IconPlusCircle />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,35 @@
|
||||
import { LineColors, usePlayground, WorkflowLineEntity } from '@flowgram.ai/free-layout-editor';
|
||||
|
||||
import './index.less';
|
||||
|
||||
export const useVisible = (params: {
|
||||
line: WorkflowLineEntity;
|
||||
selected?: boolean;
|
||||
color?: string;
|
||||
}): boolean => {
|
||||
const playground = usePlayground();
|
||||
const { line, selected = false, color } = params;
|
||||
if (line.disposed) {
|
||||
// 在 dispose 后,再去获取 line.to | line.from 会导致错误创建端口
|
||||
return false;
|
||||
}
|
||||
if (playground.config.readonly) {
|
||||
return false;
|
||||
}
|
||||
if (!selected && color !== LineColors.HOVER) {
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
line.fromPort.portID === 'loop-output-to-function' &&
|
||||
line.toPort?.portID === 'loop-function-input'
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
line.fromPort.portID === 'batch-output-to-function' &&
|
||||
line.toPort?.portID === 'batch-function-input'
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
48
apps/demo-free-layout/src/components/node-panel/index.tsx
Normal file
48
apps/demo-free-layout/src/components/node-panel/index.tsx
Normal file
@ -0,0 +1,48 @@
|
||||
import { FC } from 'react';
|
||||
|
||||
import { NodePanelRenderProps } from '@flowgram.ai/free-node-panel-plugin';
|
||||
import { Popover } from '@douyinfe/semi-ui';
|
||||
|
||||
import { NodePlaceholder } from './node-placeholder';
|
||||
import { NodeList } from './node-list';
|
||||
|
||||
export const NodePanel: FC<NodePanelRenderProps> = (props) => {
|
||||
const { onSelect, position, onClose, panelProps } = props;
|
||||
const { enableNodePlaceholder } = panelProps;
|
||||
|
||||
return (
|
||||
<Popover
|
||||
trigger="click"
|
||||
visible={true}
|
||||
onVisibleChange={(v) => (v ? null : onClose())}
|
||||
content={<NodeList onSelect={onSelect} />}
|
||||
placement="right"
|
||||
popupAlign={{ offset: [30, 0] }}
|
||||
overlayStyle={{
|
||||
padding: 0,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={
|
||||
enableNodePlaceholder
|
||||
? {
|
||||
position: 'absolute',
|
||||
top: position.y - 61.5,
|
||||
left: position.x,
|
||||
width: 300,
|
||||
height: 123,
|
||||
}
|
||||
: {
|
||||
position: 'absolute',
|
||||
top: position.y,
|
||||
left: position.x,
|
||||
width: 0,
|
||||
height: 0,
|
||||
}
|
||||
}
|
||||
>
|
||||
{enableNodePlaceholder && <NodePlaceholder />}
|
||||
</div>
|
||||
</Popover>
|
||||
);
|
||||
};
|
||||
@ -1,4 +1,7 @@
|
||||
import React, { FC } from 'react';
|
||||
|
||||
import styled from 'styled-components';
|
||||
import { NodePanelRenderProps } from '@flowgram.ai/free-node-panel-plugin';
|
||||
import { useClientContext } from '@flowgram.ai/free-layout-editor';
|
||||
|
||||
import { FlowNodeRegistry } from '../../typings';
|
||||
@ -24,7 +27,14 @@ const NodeLabel = styled.div`
|
||||
margin-left: 10px;
|
||||
`;
|
||||
|
||||
function Node(props: { label: string; icon: JSX.Element; onClick: () => void; disabled: boolean }) {
|
||||
interface NodeProps {
|
||||
label: string;
|
||||
icon: JSX.Element;
|
||||
onClick: React.MouseEventHandler<HTMLDivElement>;
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
function Node(props: NodeProps) {
|
||||
return (
|
||||
<NodeWrap
|
||||
onClick={props.disabled ? undefined : props.onClick}
|
||||
@ -44,16 +54,18 @@ const NodesWrap = styled.div`
|
||||
}
|
||||
`;
|
||||
|
||||
export function NodeList() {
|
||||
interface NodeListProps {
|
||||
onSelect: NodePanelRenderProps['onSelect'];
|
||||
}
|
||||
|
||||
export const NodeList: FC<NodeListProps> = (props) => {
|
||||
const { onSelect } = props;
|
||||
const context = useClientContext();
|
||||
const clientContext = useClientContext();
|
||||
const handleClick = (registry: FlowNodeRegistry) => {
|
||||
const nodeJSON = registry.onAdd?.(context);
|
||||
if (nodeJSON) {
|
||||
const node = clientContext.document.createWorkflowNode(nodeJSON);
|
||||
// Select New Node
|
||||
clientContext.selection.selection = [node];
|
||||
}
|
||||
const handleClick = (e: React.MouseEvent, registry: FlowNodeRegistry) => {
|
||||
onSelect({
|
||||
nodeType: registry.type as string,
|
||||
selectEvent: e,
|
||||
});
|
||||
};
|
||||
return (
|
||||
<NodesWrap style={{ width: 80 * 2 + 20 }}>
|
||||
@ -63,9 +75,9 @@ export function NodeList() {
|
||||
disabled={!(registry.canAdd?.(context) ?? true)}
|
||||
icon={<img style={{ width: 10, height: 10, borderRadius: 4 }} src={registry.info.icon} />}
|
||||
label={registry.type as string}
|
||||
onClick={() => handleClick(registry)}
|
||||
onClick={(e) => handleClick(e, registry)}
|
||||
/>
|
||||
))}
|
||||
</NodesWrap>
|
||||
);
|
||||
}
|
||||
};
|
||||
@ -0,0 +1,48 @@
|
||||
export const NodePlaceholder = () => (
|
||||
<svg width="300" height="123" viewBox="0 0 300 123" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="g1">
|
||||
<path
|
||||
id="path1"
|
||||
fill="#ffffff"
|
||||
stroke="none"
|
||||
d="M 0 13.795815 C 0 6.856102 5.625739 1.23037 12.565445 1.23037 L 287.43454 1.23037 C 294.37381 1.23037 300 6.856102 300 13.795815 L 300 109.60733 C 300 116.547066 294.37381 122.172775 287.43454 122.172775 L 12.565445 122.172775 C 5.625754 122.172775 0 116.547066 0 109.60733 L 0 13.795815 Z"
|
||||
/>
|
||||
<path
|
||||
id="path2"
|
||||
fill="none"
|
||||
stroke="#060709"
|
||||
strokeWidth="1.570681"
|
||||
strokeOpacity="0.1"
|
||||
d="M 0.78534 13.795815 C 0.78534 7.289825 6.059466 2.015709 12.565445 2.015709 L 287.43454 2.015709 C 293.940308 2.015709 299.214661 7.289825 299.214661 13.795815 L 299.214661 109.60733 C 299.214661 116.113243 293.940308 121.387436 287.43454 121.387436 L 12.565445 121.387436 C 6.059482 121.387436 0.78534 116.113243 0.78534 109.60733 L 0.78534 13.795815 Z"
|
||||
/>
|
||||
<path
|
||||
id="path3"
|
||||
fill="#d9d9d9"
|
||||
stroke="none"
|
||||
opacity="0.32"
|
||||
d="M 51.832462 70.340317 C 51.832462 66.003036 55.348591 62.486912 59.685863 62.486912 L 273.298431 62.486912 C 277.635071 62.486912 281.151825 66.003036 281.151825 70.340317 C 281.151825 74.677589 277.635071 78.193718 273.298431 78.193718 L 59.685863 78.193718 C 55.348591 78.193718 51.832462 74.677589 51.832462 70.340317 Z"
|
||||
/>
|
||||
<path
|
||||
id="path4"
|
||||
fill="#d9d9d9"
|
||||
stroke="none"
|
||||
opacity="0.32"
|
||||
d="M 51.832462 29.502617 C 51.832462 25.165344 55.348591 21.649216 59.685863 21.649216 L 138.219894 21.649216 C 142.557175 21.649216 146.073303 25.165344 146.073303 29.502617 C 146.073303 33.83989 142.557175 37.356018 138.219894 37.356018 L 59.685863 37.356018 C 55.348591 37.356018 51.832462 33.83989 51.832462 29.502617 Z"
|
||||
/>
|
||||
<path
|
||||
id="path5"
|
||||
fill="#d9d9d9"
|
||||
stroke="none"
|
||||
opacity="0.32"
|
||||
d="M 14.136126 29.502617 C 14.136126 25.165344 17.65225 21.649216 21.989529 21.649216 L 36.125656 21.649216 C 40.462933 21.649216 43.979057 25.165344 43.979057 29.502617 C 43.979057 33.83989 40.462933 37.356018 36.125656 37.356018 L 21.989529 37.356018 C 17.65225 37.356018 14.136126 33.83989 14.136126 29.502617 Z"
|
||||
/>
|
||||
<path
|
||||
id="path6"
|
||||
fill="#d9d9d9"
|
||||
stroke="none"
|
||||
opacity="0.32"
|
||||
d="M 51.832462 93.900528 C 51.832462 89.563248 55.348591 86.047119 59.685863 86.047119 L 193.19371 86.047119 C 197.530365 86.047119 201.047119 89.563248 201.047119 93.900528 C 201.047119 98.237801 197.530365 101.753929 193.19371 101.753929 L 59.685863 101.753929 C 55.348591 101.753929 51.832462 98.237801 51.832462 93.900528 Z"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
@ -21,7 +21,7 @@ export const DemoTools = () => {
|
||||
const { history, playground } = useClientContext();
|
||||
const [canUndo, setCanUndo] = useState(false);
|
||||
const [canRedo, setCanRedo] = useState(false);
|
||||
const [minimapVisible, setMinimapVisible] = useState(false);
|
||||
const [minimapVisible, setMinimapVisible] = useState(true);
|
||||
useEffect(() => {
|
||||
const disposable = history.undoRedoService.onChange(() => {
|
||||
setCanUndo(history.canUndo());
|
||||
|
||||
@ -3,6 +3,8 @@ import { useMemo } from 'react';
|
||||
|
||||
import { createMinimapPlugin } from '@flowgram.ai/minimap-plugin';
|
||||
import { createFreeSnapPlugin } from '@flowgram.ai/free-snap-plugin';
|
||||
import { createFreeNodePanelPlugin } from '@flowgram.ai/free-node-panel-plugin';
|
||||
import { createFreeLinesPlugin } from '@flowgram.ai/free-lines-plugin';
|
||||
import { FreeLayoutProps } from '@flowgram.ai/free-layout-editor';
|
||||
|
||||
import { FlowNodeRegistry, FlowDocumentJSON } from '../typings';
|
||||
@ -10,7 +12,7 @@ import { shortcuts } from '../shortcuts';
|
||||
import { createVariablePlugin } from '../plugins';
|
||||
import { defaultFormMeta } from '../nodes/default-form-meta';
|
||||
import { SelectorBoxPopover } from '../components/selector-box-popover';
|
||||
import { BaseNode } from '../components';
|
||||
import { BaseNode, LineAddButton, NodePanel } from '../components';
|
||||
|
||||
export function useEditorProps(
|
||||
initialData: FlowDocumentJSON,
|
||||
@ -123,6 +125,13 @@ export function useEditorProps(
|
||||
console.log('---- Playground Dispose ----');
|
||||
},
|
||||
plugins: () => [
|
||||
/**
|
||||
* Line render plugin
|
||||
* 连线渲染插件
|
||||
*/
|
||||
createFreeLinesPlugin({
|
||||
renderInsideLine: LineAddButton,
|
||||
}),
|
||||
/**
|
||||
* Minimap plugin
|
||||
* 缩略图插件
|
||||
@ -133,18 +142,18 @@ export function useEditorProps(
|
||||
canvasWidth: 182,
|
||||
canvasHeight: 102,
|
||||
canvasPadding: 50,
|
||||
canvasBackground: 'rgba(245, 245, 245, 1)',
|
||||
canvasBackground: 'rgba(242, 243, 245, 1)',
|
||||
canvasBorderRadius: 10,
|
||||
viewportBackground: 'rgba(235, 235, 235, 1)',
|
||||
viewportBackground: 'rgba(255, 255, 255, 1)',
|
||||
viewportBorderRadius: 4,
|
||||
viewportBorderColor: 'rgba(201, 201, 201, 1)',
|
||||
viewportBorderColor: 'rgba(6, 7, 9, 0.10)',
|
||||
viewportBorderWidth: 1,
|
||||
viewportBorderDashLength: 2,
|
||||
nodeColor: 'rgba(255, 255, 255, 1)',
|
||||
viewportBorderDashLength: undefined,
|
||||
nodeColor: 'rgba(0, 0, 0, 0.10)',
|
||||
nodeBorderRadius: 2,
|
||||
nodeBorderWidth: 0.145,
|
||||
nodeBorderColor: 'rgba(6, 7, 9, 0.10)',
|
||||
overlayColor: 'rgba(255, 255, 255, 0)',
|
||||
overlayColor: 'rgba(255, 255, 255, 0.55)',
|
||||
},
|
||||
inactiveDebounceTime: 1,
|
||||
}),
|
||||
@ -164,6 +173,9 @@ export function useEditorProps(
|
||||
alignLineWidth: 1,
|
||||
alignCrossWidth: 8,
|
||||
}),
|
||||
createFreeNodePanelPlugin({
|
||||
renderer: NodePanel,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
[]
|
||||
|
||||
@ -15,6 +15,10 @@ export const ConditionNodeRegistry: FlowNodeRegistry = {
|
||||
defaultPorts: [{ type: 'input' }],
|
||||
// Condition Outputs use dynamic port
|
||||
useDynamicPort: true,
|
||||
size: {
|
||||
width: 360,
|
||||
height: 305,
|
||||
},
|
||||
},
|
||||
formMeta,
|
||||
onAdd() {
|
||||
|
||||
@ -8,6 +8,10 @@ export const EndNodeRegistry: FlowNodeRegistry = {
|
||||
deleteDisable: true,
|
||||
copyDisable: true,
|
||||
defaultPorts: [{ type: 'input' }],
|
||||
size: {
|
||||
width: 360,
|
||||
height: 211,
|
||||
},
|
||||
},
|
||||
info: {
|
||||
icon: iconEnd,
|
||||
|
||||
@ -11,6 +11,12 @@ export const LLMNodeRegistry: FlowNodeRegistry = {
|
||||
description:
|
||||
'Call the large language model and use variables and prompt words to generate responses.',
|
||||
},
|
||||
meta: {
|
||||
size: {
|
||||
width: 360,
|
||||
height: 94,
|
||||
},
|
||||
},
|
||||
onAdd() {
|
||||
return {
|
||||
id: `llm_${nanoid(5)}`,
|
||||
|
||||
@ -9,6 +9,10 @@ export const StartNodeRegistry: FlowNodeRegistry = {
|
||||
deleteDisable: true,
|
||||
copyDisable: true,
|
||||
defaultPorts: [{ type: 'output' }],
|
||||
size: {
|
||||
width: 360,
|
||||
height: 211,
|
||||
},
|
||||
},
|
||||
info: {
|
||||
icon: iconStart,
|
||||
|
||||
581
common/config/rush/pnpm-lock.yaml
generated
581
common/config/rush/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -198,9 +198,7 @@ export function createFreeLayoutPreset(
|
||||
/**
|
||||
* 线条渲染插件
|
||||
*/
|
||||
createFreeLinesPlugin({
|
||||
renderElement: renderElement as FreeLinesPluginOptions['renderElement'],
|
||||
}),
|
||||
createFreeLinesPlugin({}),
|
||||
/**
|
||||
* 节点 hover 插件
|
||||
*/
|
||||
|
||||
@ -28,6 +28,7 @@
|
||||
"dependencies": {
|
||||
"@flowgram.ai/core": "workspace:*",
|
||||
"@flowgram.ai/free-layout-core": "workspace:*",
|
||||
"@flowgram.ai/free-stack-plugin": "workspace:*",
|
||||
"@flowgram.ai/renderer": "workspace:*",
|
||||
"@flowgram.ai/utils": "workspace:*",
|
||||
"bezier-js": "^6.1.4",
|
||||
|
||||
@ -9,13 +9,6 @@ export const createFreeLinesPlugin = definePluginCreator({
|
||||
onInit: (ctx: PluginContext, opts: FreeLinesPluginOptions) => {
|
||||
ctx.playground.registerLayer(WorkflowLinesLayer, {
|
||||
...opts,
|
||||
renderElement: () => {
|
||||
if (typeof opts.renderElement === 'function') {
|
||||
return opts.renderElement(ctx);
|
||||
} else {
|
||||
return opts.renderElement;
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
onReady: (ctx: PluginContext, opts: FreeLinesPluginOptions) => {
|
||||
|
||||
@ -3,6 +3,7 @@ import React, { ReactNode, useLayoutEffect, useState } from 'react';
|
||||
|
||||
import { inject, injectable } from 'inversify';
|
||||
import { domUtils } from '@flowgram.ai/utils';
|
||||
import { StackingContextManager } from '@flowgram.ai/free-stack-plugin';
|
||||
import {
|
||||
nanoid,
|
||||
WorkflowDocument,
|
||||
@ -35,6 +36,8 @@ export class WorkflowLinesLayer extends Layer<LinesLayerOptions> {
|
||||
|
||||
@inject(WorkflowDocument) protected workflowDocument: WorkflowDocument;
|
||||
|
||||
@inject(StackingContextManager) protected stackContext: StackingContextManager;
|
||||
|
||||
private layerID = nanoid();
|
||||
|
||||
private mountedLines: Map<
|
||||
@ -166,14 +169,6 @@ export class WorkflowLinesLayer extends Layer<LinesLayerOptions> {
|
||||
}
|
||||
|
||||
private get renderElement(): HTMLElement {
|
||||
if (typeof this.options.renderElement === 'function') {
|
||||
const element = this.options.renderElement();
|
||||
if (element) {
|
||||
return element;
|
||||
}
|
||||
} else if (typeof this.options.renderElement !== 'undefined') {
|
||||
return this.options.renderElement as HTMLElement;
|
||||
}
|
||||
return this.node;
|
||||
return this.stackContext.node;
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,7 +5,6 @@ import type {
|
||||
WorkflowLineRenderContributionFactory,
|
||||
} from '@flowgram.ai/free-layout-core';
|
||||
import { LineRenderType } from '@flowgram.ai/free-layout-core';
|
||||
import type { PluginContext } from '@flowgram.ai/core';
|
||||
|
||||
export interface LineRenderProps {
|
||||
key: string;
|
||||
@ -19,12 +18,10 @@ export interface LineRenderProps {
|
||||
}
|
||||
|
||||
export interface LinesLayerOptions {
|
||||
renderElement?: HTMLElement | (() => HTMLElement | undefined);
|
||||
renderInsideLine?: FC<LineRenderProps>;
|
||||
}
|
||||
|
||||
export interface FreeLinesPluginOptions extends Omit<LinesLayerOptions, 'renderElement'> {
|
||||
renderElement?: HTMLElement | ((ctx: PluginContext) => HTMLElement | undefined);
|
||||
export interface FreeLinesPluginOptions extends LinesLayerOptions {
|
||||
contributions?: WorkflowLineRenderContributionFactory[];
|
||||
defaultLineType?: LineRenderType;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user