feat: Implement MiniMap, Background and Controls components

This commit is contained in:
Braks
2021-07-09 17:52:21 +02:00
parent b6d9265317
commit 4319e2ac49
9 changed files with 460 additions and 3 deletions
@@ -0,0 +1,86 @@
import { defineComponent, PropType } from 'vue';
interface MiniMapNodeProps {
x: number;
y: number;
width: number;
height: number;
borderRadius: number;
color: string;
shapeRendering: string;
strokeColor: string;
strokeWidth: number;
}
const MiniMapNode = defineComponent({
name: 'MiniMapNode',
props: {
x: {
type: Number as PropType<MiniMapNodeProps['x']>,
required: false,
default: undefined
},
y: {
type: Number as PropType<MiniMapNodeProps['y']>,
required: false,
default: undefined
},
width: {
type: Number as PropType<MiniMapNodeProps['width']>,
required: false,
default: undefined
},
height: {
type: Number as PropType<MiniMapNodeProps['height']>,
required: false,
default: undefined
},
borderRadius: {
type: Number as PropType<MiniMapNodeProps['borderRadius']>,
required: false,
default: undefined
},
color: {
type: String as PropType<MiniMapNodeProps['color']>,
required: false,
default: undefined
},
shapeRendering: {
type: String as PropType<MiniMapNodeProps['shapeRendering']>,
required: false,
default: undefined
},
strokeColor: {
type: String as PropType<MiniMapNodeProps['strokeColor']>,
required: false,
default: undefined
},
strokeWidth: {
type: Number as PropType<MiniMapNodeProps['strokeWidth']>,
required: false,
default: undefined
}
},
setup(props, { attrs }: { attrs: Record<string, any> }) {
const { background, backgroundColor } = attrs.style || {};
const fill = (props.color || background || backgroundColor) as string;
return () => (
<rect
class={['react-flow__minimap-node']}
x={props.x}
y={props.y}
rx={props.borderRadius}
ry={props.borderRadius}
width={props.width}
height={props.height}
fill={fill}
stroke={props.strokeColor}
stroke-width={props.strokeWidth}
shape-rendering={props.shapeRendering}
/>
);
}
});
export default MiniMapNode;
+131
View File
@@ -0,0 +1,131 @@
import { getRectOfNodes, getBoundsofRects } from '../../utils/graph';
import { Node, Rect } from '../../types';
import MiniMapNode from './MiniMapNode';
import { computed, defineComponent, HTMLAttributes, PropType } from 'vue';
import store from '../../store';
type StringFunc = (node: Node) => string;
export interface MiniMapProps extends HTMLAttributes {
nodeColor?: string | StringFunc;
nodeStrokeColor?: string | StringFunc;
nodeClassName?: string | StringFunc;
nodeBorderRadius?: number;
nodeStrokeWidth?: number;
maskColor?: string;
}
declare const window: any;
const defaultWidth = 200;
const defaultHeight = 150;
const MiniMap = defineComponent({
name: 'MiniMap',
props: {
nodeStrokeColor: {
type: (String || Function) as PropType<MiniMapProps['nodeStrokeColor']>,
required: false,
default: '#555'
},
nodeColor: {
type: (String || Function) as PropType<MiniMapProps['nodeColor']>,
required: false,
default: '#fff'
},
nodeClassName: {
type: (String || Function) as PropType<MiniMapProps['nodeClassName']>,
required: false,
default: ''
},
nodeBorderRadius: {
type: Number as PropType<MiniMapProps['nodeBorderRadius']>,
required: false,
default: 5
},
nodeStrokeWidth: {
type: Number as PropType<MiniMapProps['nodeStrokeWidth']>,
required: false,
default: 2
},
maskColor: {
type: String as PropType<MiniMapProps['maskColor']>,
required: false,
default: 'rgb(240, 242, 243, 0.7)'
}
},
setup(props, { attrs }: { attrs: Record<string, any> }) {
const pinia = store();
const transform = computed(() => pinia.transform);
const mapClasses = ['react-flow__minimap'];
const elementWidth = computed(() => (attrs.style?.width || defaultWidth)! as number);
const elementHeight = computed(() => (attrs.style?.height || defaultHeight)! as number);
const nodeColorFunc = (props.nodeColor instanceof Function ? props.nodeColor : () => props.nodeColor) as StringFunc;
const nodeStrokeColorFunc = (
props.nodeStrokeColor instanceof Function ? props.nodeStrokeColor : () => props.nodeStrokeColor
) as StringFunc;
const nodeClassNameFunc = (
props.nodeClassName instanceof Function ? props.nodeClassName : () => props.nodeClassName
) as StringFunc;
const hasNodes = computed(() => pinia.nodes && pinia.nodes.length);
const bb = computed(() => getRectOfNodes(pinia.nodes));
const viewBB = computed<Rect>(() => ({
x: -transform.value[0] / transform.value[2],
y: -transform.value[1] / transform.value[2],
width: pinia.width / transform.value[2],
height: pinia.height / transform.value[2]
}));
const boundingRect = computed(() => (hasNodes.value ? getBoundsofRects(bb.value, viewBB.value) : viewBB.value));
const scaledWidth = computed(() => boundingRect.value.width / elementWidth.value);
const scaledHeight = computed(() => boundingRect.value.height / elementHeight.value);
const viewScale = computed(() => Math.max(scaledWidth.value, scaledHeight.value));
const viewWidth = computed(() => viewScale.value * elementWidth.value);
const viewHeight = computed(() => viewScale.value * elementHeight.value);
const offset = computed(() => 5 * viewScale.value);
const x = computed(() => boundingRect.value.x - (viewWidth.value - boundingRect.value.width) / 2 - offset.value);
const y = computed(() => boundingRect.value.y - (viewHeight.value - boundingRect.value.height) / 2 - offset.value);
const width = computed(() => viewWidth.value + offset.value * 2);
const height = computed(() => viewHeight.value + offset.value * 2);
const shapeRendering = typeof window === 'undefined' || !!window.chrome ? 'crispEdges' : 'geometricPrecision';
return () => (
<svg
width={elementWidth.value}
height={elementHeight.value}
viewBox={`${x.value} ${y.value} ${width.value} ${height.value}`}
class={mapClasses}
>
{pinia.nodes
.filter((node) => !node.isHidden)
.map((node) => (
<MiniMapNode
key={node.id}
x={node.__rf.position.x}
y={node.__rf.position.y}
width={node.__rf.width}
height={node.__rf.height}
style={node.style}
class={nodeClassNameFunc(node)}
color={nodeColorFunc(node)}
borderRadius={props.nodeBorderRadius}
strokeColor={nodeStrokeColorFunc(node)}
strokeWidth={props.nodeStrokeWidth}
shapeRendering={shapeRendering}
/>
))}
<path
class="react-flow__minimap-mask"
d={`M${x.value - offset.value},${y.value - offset.value}h${width.value + offset.value * 2}v${
height.value + offset.value * 2
}h${-width.value - offset.value * 2}z
M${viewBB.value.x},${viewBB.value.y}h${viewBB.value.width}v${viewBB.value.height}h${-viewBB.value.width}z`}
fill={props.maskColor}
fill-rule="evenodd"
/>
</svg>
);
}
});
export default MiniMap;