mirror of
https://gitee.com/ByteDance/flowgram.ai.git
synced 2025-07-07 17:43:29 +08:00
feat(demo): workflow running style support
This commit is contained in:
parent
e2e80efb7a
commit
e8c60a8443
@ -8,6 +8,7 @@ import { ZoomSelect } from './zoom-select';
|
|||||||
import { SwitchVertical } from './switch-vertical';
|
import { SwitchVertical } from './switch-vertical';
|
||||||
import { ToolContainer, ToolSection } from './styles';
|
import { ToolContainer, ToolSection } from './styles';
|
||||||
import { Save } from './save';
|
import { Save } from './save';
|
||||||
|
import { Run } from './run';
|
||||||
import { Readonly } from './readonly';
|
import { Readonly } from './readonly';
|
||||||
import { MinimapSwitch } from './minimap-switch';
|
import { MinimapSwitch } from './minimap-switch';
|
||||||
import { Minimap } from './minimap';
|
import { Minimap } from './minimap';
|
||||||
@ -50,6 +51,7 @@ export const DemoTools = () => {
|
|||||||
/>
|
/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Save disabled={playground.config.readonly} />
|
<Save disabled={playground.config.readonly} />
|
||||||
|
<Run />
|
||||||
</ToolSection>
|
</ToolSection>
|
||||||
</ToolContainer>
|
</ToolContainer>
|
||||||
);
|
);
|
||||||
|
|||||||
108
apps/demo-fixed-layout/src/components/tools/run.tsx
Normal file
108
apps/demo-fixed-layout/src/components/tools/run.tsx
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
import { useState } from 'react';
|
||||||
|
|
||||||
|
import {
|
||||||
|
usePlayground,
|
||||||
|
FlowNodeEntity,
|
||||||
|
FixedLayoutPluginContext,
|
||||||
|
useClientContext,
|
||||||
|
delay,
|
||||||
|
} from '@flowgram.ai/fixed-layout-editor';
|
||||||
|
import { Button } from '@douyinfe/semi-ui';
|
||||||
|
|
||||||
|
const styleElement = document.createElement('style');
|
||||||
|
const RUNNING_COLOR = 'rgb(78, 64, 229)';
|
||||||
|
const RUNNING_INTERVAL = 1000;
|
||||||
|
|
||||||
|
function getRunningNodes(targetNode?: FlowNodeEntity | undefined, addChildren?: boolean): string[] {
|
||||||
|
const result: string[] = [];
|
||||||
|
if (targetNode) {
|
||||||
|
result.push(targetNode.id);
|
||||||
|
if (addChildren) {
|
||||||
|
result.push(...targetNode.allChildren.map((n) => n.id));
|
||||||
|
}
|
||||||
|
if (targetNode.parent) {
|
||||||
|
result.push(targetNode.parent.id);
|
||||||
|
}
|
||||||
|
if (targetNode.pre) {
|
||||||
|
result.push(...getRunningNodes(targetNode.pre, true));
|
||||||
|
}
|
||||||
|
if (targetNode.parent) {
|
||||||
|
if (targetNode.parent.pre) {
|
||||||
|
result.push(...getRunningNodes(targetNode.parent.pre, true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function clear() {
|
||||||
|
styleElement.innerText = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function runningNode(ctx: FixedLayoutPluginContext, nodeId: string) {
|
||||||
|
const nodes = getRunningNodes(ctx.document.getNode(nodeId), true);
|
||||||
|
if (nodes.length === 0) {
|
||||||
|
styleElement.innerText = '';
|
||||||
|
} else {
|
||||||
|
const content = nodes
|
||||||
|
.map(
|
||||||
|
(n) => `
|
||||||
|
path[data-line-id$="${n}"] {
|
||||||
|
animation: flowingDash 0.5s linear infinite;
|
||||||
|
stroke-dasharray: 8, 5;
|
||||||
|
stroke: ${RUNNING_COLOR} !important;
|
||||||
|
}
|
||||||
|
marker[data-line-id$="${n}"] path {
|
||||||
|
fill: ${RUNNING_COLOR} !important;
|
||||||
|
}
|
||||||
|
[data-node-id$="${n}"] {
|
||||||
|
border: 1px dashed ${RUNNING_COLOR} !important;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
[data-label-id$="${n}"] {
|
||||||
|
color: ${RUNNING_COLOR} !important;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
)
|
||||||
|
.join('\n');
|
||||||
|
styleElement.innerText = `
|
||||||
|
@keyframes flowingDash {
|
||||||
|
to {
|
||||||
|
stroke-dashoffset: -13;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
${content}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
if (!styleElement.parentNode) {
|
||||||
|
document.body.appendChild(styleElement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run the simulation and highlight the lines
|
||||||
|
*/
|
||||||
|
export function Run() {
|
||||||
|
const [isRunning, setRunning] = useState(false);
|
||||||
|
const ctx = useClientContext();
|
||||||
|
const playground = usePlayground();
|
||||||
|
const onRun = async () => {
|
||||||
|
setRunning(true);
|
||||||
|
playground.config.readonly = true;
|
||||||
|
const nodes = ctx.document.root.blocks.slice();
|
||||||
|
while (nodes.length > 0) {
|
||||||
|
const currentNode = nodes.shift();
|
||||||
|
runningNode(ctx, currentNode!.id);
|
||||||
|
await delay(RUNNING_INTERVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
playground.config.readonly = false;
|
||||||
|
clear();
|
||||||
|
setRunning(false);
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<Button onClick={onRun} loading={isRunning}>
|
||||||
|
Run
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -10,6 +10,7 @@ import { ZoomSelect } from './zoom-select';
|
|||||||
import { SwitchLine } from './switch-line';
|
import { SwitchLine } from './switch-line';
|
||||||
import { ToolContainer, ToolSection } from './styles';
|
import { ToolContainer, ToolSection } from './styles';
|
||||||
import { Save } from './save';
|
import { Save } from './save';
|
||||||
|
import { Run } from './run';
|
||||||
import { Readonly } from './readonly';
|
import { Readonly } from './readonly';
|
||||||
import { MinimapSwitch } from './minimap-switch';
|
import { MinimapSwitch } from './minimap-switch';
|
||||||
import { Minimap } from './minimap';
|
import { Minimap } from './minimap';
|
||||||
@ -71,6 +72,7 @@ export const DemoTools = () => {
|
|||||||
<AddNode disabled={playground.config.readonly} />
|
<AddNode disabled={playground.config.readonly} />
|
||||||
<Divider layout="vertical" style={{ height: '16px' }} margin={3} />
|
<Divider layout="vertical" style={{ height: '16px' }} margin={3} />
|
||||||
<Save disabled={playground.config.readonly} />
|
<Save disabled={playground.config.readonly} />
|
||||||
|
<Run />
|
||||||
</ToolSection>
|
</ToolSection>
|
||||||
</ToolContainer>
|
</ToolContainer>
|
||||||
);
|
);
|
||||||
|
|||||||
28
apps/demo-free-layout/src/components/tools/run.tsx
Normal file
28
apps/demo-free-layout/src/components/tools/run.tsx
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import { useState } from 'react';
|
||||||
|
|
||||||
|
import { useService } from '@flowgram.ai/free-layout-editor';
|
||||||
|
import { Button } from '@douyinfe/semi-ui';
|
||||||
|
|
||||||
|
import { RunningService } from '../../services';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run the simulation and highlight the lines
|
||||||
|
*/
|
||||||
|
export function Run() {
|
||||||
|
const [isRunning, setRunning] = useState(false);
|
||||||
|
const runningService = useService(RunningService);
|
||||||
|
const onRun = async () => {
|
||||||
|
setRunning(true);
|
||||||
|
await runningService.startRun();
|
||||||
|
setRunning(false);
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
onClick={onRun}
|
||||||
|
loading={isRunning}
|
||||||
|
style={{ backgroundColor: 'rgba(171,181,255,0.3)', borderRadius: '8px' }}
|
||||||
|
>
|
||||||
|
Run
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -13,7 +13,7 @@ import { createContainerNodePlugin } from '@flowgram.ai/free-container-plugin';
|
|||||||
import { onDragLineEnd } from '../utils';
|
import { onDragLineEnd } from '../utils';
|
||||||
import { FlowNodeRegistry, FlowDocumentJSON } from '../typings';
|
import { FlowNodeRegistry, FlowDocumentJSON } from '../typings';
|
||||||
import { shortcuts } from '../shortcuts';
|
import { shortcuts } from '../shortcuts';
|
||||||
import { CustomService } from '../services';
|
import { CustomService, RunningService } from '../services';
|
||||||
import { createSyncVariablePlugin } from '../plugins';
|
import { createSyncVariablePlugin } from '../plugins';
|
||||||
import { defaultFormMeta } from '../nodes/default-form-meta';
|
import { defaultFormMeta } from '../nodes/default-form-meta';
|
||||||
import { WorkflowNodeType } from '../nodes';
|
import { WorkflowNodeType } from '../nodes';
|
||||||
@ -135,6 +135,10 @@ export function useEditorProps(
|
|||||||
onContentChange: debounce((ctx, event) => {
|
onContentChange: debounce((ctx, event) => {
|
||||||
console.log('Auto Save: ', event, ctx.document.toJSON());
|
console.log('Auto Save: ', event, ctx.document.toJSON());
|
||||||
}, 1000),
|
}, 1000),
|
||||||
|
/**
|
||||||
|
* Running line
|
||||||
|
*/
|
||||||
|
isFlowingLine: (ctx, line) => ctx.get(RunningService).isFlowingLine(line),
|
||||||
/**
|
/**
|
||||||
* Shortcuts
|
* Shortcuts
|
||||||
*/
|
*/
|
||||||
@ -144,6 +148,7 @@ export function useEditorProps(
|
|||||||
*/
|
*/
|
||||||
onBind: ({ bind }) => {
|
onBind: ({ bind }) => {
|
||||||
bind(CustomService).toSelf().inSingletonScope();
|
bind(CustomService).toSelf().inSingletonScope();
|
||||||
|
bind(RunningService).toSelf().inSingletonScope();
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Playground init
|
* Playground init
|
||||||
|
|||||||
@ -1 +1,2 @@
|
|||||||
export { CustomService } from './custom-service';
|
export { CustomService } from './custom-service';
|
||||||
|
export { RunningService } from './running-service';
|
||||||
|
|||||||
47
apps/demo-free-layout/src/services/running-service.ts
Normal file
47
apps/demo-free-layout/src/services/running-service.ts
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import {
|
||||||
|
injectable,
|
||||||
|
inject,
|
||||||
|
WorkflowDocument,
|
||||||
|
Playground,
|
||||||
|
delay,
|
||||||
|
WorkflowLineEntity,
|
||||||
|
WorkflowNodeEntity,
|
||||||
|
WorkflowNodeLinesData,
|
||||||
|
} from '@flowgram.ai/free-layout-editor';
|
||||||
|
const RUNNING_INTERVAL = 1000;
|
||||||
|
|
||||||
|
@injectable()
|
||||||
|
export class RunningService {
|
||||||
|
@inject(Playground) playground: Playground;
|
||||||
|
|
||||||
|
@inject(WorkflowDocument) document: WorkflowDocument;
|
||||||
|
|
||||||
|
private _runningNodes: WorkflowNodeEntity[] = [];
|
||||||
|
|
||||||
|
async addRunningNode(node: WorkflowNodeEntity): Promise<void> {
|
||||||
|
this._runningNodes.push(node);
|
||||||
|
node.renderData.node.classList.add('node-running');
|
||||||
|
this.document.linesManager.forceUpdate(); // Refresh line renderer
|
||||||
|
await delay(RUNNING_INTERVAL);
|
||||||
|
// Child Nodes
|
||||||
|
await Promise.all(node.blocks.map((nextNode) => this.addRunningNode(nextNode)));
|
||||||
|
// Sibling Nodes
|
||||||
|
const nextNodes = node.getData(WorkflowNodeLinesData).outputNodes;
|
||||||
|
await Promise.all(nextNodes.map((nextNode) => this.addRunningNode(nextNode)));
|
||||||
|
}
|
||||||
|
|
||||||
|
async startRun(): Promise<void> {
|
||||||
|
await this.addRunningNode(this.document.getNode('start_0')!);
|
||||||
|
this._runningNodes.forEach((node) => {
|
||||||
|
node.renderData.node.classList.remove('node-running');
|
||||||
|
});
|
||||||
|
this._runningNodes = [];
|
||||||
|
this.document.linesManager.forceUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
isFlowingLine(line: WorkflowLineEntity) {
|
||||||
|
return this._runningNodes.some((node) =>
|
||||||
|
node.getData(WorkflowNodeLinesData).outputLines.includes(line)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -15,6 +15,16 @@
|
|||||||
background-color: var(--g-playground-selectBox-background);
|
background-color: var(--g-playground-selectBox-background);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@keyframes blink {
|
||||||
|
0% { opacity: 1; }
|
||||||
|
50% { opacity: 0; }
|
||||||
|
100% { opacity: 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.node-running {
|
||||||
|
border: 1px dashed rgb(78, 64, 229) !important;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
.demo-editor {
|
.demo-editor {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|||||||
@ -8,5 +8,5 @@ import { usePlaygroundContainer } from './use-playground-container';
|
|||||||
*/
|
*/
|
||||||
export function useService<T>(identifier: interfaces.ServiceIdentifier<T>): T {
|
export function useService<T>(identifier: interfaces.ServiceIdentifier<T>): T {
|
||||||
const container = usePlaygroundContainer();
|
const container = usePlaygroundContainer();
|
||||||
return container.get(identifier) as T;
|
return container.get?.(identifier) as T;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -32,6 +32,7 @@ export interface FlowTransitionLine {
|
|||||||
activated?: boolean; // 是否激活态
|
activated?: boolean; // 是否激活态
|
||||||
side?: LABEL_SIDE_TYPE; // 区分是否分支前缀线条
|
side?: LABEL_SIDE_TYPE; // 区分是否分支前缀线条
|
||||||
style?: React.CSSProperties;
|
style?: React.CSSProperties;
|
||||||
|
lineId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 内置几种标签
|
// 内置几种标签
|
||||||
@ -52,6 +53,7 @@ export interface FlowTransitionLabel {
|
|||||||
width?: number; // 宽度
|
width?: number; // 宽度
|
||||||
rotate?: string; // 循环, 如 '60deg'
|
rotate?: string; // 循环, 如 '60deg'
|
||||||
props?: Record<string, any>;
|
props?: Record<string, any>;
|
||||||
|
labelId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AdderProps {
|
export interface AdderProps {
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
exports[`flow-label-layer > test render 1`] = `
|
exports[`flow-label-layer > test render 1`] = `
|
||||||
<DocumentFragment>
|
<DocumentFragment>
|
||||||
<div
|
<div
|
||||||
|
data-label-id="start"
|
||||||
style="position: absolute; left: 0px; top: 0px; transform: translate(-50%, -50%);"
|
style="position: absolute; left: 0px; top: 0px; transform: translate(-50%, -50%);"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@ -14,6 +15,7 @@ exports[`flow-label-layer > test render 1`] = `
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
data-label-id="mock"
|
||||||
style="position: absolute; left: 0px; top: 0px; transform: translate(-50%, -50%);"
|
style="position: absolute; left: 0px; top: 0px; transform: translate(-50%, -50%);"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@ -21,9 +23,11 @@ exports[`flow-label-layer > test render 1`] = `
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
data-label-id="mock"
|
||||||
style="position: absolute; left: 0px; top: 0px; transform: translate(-50%, -50%);"
|
style="position: absolute; left: 0px; top: 0px; transform: translate(-50%, -50%);"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
|
data-label-id="mock"
|
||||||
style="position: absolute; left: 0px; top: 0px; transform: translate(-50%, -50%);"
|
style="position: absolute; left: 0px; top: 0px; transform: translate(-50%, -50%);"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@ -32,6 +36,7 @@ exports[`flow-label-layer > test render 1`] = `
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
data-label-id="mock"
|
||||||
style="position: absolute; left: 0px; top: 0px; transform: translate(-50%, -50%);"
|
style="position: absolute; left: 0px; top: 0px; transform: translate(-50%, -50%);"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@ -48,6 +53,7 @@ exports[`flow-label-layer > test render 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
data-label-id="mock"
|
||||||
style="position: absolute; left: 0px; top: 0px; transform: translate(-50%, -50%);"
|
style="position: absolute; left: 0px; top: 0px; transform: translate(-50%, -50%);"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@ -67,6 +73,7 @@ exports[`flow-label-layer > test render 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
data-label-id="mock"
|
||||||
style="position: absolute; left: 0px; top: 0px; transform: translate(-50%, -50%);"
|
style="position: absolute; left: 0px; top: 0px; transform: translate(-50%, -50%);"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@ -86,27 +93,33 @@ exports[`flow-label-layer > test render 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
data-label-id="mock"
|
||||||
style="position: absolute; left: 0px; top: 0px; transform: translate(-50%, -50%);"
|
style="position: absolute; left: 0px; top: 0px; transform: translate(-50%, -50%);"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
data-label-id="mock"
|
||||||
style="font-size: 12px; color: rgb(187, 191, 196); text-align: center; white-space: nowrap; line-height: 20px;"
|
style="font-size: 12px; color: rgb(187, 191, 196); text-align: center; white-space: nowrap; line-height: 20px;"
|
||||||
>
|
>
|
||||||
123
|
123
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
data-label-id="mock"
|
||||||
style="position: absolute; left: 0px; top: 0px; transform: translate(-50%, -50%);"
|
style="position: absolute; left: 0px; top: 0px; transform: translate(-50%, -50%);"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
data-label-id="mock"
|
||||||
style="font-size: 12px; color: rgb(187, 191, 196); text-align: center; white-space: nowrap; line-height: 20px; width: 100px; transform: rotate(90deg);"
|
style="font-size: 12px; color: rgb(187, 191, 196); text-align: center; white-space: nowrap; line-height: 20px; width: 100px; transform: rotate(90deg);"
|
||||||
>
|
>
|
||||||
catch-text
|
catch-text
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
data-label-id="mock"
|
||||||
style="position: absolute; left: 0px; top: 0px; transform: translate(-50%, -50%);"
|
style="position: absolute; left: 0px; top: 0px; transform: translate(-50%, -50%);"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
|
data-label-id="mock"
|
||||||
style="position: absolute; left: 0px; top: 0px; transform: translate(-50%, -50%);"
|
style="position: absolute; left: 0px; top: 0px; transform: translate(-50%, -50%);"
|
||||||
/>
|
/>
|
||||||
</DocumentFragment>
|
</DocumentFragment>
|
||||||
|
|||||||
@ -23,7 +23,7 @@ function CustomLine(props: PropsType): JSX.Element {
|
|||||||
|
|
||||||
const Component = renderer.renderer as (props: FlowTransitionLine) => JSX.Element;
|
const Component = renderer.renderer as (props: FlowTransitionLine) => JSX.Element;
|
||||||
|
|
||||||
return <Component {...line} />;
|
return <Component lineId={props.lineId} {...line} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default CustomLine;
|
export default CustomLine;
|
||||||
|
|||||||
@ -59,19 +59,45 @@ export function createLabels(labelProps: LabelOpts): void {
|
|||||||
switch (type) {
|
switch (type) {
|
||||||
case FlowTransitionLabelEnum.BRANCH_DRAGGING_LABEL:
|
case FlowTransitionLabelEnum.BRANCH_DRAGGING_LABEL:
|
||||||
child = (
|
child = (
|
||||||
<BranchDraggableRenderer rendererRegistry={rendererRegistry} data={data} {...props} />
|
<BranchDraggableRenderer
|
||||||
|
labelId={label.labelId || labelProps.data.entity.id}
|
||||||
|
rendererRegistry={rendererRegistry}
|
||||||
|
data={data}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case FlowTransitionLabelEnum.ADDER_LABEL:
|
case FlowTransitionLabelEnum.ADDER_LABEL:
|
||||||
child = <Adder rendererRegistry={rendererRegistry} data={data} {...props} />;
|
child = (
|
||||||
|
<Adder
|
||||||
|
labelId={label.labelId || labelProps.data.entity.id}
|
||||||
|
rendererRegistry={rendererRegistry}
|
||||||
|
data={data}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FlowTransitionLabelEnum.COLLAPSE_LABEL:
|
case FlowTransitionLabelEnum.COLLAPSE_LABEL:
|
||||||
child = <Collapse rendererRegistry={rendererRegistry} data={data} {...props} />;
|
child = (
|
||||||
|
<Collapse
|
||||||
|
labelId={label.labelId || labelProps.data.entity.id}
|
||||||
|
rendererRegistry={rendererRegistry}
|
||||||
|
data={data}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FlowTransitionLabelEnum.COLLAPSE_ADDER_LABEL:
|
case FlowTransitionLabelEnum.COLLAPSE_ADDER_LABEL:
|
||||||
child = <CollapseAdder rendererRegistry={rendererRegistry} data={data} {...props} />;
|
child = (
|
||||||
|
<CollapseAdder
|
||||||
|
labelId={label.labelId || labelProps.data.entity.id}
|
||||||
|
rendererRegistry={rendererRegistry}
|
||||||
|
data={data}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FlowTransitionLabelEnum.TEXT_LABEL:
|
case FlowTransitionLabelEnum.TEXT_LABEL:
|
||||||
@ -81,6 +107,7 @@ export function createLabels(labelProps: LabelOpts): void {
|
|||||||
const text = rendererRegistry.getText(renderKey) || renderKey;
|
const text = rendererRegistry.getText(renderKey) || renderKey;
|
||||||
child = (
|
child = (
|
||||||
<div
|
<div
|
||||||
|
data-label-id={label.labelId || labelProps.data.entity.id}
|
||||||
style={{
|
style={{
|
||||||
...TEXT_LABEL_STYLE,
|
...TEXT_LABEL_STYLE,
|
||||||
...props?.style,
|
...props?.style,
|
||||||
@ -103,6 +130,7 @@ export function createLabels(labelProps: LabelOpts): void {
|
|||||||
renderer.renderer as (props: any) => JSX.Element,
|
renderer.renderer as (props: any) => JSX.Element,
|
||||||
{
|
{
|
||||||
node: data.entity,
|
node: data.entity,
|
||||||
|
labelId: label.labelId || labelProps.data.entity.id,
|
||||||
...props,
|
...props,
|
||||||
} as CustomLabelProps
|
} as CustomLabelProps
|
||||||
);
|
);
|
||||||
@ -118,6 +146,7 @@ export function createLabels(labelProps: LabelOpts): void {
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={`${data.entity.id}${index}`}
|
key={`${data.entity.id}${index}`}
|
||||||
|
data-label-id={label.labelId || labelProps.data.entity.id}
|
||||||
style={{
|
style={{
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
left: offsetX,
|
left: offsetX,
|
||||||
|
|||||||
@ -48,7 +48,12 @@ export function createLines(props: PropsType): void {
|
|||||||
switch (line.type) {
|
switch (line.type) {
|
||||||
case FlowTransitionLineEnum.STRAIGHT_LINE:
|
case FlowTransitionLineEnum.STRAIGHT_LINE:
|
||||||
return (
|
return (
|
||||||
<StraightLine key={`${data.entity.id}${index}`} activated={lineActivated} {...line} />
|
<StraightLine
|
||||||
|
key={`${data.entity.id}_${index}`}
|
||||||
|
lineId={data.entity.id}
|
||||||
|
activated={lineActivated}
|
||||||
|
{...line}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
case FlowTransitionLineEnum.DIVERGE_LINE:
|
case FlowTransitionLineEnum.DIVERGE_LINE:
|
||||||
@ -57,7 +62,8 @@ export function createLines(props: PropsType): void {
|
|||||||
case FlowTransitionLineEnum.ROUNDED_LINE:
|
case FlowTransitionLineEnum.ROUNDED_LINE:
|
||||||
return (
|
return (
|
||||||
<RoundedTurningLine
|
<RoundedTurningLine
|
||||||
key={`${data.entity.id}${index}`}
|
key={`${data.entity.id}_${index}`}
|
||||||
|
lineId={data.entity.id}
|
||||||
isHorizontal={!isVertical}
|
isHorizontal={!isVertical}
|
||||||
activated={lineActivated || draggingLineActivated}
|
activated={lineActivated || draggingLineActivated}
|
||||||
{...line}
|
{...line}
|
||||||
@ -70,7 +76,8 @@ export function createLines(props: PropsType): void {
|
|||||||
case FlowTransitionLineEnum.CUSTOM_LINE:
|
case FlowTransitionLineEnum.CUSTOM_LINE:
|
||||||
return (
|
return (
|
||||||
<CustomLine
|
<CustomLine
|
||||||
key={`${data.entity.id}${index}`}
|
key={`${data.entity.id}_${index}`}
|
||||||
|
lineId={data.entity.id}
|
||||||
{...line}
|
{...line}
|
||||||
rendererRegistry={rendererRegistry}
|
rendererRegistry={rendererRegistry}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -2,14 +2,15 @@ import React from 'react';
|
|||||||
|
|
||||||
import { useBaseColor } from '../hooks/use-base-color';
|
import { useBaseColor } from '../hooks/use-base-color';
|
||||||
|
|
||||||
export const MARK_ACTIVATED_ARROW_ID = 'line-marker-arrow-activated';
|
export const MARK_ACTIVATED_ARROW_ID = '$marker_arrow_activated$';
|
||||||
export const MARK_ACTIVATED_ARROW_URL = `url(#${MARK_ACTIVATED_ARROW_ID})`;
|
// export const MARK_ACTIVATED_ARROW_URL = `url(#${MARK_ACTIVATED_ARROW_ID})`;
|
||||||
|
|
||||||
function MarkerActivatedArrow(): JSX.Element {
|
function MarkerActivatedArrow(props: { id?: string }): JSX.Element {
|
||||||
const { baseActivatedColor } = useBaseColor();
|
const { baseActivatedColor } = useBaseColor();
|
||||||
return (
|
return (
|
||||||
<marker
|
<marker
|
||||||
id={MARK_ACTIVATED_ARROW_ID}
|
data-line-id={props.id}
|
||||||
|
id={props.id || MARK_ACTIVATED_ARROW_ID}
|
||||||
markerWidth="11"
|
markerWidth="11"
|
||||||
markerHeight="14"
|
markerHeight="14"
|
||||||
refX="10"
|
refX="10"
|
||||||
|
|||||||
@ -2,13 +2,21 @@ import React from 'react';
|
|||||||
|
|
||||||
import { useBaseColor } from '../hooks/use-base-color';
|
import { useBaseColor } from '../hooks/use-base-color';
|
||||||
|
|
||||||
export const MARK_ARROW_ID = 'line-marker-arrow';
|
export const MARK_ARROW_ID = '$marker_arrow$';
|
||||||
export const MARK_ARROW_URL = `url(#${MARK_ARROW_ID})`;
|
// export const MARK_ARROW_URL = `url(#${MARK_ARROW_ID})`;
|
||||||
|
|
||||||
function MarkerArrow(): JSX.Element {
|
function MarkerArrow(props: { id: string }): JSX.Element {
|
||||||
const { baseColor } = useBaseColor();
|
const { baseColor } = useBaseColor();
|
||||||
return (
|
return (
|
||||||
<marker id={MARK_ARROW_ID} markerWidth="11" markerHeight="14" refX="10" refY="7" orient="auto">
|
<marker
|
||||||
|
data-line-id={props.id}
|
||||||
|
id={props.id || MARK_ARROW_ID}
|
||||||
|
markerWidth="11"
|
||||||
|
markerHeight="14"
|
||||||
|
refX="10"
|
||||||
|
refY="7"
|
||||||
|
orient="auto"
|
||||||
|
>
|
||||||
<path
|
<path
|
||||||
d="M9.6 5.2C10.8 6.1 10.8 7.9 9.6 8.8L3.6 13.3C2.11672 14.4125 0 13.3541 0 11.5L0 2.5C0 0.645898 2.11672 -0.412461 3.6 0.7L9.6 5.2Z"
|
d="M9.6 5.2C10.8 6.1 10.8 7.9 9.6 8.8L3.6 13.3C2.11672 14.4125 0 13.3541 0 11.5L0 2.5C0 0.645898 2.11672 -0.412461 3.6 0.7L9.6 5.2Z"
|
||||||
fill={baseColor}
|
fill={baseColor}
|
||||||
|
|||||||
@ -3,11 +3,13 @@ import React, { useMemo } from 'react';
|
|||||||
import { isNil } from 'lodash';
|
import { isNil } from 'lodash';
|
||||||
import { Point } from '@flowgram.ai/utils';
|
import { Point } from '@flowgram.ai/utils';
|
||||||
import { type FlowTransitionLine } from '@flowgram.ai/document';
|
import { type FlowTransitionLine } from '@flowgram.ai/document';
|
||||||
|
import { useService } from '@flowgram.ai/core';
|
||||||
|
|
||||||
import { useBaseColor } from '../hooks/use-base-color';
|
import { useBaseColor } from '../hooks/use-base-color';
|
||||||
import { DEFAULT_LINE_ATTRS, DEFAULT_RADIUS, getHorizontalVertices, getVertices } from './utils';
|
import { DEFAULT_LINE_ATTRS, DEFAULT_RADIUS, getHorizontalVertices, getVertices } from './utils';
|
||||||
import { MARK_ARROW_URL } from './MarkerArrow';
|
import MarkerArrow, { MARK_ARROW_ID } from './MarkerArrow';
|
||||||
import { MARK_ACTIVATED_ARROW_URL } from './MarkerActivatedArrow';
|
import MarkerActivatedArrow, { MARK_ACTIVATED_ARROW_ID } from './MarkerActivatedArrow';
|
||||||
|
import { FlowRendererKey, FlowRendererRegistry } from '../flow-renderer-registry';
|
||||||
|
|
||||||
interface PropsType extends FlowTransitionLine {
|
interface PropsType extends FlowTransitionLine {
|
||||||
radius?: number;
|
radius?: number;
|
||||||
@ -16,6 +18,27 @@ interface PropsType extends FlowTransitionLine {
|
|||||||
yRadius?: number;
|
yRadius?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function MarkerDefs(props: { id: string; activated?: boolean }): JSX.Element {
|
||||||
|
const renderRegistry = useService(FlowRendererRegistry);
|
||||||
|
const ArrowRenderer = renderRegistry?.tryToGetRendererComponent(
|
||||||
|
props.activated ? FlowRendererKey.MARKER_ACTIVATE_ARROW : FlowRendererKey.MARKER_ARROW
|
||||||
|
);
|
||||||
|
if (ArrowRenderer) {
|
||||||
|
return <ArrowRenderer.renderer {...props} />;
|
||||||
|
}
|
||||||
|
if (props.activated) {
|
||||||
|
return (
|
||||||
|
<defs>
|
||||||
|
<MarkerActivatedArrow id={props.id} />
|
||||||
|
</defs>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<defs>
|
||||||
|
<MarkerArrow id={props.id} />
|
||||||
|
</defs>
|
||||||
|
);
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* 圆角转弯线
|
* 圆角转弯线
|
||||||
*/
|
*/
|
||||||
@ -100,19 +123,26 @@ function RoundedTurningLine(props: PropsType): JSX.Element | null {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const pathStr = `M ${from.x} ${from.y} ${middleStr} L ${to.x} ${to.y}`;
|
const pathStr = `M ${from.x} ${from.y} ${middleStr} L ${to.x} ${to.y}`;
|
||||||
|
const markerId = activated
|
||||||
|
? `${MARK_ACTIVATED_ARROW_ID}${props.lineId}`
|
||||||
|
: `${MARK_ARROW_ID}${props.lineId}`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<path
|
<>
|
||||||
d={pathStr}
|
{arrow ? <MarkerDefs id={markerId} activated={activated} /> : null}
|
||||||
{...DEFAULT_LINE_ATTRS}
|
<path
|
||||||
stroke={activated ? baseActivatedColor : baseColor}
|
data-line-id={props.lineId}
|
||||||
{...(arrow
|
d={pathStr}
|
||||||
? {
|
{...DEFAULT_LINE_ATTRS}
|
||||||
markerEnd: activated ? MARK_ACTIVATED_ARROW_URL : MARK_ARROW_URL,
|
stroke={activated ? baseActivatedColor : baseColor}
|
||||||
}
|
{...(arrow
|
||||||
: {})}
|
? {
|
||||||
style={style}
|
markerEnd: `url(#${markerId})`,
|
||||||
/>
|
}
|
||||||
|
: {})}
|
||||||
|
style={style}
|
||||||
|
></path>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -11,6 +11,7 @@ function StraightLine(props: FlowTransitionLine): JSX.Element {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<path
|
<path
|
||||||
|
data-line-id={props.lineId}
|
||||||
d={`M ${from.x} ${from.y} L ${to.x} ${to.y}`}
|
d={`M ${from.x} ${from.y} L ${to.x} ${to.y}`}
|
||||||
{...DEFAULT_LINE_ATTRS}
|
{...DEFAULT_LINE_ATTRS}
|
||||||
stroke={activated ? baseActivatedColor : baseColor}
|
stroke={activated ? baseActivatedColor : baseColor}
|
||||||
|
|||||||
@ -13,9 +13,7 @@ import {
|
|||||||
} from '@flowgram.ai/document';
|
} from '@flowgram.ai/document';
|
||||||
import { Layer, observeEntity, observeEntityDatas } from '@flowgram.ai/core';
|
import { Layer, observeEntity, observeEntityDatas } from '@flowgram.ai/core';
|
||||||
|
|
||||||
import { FlowRendererKey, FlowRendererRegistry } from '../flow-renderer-registry';
|
import { FlowRendererRegistry } from '../flow-renderer-registry';
|
||||||
import MarkerArrow from '../components/MarkerArrow';
|
|
||||||
import MarkerActivatedArrow from '../components/MarkerActivatedArrow';
|
|
||||||
import { createLines } from '../components/LinesRenderer';
|
import { createLines } from '../components/LinesRenderer';
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
@ -90,19 +88,6 @@ export class FlowLinesLayer extends Layer {
|
|||||||
);
|
);
|
||||||
const resultLines = [...normalLines, ...activateLines];
|
const resultLines = [...normalLines, ...activateLines];
|
||||||
|
|
||||||
const arrowRenderer = this.rendererRegistry.tryToGetRendererComponent(
|
|
||||||
FlowRendererKey.MARKER_ARROW
|
|
||||||
);
|
|
||||||
const activateArrowRenderer = this.rendererRegistry.tryToGetRendererComponent(
|
|
||||||
FlowRendererKey.MARKER_ACTIVATE_ARROW
|
|
||||||
);
|
|
||||||
const arrow = arrowRenderer
|
|
||||||
? React.createElement(arrowRenderer.renderer as () => JSX.Element)
|
|
||||||
: null;
|
|
||||||
const activateArrow = activateArrowRenderer
|
|
||||||
? React.createElement(activateArrowRenderer.renderer as () => JSX.Element)
|
|
||||||
: null;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
className="flow-lines-container"
|
className="flow-lines-container"
|
||||||
@ -112,10 +97,6 @@ export class FlowLinesLayer extends Layer {
|
|||||||
viewBox={this.viewBox}
|
viewBox={this.viewBox}
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
>
|
>
|
||||||
<defs>
|
|
||||||
{arrowRenderer ? arrow : <MarkerArrow />}
|
|
||||||
{activateArrow ? activateArrow : <MarkerActivatedArrow />}
|
|
||||||
</defs>
|
|
||||||
{resultLines}
|
{resultLines}
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -129,6 +129,7 @@ function TryCatchCollapse(props: CustomLabelProps): JSX.Element {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
data-label-id={props.labelId}
|
||||||
style={{
|
style={{
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
color: hoverActivated ? baseActivatedColor : baseColor,
|
color: hoverActivated ? baseActivatedColor : baseColor,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user