@flowgram-vue/renderer 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/dist/index.cjs +2933 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +721 -0
- package/dist/index.js +2918 -0
- package/dist/index.js.map +1 -0
- package/index.module.less +167 -0
- package/package.json +64 -0
- package/src/components/Adder.ts +107 -0
- package/src/components/BranchDraggableRenderer.ts +77 -0
- package/src/components/Collapse.ts +87 -0
- package/src/components/CollapseAdder.ts +94 -0
- package/src/components/CustomLine.ts +35 -0
- package/src/components/LabelsRenderer.ts +162 -0
- package/src/components/LinesRenderer.ts +99 -0
- package/src/components/MarkerActivatedArrow.ts +44 -0
- package/src/components/MarkerArrow.ts +44 -0
- package/src/components/RoundedTurningLine.ts +165 -0
- package/src/components/StraightLine.ts +31 -0
- package/src/components/utils.ts +295 -0
- package/src/entities/README.md +3 -0
- package/src/entities/flow-drag-entity.ts +267 -0
- package/src/entities/flow-select-config-entity.ts +114 -0
- package/src/entities/index.ts +8 -0
- package/src/entities/selector-box-config-entity.ts +88 -0
- package/src/env.d.ts +10 -0
- package/src/flow-renderer-container-module.ts +14 -0
- package/src/flow-renderer-contribution.ts +12 -0
- package/src/flow-renderer-registry.ts +155 -0
- package/src/flow-renderer-resize-observer.ts +56 -0
- package/src/hooks/use-base-color.ts +26 -0
- package/src/index.ts +16 -0
- package/src/layer-vue-provide.ts +44 -0
- package/src/layers/flow-context-menu-layer.ts +153 -0
- package/src/layers/flow-debug-layer.ts +227 -0
- package/src/layers/flow-drag-layer.ts +413 -0
- package/src/layers/flow-labels-layer.ts +100 -0
- package/src/layers/flow-lines-layer.ts +111 -0
- package/src/layers/flow-nodes-content-layer.ts +155 -0
- package/src/layers/flow-nodes-transform-layer.ts +151 -0
- package/src/layers/flow-scroll-bar-layer.ts +401 -0
- package/src/layers/flow-scroll-limit-layer.ts +36 -0
- package/src/layers/flow-selector-bounds-layer.ts +191 -0
- package/src/layers/flow-selector-box-layer.ts +244 -0
- package/src/layers/index.ts +16 -0
- package/src/utils/element.ts +36 -0
- package/src/utils/find-selected-nodes.ts +80 -0
- package/src/utils/index.ts +7 -0
- package/src/utils/scroll-bar-events.ts +13 -0
- package/src/utils/scroll-limit.ts +58 -0
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { h, type CSSProperties, type Component, type VNode } from 'vue';
|
|
7
|
+
import { type IPoint, Rectangle } from '@flowgram-vue/utils';
|
|
8
|
+
import {
|
|
9
|
+
type CustomLabelProps,
|
|
10
|
+
type FlowNodeTransitionData,
|
|
11
|
+
type FlowTransitionLabel,
|
|
12
|
+
FlowTransitionLabelEnum,
|
|
13
|
+
} from '@flowgram-vue/document';
|
|
14
|
+
|
|
15
|
+
import { type FlowRendererRegistry } from '../flow-renderer-registry';
|
|
16
|
+
import CollapseAdder from './CollapseAdder';
|
|
17
|
+
import Collapse from './Collapse';
|
|
18
|
+
import BranchDraggableRenderer from './BranchDraggableRenderer';
|
|
19
|
+
import Adder from './Adder';
|
|
20
|
+
|
|
21
|
+
export interface LabelOpts {
|
|
22
|
+
data: FlowNodeTransitionData;
|
|
23
|
+
rendererRegistry: FlowRendererRegistry;
|
|
24
|
+
isViewportVisible: (bounds: Rectangle) => boolean;
|
|
25
|
+
labelsSave: VNode[];
|
|
26
|
+
getLabelColor: (activated?: boolean) => string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const TEXT_LABEL_STYLE: CSSProperties = {
|
|
30
|
+
fontSize: '12px',
|
|
31
|
+
color: '#8F959E',
|
|
32
|
+
textAlign: 'center',
|
|
33
|
+
whiteSpace: 'nowrap',
|
|
34
|
+
backgroundColor: 'var(--g-editor-background)',
|
|
35
|
+
lineHeight: '20px',
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const LABEL_MAX_WIDTH = 150;
|
|
39
|
+
const LABEL_MAX_HEIGHT = 60;
|
|
40
|
+
|
|
41
|
+
function getLabelBounds(offset: IPoint) {
|
|
42
|
+
return new Rectangle(
|
|
43
|
+
offset.x - LABEL_MAX_WIDTH / 2,
|
|
44
|
+
offset.y - LABEL_MAX_HEIGHT / 2,
|
|
45
|
+
LABEL_MAX_WIDTH,
|
|
46
|
+
LABEL_MAX_HEIGHT
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function createLabels(labelProps: LabelOpts): void {
|
|
51
|
+
const { data, rendererRegistry, labelsSave, getLabelColor } = labelProps;
|
|
52
|
+
const { labels, renderData } = data || {};
|
|
53
|
+
const { activated } = renderData || {};
|
|
54
|
+
|
|
55
|
+
const renderLabel = (label: FlowTransitionLabel, index: number) => {
|
|
56
|
+
const { offset, renderKey, props, rotate, origin, type } = label || {};
|
|
57
|
+
const offsetX = offset.x;
|
|
58
|
+
const offsetY = offset.y;
|
|
59
|
+
|
|
60
|
+
let child = null as VNode | string | null;
|
|
61
|
+
switch (type) {
|
|
62
|
+
case FlowTransitionLabelEnum.BRANCH_DRAGGING_LABEL:
|
|
63
|
+
child = h(BranchDraggableRenderer, {
|
|
64
|
+
labelId: label.labelId || labelProps.data.entity.id,
|
|
65
|
+
rendererRegistry,
|
|
66
|
+
data,
|
|
67
|
+
...props,
|
|
68
|
+
});
|
|
69
|
+
break;
|
|
70
|
+
case FlowTransitionLabelEnum.ADDER_LABEL:
|
|
71
|
+
child = h(Adder, {
|
|
72
|
+
labelId: label.labelId || labelProps.data.entity.id,
|
|
73
|
+
rendererRegistry,
|
|
74
|
+
data,
|
|
75
|
+
...props,
|
|
76
|
+
});
|
|
77
|
+
break;
|
|
78
|
+
|
|
79
|
+
case FlowTransitionLabelEnum.COLLAPSE_LABEL:
|
|
80
|
+
child = h(Collapse, {
|
|
81
|
+
labelId: label.labelId || labelProps.data.entity.id,
|
|
82
|
+
rendererRegistry,
|
|
83
|
+
data,
|
|
84
|
+
...props,
|
|
85
|
+
});
|
|
86
|
+
break;
|
|
87
|
+
|
|
88
|
+
case FlowTransitionLabelEnum.COLLAPSE_ADDER_LABEL:
|
|
89
|
+
child = h(CollapseAdder, {
|
|
90
|
+
labelId: label.labelId || labelProps.data.entity.id,
|
|
91
|
+
rendererRegistry,
|
|
92
|
+
data,
|
|
93
|
+
...props,
|
|
94
|
+
});
|
|
95
|
+
break;
|
|
96
|
+
|
|
97
|
+
case FlowTransitionLabelEnum.TEXT_LABEL:
|
|
98
|
+
if (!renderKey) {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
const text = rendererRegistry.getText(renderKey) || renderKey;
|
|
102
|
+
child = h(
|
|
103
|
+
'div',
|
|
104
|
+
{
|
|
105
|
+
'data-label-id': label.labelId || labelProps.data.entity.id,
|
|
106
|
+
style: {
|
|
107
|
+
...TEXT_LABEL_STYLE,
|
|
108
|
+
...props?.style,
|
|
109
|
+
color: getLabelColor(activated),
|
|
110
|
+
transform: rotate ? `rotate(${rotate})` : undefined,
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
text
|
|
114
|
+
);
|
|
115
|
+
break;
|
|
116
|
+
|
|
117
|
+
case FlowTransitionLabelEnum.CUSTOM_LABEL:
|
|
118
|
+
if (!renderKey) {
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
try {
|
|
122
|
+
const renderer = rendererRegistry.getRendererComponent(renderKey);
|
|
123
|
+
child = h(renderer.renderer as Component, {
|
|
124
|
+
node: data.entity,
|
|
125
|
+
labelId: label.labelId || labelProps.data.entity.id,
|
|
126
|
+
...props,
|
|
127
|
+
} as CustomLabelProps);
|
|
128
|
+
} catch (err) {
|
|
129
|
+
console.error(err);
|
|
130
|
+
child = renderKey;
|
|
131
|
+
}
|
|
132
|
+
break;
|
|
133
|
+
default:
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const originX = typeof origin?.[0] === 'number' ? origin?.[0] : 0.5;
|
|
138
|
+
const originY = typeof origin?.[1] === 'number' ? origin?.[1] : 0.5;
|
|
139
|
+
|
|
140
|
+
return h(
|
|
141
|
+
'div',
|
|
142
|
+
{
|
|
143
|
+
key: `${data.entity.id}${index}`,
|
|
144
|
+
'data-label-id': label.labelId || labelProps.data.entity.id,
|
|
145
|
+
style: {
|
|
146
|
+
position: 'absolute',
|
|
147
|
+
left: `${offsetX}px`,
|
|
148
|
+
top: `${offsetY}px`,
|
|
149
|
+
transform: `translate(-${originX * 100}%, -${originY * 100}%)`,
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
[child]
|
|
153
|
+
);
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
labels.forEach((label, index) => {
|
|
157
|
+
if (labelProps.isViewportVisible(getLabelBounds(label.offset))) {
|
|
158
|
+
const vnode = renderLabel(label, index);
|
|
159
|
+
if (vnode) labelsSave.push(vnode);
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { VNode } from 'vue';
|
|
7
|
+
import { h } from 'vue';
|
|
8
|
+
import { Rectangle } from '@flowgram-vue/utils';
|
|
9
|
+
import {
|
|
10
|
+
FlowDragService,
|
|
11
|
+
type FlowNodeTransitionData,
|
|
12
|
+
type FlowTransitionLine,
|
|
13
|
+
FlowTransitionLineEnum,
|
|
14
|
+
DefaultSpacingKey,
|
|
15
|
+
} from '@flowgram-vue/document';
|
|
16
|
+
import { getDefaultSpacing } from '@flowgram-vue/document';
|
|
17
|
+
|
|
18
|
+
import { type FlowRendererRegistry } from '../flow-renderer-registry';
|
|
19
|
+
import StraightLine from './StraightLine';
|
|
20
|
+
import RoundedTurningLine from './RoundedTurningLine';
|
|
21
|
+
import CustomLine from './CustomLine';
|
|
22
|
+
|
|
23
|
+
export interface PropsType {
|
|
24
|
+
data: FlowNodeTransitionData;
|
|
25
|
+
rendererRegistry: FlowRendererRegistry;
|
|
26
|
+
isViewportVisible: (bounds: Rectangle) => boolean;
|
|
27
|
+
linesSave: VNode[];
|
|
28
|
+
dragService: FlowDragService;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function createLines(props: PropsType): void {
|
|
32
|
+
const { data, rendererRegistry, linesSave, dragService } = props;
|
|
33
|
+
const { lines, entity } = data || {};
|
|
34
|
+
|
|
35
|
+
const radius = getDefaultSpacing(entity, DefaultSpacingKey.ROUNDED_LINE_RADIUS);
|
|
36
|
+
const xRadius = getDefaultSpacing(entity, DefaultSpacingKey.ROUNDED_LINE_X_RADIUS);
|
|
37
|
+
const yRadius = getDefaultSpacing(entity, DefaultSpacingKey.ROUNDED_LINE_Y_RADIUS);
|
|
38
|
+
|
|
39
|
+
const renderLine = (line: FlowTransitionLine, index: number) => {
|
|
40
|
+
const { renderData } = data;
|
|
41
|
+
const { isVertical } = data.entity;
|
|
42
|
+
const { lineActivated } = renderData || {};
|
|
43
|
+
|
|
44
|
+
const draggingLineHide =
|
|
45
|
+
(line.type === FlowTransitionLineEnum.DRAGGING_LINE || line.isDraggingLine) &&
|
|
46
|
+
!dragService.isDroppableBranch(data.entity, line.side);
|
|
47
|
+
|
|
48
|
+
const draggingLineActivated =
|
|
49
|
+
(line.type === FlowTransitionLineEnum.DRAGGING_LINE || line.isDraggingLine) &&
|
|
50
|
+
data.entity?.id === dragService.dropNodeId &&
|
|
51
|
+
line.side === dragService.labelSide;
|
|
52
|
+
|
|
53
|
+
switch (line.type) {
|
|
54
|
+
case FlowTransitionLineEnum.STRAIGHT_LINE:
|
|
55
|
+
return h(StraightLine, {
|
|
56
|
+
key: `${data.entity.id}_${index}`,
|
|
57
|
+
lineId: data.entity.id,
|
|
58
|
+
activated: lineActivated,
|
|
59
|
+
...line,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
case FlowTransitionLineEnum.DIVERGE_LINE:
|
|
63
|
+
case FlowTransitionLineEnum.DRAGGING_LINE:
|
|
64
|
+
case FlowTransitionLineEnum.MERGE_LINE:
|
|
65
|
+
case FlowTransitionLineEnum.ROUNDED_LINE:
|
|
66
|
+
return h(RoundedTurningLine, {
|
|
67
|
+
key: `${data.entity.id}_${index}`,
|
|
68
|
+
lineId: data.entity.id,
|
|
69
|
+
isHorizontal: !isVertical,
|
|
70
|
+
activated: lineActivated || draggingLineActivated,
|
|
71
|
+
radius,
|
|
72
|
+
...line,
|
|
73
|
+
xRadius,
|
|
74
|
+
yRadius,
|
|
75
|
+
hide: draggingLineHide,
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
case FlowTransitionLineEnum.CUSTOM_LINE:
|
|
79
|
+
return h(CustomLine, {
|
|
80
|
+
key: `${data.entity.id}_${index}`,
|
|
81
|
+
lineId: data.entity.id,
|
|
82
|
+
...line,
|
|
83
|
+
rendererRegistry,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
default:
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return undefined;
|
|
91
|
+
};
|
|
92
|
+
lines.forEach((line, index) => {
|
|
93
|
+
const bounds = Rectangle.createRectangleWithTwoPoints(line.from, line.to).pad(10);
|
|
94
|
+
if (props.isViewportVisible(bounds)) {
|
|
95
|
+
const vnode = renderLine(line, index);
|
|
96
|
+
if (vnode) linesSave.push(vnode);
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { defineComponent, h } from 'vue';
|
|
7
|
+
|
|
8
|
+
import { useBaseColor } from '../hooks/use-base-color';
|
|
9
|
+
|
|
10
|
+
export const MARK_ACTIVATED_ARROW_ID = '$marker_arrow_activated$';
|
|
11
|
+
|
|
12
|
+
const MarkerActivatedArrow = defineComponent({
|
|
13
|
+
name: 'MarkerActivatedArrow',
|
|
14
|
+
props: {
|
|
15
|
+
id: {
|
|
16
|
+
type: String,
|
|
17
|
+
default: undefined,
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
setup(props) {
|
|
21
|
+
const { baseActivatedColor } = useBaseColor();
|
|
22
|
+
return () =>
|
|
23
|
+
h(
|
|
24
|
+
'marker',
|
|
25
|
+
{
|
|
26
|
+
'data-line-id': props.id,
|
|
27
|
+
id: props.id || MARK_ACTIVATED_ARROW_ID,
|
|
28
|
+
markerWidth: '11',
|
|
29
|
+
markerHeight: '14',
|
|
30
|
+
refX: '10',
|
|
31
|
+
refY: '7',
|
|
32
|
+
orient: 'auto',
|
|
33
|
+
},
|
|
34
|
+
[
|
|
35
|
+
h('path', {
|
|
36
|
+
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',
|
|
37
|
+
fill: baseActivatedColor,
|
|
38
|
+
}),
|
|
39
|
+
]
|
|
40
|
+
);
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
export default MarkerActivatedArrow;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { defineComponent, h } from 'vue';
|
|
7
|
+
|
|
8
|
+
import { useBaseColor } from '../hooks/use-base-color';
|
|
9
|
+
|
|
10
|
+
export const MARK_ARROW_ID = '$marker_arrow$';
|
|
11
|
+
|
|
12
|
+
const MarkerArrow = defineComponent({
|
|
13
|
+
name: 'MarkerArrow',
|
|
14
|
+
props: {
|
|
15
|
+
id: {
|
|
16
|
+
type: String,
|
|
17
|
+
required: true,
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
setup(props) {
|
|
21
|
+
const { baseColor } = useBaseColor();
|
|
22
|
+
return () =>
|
|
23
|
+
h(
|
|
24
|
+
'marker',
|
|
25
|
+
{
|
|
26
|
+
'data-line-id': props.id,
|
|
27
|
+
id: props.id || MARK_ARROW_ID,
|
|
28
|
+
markerWidth: '11',
|
|
29
|
+
markerHeight: '14',
|
|
30
|
+
refX: '10',
|
|
31
|
+
refY: '7',
|
|
32
|
+
orient: 'auto',
|
|
33
|
+
},
|
|
34
|
+
[
|
|
35
|
+
h('path', {
|
|
36
|
+
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',
|
|
37
|
+
fill: baseColor,
|
|
38
|
+
}),
|
|
39
|
+
]
|
|
40
|
+
);
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
export default MarkerArrow;
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { computed, defineComponent, h, inject } from 'vue';
|
|
7
|
+
import type { Component } from 'vue';
|
|
8
|
+
import { isNil } from 'lodash-es';
|
|
9
|
+
import { Point } from '@flowgram-vue/utils';
|
|
10
|
+
import { type FlowTransitionLine } from '@flowgram-vue/document';
|
|
11
|
+
import { PlaygroundVueContainerKey } from '@flowgram-vue/core';
|
|
12
|
+
|
|
13
|
+
import { useBaseColor } from '../hooks/use-base-color';
|
|
14
|
+
import { DEFAULT_LINE_ATTRS, DEFAULT_RADIUS, getHorizontalVertices, getVertices } from './utils';
|
|
15
|
+
import MarkerArrow, { MARK_ARROW_ID } from './MarkerArrow';
|
|
16
|
+
import MarkerActivatedArrow, { MARK_ACTIVATED_ARROW_ID } from './MarkerActivatedArrow';
|
|
17
|
+
import { FlowRendererKey, FlowRendererRegistry } from '../flow-renderer-registry';
|
|
18
|
+
|
|
19
|
+
interface PropsType extends FlowTransitionLine {
|
|
20
|
+
radius?: number;
|
|
21
|
+
hide?: boolean;
|
|
22
|
+
xRadius?: number;
|
|
23
|
+
yRadius?: number;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const MarkerDefs = defineComponent({
|
|
27
|
+
name: 'MarkerDefs',
|
|
28
|
+
props: {
|
|
29
|
+
id: { type: String, required: true },
|
|
30
|
+
activated: { type: Boolean, default: false },
|
|
31
|
+
},
|
|
32
|
+
setup(props) {
|
|
33
|
+
const container = inject(PlaygroundVueContainerKey, null as any);
|
|
34
|
+
return () => {
|
|
35
|
+
const renderRegistry = container?.get?.(FlowRendererRegistry) as
|
|
36
|
+
| FlowRendererRegistry
|
|
37
|
+
| undefined;
|
|
38
|
+
const ArrowRenderer = renderRegistry?.tryToGetRendererComponent(
|
|
39
|
+
props.activated ? FlowRendererKey.MARKER_ACTIVATE_ARROW : FlowRendererKey.MARKER_ARROW
|
|
40
|
+
);
|
|
41
|
+
if (ArrowRenderer) {
|
|
42
|
+
return h(ArrowRenderer.renderer as Component, { id: props.id, activated: props.activated });
|
|
43
|
+
}
|
|
44
|
+
if (props.activated) {
|
|
45
|
+
return h('defs', null, [h(MarkerActivatedArrow, { id: props.id })]);
|
|
46
|
+
}
|
|
47
|
+
return h('defs', null, [h(MarkerArrow, { id: props.id })]);
|
|
48
|
+
};
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* 圆角转弯线
|
|
54
|
+
*/
|
|
55
|
+
const RoundedTurningLine = defineComponent({
|
|
56
|
+
name: 'RoundedTurningLine',
|
|
57
|
+
inheritAttrs: false,
|
|
58
|
+
setup(_, { attrs }) {
|
|
59
|
+
const props = attrs as unknown as PropsType;
|
|
60
|
+
const { baseActivatedColor, baseColor } = useBaseColor();
|
|
61
|
+
|
|
62
|
+
const realVertices = computed(() => {
|
|
63
|
+
const { vertices, xRadius, yRadius } = props;
|
|
64
|
+
return (
|
|
65
|
+
vertices ||
|
|
66
|
+
(props.isHorizontal
|
|
67
|
+
? getHorizontalVertices(props, xRadius, yRadius)
|
|
68
|
+
: getVertices(props, xRadius, yRadius))
|
|
69
|
+
);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
const middleStr = computed(() => {
|
|
73
|
+
const { radius = DEFAULT_RADIUS, from, to } = props;
|
|
74
|
+
const vertices = realVertices.value;
|
|
75
|
+
return vertices
|
|
76
|
+
.map((point, idx) => {
|
|
77
|
+
const prev = vertices[idx - 1] || from;
|
|
78
|
+
const next = vertices[idx + 1] || to;
|
|
79
|
+
|
|
80
|
+
const prevDelta = { x: Math.abs(prev.x - point.x), y: Math.abs(prev.y - point.y) };
|
|
81
|
+
const nextDelta = { x: Math.abs(next.x - point.x), y: Math.abs(next.y - point.y) };
|
|
82
|
+
|
|
83
|
+
const isRightAngleX = prevDelta.x === 0 && nextDelta.y === 0;
|
|
84
|
+
const isRightAngleY = prevDelta.y === 0 && nextDelta.x === 0;
|
|
85
|
+
const isRightAngle = isRightAngleX || isRightAngleY;
|
|
86
|
+
|
|
87
|
+
if (!isRightAngle) {
|
|
88
|
+
console.error(`vertex ${point.x},${point.y} is not right angle`);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const inPoint = new Point().copyFrom(point);
|
|
92
|
+
const outPoint = new Point().copyFrom(point);
|
|
93
|
+
const radiusX = isNil(point.radiusX) ? radius : point.radiusX;
|
|
94
|
+
const radiusY = isNil(point.radiusY) ? radius : point.radiusY;
|
|
95
|
+
let rx = radiusX;
|
|
96
|
+
let ry = radiusY;
|
|
97
|
+
|
|
98
|
+
if (isRightAngleX) {
|
|
99
|
+
ry = Math.min(prevDelta.y, radiusY);
|
|
100
|
+
const moveY = isNil(point.moveY) ? ry : point.moveY;
|
|
101
|
+
inPoint.y += from.y < point.y ? -moveY : +moveY;
|
|
102
|
+
|
|
103
|
+
rx = Math.min(nextDelta.x, radiusX);
|
|
104
|
+
const moveX = isNil(point.moveX) ? rx : point.moveX;
|
|
105
|
+
outPoint.x += to.x < point.x ? -moveX : +moveX;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (isRightAngleY) {
|
|
109
|
+
rx = Math.min(prevDelta.x, radiusX);
|
|
110
|
+
const moveX = isNil(point.moveX) ? rx : point.moveX;
|
|
111
|
+
inPoint.x += from.x < point.x ? -moveX : +moveX;
|
|
112
|
+
|
|
113
|
+
ry = Math.min(nextDelta.y, radiusY);
|
|
114
|
+
const moveY = isNil(point.moveY) ? ry : point.moveY;
|
|
115
|
+
outPoint.y += to.y < point.y ? -moveY : +moveY;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (point.radiusOverflow === 'truncate') {
|
|
119
|
+
rx = radiusX;
|
|
120
|
+
ry = radiusY;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const crossProduct =
|
|
124
|
+
(point.x - inPoint.x) * (outPoint.y - inPoint.y) -
|
|
125
|
+
(point.y - inPoint.y) * (outPoint.x - inPoint.x);
|
|
126
|
+
const isClockWise = crossProduct > 0;
|
|
127
|
+
|
|
128
|
+
return `L ${inPoint.x} ${inPoint.y} A ${rx} ${ry} 0 0 ${isClockWise ? 1 : 0} ${
|
|
129
|
+
outPoint.x
|
|
130
|
+
} ${outPoint.y}`;
|
|
131
|
+
})
|
|
132
|
+
.join(' ');
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
return () => {
|
|
136
|
+
const { hide, from, to, arrow, activated, style } = props;
|
|
137
|
+
if (hide) {
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const pathStr = `M ${from.x} ${from.y} ${middleStr.value} L ${to.x} ${to.y}`;
|
|
142
|
+
const markerId = activated
|
|
143
|
+
? `${MARK_ACTIVATED_ARROW_ID}${props.lineId}`
|
|
144
|
+
: `${MARK_ARROW_ID}${props.lineId}`;
|
|
145
|
+
|
|
146
|
+
return [
|
|
147
|
+
arrow ? h(MarkerDefs, { id: markerId, activated }) : null,
|
|
148
|
+
h('path', {
|
|
149
|
+
'data-line-id': props.lineId,
|
|
150
|
+
d: pathStr,
|
|
151
|
+
...DEFAULT_LINE_ATTRS,
|
|
152
|
+
stroke: activated ? baseActivatedColor : baseColor,
|
|
153
|
+
...(arrow
|
|
154
|
+
? {
|
|
155
|
+
markerEnd: `url(#${markerId})`,
|
|
156
|
+
}
|
|
157
|
+
: {}),
|
|
158
|
+
style,
|
|
159
|
+
}),
|
|
160
|
+
];
|
|
161
|
+
};
|
|
162
|
+
},
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
export default RoundedTurningLine;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { defineComponent, h } from 'vue';
|
|
7
|
+
import type { FlowTransitionLine } from '@flowgram-vue/document';
|
|
8
|
+
|
|
9
|
+
import { useBaseColor } from '../hooks/use-base-color';
|
|
10
|
+
import { DEFAULT_LINE_ATTRS } from './utils';
|
|
11
|
+
|
|
12
|
+
const StraightLine = defineComponent({
|
|
13
|
+
name: 'StraightLine',
|
|
14
|
+
inheritAttrs: false,
|
|
15
|
+
setup(_, { attrs }) {
|
|
16
|
+
const line = attrs as unknown as FlowTransitionLine;
|
|
17
|
+
const { baseColor, baseActivatedColor } = useBaseColor();
|
|
18
|
+
return () => {
|
|
19
|
+
const { from, to, activated, style } = line;
|
|
20
|
+
return h('path', {
|
|
21
|
+
'data-line-id': line.lineId,
|
|
22
|
+
d: `M ${from.x} ${from.y} L ${to.x} ${to.y}`,
|
|
23
|
+
...DEFAULT_LINE_ATTRS,
|
|
24
|
+
stroke: activated ? baseActivatedColor : baseColor,
|
|
25
|
+
style,
|
|
26
|
+
});
|
|
27
|
+
};
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
export default StraightLine;
|