mirror of
https://gitee.com/antv/g6.git
synced 2024-12-03 12:18:40 +08:00
feat: style standards; perf: type refine (#4470)
* feat: style standards; perf: type refine * chore: refine
This commit is contained in:
parent
9c8db01fe1
commit
05948c5e7d
@ -1,4 +1,12 @@
|
|||||||
export const RESERVED_SHAPE_IDS = ['keyShape', 'labelShape', 'iconShape'];
|
export const RESERVED_SHAPE_IDS = [
|
||||||
|
'keyShape',
|
||||||
|
'labelShape',
|
||||||
|
'labelBackgroundShape',
|
||||||
|
'iconShape',
|
||||||
|
'haloShape',
|
||||||
|
'anchorShapes',
|
||||||
|
'badgeShapes',
|
||||||
|
];
|
||||||
export const OTHER_SHAPES_FIELD_NAME = 'otherShapes';
|
export const OTHER_SHAPES_FIELD_NAME = 'otherShapes';
|
||||||
|
|
||||||
export const DEFAULT_LABEL_BG_PADDING = [4, 4, 4, 4];
|
export const DEFAULT_LABEL_BG_PADDING = [4, 4, 4, 4];
|
||||||
@ -9,6 +17,7 @@ export const DEFAULT_SHAPE_STYLE = {
|
|||||||
shadowColor: undefined,
|
shadowColor: undefined,
|
||||||
shadowBlur: 0,
|
shadowBlur: 0,
|
||||||
lineDash: undefined,
|
lineDash: undefined,
|
||||||
|
zIndex: 0,
|
||||||
};
|
};
|
||||||
/** Default text style to avoid shape value missing */
|
/** Default text style to avoid shape value missing */
|
||||||
export const DEFAULT_TEXT_STYLE = {
|
export const DEFAULT_TEXT_STYLE = {
|
||||||
|
@ -1,16 +1,12 @@
|
|||||||
import { Group } from '@antv/g';
|
import { Group } from '@antv/g';
|
||||||
import { clone } from '@antv/util';
|
import { clone } from '@antv/util';
|
||||||
import { EdgeDisplayModel, EdgeModel } from '../types';
|
import { EdgeDisplayModel, EdgeModel, NodeModelData } from '../types';
|
||||||
import { EdgeModelData } from '../types/edge';
|
import { EdgeModelData } from '../types/edge';
|
||||||
import {
|
import { DisplayMapper, State } from '../types/item';
|
||||||
DisplayMapper,
|
|
||||||
ItemShapeStyles,
|
|
||||||
ITEM_TYPE,
|
|
||||||
State,
|
|
||||||
} from '../types/item';
|
|
||||||
import { updateShapes } from '../util/shape';
|
import { updateShapes } from '../util/shape';
|
||||||
import Item from './item';
|
import Item from './item';
|
||||||
import Node from './node';
|
import Node from './node';
|
||||||
|
import { EdgeStyleSet } from 'types/theme';
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
model: EdgeModel;
|
model: EdgeModel;
|
||||||
@ -22,7 +18,7 @@ interface IProps {
|
|||||||
};
|
};
|
||||||
sourceItem: Node;
|
sourceItem: Node;
|
||||||
targetItem: Node;
|
targetItem: Node;
|
||||||
themeStyles: ItemShapeStyles;
|
themeStyles: EdgeStyleSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class Edge extends Item {
|
export default class Edge extends Item {
|
||||||
@ -51,18 +47,10 @@ export default class Edge extends Item {
|
|||||||
diffState?: { previous: State[]; current: State[] },
|
diffState?: { previous: State[]; current: State[] },
|
||||||
) {
|
) {
|
||||||
// get the end points
|
// get the end points
|
||||||
const sourceBBox = this.sourceItem.getKeyBBox();
|
const { x: sx, y: sy, z: sz } = this.sourceItem.model.data as NodeModelData;
|
||||||
const targetBBox = this.targetItem.getKeyBBox();
|
const { x: tx, y: ty, z: tz } = this.targetItem.model.data as NodeModelData;
|
||||||
const sourcePoint = {
|
const sourcePoint = this.sourceItem.getAnchorPoint({ x: tx, y: ty, z: tz });
|
||||||
x: sourceBBox.center[0],
|
const targetPoint = this.targetItem.getAnchorPoint({ x: sx, y: sy, z: sz });
|
||||||
y: sourceBBox.center[1],
|
|
||||||
z: sourceBBox.center[2],
|
|
||||||
};
|
|
||||||
const targetPoint = {
|
|
||||||
x: targetBBox.center[0],
|
|
||||||
y: targetBBox.center[1],
|
|
||||||
z: targetBBox.center[2],
|
|
||||||
};
|
|
||||||
this.renderExt.mergeStyles(displayModel);
|
this.renderExt.mergeStyles(displayModel);
|
||||||
const shapeMap = this.renderExt.draw(
|
const shapeMap = this.renderExt.draw(
|
||||||
displayModel,
|
displayModel,
|
||||||
@ -75,8 +63,8 @@ export default class Edge extends Item {
|
|||||||
|
|
||||||
// add shapes to group, and update shapeMap
|
// add shapes to group, and update shapeMap
|
||||||
this.shapeMap = updateShapes(this.shapeMap, shapeMap, this.group);
|
this.shapeMap = updateShapes(this.shapeMap, shapeMap, this.group);
|
||||||
|
const { haloShape, labelShape, labelBackgroundShape } = this.shapeMap;
|
||||||
const { labelShape } = this.shapeMap;
|
haloShape?.toBack();
|
||||||
labelShape?.toFront();
|
labelShape?.toFront();
|
||||||
|
|
||||||
super.draw(displayModel, diffData, diffState);
|
super.draw(displayModel, diffData, diffState);
|
||||||
|
@ -13,10 +13,11 @@ import {
|
|||||||
State,
|
State,
|
||||||
} from '../types/item';
|
} from '../types/item';
|
||||||
import { NodeShapeMap } from '../types/node';
|
import { NodeShapeMap } from '../types/node';
|
||||||
import { ItemStyleSet } from '../types/theme';
|
import { EdgeStyleSet, NodeStyleSet } from '../types/theme';
|
||||||
import { isArrayOverlap } from '../util/array';
|
import { isArrayOverlap } from '../util/array';
|
||||||
import { mergeStyles, updateShapes } from '../util/shape';
|
import { mergeStyles, updateShapes } from '../util/shape';
|
||||||
import { isEncode } from '../util/type';
|
import { isEncode } from '../util/type';
|
||||||
|
import { DEFAULT_MAPPER } from '../util/mapper';
|
||||||
|
|
||||||
export default abstract class Item implements IItem {
|
export default abstract class Item implements IItem {
|
||||||
public destroyed = false;
|
public destroyed = false;
|
||||||
@ -116,7 +117,7 @@ export default abstract class Item implements IItem {
|
|||||||
model: ItemModel,
|
model: ItemModel,
|
||||||
diffData?: { previous: ItemModelData; current: ItemModelData },
|
diffData?: { previous: ItemModelData; current: ItemModelData },
|
||||||
isReplace?: boolean,
|
isReplace?: boolean,
|
||||||
themeStyles?: ItemStyleSet,
|
themeStyles?: NodeStyleSet | EdgeStyleSet,
|
||||||
) {
|
) {
|
||||||
// 1. merge model into this model
|
// 1. merge model into this model
|
||||||
this.model = model;
|
this.model = model;
|
||||||
@ -170,26 +171,34 @@ export default abstract class Item implements IItem {
|
|||||||
model: ItemDisplayModel;
|
model: ItemDisplayModel;
|
||||||
typeChange?: boolean;
|
typeChange?: boolean;
|
||||||
} {
|
} {
|
||||||
const { mapper } = this;
|
const { mapper, type } = this;
|
||||||
|
const defaultMapper = DEFAULT_MAPPER[type];
|
||||||
|
|
||||||
const { data: innerModelData, ...otherFields } = innerModel;
|
const { data: innerModelData, ...otherFields } = innerModel;
|
||||||
const { current = innerModelData, previous } = diffData || {};
|
const { current = innerModelData, previous } = diffData || {};
|
||||||
|
|
||||||
// === no mapper, displayModel = model ===
|
// === no mapper, displayModel = model ===
|
||||||
if (!mapper) {
|
if (!mapper) {
|
||||||
this.displayModel = innerModel; // TODO: need clone?
|
this.displayModel = defaultMapper(innerModel);
|
||||||
// compare the previous data and current data to find shape changes
|
// compare the previous data and current data to find shape changes
|
||||||
let typeChange = false;
|
let typeChange = false;
|
||||||
if (current) {
|
if (current) {
|
||||||
typeChange = Boolean(current.type);
|
typeChange = Boolean(current.type);
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
model: innerModel,
|
model: this.displayModel,
|
||||||
typeChange,
|
typeChange,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// === mapper is function, displayModel is mapper(model), cannot diff the displayModel, so all the shapes need to be updated ===
|
// === mapper is function, displayModel is mapper(model), cannot diff the displayModel, so all the shapes need to be updated ===
|
||||||
if (isFunction(mapper)) return { model: (mapper as Function)(innerModel) };
|
if (isFunction(mapper))
|
||||||
|
return {
|
||||||
|
model: {
|
||||||
|
...defaultMapper(innerModel),
|
||||||
|
...(mapper as Function)(innerModel),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
// === fields' values in mapper are final value or Encode ===
|
// === fields' values in mapper are final value or Encode ===
|
||||||
const dataChangedFields = isReplace
|
const dataChangedFields = isReplace
|
||||||
@ -199,20 +208,49 @@ export default abstract class Item implements IItem {
|
|||||||
|
|
||||||
let typeChange = false;
|
let typeChange = false;
|
||||||
const { data, ...otherProps } = innerModel;
|
const { data, ...otherProps } = innerModel;
|
||||||
const displayModelData = clone(data);
|
const displayModelData = defaultMapper(innerModel).data; //clone(data);
|
||||||
|
// const defaultMappedModel = defaultMapper(innerModel);
|
||||||
Object.keys(mapper).forEach((fieldName) => {
|
Object.keys(mapper).forEach((fieldName) => {
|
||||||
const subMapper = mapper[fieldName];
|
debugger;
|
||||||
if (RESERVED_SHAPE_IDS.includes(fieldName)) {
|
let subMapper = mapper[fieldName];
|
||||||
// reserved shapes, fieldName is shapeId
|
const isReservedShapeId = RESERVED_SHAPE_IDS.includes(fieldName);
|
||||||
|
const isShapeId =
|
||||||
|
RESERVED_SHAPE_IDS.includes(fieldName) ||
|
||||||
|
fieldName === OTHER_SHAPES_FIELD_NAME;
|
||||||
|
|
||||||
|
if ((isShapeId && isEncode(subMapper)) || !isShapeId) {
|
||||||
|
// fields not about shape
|
||||||
if (!displayModelData.hasOwnProperty(fieldName)) {
|
if (!displayModelData.hasOwnProperty(fieldName)) {
|
||||||
displayModelData[fieldName] = {};
|
const { changed, value: mappedValue } = updateChange({
|
||||||
|
innerModel,
|
||||||
|
mapper,
|
||||||
|
fieldName,
|
||||||
|
dataChangedFields,
|
||||||
|
});
|
||||||
|
if (isShapeId) {
|
||||||
|
if (!mappedValue) return;
|
||||||
|
subMapper = mappedValue;
|
||||||
|
} else {
|
||||||
|
displayModelData[fieldName] = mappedValue;
|
||||||
|
}
|
||||||
|
if (changed && fieldName === 'type') typeChange = true;
|
||||||
|
} else if (
|
||||||
|
fieldName === 'type' &&
|
||||||
|
(!dataChangedFields || dataChangedFields.includes('type'))
|
||||||
|
) {
|
||||||
|
typeChange = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isReservedShapeId) {
|
||||||
|
// reserved shapes, fieldName is shapeId
|
||||||
|
displayModelData[fieldName] = displayModelData[fieldName] || {};
|
||||||
updateShapeChange({
|
updateShapeChange({
|
||||||
innerModel,
|
innerModel,
|
||||||
mapper: subMapper,
|
mapper: subMapper,
|
||||||
dataChangedFields,
|
dataChangedFields,
|
||||||
shapeConfig: displayModelData[fieldName],
|
shapeConfig: displayModelData[fieldName],
|
||||||
});
|
});
|
||||||
}
|
|
||||||
} else if (fieldName === OTHER_SHAPES_FIELD_NAME) {
|
} else if (fieldName === OTHER_SHAPES_FIELD_NAME) {
|
||||||
// other shapes
|
// other shapes
|
||||||
displayModelData[fieldName] = displayModelData[fieldName] || {};
|
displayModelData[fieldName] = displayModelData[fieldName] || {};
|
||||||
@ -229,23 +267,6 @@ export default abstract class Item implements IItem {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
// fields not about shape
|
|
||||||
if (!displayModelData.hasOwnProperty(fieldName)) {
|
|
||||||
const { changed, value: mappedValue } = updateChange({
|
|
||||||
innerModel,
|
|
||||||
mapper,
|
|
||||||
fieldName,
|
|
||||||
dataChangedFields,
|
|
||||||
});
|
|
||||||
displayModelData[fieldName] = mappedValue;
|
|
||||||
if (changed && fieldName === 'type') typeChange = true;
|
|
||||||
} else if (
|
|
||||||
fieldName === 'type' &&
|
|
||||||
(!dataChangedFields || dataChangedFields.includes('type'))
|
|
||||||
) {
|
|
||||||
typeChange = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const displayModel = {
|
const displayModel = {
|
||||||
|
@ -1,11 +1,18 @@
|
|||||||
import { Group } from '@antv/g';
|
import { Group } from '@antv/g';
|
||||||
import { clone } from '@antv/util';
|
import { clone } from '@antv/util';
|
||||||
|
import { Point } from '../types/common';
|
||||||
import { NodeModel } from '../types';
|
import { NodeModel } from '../types';
|
||||||
import { DisplayMapper, ItemShapeStyles, State } from '../types/item';
|
import { DisplayMapper, State } from '../types/item';
|
||||||
import { NodeDisplayModel, NodeModelData } from '../types/node';
|
import { NodeDisplayModel, NodeModelData } from '../types/node';
|
||||||
import { ItemStyleSet } from '../types/theme';
|
import { NodeStyleSet } from '../types/theme';
|
||||||
import { updateShapes } from '../util/shape';
|
import { updateShapes } from '../util/shape';
|
||||||
import Item from './item';
|
import Item from './item';
|
||||||
|
import {
|
||||||
|
getCircleIntersectByPoint,
|
||||||
|
getEllipseIntersectByPoint,
|
||||||
|
getNearestPoint,
|
||||||
|
getRectIntersectByPoint,
|
||||||
|
} from 'util/point';
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
model: NodeModel;
|
model: NodeModel;
|
||||||
@ -15,11 +22,12 @@ interface IProps {
|
|||||||
stateMapper: {
|
stateMapper: {
|
||||||
[stateName: string]: DisplayMapper;
|
[stateName: string]: DisplayMapper;
|
||||||
};
|
};
|
||||||
themeStyles: ItemShapeStyles;
|
themeStyles: NodeStyleSet;
|
||||||
device?: any; // for 3d shapes
|
device?: any; // for 3d shapes
|
||||||
}
|
}
|
||||||
export default class Node extends Item {
|
export default class Node extends Item {
|
||||||
public type: 'node';
|
public type: 'node';
|
||||||
|
private anchorPointsCache: Point[];
|
||||||
|
|
||||||
constructor(props: IProps) {
|
constructor(props: IProps) {
|
||||||
super(props);
|
super(props);
|
||||||
@ -48,17 +56,20 @@ export default class Node extends Item {
|
|||||||
|
|
||||||
// add shapes to group, and update shapeMap
|
// add shapes to group, and update shapeMap
|
||||||
this.shapeMap = updateShapes(prevShapeMap, shapeMap, group);
|
this.shapeMap = updateShapes(prevShapeMap, shapeMap, group);
|
||||||
|
const { haloShape, labelShape, labelBackgroundShape } = this.shapeMap;
|
||||||
this.shapeMap.labelShape?.toFront();
|
haloShape?.toBack();
|
||||||
|
labelShape?.toFront();
|
||||||
|
labelBackgroundShape?.toBack();
|
||||||
|
|
||||||
super.draw(displayModel, diffData, diffState);
|
super.draw(displayModel, diffData, diffState);
|
||||||
|
this.anchorPointsCache = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
public update(
|
public update(
|
||||||
model: NodeModel,
|
model: NodeModel,
|
||||||
diffData?: { previous: NodeModelData; current: NodeModelData },
|
diffData?: { previous: NodeModelData; current: NodeModelData },
|
||||||
isReplace?: boolean,
|
isReplace?: boolean,
|
||||||
themeStyles?: ItemStyleSet,
|
themeStyles?: NodeStyleSet,
|
||||||
) {
|
) {
|
||||||
super.update(model, diffData, isReplace, themeStyles);
|
super.update(model, diffData, isReplace, themeStyles);
|
||||||
const { data } = this.displayModel;
|
const { data } = this.displayModel;
|
||||||
@ -86,4 +97,83 @@ export default class Node extends Item {
|
|||||||
themeStyles: clone(this.themeStyles),
|
themeStyles: clone(this.themeStyles),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getAnchorPoint(point: Point) {
|
||||||
|
const { keyShape } = this.shapeMap;
|
||||||
|
const shapeType = keyShape.nodeName;
|
||||||
|
const { x, y, anchorPoints = [] } = this.model.data as NodeModelData;
|
||||||
|
|
||||||
|
let intersectPoint: Point | null;
|
||||||
|
switch (shapeType) {
|
||||||
|
case 'circle':
|
||||||
|
intersectPoint = getCircleIntersectByPoint(
|
||||||
|
{
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
r: keyShape.attributes.r,
|
||||||
|
},
|
||||||
|
point,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case 'ellipse':
|
||||||
|
intersectPoint = getEllipseIntersectByPoint(
|
||||||
|
{
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
rx: keyShape.attributes.rx,
|
||||||
|
ry: keyShape.attributes.ry,
|
||||||
|
},
|
||||||
|
point,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
const bbox =
|
||||||
|
this.renderExt.boundsCache?.keyShapeLocal ||
|
||||||
|
keyShape.getLocalBounds();
|
||||||
|
intersectPoint = getRectIntersectByPoint(
|
||||||
|
{
|
||||||
|
x: bbox.halfExtents[0],
|
||||||
|
y: bbox.halfExtents[1],
|
||||||
|
width: bbox.max[0] - bbox.min[0],
|
||||||
|
height: bbox.max[1] - bbox.min[1],
|
||||||
|
},
|
||||||
|
point,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let anchorPointsPositions = this.anchorPointsCache;
|
||||||
|
if (!anchorPointsPositions) {
|
||||||
|
const keyShapeBBox =
|
||||||
|
this.renderExt.boundsCache?.keyShapeLocal ||
|
||||||
|
this.shapeMap.keyShape.getLocalBounds();
|
||||||
|
const keyShapeWidth = keyShapeBBox.max[0] - keyShapeBBox.min[0];
|
||||||
|
const keyShapeHeight = keyShapeBBox.max[1] - keyShapeBBox.min[1];
|
||||||
|
anchorPointsPositions = anchorPoints.map((pointRatio) => {
|
||||||
|
const [xRatio, yRatio] = pointRatio;
|
||||||
|
return {
|
||||||
|
x: keyShapeWidth * (xRatio - 0.5) + x,
|
||||||
|
y: keyShapeHeight * (yRatio - 0.5) + y,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
this.anchorPointsCache = anchorPointsPositions;
|
||||||
|
}
|
||||||
|
|
||||||
|
let linkPoint = intersectPoint;
|
||||||
|
// If the node has anchorPoints in the data, find the nearest anchor point.
|
||||||
|
if (anchorPoints.length) {
|
||||||
|
if (!linkPoint) {
|
||||||
|
// If the linkPoint is failed to calculate.
|
||||||
|
linkPoint = point;
|
||||||
|
}
|
||||||
|
linkPoint = getNearestPoint(
|
||||||
|
anchorPointsPositions,
|
||||||
|
linkPoint,
|
||||||
|
).nearestPoint;
|
||||||
|
}
|
||||||
|
if (!linkPoint) {
|
||||||
|
// If the calculations above are all failed, return the data's position
|
||||||
|
return { x, y };
|
||||||
|
}
|
||||||
|
return linkPoint;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -551,7 +551,12 @@ const mergeOneLevelData = (
|
|||||||
const { data: prevData } = prevModel;
|
const { data: prevData } = prevModel;
|
||||||
const mergedData = {};
|
const mergedData = {};
|
||||||
Object.keys(newData).forEach((key) => {
|
Object.keys(newData).forEach((key) => {
|
||||||
if (isObject(prevData[key]) && isObject(newData[key])) {
|
if (isArray(prevData[key]) || isArray(newData[key])) {
|
||||||
|
mergedData[key] = newData[key];
|
||||||
|
} else if (
|
||||||
|
typeof prevData[key] === 'object' &&
|
||||||
|
typeof newData[key] === 'object'
|
||||||
|
) {
|
||||||
mergedData[key] = {
|
mergedData[key] = {
|
||||||
...(prevData[key] as object),
|
...(prevData[key] as object),
|
||||||
...(newData[key] as object),
|
...(newData[key] as object),
|
||||||
|
@ -26,8 +26,10 @@ import { upsertTransientItem } from '../../util/item';
|
|||||||
import { ITEM_TYPE, ShapeStyle, SHAPE_TYPE } from '../../types/item';
|
import { ITEM_TYPE, ShapeStyle, SHAPE_TYPE } from '../../types/item';
|
||||||
import {
|
import {
|
||||||
ThemeSpecification,
|
ThemeSpecification,
|
||||||
ItemThemeSpecifications,
|
NodeThemeSpecifications,
|
||||||
ItemStyleSet,
|
EdgeThemeSpecifications,
|
||||||
|
NodeStyleSet,
|
||||||
|
EdgeStyleSet,
|
||||||
} from '../../types/theme';
|
} from '../../types/theme';
|
||||||
import { isArray, isObject } from '@antv/util';
|
import { isArray, isObject } from '@antv/util';
|
||||||
import { DirectionalLight, AmbientLight } from '@antv/g-plugin-3d';
|
import { DirectionalLight, AmbientLight } from '@antv/g-plugin-3d';
|
||||||
@ -277,7 +279,10 @@ export class ItemController {
|
|||||||
const { isReplace, previous, current } = nodeUpdate[id];
|
const { isReplace, previous, current } = nodeUpdate[id];
|
||||||
// update the theme if the dataType value is changed
|
// update the theme if the dataType value is changed
|
||||||
let themeStyles;
|
let themeStyles;
|
||||||
if (previous[nodeDataTypeField] !== current[nodeDataTypeField]) {
|
if (
|
||||||
|
nodeDataTypeField &&
|
||||||
|
previous[nodeDataTypeField] !== current[nodeDataTypeField]
|
||||||
|
) {
|
||||||
themeStyles = getThemeStyles(
|
themeStyles = getThemeStyles(
|
||||||
this.nodeDataTypeSet,
|
this.nodeDataTypeSet,
|
||||||
nodeDataTypeField,
|
nodeDataTypeField,
|
||||||
@ -488,7 +493,7 @@ export class ItemController {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const shape = upsertShape(type, String(id), style, transientObjectMap);
|
const { shape } = upsertShape(type, String(id), style, transientObjectMap);
|
||||||
shape.style.pointerEvents = capture ? 'auto' : 'none';
|
shape.style.pointerEvents = capture ? 'auto' : 'none';
|
||||||
canvas.getRoot().appendChild(shape);
|
canvas.getRoot().appendChild(shape);
|
||||||
}
|
}
|
||||||
@ -502,7 +507,7 @@ export class ItemController {
|
|||||||
*/
|
*/
|
||||||
private renderNodes(
|
private renderNodes(
|
||||||
models: NodeModel[],
|
models: NodeModel[],
|
||||||
nodeTheme: ItemThemeSpecifications = {},
|
nodeTheme: NodeThemeSpecifications = {},
|
||||||
) {
|
) {
|
||||||
const { nodeExtensions, nodeGroup, nodeDataTypeSet, graph } = this;
|
const { nodeExtensions, nodeGroup, nodeDataTypeSet, graph } = this;
|
||||||
const { dataTypeField } = nodeTheme;
|
const { dataTypeField } = nodeTheme;
|
||||||
@ -523,7 +528,7 @@ export class ItemController {
|
|||||||
containerGroup: nodeGroup,
|
containerGroup: nodeGroup,
|
||||||
mapper: this.nodeMapper,
|
mapper: this.nodeMapper,
|
||||||
stateMapper: this.nodeStateMapper,
|
stateMapper: this.nodeStateMapper,
|
||||||
themeStyles: themeStyle,
|
themeStyles: themeStyle as NodeStyleSet,
|
||||||
device:
|
device:
|
||||||
graph.rendererType === 'webgl-3d'
|
graph.rendererType === 'webgl-3d'
|
||||||
? // TODO: G type
|
? // TODO: G type
|
||||||
@ -539,7 +544,7 @@ export class ItemController {
|
|||||||
*/
|
*/
|
||||||
private renderEdges(
|
private renderEdges(
|
||||||
models: EdgeModel[],
|
models: EdgeModel[],
|
||||||
edgeTheme: ItemThemeSpecifications = {},
|
edgeTheme: EdgeThemeSpecifications = {},
|
||||||
) {
|
) {
|
||||||
const { edgeExtensions, edgeGroup, itemMap, edgeDataTypeSet } = this;
|
const { edgeExtensions, edgeGroup, itemMap, edgeDataTypeSet } = this;
|
||||||
const { dataTypeField } = edgeTheme;
|
const { dataTypeField } = edgeTheme;
|
||||||
@ -575,7 +580,7 @@ export class ItemController {
|
|||||||
stateMapper: this.edgeStateMapper,
|
stateMapper: this.edgeStateMapper,
|
||||||
sourceItem,
|
sourceItem,
|
||||||
targetItem,
|
targetItem,
|
||||||
themeStyles: themeStyle,
|
themeStyles: themeStyle as EdgeStyleSet,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -648,8 +653,8 @@ const getThemeStyles = (
|
|||||||
dataTypeSet: Set<string>,
|
dataTypeSet: Set<string>,
|
||||||
dataTypeField: string,
|
dataTypeField: string,
|
||||||
dataType: string,
|
dataType: string,
|
||||||
itemTheme: ItemThemeSpecifications,
|
itemTheme: NodeThemeSpecifications | EdgeThemeSpecifications,
|
||||||
): ItemStyleSet => {
|
): NodeStyleSet | EdgeStyleSet => {
|
||||||
const { styles: themeStyles } = itemTheme;
|
const { styles: themeStyles } = itemTheme;
|
||||||
if (!dataTypeField) {
|
if (!dataTypeField) {
|
||||||
// dataType field is not assigned
|
// dataType field is not assigned
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { DisplayObject, Line, Polyline } from '@antv/g';
|
import { AABB, DisplayObject, Line, Polyline } from '@antv/g';
|
||||||
import { isNumber } from '@antv/util';
|
import { isNumber } from '@antv/util';
|
||||||
import {
|
import {
|
||||||
DEFAULT_LABEL_BG_PADDING,
|
DEFAULT_LABEL_BG_PADDING,
|
||||||
@ -10,31 +10,47 @@ import {
|
|||||||
EdgeDisplayModel,
|
EdgeDisplayModel,
|
||||||
EdgeModelData,
|
EdgeModelData,
|
||||||
EdgeShapeMap,
|
EdgeShapeMap,
|
||||||
|
EdgeShapeStyles,
|
||||||
} from '../../../types/edge';
|
} from '../../../types/edge';
|
||||||
import {
|
import {
|
||||||
GShapeStyle,
|
GShapeStyle,
|
||||||
ItemShapeStyles,
|
|
||||||
SHAPE_TYPE,
|
SHAPE_TYPE,
|
||||||
ShapeStyle,
|
ShapeStyle,
|
||||||
State,
|
State,
|
||||||
} from '../../../types/item';
|
} from '../../../types/item';
|
||||||
import { formatPadding, mergeStyles, upsertShape } from '../../../util/shape';
|
import {
|
||||||
|
formatPadding,
|
||||||
|
isStyleAffectBBox,
|
||||||
|
mergeStyles,
|
||||||
|
upsertShape,
|
||||||
|
} from '../../../util/shape';
|
||||||
|
|
||||||
export abstract class BaseEdge {
|
export abstract class BaseEdge {
|
||||||
type: string;
|
type: string;
|
||||||
defaultStyles: ItemShapeStyles = {};
|
defaultStyles: EdgeShapeStyles = {};
|
||||||
themeStyles: ItemShapeStyles;
|
themeStyles: EdgeShapeStyles;
|
||||||
mergedStyles: ItemShapeStyles;
|
mergedStyles: EdgeShapeStyles;
|
||||||
|
labelPosition: {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
transform: string;
|
||||||
|
isRevert: boolean;
|
||||||
|
};
|
||||||
|
boundsCache: {
|
||||||
|
labelShapeGeometry?: AABB;
|
||||||
|
labelBackgroundShapeGeometry?: AABB;
|
||||||
|
};
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
const { themeStyles } = props;
|
const { themeStyles } = props;
|
||||||
if (themeStyles) this.themeStyles = themeStyles;
|
if (themeStyles) this.themeStyles = themeStyles;
|
||||||
|
this.boundsCache = {};
|
||||||
}
|
}
|
||||||
private mergeStyles(model: EdgeDisplayModel) {
|
public mergeStyles(model: EdgeDisplayModel) {
|
||||||
this.mergedStyles = this.getMergedStyles(model);
|
this.mergedStyles = this.getMergedStyles(model);
|
||||||
}
|
}
|
||||||
public getMergedStyles(model: EdgeDisplayModel) {
|
public getMergedStyles(model: EdgeDisplayModel) {
|
||||||
const { data } = model;
|
const { data } = model;
|
||||||
const dataStyles = {} as ItemShapeStyles;
|
const dataStyles = {} as EdgeShapeStyles;
|
||||||
Object.keys(data).forEach((fieldName) => {
|
Object.keys(data).forEach((fieldName) => {
|
||||||
if (RESERVED_SHAPE_IDS.includes(fieldName))
|
if (RESERVED_SHAPE_IDS.includes(fieldName))
|
||||||
dataStyles[fieldName] = data[fieldName] as ShapeStyle;
|
dataStyles[fieldName] = data[fieldName] as ShapeStyle;
|
||||||
@ -79,16 +95,12 @@ export abstract class BaseEdge {
|
|||||||
shapeMap: EdgeShapeMap,
|
shapeMap: EdgeShapeMap,
|
||||||
diffData?: { previous: EdgeModelData; current: EdgeModelData },
|
diffData?: { previous: EdgeModelData; current: EdgeModelData },
|
||||||
diffState?: { previous: State[]; current: State[] },
|
diffState?: { previous: State[]; current: State[] },
|
||||||
): {
|
): DisplayObject {
|
||||||
labelShape: DisplayObject;
|
|
||||||
[id: string]: DisplayObject;
|
|
||||||
} {
|
|
||||||
const { keyShape } = shapeMap;
|
const { keyShape } = shapeMap;
|
||||||
|
|
||||||
const { labelShape: shapeStyle } = this.mergedStyles;
|
const { labelShape: shapeStyle } = this.mergedStyles;
|
||||||
const {
|
const {
|
||||||
position,
|
position,
|
||||||
background,
|
|
||||||
offsetX: propsOffsetX,
|
offsetX: propsOffsetX,
|
||||||
offsetY: propsOffsetY,
|
offsetY: propsOffsetY,
|
||||||
autoRotate = true,
|
autoRotate = true,
|
||||||
@ -123,6 +135,7 @@ export abstract class BaseEdge {
|
|||||||
positionPreset.pointRatio[0],
|
positionPreset.pointRatio[0],
|
||||||
);
|
);
|
||||||
let positionStyle: any = { x: point.x, y: point.y };
|
let positionStyle: any = { x: point.x, y: point.y };
|
||||||
|
let isRevert = false;
|
||||||
if (autoRotate) {
|
if (autoRotate) {
|
||||||
const pointOffset = (keyShape as Line | Polyline).getPoint(
|
const pointOffset = (keyShape as Line | Polyline).getPoint(
|
||||||
positionPreset.pointRatio[1],
|
positionPreset.pointRatio[1],
|
||||||
@ -130,6 +143,18 @@ export abstract class BaseEdge {
|
|||||||
const angle = Math.atan(
|
const angle = Math.atan(
|
||||||
(point.y - pointOffset.y) / (point.x - pointOffset.x),
|
(point.y - pointOffset.y) / (point.x - pointOffset.x),
|
||||||
); // TODO: NaN
|
); // TODO: NaN
|
||||||
|
|
||||||
|
// revert
|
||||||
|
isRevert = pointOffset.x < point.x;
|
||||||
|
if (isRevert) {
|
||||||
|
if (position === 'start') {
|
||||||
|
positionPreset.textAlign = 'right';
|
||||||
|
positionPreset.offsetX = -4;
|
||||||
|
} else if (position === 'end') {
|
||||||
|
positionPreset.textAlign = 'left';
|
||||||
|
positionPreset.offsetX = 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
const offsetX = (
|
const offsetX = (
|
||||||
propsOffsetX === undefined ? positionPreset.offsetX : propsOffsetX
|
propsOffsetX === undefined ? positionPreset.offsetX : propsOffsetX
|
||||||
) as number;
|
) as number;
|
||||||
@ -148,51 +173,88 @@ export abstract class BaseEdge {
|
|||||||
transform: `rotate(${(angle / Math.PI) * 180})`,
|
transform: `rotate(${(angle / Math.PI) * 180})`,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
this.labelPosition = {
|
||||||
|
...positionStyle,
|
||||||
|
isRevert,
|
||||||
|
};
|
||||||
const style = {
|
const style = {
|
||||||
...this.defaultStyles.labelShape,
|
...this.defaultStyles.labelShape,
|
||||||
textAlign: positionPreset.textAlign,
|
textAlign: positionPreset.textAlign,
|
||||||
...positionStyle,
|
...positionStyle,
|
||||||
...otherStyle,
|
...otherStyle,
|
||||||
};
|
};
|
||||||
|
const { shape, updateStyles } = this.upsertShape(
|
||||||
|
'text',
|
||||||
|
'labelShape',
|
||||||
|
style,
|
||||||
|
shapeMap,
|
||||||
|
);
|
||||||
|
if (isStyleAffectBBox('text', updateStyles)) {
|
||||||
|
this.boundsCache.labelShapeGeometry = shape.getGeometryBounds();
|
||||||
|
}
|
||||||
|
return shape;
|
||||||
|
}
|
||||||
|
|
||||||
const labelShape = upsertShape('text', 'labelShape', style, shapeMap);
|
public drawLabelBackgroundShape(
|
||||||
const shapes = { labelShape };
|
model: EdgeDisplayModel,
|
||||||
if (background) {
|
shapeMap: EdgeShapeMap,
|
||||||
const textBBox = labelShape.getGeometryBounds();
|
diffData?: { previous: EdgeModelData; current: EdgeModelData },
|
||||||
// TODO: update type define.
|
diffState?: { previous: State[]; current: State[] },
|
||||||
// @ts-ignore
|
): DisplayObject {
|
||||||
const { padding: propsPadding, ...backgroundStyle } = background;
|
const { labelShape } = shapeMap;
|
||||||
|
if (!labelShape || !model.data.labelShape) return;
|
||||||
|
|
||||||
|
const { labelBackgroundShape, labelShape: labelShapeStyle } =
|
||||||
|
this.mergedStyles;
|
||||||
|
|
||||||
|
const textBBox =
|
||||||
|
this.boundsCache.labelShapeGeometry || labelShape.getGeometryBounds();
|
||||||
|
const { x, y, transform, isRevert } = this.labelPosition;
|
||||||
|
const { padding: propsPadding, ...backgroundStyle } = labelBackgroundShape;
|
||||||
const padding = formatPadding(propsPadding, DEFAULT_LABEL_BG_PADDING);
|
const padding = formatPadding(propsPadding, DEFAULT_LABEL_BG_PADDING);
|
||||||
|
const textWidth = textBBox.max[0] - textBBox.min[0];
|
||||||
|
const textHeight = textBBox.max[1] - textBBox.min[1];
|
||||||
const bgStyle = {
|
const bgStyle = {
|
||||||
fill: '#fff',
|
fill: '#fff',
|
||||||
radius: 4,
|
|
||||||
...backgroundStyle,
|
...backgroundStyle,
|
||||||
x: textBBox.min[0] - padding[3] + style.x,
|
x: textBBox.min[0] - padding[3] + x,
|
||||||
y: textBBox.min[1] - padding[0] + style.y,
|
y: textBBox.min[1] - padding[0] + y,
|
||||||
width: textBBox.max[0] - textBBox.min[0] + padding[1] + padding[3],
|
width: textWidth + padding[1] + padding[3],
|
||||||
height: textBBox.max[1] - textBBox.min[1] + padding[0] + padding[2],
|
height: textHeight + padding[0] + padding[2],
|
||||||
transform: positionStyle.transform,
|
transform: transform,
|
||||||
transformOrigin: 'center',
|
|
||||||
};
|
};
|
||||||
if (position === 'start') {
|
if (labelShapeStyle.position === 'start') {
|
||||||
bgStyle.transformOrigin = `${padding[3]} ${
|
if (isRevert) {
|
||||||
padding[0] + bgStyle.height / 2
|
bgStyle.transformOrigin = `${bgStyle.width - padding[1]} ${
|
||||||
|
bgStyle.height / 2
|
||||||
|
}`;
|
||||||
|
} else {
|
||||||
|
bgStyle.transformOrigin = `${padding[3]} ${bgStyle.height / 2}`;
|
||||||
|
}
|
||||||
|
} else if (labelShapeStyle.position === 'end') {
|
||||||
|
if (isRevert) {
|
||||||
|
bgStyle.transformOrigin = `${padding[3]} ${bgStyle.height / 2}`;
|
||||||
|
} else {
|
||||||
|
bgStyle.transformOrigin = `${bgStyle.width - padding[1]} ${
|
||||||
|
bgStyle.height / 2
|
||||||
}`;
|
}`;
|
||||||
}
|
}
|
||||||
if (position === 'end') {
|
} else {
|
||||||
bgStyle.transformOrigin = `${padding[3] + bgStyle.width} ${
|
bgStyle.transformOrigin = `${textWidth / 2 + padding[3]} ${
|
||||||
padding[0] + bgStyle.height / 2
|
textHeight / 2 + padding[0]
|
||||||
}`;
|
}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
shapes['labelBgShape'] = upsertShape(
|
const { shape, updateStyles } = this.upsertShape(
|
||||||
'rect',
|
'rect',
|
||||||
'labelBgShape',
|
'labelBackgroundShape',
|
||||||
bgStyle,
|
bgStyle,
|
||||||
shapeMap,
|
shapeMap,
|
||||||
);
|
);
|
||||||
|
if (isStyleAffectBBox('rect', updateStyles)) {
|
||||||
|
this.boundsCache.labelBackgroundShapeGeometry = shape.getGeometryBounds();
|
||||||
}
|
}
|
||||||
return shapes;
|
return shape;
|
||||||
}
|
}
|
||||||
|
|
||||||
public drawIconShape(
|
public drawIconShape(
|
||||||
@ -201,69 +263,112 @@ export abstract class BaseEdge {
|
|||||||
diffData?: { previous: EdgeModelData; current: EdgeModelData },
|
diffData?: { previous: EdgeModelData; current: EdgeModelData },
|
||||||
diffState?: { previous: State[]; current: State[] },
|
diffState?: { previous: State[]; current: State[] },
|
||||||
): DisplayObject {
|
): DisplayObject {
|
||||||
const { labelShape, labelBgShape, keyShape } = shapeMap;
|
const { labelShape, labelBackgroundShape, keyShape } = shapeMap;
|
||||||
const { iconShape: shapeStyle, labelShape: labelShapeProps } =
|
const { iconShape: shapeStyle, labelShape: labelShapeProps } =
|
||||||
this.mergedStyles;
|
this.mergedStyles;
|
||||||
|
|
||||||
const iconShapeType = shapeStyle.text ? 'text' : 'image';
|
const {
|
||||||
if (iconShapeType === 'text') {
|
width,
|
||||||
shapeStyle.textAlign = 'left';
|
height,
|
||||||
shapeStyle.textBaseline = 'top';
|
fontSize,
|
||||||
}
|
text,
|
||||||
const { width, height, fontSize } = shapeStyle;
|
offsetX = 0,
|
||||||
|
offsetY = 0,
|
||||||
|
} = shapeStyle;
|
||||||
const w = (width || fontSize) as number;
|
const w = (width || fontSize) as number;
|
||||||
const h = (height || fontSize) as number;
|
const h = (height || fontSize) as number;
|
||||||
|
|
||||||
|
const iconShapeType = text ? 'text' : 'image';
|
||||||
|
if (iconShapeType === 'text') {
|
||||||
|
shapeStyle.textAlign = 'left';
|
||||||
|
shapeStyle.textBaseline = 'top';
|
||||||
|
shapeStyle.fontSize = w;
|
||||||
|
} else {
|
||||||
|
shapeStyle.width = w;
|
||||||
|
shapeStyle.height = h;
|
||||||
|
}
|
||||||
|
|
||||||
if (labelShapeProps) {
|
if (labelShapeProps) {
|
||||||
const referShape = labelBgShape || labelShape;
|
const referShape = labelBackgroundShape || labelShape;
|
||||||
const { min: referMin, halfExtents: referHalExtents } =
|
const referBounds =
|
||||||
|
this.boundsCache.labelBackgroundShapeGeometry ||
|
||||||
|
this.boundsCache.labelShapeGeometry ||
|
||||||
referShape.getGeometryBounds();
|
referShape.getGeometryBounds();
|
||||||
|
const {
|
||||||
|
min: referMin,
|
||||||
|
max: referMax,
|
||||||
|
halfExtents: referHalExtents,
|
||||||
|
} = referBounds;
|
||||||
|
const referHeight = referMax[1] - referMin[1];
|
||||||
|
const referWidth = referMax[0] - referMin[0];
|
||||||
const {
|
const {
|
||||||
x: referX,
|
x: referX,
|
||||||
y: referY,
|
y: referY,
|
||||||
transform: referTransform,
|
transform: referTransform,
|
||||||
textAlign: labelAlign,
|
|
||||||
} = referShape.attributes;
|
} = referShape.attributes;
|
||||||
shapeStyle.x = referMin[0] - w - 4 + referX;
|
const { textAlign: labelAlign } = labelShape.attributes;
|
||||||
shapeStyle.y = referMin[1] + 2 + referY;
|
shapeStyle.x = referMin[0] - w + 4 + referX + offsetX;
|
||||||
|
shapeStyle.y = referMin[1] + (referHeight - h) / 2 + referY + offsetY;
|
||||||
if (referTransform) {
|
if (referTransform) {
|
||||||
shapeStyle.transform = referTransform;
|
shapeStyle.transform = referTransform;
|
||||||
if (labelAlign === 'right') {
|
if (labelAlign === 'right') {
|
||||||
shapeStyle.transformOrigin = `${w + 4 + referHalExtents[0] * 2}px ${
|
shapeStyle.transformOrigin = `${
|
||||||
h / 2
|
referWidth / 2 - w / 2 + 4 + referHalExtents[0] - offsetX
|
||||||
}px`;
|
} ${h / 2 - offsetY}`;
|
||||||
} else if (labelAlign === 'left') {
|
} else if (labelAlign === 'left') {
|
||||||
shapeStyle.transformOrigin = `${w + 4}px ${h / 2}px`;
|
shapeStyle.transformOrigin = `${w + 4 - offsetX} ${h / 2 - offsetY}`;
|
||||||
} else {
|
} else {
|
||||||
// labelShape align 'center'
|
// labelShape align 'center'
|
||||||
shapeStyle.transformOrigin = `${w + 4 + referHalExtents[0]}px ${
|
shapeStyle.transformOrigin = `${(w + referWidth) / 2 - offsetX} ${
|
||||||
h / 2
|
h / 2 - offsetY
|
||||||
}px`;
|
}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const midPoint = (keyShape as Line | Polyline).getPoint(0.5);
|
const midPoint = (keyShape as Line | Polyline).getPoint(0.5);
|
||||||
shapeStyle.x = midPoint.x;
|
shapeStyle.x = midPoint.x + offsetX;
|
||||||
shapeStyle.y = midPoint.y;
|
shapeStyle.y = midPoint.y + offsetY;
|
||||||
// TODO: rotate
|
// TODO: rotate
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: update type define.
|
return this.upsertShape(
|
||||||
return upsertShape(
|
|
||||||
iconShapeType,
|
iconShapeType,
|
||||||
'iconShape',
|
'iconShape',
|
||||||
shapeStyle as unknown as GShapeStyle,
|
shapeStyle as GShapeStyle,
|
||||||
shapeMap,
|
shapeMap,
|
||||||
);
|
).shape;
|
||||||
|
}
|
||||||
|
|
||||||
|
public drawHaloShape(
|
||||||
|
model: EdgeDisplayModel,
|
||||||
|
shapeMap: EdgeShapeMap,
|
||||||
|
diffData?: { previous: EdgeModelData; current: EdgeModelData },
|
||||||
|
diffState?: { previous: State[]; current: State[] },
|
||||||
|
): DisplayObject {
|
||||||
|
const { keyShape } = shapeMap;
|
||||||
|
const { haloShape: haloShapeStyle } = this.mergedStyles;
|
||||||
|
const { nodeName, attributes } = keyShape;
|
||||||
|
return this.upsertShape(
|
||||||
|
nodeName as SHAPE_TYPE,
|
||||||
|
'haloShape',
|
||||||
|
{
|
||||||
|
...attributes,
|
||||||
|
...haloShapeStyle,
|
||||||
|
isBillboard: true,
|
||||||
|
},
|
||||||
|
shapeMap,
|
||||||
|
).shape;
|
||||||
}
|
}
|
||||||
|
|
||||||
public upsertShape(
|
public upsertShape(
|
||||||
type: SHAPE_TYPE,
|
type: SHAPE_TYPE,
|
||||||
id: string,
|
id: string,
|
||||||
style: { [shapeAttr: string]: unknown },
|
style: ShapeStyle,
|
||||||
shapeMap: { [shapeId: string]: DisplayObject },
|
shapeMap: { [shapeId: string]: DisplayObject },
|
||||||
): DisplayObject {
|
): {
|
||||||
// TODO: update type define.
|
updateStyles: ShapeStyle;
|
||||||
return upsertShape(type, id, style as unknown as GShapeStyle, shapeMap);
|
shape: DisplayObject;
|
||||||
|
} {
|
||||||
|
return upsertShape(type, id, style as GShapeStyle, shapeMap);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { isStyleAffectBBox } from 'util/shape';
|
||||||
import { Point } from '../../../types/common';
|
import { Point } from '../../../types/common';
|
||||||
import {
|
import {
|
||||||
EdgeDisplayModel,
|
EdgeDisplayModel,
|
||||||
@ -35,6 +36,7 @@ export class LineEdge extends BaseEdge {
|
|||||||
const { data = {} } = model;
|
const { data = {} } = model;
|
||||||
|
|
||||||
let shapes: EdgeShapeMap = { keyShape: undefined };
|
let shapes: EdgeShapeMap = { keyShape: undefined };
|
||||||
|
|
||||||
shapes.keyShape = this.drawKeyShape(
|
shapes.keyShape = this.drawKeyShape(
|
||||||
model,
|
model,
|
||||||
sourcePoint,
|
sourcePoint,
|
||||||
@ -42,13 +44,27 @@ export class LineEdge extends BaseEdge {
|
|||||||
shapeMap,
|
shapeMap,
|
||||||
diffData,
|
diffData,
|
||||||
);
|
);
|
||||||
if (data.labelShape)
|
|
||||||
shapes = {
|
if (data.haloShape) {
|
||||||
...shapes,
|
shapes.haloShape = this.drawHaloShape(model, shapeMap, diffData);
|
||||||
...this.drawLabelShape(model, shapeMap, diffData),
|
}
|
||||||
};
|
|
||||||
if (data.iconShape)
|
if (data.labelShape) {
|
||||||
|
shapes.labelShape = this.drawLabelShape(model, shapeMap, diffData);
|
||||||
|
}
|
||||||
|
|
||||||
|
// labelBackgroundShape
|
||||||
|
if (data.labelBackgroundShape) {
|
||||||
|
shapes.labelBackgroundShape = this.drawLabelBackgroundShape(
|
||||||
|
model,
|
||||||
|
shapeMap,
|
||||||
|
diffData,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.iconShape) {
|
||||||
shapes.iconShape = this.drawIconShape(model, shapeMap, diffData);
|
shapes.iconShape = this.drawIconShape(model, shapeMap, diffData);
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: other shapes
|
// TODO: other shapes
|
||||||
|
|
||||||
@ -63,11 +79,9 @@ export class LineEdge extends BaseEdge {
|
|||||||
diffState?: { previous: State[]; current: State[] },
|
diffState?: { previous: State[]; current: State[] },
|
||||||
) {
|
) {
|
||||||
const { keyShape: keyShapeStyle } = this.mergedStyles;
|
const { keyShape: keyShapeStyle } = this.mergedStyles;
|
||||||
const keyShape = this.upsertShape(
|
return this.upsertShape(
|
||||||
'line',
|
'line',
|
||||||
'keyShape',
|
'keyShape',
|
||||||
// TODO: update type define.
|
|
||||||
// @ts-ignore
|
|
||||||
{
|
{
|
||||||
...keyShapeStyle,
|
...keyShapeStyle,
|
||||||
x1: sourcePoint.x,
|
x1: sourcePoint.x,
|
||||||
@ -79,7 +93,6 @@ export class LineEdge extends BaseEdge {
|
|||||||
isBillboard: true,
|
isBillboard: true,
|
||||||
},
|
},
|
||||||
shapeMap,
|
shapeMap,
|
||||||
);
|
).shape;
|
||||||
return keyShape;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { DisplayObject } from '@antv/g';
|
import { AABB, DisplayObject, ImageStyleProps, TextStyleProps } from '@antv/g';
|
||||||
import {
|
import {
|
||||||
DEFAULT_LABEL_BG_PADDING,
|
DEFAULT_LABEL_BG_PADDING,
|
||||||
OTHER_SHAPES_FIELD_NAME,
|
OTHER_SHAPES_FIELD_NAME,
|
||||||
@ -7,34 +7,54 @@ import {
|
|||||||
import { NodeDisplayModel } from '../../../types';
|
import { NodeDisplayModel } from '../../../types';
|
||||||
import {
|
import {
|
||||||
GShapeStyle,
|
GShapeStyle,
|
||||||
ItemShapeStyles,
|
|
||||||
SHAPE_TYPE,
|
SHAPE_TYPE,
|
||||||
SHAPE_TYPE_3D,
|
SHAPE_TYPE_3D,
|
||||||
ShapeStyle,
|
ShapeStyle,
|
||||||
State,
|
State,
|
||||||
} from '../../../types/item';
|
} from '../../../types/item';
|
||||||
import { NodeModelData, NodeShapeMap } from '../../../types/node';
|
import {
|
||||||
import { formatPadding, mergeStyles, upsertShape } from '../../../util/shape';
|
NodeModelData,
|
||||||
|
NodeShapeMap,
|
||||||
|
NodeShapeStyles,
|
||||||
|
} from '../../../types/node';
|
||||||
|
import {
|
||||||
|
formatPadding,
|
||||||
|
isStyleAffectBBox,
|
||||||
|
mergeStyles,
|
||||||
|
upsertShape,
|
||||||
|
} from '../../../util/shape';
|
||||||
|
|
||||||
export abstract class BaseNode {
|
export abstract class BaseNode {
|
||||||
type: string;
|
type: string;
|
||||||
defaultStyles: ItemShapeStyles;
|
defaultStyles: NodeShapeStyles;
|
||||||
themeStyles: ItemShapeStyles;
|
themeStyles: NodeShapeStyles;
|
||||||
mergedStyles: ItemShapeStyles;
|
mergedStyles: NodeShapeStyles;
|
||||||
|
boundsCache: {
|
||||||
|
keyShapeLocal?: AABB;
|
||||||
|
labelShapeLocal?: AABB;
|
||||||
|
};
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
const { themeStyles } = props;
|
const { themeStyles } = props;
|
||||||
if (themeStyles) this.themeStyles = themeStyles;
|
if (themeStyles) this.themeStyles = themeStyles;
|
||||||
|
this.boundsCache = {};
|
||||||
}
|
}
|
||||||
public mergeStyles(model: NodeDisplayModel) {
|
public mergeStyles(model: NodeDisplayModel) {
|
||||||
this.mergedStyles = this.getMergedStyles(model);
|
this.mergedStyles = this.getMergedStyles(model);
|
||||||
}
|
}
|
||||||
public getMergedStyles(model: NodeDisplayModel) {
|
public getMergedStyles(model: NodeDisplayModel) {
|
||||||
const { data } = model;
|
const { data } = model;
|
||||||
const dataStyles = {} as ItemShapeStyles;
|
const dataStyles = {} as NodeShapeStyles;
|
||||||
Object.keys(data).forEach((fieldName) => {
|
Object.keys(data).forEach((fieldName) => {
|
||||||
if (RESERVED_SHAPE_IDS.includes(fieldName))
|
if (RESERVED_SHAPE_IDS.includes(fieldName)) {
|
||||||
|
if (fieldName === 'BadgeShapes') {
|
||||||
|
Object.keys(data[fieldName]).forEach(
|
||||||
|
(badgeShapeId) =>
|
||||||
|
(dataStyles[badgeShapeId] = data[fieldName][badgeShapeId]),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
dataStyles[fieldName] = data[fieldName] as ShapeStyle;
|
dataStyles[fieldName] = data[fieldName] as ShapeStyle;
|
||||||
else if (fieldName === OTHER_SHAPES_FIELD_NAME) {
|
}
|
||||||
|
} else if (fieldName === OTHER_SHAPES_FIELD_NAME) {
|
||||||
Object.keys(data[fieldName]).forEach(
|
Object.keys(data[fieldName]).forEach(
|
||||||
(otherShapeId) =>
|
(otherShapeId) =>
|
||||||
(dataStyles[otherShapeId] = data[fieldName][otherShapeId]),
|
(dataStyles[otherShapeId] = data[fieldName][otherShapeId]),
|
||||||
@ -81,17 +101,15 @@ export abstract class BaseNode {
|
|||||||
shapeMap: NodeShapeMap,
|
shapeMap: NodeShapeMap,
|
||||||
diffData?: { previous: NodeModelData; current: NodeModelData },
|
diffData?: { previous: NodeModelData; current: NodeModelData },
|
||||||
diffState?: { oldState: State[]; newState: State[] },
|
diffState?: { oldState: State[]; newState: State[] },
|
||||||
): {
|
): DisplayObject {
|
||||||
labelShape: DisplayObject;
|
|
||||||
[id: string]: DisplayObject;
|
|
||||||
} {
|
|
||||||
const { keyShape } = shapeMap;
|
const { keyShape } = shapeMap;
|
||||||
const keyShapeBox = keyShape.getGeometryBounds();
|
this.boundsCache.keyShapeLocal =
|
||||||
|
this.boundsCache.keyShapeLocal || keyShape.getLocalBounds();
|
||||||
|
const keyShapeBox = this.boundsCache.keyShapeLocal;
|
||||||
const { labelShape: shapeStyle } = this.mergedStyles;
|
const { labelShape: shapeStyle } = this.mergedStyles;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
position,
|
position,
|
||||||
background,
|
|
||||||
offsetX: propsOffsetX,
|
offsetX: propsOffsetX,
|
||||||
offsetY: propsOffsetY,
|
offsetY: propsOffsetY,
|
||||||
...otherStyle
|
...otherStyle
|
||||||
@ -145,46 +163,60 @@ export abstract class BaseNode {
|
|||||||
...positionPreset,
|
...positionPreset,
|
||||||
...otherStyle,
|
...otherStyle,
|
||||||
};
|
};
|
||||||
const labelShape = upsertShape('text', 'labelShape', style, shapeMap);
|
const { shape, updateStyles } = this.upsertShape(
|
||||||
const shapes = { labelShape };
|
'text',
|
||||||
if (background) {
|
'labelShape',
|
||||||
const textBBox = labelShape.getGeometryBounds();
|
style,
|
||||||
// TODO: update type define.
|
shapeMap,
|
||||||
// @ts-ignore
|
);
|
||||||
const { padding: propsPadding, ...backgroundStyle } = background;
|
|
||||||
|
if (isStyleAffectBBox('text', updateStyles)) {
|
||||||
|
this.boundsCache.labelShapeLocal = undefined;
|
||||||
|
}
|
||||||
|
return shape;
|
||||||
|
}
|
||||||
|
|
||||||
|
public drawLabelBackgroundShape(
|
||||||
|
model: NodeDisplayModel,
|
||||||
|
shapeMap: NodeShapeMap,
|
||||||
|
diffData?: { previous: NodeModelData; current: NodeModelData },
|
||||||
|
diffState?: { oldState: State[]; newState: State[] },
|
||||||
|
): DisplayObject {
|
||||||
|
const { labelShape } = shapeMap;
|
||||||
|
if (!labelShape || !model.data.labelShape) return;
|
||||||
|
this.boundsCache.labelShapeLocal =
|
||||||
|
this.boundsCache.labelShapeLocal || labelShape.getLocalBounds();
|
||||||
|
const { labelShapeLocal: textBBox } = this.boundsCache;
|
||||||
|
|
||||||
|
const { padding: propsPadding, ...backgroundStyle } =
|
||||||
|
this.mergedStyles.labelBackgroundShape;
|
||||||
const padding = formatPadding(propsPadding, DEFAULT_LABEL_BG_PADDING);
|
const padding = formatPadding(propsPadding, DEFAULT_LABEL_BG_PADDING);
|
||||||
const bgStyle: any = {
|
const bgStyle: any = {
|
||||||
fill: '#fff',
|
fill: '#fff',
|
||||||
radius: 4,
|
|
||||||
...backgroundStyle,
|
...backgroundStyle,
|
||||||
x: textBBox.min[0] - padding[3] + style.x,
|
x: textBBox.min[0] - padding[3],
|
||||||
y: textBBox.min[1] - padding[0] + style.y,
|
y: textBBox.min[1] - padding[0],
|
||||||
width: textBBox.max[0] - textBBox.min[0] + padding[1] + padding[3],
|
width: textBBox.max[0] - textBBox.min[0] + padding[1] + padding[3],
|
||||||
height: textBBox.max[1] - textBBox.min[1] + padding[0] + padding[2],
|
height: textBBox.max[1] - textBBox.min[1] + padding[0] + padding[2],
|
||||||
};
|
};
|
||||||
if (style.stransform) {
|
const labelShapeAttr = labelShape.attributes;
|
||||||
bgStyle.transform = style.transform;
|
if (labelShapeAttr.transform) {
|
||||||
|
bgStyle.transform = labelShapeAttr.transform;
|
||||||
bgStyle.transformOrigin = 'center';
|
bgStyle.transformOrigin = 'center';
|
||||||
if (style.textAlign === 'left') {
|
if (labelShapeAttr.textAlign === 'left') {
|
||||||
bgStyle.transformOrigin = `${padding[3]} ${
|
bgStyle.transformOrigin = `${padding[3]} ${
|
||||||
padding[0] + bgStyle.height / 2
|
padding[0] + bgStyle.height / 2
|
||||||
}`;
|
}`;
|
||||||
}
|
}
|
||||||
if (style.textAlign === 'right') {
|
if (labelShapeAttr.textAlign === 'right') {
|
||||||
bgStyle.transformOrigin = `${padding[3] + bgStyle.width} ${
|
bgStyle.transformOrigin = `${padding[3] + bgStyle.width} ${
|
||||||
padding[0] + bgStyle.height / 2
|
padding[0] + bgStyle.height / 2
|
||||||
}`;
|
}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
shapes['labelBgShape'] = upsertShape(
|
return this.upsertShape('rect', 'labelBackgroundShape', bgStyle, shapeMap)
|
||||||
'rect',
|
.shape;
|
||||||
'labelBgShape',
|
|
||||||
bgStyle,
|
|
||||||
shapeMap,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return shapes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public drawIconShape(
|
public drawIconShape(
|
||||||
@ -193,26 +225,237 @@ export abstract class BaseNode {
|
|||||||
diffData?: { previous: NodeModelData; current: NodeModelData },
|
diffData?: { previous: NodeModelData; current: NodeModelData },
|
||||||
diffState?: { oldState: State[]; newState: State[] },
|
diffState?: { oldState: State[]; newState: State[] },
|
||||||
): DisplayObject {
|
): DisplayObject {
|
||||||
const { iconShape } = model.data || {};
|
|
||||||
const { iconShape: shapeStyle } = this.mergedStyles;
|
const { iconShape: shapeStyle } = this.mergedStyles;
|
||||||
const iconShapeType = shapeStyle.text ? 'text' : 'image';
|
const {
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
fontSize,
|
||||||
|
text,
|
||||||
|
offsetX = 0,
|
||||||
|
offsetY = 0,
|
||||||
|
} = shapeStyle;
|
||||||
|
const w = (width || fontSize) as number;
|
||||||
|
const h = (height || fontSize) as number;
|
||||||
|
const iconShapeType = text ? 'text' : 'image';
|
||||||
if (iconShapeType === 'image') {
|
if (iconShapeType === 'image') {
|
||||||
const { width, height } = shapeStyle;
|
shapeStyle.x = -w / 2 + offsetX;
|
||||||
if (!Object.prototype.hasOwnProperty.call(iconShape, 'x'))
|
shapeStyle.y = -h / 2 + offsetY;
|
||||||
shapeStyle.x = -width / 2;
|
shapeStyle.width = w;
|
||||||
if (!Object.prototype.hasOwnProperty.call(iconShape, 'y'))
|
shapeStyle.height = h;
|
||||||
shapeStyle.y = -height / 2;
|
|
||||||
} else {
|
} else {
|
||||||
shapeStyle.textAlign = 'center';
|
shapeStyle.textAlign = 'center';
|
||||||
shapeStyle.textBaseline = 'middle';
|
shapeStyle.textBaseline = 'middle';
|
||||||
|
shapeStyle.x = offsetX;
|
||||||
|
shapeStyle.y = offsetY;
|
||||||
|
shapeStyle.fontSize = w;
|
||||||
}
|
}
|
||||||
// TODO: update type define.
|
|
||||||
return upsertShape(
|
return this.upsertShape(
|
||||||
iconShapeType,
|
iconShapeType,
|
||||||
'iconShape',
|
'iconShape',
|
||||||
shapeStyle as unknown as GShapeStyle,
|
shapeStyle as GShapeStyle,
|
||||||
shapeMap,
|
shapeMap,
|
||||||
);
|
).shape;
|
||||||
|
}
|
||||||
|
|
||||||
|
public drawHaloShape(
|
||||||
|
model: NodeDisplayModel,
|
||||||
|
shapeMap: NodeShapeMap,
|
||||||
|
diffData?: { previous: NodeModelData; current: NodeModelData },
|
||||||
|
diffState?: { previous: State[]; current: State[] },
|
||||||
|
): DisplayObject {
|
||||||
|
const { keyShape } = shapeMap;
|
||||||
|
const { haloShape: haloShapeStyle } = this.mergedStyles;
|
||||||
|
const { nodeName, attributes } = keyShape;
|
||||||
|
return this.upsertShape(
|
||||||
|
nodeName as SHAPE_TYPE,
|
||||||
|
'haloShape',
|
||||||
|
{
|
||||||
|
...attributes,
|
||||||
|
...haloShapeStyle,
|
||||||
|
isBillboard: true,
|
||||||
|
},
|
||||||
|
shapeMap,
|
||||||
|
).shape;
|
||||||
|
}
|
||||||
|
|
||||||
|
public drawAnchorShapes(
|
||||||
|
model: NodeDisplayModel,
|
||||||
|
shapeMap: NodeShapeMap,
|
||||||
|
diffData?: { previous: NodeModelData; current: NodeModelData },
|
||||||
|
diffState?: { previous: State[]; current: State[] },
|
||||||
|
): {
|
||||||
|
[shapeId: string]: DisplayObject;
|
||||||
|
} {
|
||||||
|
const { anchorShapes: configs, keyShape: keyShapeStyle } =
|
||||||
|
this.mergedStyles;
|
||||||
|
const commonStyle = {};
|
||||||
|
const individualConfigs = [];
|
||||||
|
Object.keys(configs).forEach((key) => {
|
||||||
|
const value = configs[key];
|
||||||
|
if (typeof value === 'object' && value.position) {
|
||||||
|
individualConfigs.push(value);
|
||||||
|
} else {
|
||||||
|
commonStyle[key] = value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!individualConfigs.length) return;
|
||||||
|
this.boundsCache.keyShapeLocal =
|
||||||
|
this.boundsCache.keyShapeLocal || shapeMap.keyShape.getLocalBounds();
|
||||||
|
const keyShapeBBox = this.boundsCache.keyShapeLocal;
|
||||||
|
const keyShapeWidth = keyShapeBBox.max[0] - keyShapeBBox.min[0];
|
||||||
|
const keyShapeHeight = keyShapeBBox.max[1] - keyShapeBBox.min[1];
|
||||||
|
|
||||||
|
const shapes = {};
|
||||||
|
individualConfigs.forEach((config, i) => {
|
||||||
|
const { position, fill = keyShapeStyle.fill, ...style } = config;
|
||||||
|
const id = `anchorShape${i}`;
|
||||||
|
shapes[id] = this.upsertShape(
|
||||||
|
'circle',
|
||||||
|
id,
|
||||||
|
{
|
||||||
|
cx: keyShapeWidth * (position[0] - 0.5),
|
||||||
|
cy: keyShapeHeight * (position[1] - 0.5),
|
||||||
|
fill,
|
||||||
|
...commonStyle,
|
||||||
|
...style,
|
||||||
|
} as GShapeStyle,
|
||||||
|
shapeMap,
|
||||||
|
).shape;
|
||||||
|
});
|
||||||
|
return shapes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public drawBadgeShapes(
|
||||||
|
model: NodeDisplayModel,
|
||||||
|
shapeMap: NodeShapeMap,
|
||||||
|
diffData?: { previous: NodeModelData; current: NodeModelData },
|
||||||
|
diffState?: { previous: State[]; current: State[] },
|
||||||
|
): {
|
||||||
|
[shapeId: string]: DisplayObject;
|
||||||
|
} {
|
||||||
|
const configs = this.mergedStyles.badgeShapes;
|
||||||
|
const commonStyle = {};
|
||||||
|
const individualConfigs = [];
|
||||||
|
Object.keys(configs).forEach((key) => {
|
||||||
|
const value = configs[key];
|
||||||
|
if (typeof value === 'object' && value.position) {
|
||||||
|
individualConfigs.push(value);
|
||||||
|
} else {
|
||||||
|
commonStyle[key] = value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!individualConfigs.length) return {};
|
||||||
|
this.boundsCache.keyShapeLocal =
|
||||||
|
this.boundsCache.keyShapeLocal || shapeMap.keyShape.getLocalBounds();
|
||||||
|
const { keyShapeLocal: keyShapeBBox } = this.boundsCache;
|
||||||
|
const keyShapeWidth = keyShapeBBox.max[0] - keyShapeBBox.min[0];
|
||||||
|
const shapes = {};
|
||||||
|
individualConfigs.forEach((config) => {
|
||||||
|
const { position, ...individualStyle } = config;
|
||||||
|
const id = `${position}BadgeShape`;
|
||||||
|
const style = {
|
||||||
|
...commonStyle,
|
||||||
|
...individualStyle,
|
||||||
|
};
|
||||||
|
const {
|
||||||
|
text = '',
|
||||||
|
type,
|
||||||
|
color,
|
||||||
|
size = keyShapeWidth / 3,
|
||||||
|
textColor,
|
||||||
|
zIndex = 2,
|
||||||
|
offsetX = 0,
|
||||||
|
offsetY = 0,
|
||||||
|
...otherStyles
|
||||||
|
} = style;
|
||||||
|
|
||||||
|
const bgHeight = size as number;
|
||||||
|
|
||||||
|
let pos = { x: 0, y: 0 };
|
||||||
|
switch (position) {
|
||||||
|
case 'rightTop':
|
||||||
|
pos.x = keyShapeBBox.max[0] - bgHeight / 2 + offsetX;
|
||||||
|
pos.y = keyShapeBBox.min[1] + size / 4 + offsetY;
|
||||||
|
break;
|
||||||
|
case 'right':
|
||||||
|
pos.x = keyShapeBBox.max[0] - bgHeight / 2 + offsetX;
|
||||||
|
pos.y = offsetY;
|
||||||
|
break;
|
||||||
|
case 'rightBottom':
|
||||||
|
case 'bottomRight':
|
||||||
|
pos.x = keyShapeBBox.max[0] - bgHeight / 2 + offsetX;
|
||||||
|
pos.y = keyShapeBBox.max[1] - size / 4 + offsetY;
|
||||||
|
break;
|
||||||
|
case 'leftTop':
|
||||||
|
case 'topLeft':
|
||||||
|
pos.x = keyShapeBBox.min[0] + bgHeight / 2 + offsetX;
|
||||||
|
pos.y = keyShapeBBox.min[1] + size / 4 + offsetY;
|
||||||
|
break;
|
||||||
|
case 'left':
|
||||||
|
pos.x = keyShapeBBox.min[0] + bgHeight / 2 + offsetX;
|
||||||
|
pos.y = offsetY;
|
||||||
|
break;
|
||||||
|
case 'leftBottom':
|
||||||
|
case 'bottomLeft':
|
||||||
|
pos.x = keyShapeBBox.min[0] + bgHeight / 2 + offsetX;
|
||||||
|
pos.y = keyShapeBBox.max[1] - size / 4 + offsetY;
|
||||||
|
break;
|
||||||
|
case 'top':
|
||||||
|
pos.x = offsetX;
|
||||||
|
pos.y = keyShapeBBox.min[1] + size / 4;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// bottom
|
||||||
|
pos.x = offsetX;
|
||||||
|
pos.y = keyShapeBBox.max[1] - size / 4;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// a radius rect (as container) + a text / icon
|
||||||
|
shapes[id] = this.upsertShape(
|
||||||
|
'text',
|
||||||
|
id,
|
||||||
|
{
|
||||||
|
text,
|
||||||
|
fill: textColor,
|
||||||
|
fontSize: bgHeight - 2,
|
||||||
|
x: pos.x,
|
||||||
|
y: pos.y,
|
||||||
|
...otherStyles,
|
||||||
|
textAlign: position.includes('right') ? 'left' : 'right',
|
||||||
|
textBaseline: 'middle',
|
||||||
|
zIndex: (zIndex as number) + 1,
|
||||||
|
} as GShapeStyle,
|
||||||
|
shapeMap,
|
||||||
|
).shape;
|
||||||
|
const bbox = shapes[id].getLocalBounds();
|
||||||
|
const bgShapeId = `${position}BadgeBackgroundShape`;
|
||||||
|
const bgWidth =
|
||||||
|
(text as string).length <= 1
|
||||||
|
? bgHeight
|
||||||
|
: Math.max(bgHeight, bbox.max[0] - bbox.min[0]) + 8;
|
||||||
|
shapes[bgShapeId] = this.upsertShape(
|
||||||
|
'rect',
|
||||||
|
bgShapeId,
|
||||||
|
{
|
||||||
|
text,
|
||||||
|
fill: color,
|
||||||
|
height: bgHeight,
|
||||||
|
width: bgWidth,
|
||||||
|
x: bbox.min[0] - 3, // begin at the border, minus half height
|
||||||
|
y: bbox.min[1],
|
||||||
|
radius: bgHeight / 2,
|
||||||
|
zIndex,
|
||||||
|
...otherStyles,
|
||||||
|
} as GShapeStyle,
|
||||||
|
shapeMap,
|
||||||
|
).shape;
|
||||||
|
});
|
||||||
|
|
||||||
|
return shapes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public drawOtherShapes(
|
public drawOtherShapes(
|
||||||
@ -227,15 +470,12 @@ export abstract class BaseNode {
|
|||||||
public upsertShape(
|
public upsertShape(
|
||||||
type: SHAPE_TYPE | SHAPE_TYPE_3D,
|
type: SHAPE_TYPE | SHAPE_TYPE_3D,
|
||||||
id: string,
|
id: string,
|
||||||
style: { [shapeAttr: string]: unknown },
|
style: ShapeStyle,
|
||||||
shapeMap: { [shapeId: string]: DisplayObject },
|
shapeMap: { [shapeId: string]: DisplayObject },
|
||||||
): DisplayObject {
|
): {
|
||||||
// TODO: update type define.
|
updateStyles: ShapeStyle;
|
||||||
return upsertShape(
|
shape: DisplayObject;
|
||||||
type as SHAPE_TYPE,
|
} {
|
||||||
id,
|
return upsertShape(type as SHAPE_TYPE, id, style as GShapeStyle, shapeMap);
|
||||||
style as unknown as GShapeStyle,
|
|
||||||
shapeMap,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,18 +6,22 @@ import {
|
|||||||
ItemShapeStyles,
|
ItemShapeStyles,
|
||||||
SHAPE_TYPE,
|
SHAPE_TYPE,
|
||||||
SHAPE_TYPE_3D,
|
SHAPE_TYPE_3D,
|
||||||
|
ShapeStyle,
|
||||||
State,
|
State,
|
||||||
} from '../../../types/item';
|
} from '../../../types/item';
|
||||||
import { NodeModelData, NodeShapeMap } from '../../../types/node';
|
import {
|
||||||
import { formatPadding, mergeStyles, upsertShape } from '../../../util/shape';
|
NodeModelData,
|
||||||
|
NodeShapeMap,
|
||||||
|
NodeShapeStyles,
|
||||||
|
} from '../../../types/node';
|
||||||
import { upsertShape3D } from '../../../util/shape3d';
|
import { upsertShape3D } from '../../../util/shape3d';
|
||||||
import { BaseNode } from './base';
|
import { BaseNode } from './base';
|
||||||
|
|
||||||
export abstract class BaseNode3D extends BaseNode {
|
export abstract class BaseNode3D extends BaseNode {
|
||||||
type: string;
|
type: string;
|
||||||
defaultStyles: ItemShapeStyles;
|
defaultStyles: ItemShapeStyles;
|
||||||
themeStyles: ItemShapeStyles;
|
themeStyles: NodeShapeStyles;
|
||||||
mergedStyles: ItemShapeStyles;
|
mergedStyles: NodeShapeStyles;
|
||||||
device: any; // for 3d renderer
|
device: any; // for 3d renderer
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
@ -30,110 +34,8 @@ export abstract class BaseNode3D extends BaseNode {
|
|||||||
shapeMap: NodeShapeMap,
|
shapeMap: NodeShapeMap,
|
||||||
diffData?: { previous: NodeModelData; current: NodeModelData },
|
diffData?: { previous: NodeModelData; current: NodeModelData },
|
||||||
diffState?: { oldState: State[]; newState: State[] },
|
diffState?: { oldState: State[]; newState: State[] },
|
||||||
): {
|
): DisplayObject {
|
||||||
labelShape: DisplayObject;
|
return super.drawLabelShape(model, shapeMap, diffData, diffState);
|
||||||
[id: string]: DisplayObject;
|
|
||||||
} {
|
|
||||||
const { keyShape } = shapeMap;
|
|
||||||
const keyShapeBox = keyShape.getGeometryBounds();
|
|
||||||
const { labelShape: shapeStyle } = this.mergedStyles;
|
|
||||||
|
|
||||||
const {
|
|
||||||
position,
|
|
||||||
background,
|
|
||||||
offsetX: propsOffsetX,
|
|
||||||
offsetY: propsOffsetY,
|
|
||||||
...otherStyle
|
|
||||||
} = shapeStyle;
|
|
||||||
const positionPreset = {
|
|
||||||
x: keyShapeBox.center[0],
|
|
||||||
y: keyShapeBox.max[1],
|
|
||||||
textBaseline: 'top',
|
|
||||||
textAlign: 'center',
|
|
||||||
offsetX: 0,
|
|
||||||
offsetY: 0,
|
|
||||||
};
|
|
||||||
switch (position) {
|
|
||||||
case 'center':
|
|
||||||
positionPreset.y = keyShapeBox.center[1];
|
|
||||||
break;
|
|
||||||
case 'top':
|
|
||||||
positionPreset.y = keyShapeBox.min[1];
|
|
||||||
positionPreset.textBaseline = 'bottom';
|
|
||||||
positionPreset.offsetY = -4;
|
|
||||||
break;
|
|
||||||
case 'left':
|
|
||||||
positionPreset.x = keyShapeBox.min[0];
|
|
||||||
positionPreset.y = keyShapeBox.center[1];
|
|
||||||
positionPreset.textAlign = 'right';
|
|
||||||
positionPreset.textBaseline = 'middle';
|
|
||||||
positionPreset.offsetX = -4;
|
|
||||||
break;
|
|
||||||
case 'right':
|
|
||||||
positionPreset.x = keyShapeBox.max[0];
|
|
||||||
positionPreset.y = keyShapeBox.center[1];
|
|
||||||
positionPreset.textAlign = 'left';
|
|
||||||
positionPreset.textBaseline = 'middle';
|
|
||||||
positionPreset.offsetX = 4;
|
|
||||||
break;
|
|
||||||
default: // at bottom by default
|
|
||||||
positionPreset.offsetY = 4;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
const offsetX = (
|
|
||||||
propsOffsetX === undefined ? positionPreset.offsetX : propsOffsetX
|
|
||||||
) as number;
|
|
||||||
const offsetY = (
|
|
||||||
propsOffsetY === undefined ? positionPreset.offsetY : propsOffsetY
|
|
||||||
) as number;
|
|
||||||
positionPreset.x += offsetX;
|
|
||||||
positionPreset.y += offsetY;
|
|
||||||
|
|
||||||
const style: any = {
|
|
||||||
...this.defaultStyles.labelShape,
|
|
||||||
...positionPreset,
|
|
||||||
...otherStyle,
|
|
||||||
};
|
|
||||||
const labelShape = upsertShape('text', 'labelShape', style, shapeMap);
|
|
||||||
const shapes = { labelShape };
|
|
||||||
if (background) {
|
|
||||||
const textBBox = labelShape.getGeometryBounds();
|
|
||||||
// TODO: update type define.
|
|
||||||
// @ts-ignore
|
|
||||||
const { padding: propsPadding, ...backgroundStyle } = background;
|
|
||||||
const padding = formatPadding(propsPadding, DEFAULT_LABEL_BG_PADDING);
|
|
||||||
const bgStyle: any = {
|
|
||||||
fill: '#fff',
|
|
||||||
radius: 4,
|
|
||||||
...backgroundStyle,
|
|
||||||
x: textBBox.min[0] - padding[3] + style.x,
|
|
||||||
y: textBBox.min[1] - padding[0] + style.y,
|
|
||||||
width: textBBox.max[0] - textBBox.min[0] + padding[1] + padding[3],
|
|
||||||
height: textBBox.max[1] - textBBox.min[1] + padding[0] + padding[2],
|
|
||||||
};
|
|
||||||
if (style.stransform) {
|
|
||||||
bgStyle.transform = style.transform;
|
|
||||||
bgStyle.transformOrigin = 'center';
|
|
||||||
if (style.textAlign === 'left') {
|
|
||||||
bgStyle.transformOrigin = `${padding[3]} ${
|
|
||||||
padding[0] + bgStyle.height / 2
|
|
||||||
}`;
|
|
||||||
}
|
|
||||||
if (style.textAlign === 'right') {
|
|
||||||
bgStyle.transformOrigin = `${padding[3] + bgStyle.width} ${
|
|
||||||
padding[0] + bgStyle.height / 2
|
|
||||||
}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
shapes['labelBgShape'] = upsertShape(
|
|
||||||
'rect',
|
|
||||||
'labelBgShape',
|
|
||||||
bgStyle,
|
|
||||||
shapeMap,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return shapes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: 3d icon? - billboard image or text for alpha
|
// TODO: 3d icon? - billboard image or text for alpha
|
||||||
@ -143,18 +45,59 @@ export abstract class BaseNode3D extends BaseNode {
|
|||||||
diffData?: { previous: NodeModelData; current: NodeModelData },
|
diffData?: { previous: NodeModelData; current: NodeModelData },
|
||||||
diffState?: { oldState: State[]; newState: State[] },
|
diffState?: { oldState: State[]; newState: State[] },
|
||||||
): DisplayObject {
|
): DisplayObject {
|
||||||
const { iconShape } = model.data || {};
|
return super.drawIconShape(model, shapeMap, diffData, diffState);
|
||||||
const { iconShape: shapeStyle } = this.mergedStyles;
|
|
||||||
const iconShapeType = shapeStyle.text ? 'text' : 'image';
|
|
||||||
if (iconShapeType === 'image') {
|
|
||||||
const { width, height } = shapeStyle;
|
|
||||||
if (!iconShape.hasOwnProperty('x')) shapeStyle.x = -width / 2;
|
|
||||||
if (!iconShape.hasOwnProperty('y')) shapeStyle.y = -height / 2;
|
|
||||||
} else {
|
|
||||||
shapeStyle.textAlign = 'center';
|
|
||||||
shapeStyle.textBaseline = 'middle';
|
|
||||||
}
|
}
|
||||||
return this.upsertShape(iconShapeType, 'iconShape', shapeStyle, shapeMap);
|
|
||||||
|
// TODO: 3d billboard
|
||||||
|
public drawHaloShape(
|
||||||
|
model: NodeDisplayModel,
|
||||||
|
shapeMap: NodeShapeMap,
|
||||||
|
diffData?: { previous: NodeModelData; current: NodeModelData },
|
||||||
|
diffState?: { previous: State[]; current: State[] },
|
||||||
|
): DisplayObject {
|
||||||
|
const { keyShape } = shapeMap;
|
||||||
|
const { haloShape: haloShapeStyle } = this.mergedStyles;
|
||||||
|
const { nodeName, attributes } = keyShape;
|
||||||
|
return this.upsertShape(
|
||||||
|
nodeName as SHAPE_TYPE,
|
||||||
|
'haloShape',
|
||||||
|
{
|
||||||
|
...attributes,
|
||||||
|
...haloShapeStyle,
|
||||||
|
isBillboard: true,
|
||||||
|
},
|
||||||
|
shapeMap,
|
||||||
|
).shape;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 3D node does not support anchor shapes.
|
||||||
|
* @param model
|
||||||
|
* @param shapeMap
|
||||||
|
* @param diffData
|
||||||
|
* @param diffState
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
public drawAnchorShapes(
|
||||||
|
model: NodeDisplayModel,
|
||||||
|
shapeMap: NodeShapeMap,
|
||||||
|
diffData?: { previous: NodeModelData; current: NodeModelData },
|
||||||
|
diffState?: { previous: State[]; current: State[] },
|
||||||
|
): {
|
||||||
|
[shapeId: string]: DisplayObject;
|
||||||
|
} {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
public drawBadgeShapes(
|
||||||
|
model: NodeDisplayModel,
|
||||||
|
shapeMap: NodeShapeMap,
|
||||||
|
diffData?: { previous: NodeModelData; current: NodeModelData },
|
||||||
|
diffState?: { previous: State[]; current: State[] },
|
||||||
|
): {
|
||||||
|
[shapeId: string]: DisplayObject;
|
||||||
|
} {
|
||||||
|
return super.drawBadgeShapes(model, shapeMap, diffData, diffState);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: 3d shapes?
|
// TODO: 3d shapes?
|
||||||
@ -171,15 +114,12 @@ export abstract class BaseNode3D extends BaseNode {
|
|||||||
public upsertShape(
|
public upsertShape(
|
||||||
type: SHAPE_TYPE_3D | SHAPE_TYPE,
|
type: SHAPE_TYPE_3D | SHAPE_TYPE,
|
||||||
id: string,
|
id: string,
|
||||||
style: { [shapeAttr: string]: unknown },
|
style: ShapeStyle,
|
||||||
shapeMap: { [shapeId: string]: DisplayObject },
|
shapeMap: { [shapeId: string]: DisplayObject },
|
||||||
): DisplayObject {
|
): {
|
||||||
return upsertShape3D(
|
shape: DisplayObject;
|
||||||
type,
|
updateStyles: ShapeStyle;
|
||||||
id,
|
} {
|
||||||
style as unknown as GShapeStyle,
|
return upsertShape3D(type, id, style as GShapeStyle, shapeMap, this.device);
|
||||||
shapeMap,
|
|
||||||
this.device,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,23 @@
|
|||||||
import { DisplayObject } from '@antv/g';
|
import { DisplayObject } from '@antv/g';
|
||||||
import { NodeDisplayModel } from '../../../types';
|
import { NodeDisplayModel } from '../../../types';
|
||||||
import { GShapeStyle, ItemShapeStyles, State } from '../../../types/item';
|
import { State } from '../../../types/item';
|
||||||
import { NodeModelData, NodeShapeMap } from '../../../types/node';
|
import {
|
||||||
|
NodeModelData,
|
||||||
|
NodeShapeMap,
|
||||||
|
NodeShapeStyles,
|
||||||
|
} from '../../../types/node';
|
||||||
import { BaseNode } from './base';
|
import { BaseNode } from './base';
|
||||||
|
import { isStyleAffectBBox } from 'util/shape';
|
||||||
|
|
||||||
export class CircleNode extends BaseNode {
|
export class CircleNode extends BaseNode {
|
||||||
override defaultStyles = {
|
override defaultStyles = {
|
||||||
keyShape: {
|
keyShape: {
|
||||||
r: 15,
|
r: 16,
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
mergedStyles: ItemShapeStyles;
|
mergedStyles: NodeShapeStyles;
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
// suggest to merge default styles like this to avoid style value missing
|
// suggest to merge default styles like this to avoid style value missing
|
||||||
@ -27,16 +32,62 @@ export class CircleNode extends BaseNode {
|
|||||||
const { data = {} } = model;
|
const { data = {} } = model;
|
||||||
let shapes: NodeShapeMap = { keyShape: undefined };
|
let shapes: NodeShapeMap = { keyShape: undefined };
|
||||||
|
|
||||||
|
// keyShape
|
||||||
shapes.keyShape = this.drawKeyShape(model, shapeMap, diffData);
|
shapes.keyShape = this.drawKeyShape(model, shapeMap, diffData);
|
||||||
|
|
||||||
|
// haloShape
|
||||||
|
if (data.haloShape && this.drawHaloShape) {
|
||||||
|
shapes.haloShape = this.drawHaloShape(model, shapeMap, diffData);
|
||||||
|
}
|
||||||
|
|
||||||
|
// labelShape
|
||||||
if (data.labelShape) {
|
if (data.labelShape) {
|
||||||
|
shapes.labelShape = this.drawLabelShape(model, shapeMap, diffData);
|
||||||
|
}
|
||||||
|
|
||||||
|
// labelBackgroundShape
|
||||||
|
if (data.labelBackgroundShape) {
|
||||||
|
shapes.labelBackgroundShape = this.drawLabelBackgroundShape(
|
||||||
|
model,
|
||||||
|
shapeMap,
|
||||||
|
diffData,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// anchor shapes
|
||||||
|
if (data.anchorShapes) {
|
||||||
|
const anchorShapes = this.drawAnchorShapes(
|
||||||
|
model,
|
||||||
|
shapeMap,
|
||||||
|
diffData,
|
||||||
|
diffState,
|
||||||
|
);
|
||||||
shapes = {
|
shapes = {
|
||||||
...shapes,
|
...shapes,
|
||||||
...this.drawLabelShape(model, shapeMap, diffData),
|
...anchorShapes,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// iconShape
|
||||||
if (data.iconShape) {
|
if (data.iconShape) {
|
||||||
shapes.iconShape = this.drawIconShape(model, shapeMap, diffData);
|
shapes.iconShape = this.drawIconShape(model, shapeMap, diffData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// badgeShape
|
||||||
|
if (data.badgeShapes) {
|
||||||
|
const badgeShapes = this.drawBadgeShapes(
|
||||||
|
model,
|
||||||
|
shapeMap,
|
||||||
|
diffData,
|
||||||
|
diffState,
|
||||||
|
);
|
||||||
|
shapes = {
|
||||||
|
...shapes,
|
||||||
|
...badgeShapes,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherShapes
|
||||||
if (data.otherShapes && this.drawOtherShapes) {
|
if (data.otherShapes && this.drawOtherShapes) {
|
||||||
shapes = {
|
shapes = {
|
||||||
...shapes,
|
...shapes,
|
||||||
@ -52,11 +103,15 @@ export class CircleNode extends BaseNode {
|
|||||||
diffData?: { previous: NodeModelData; current: NodeModelData },
|
diffData?: { previous: NodeModelData; current: NodeModelData },
|
||||||
diffState?: { previous: State[]; current: State[] },
|
diffState?: { previous: State[]; current: State[] },
|
||||||
): DisplayObject {
|
): DisplayObject {
|
||||||
return this.upsertShape(
|
const { shape, updateStyles } = this.upsertShape(
|
||||||
'circle',
|
'circle',
|
||||||
'keyShape',
|
'keyShape',
|
||||||
this.mergedStyles.keyShape,
|
this.mergedStyles.keyShape,
|
||||||
shapeMap,
|
shapeMap,
|
||||||
);
|
);
|
||||||
|
if (isStyleAffectBBox('circle', updateStyles)) {
|
||||||
|
this.boundsCache.keyShapeLocal = shape.getLocalBounds();
|
||||||
|
}
|
||||||
|
return shape;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
import { DisplayObject } from '@antv/g';
|
import { DisplayObject } from '@antv/g';
|
||||||
import { NodeDisplayModel } from '../../../types';
|
import { NodeDisplayModel } from '../../../types';
|
||||||
import { ItemShapeStyles, State } from '../../../types/item';
|
import { State } from '../../../types/item';
|
||||||
import { NodeModelData, NodeShapeMap } from '../../../types/node';
|
import {
|
||||||
|
NodeModelData,
|
||||||
|
NodeShapeMap,
|
||||||
|
NodeShapeStyles,
|
||||||
|
} from '../../../types/node';
|
||||||
import { BaseNode3D } from './base3d';
|
import { BaseNode3D } from './base3d';
|
||||||
|
|
||||||
export class SphereNode extends BaseNode3D {
|
export class SphereNode extends BaseNode3D {
|
||||||
@ -15,7 +19,7 @@ export class SphereNode extends BaseNode3D {
|
|||||||
z: 0,
|
z: 0,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
mergedStyles: ItemShapeStyles;
|
mergedStyles: NodeShapeStyles;
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
}
|
}
|
||||||
@ -28,16 +32,48 @@ export class SphereNode extends BaseNode3D {
|
|||||||
const { data = {} } = model;
|
const { data = {} } = model;
|
||||||
let shapes: NodeShapeMap = { keyShape: undefined };
|
let shapes: NodeShapeMap = { keyShape: undefined };
|
||||||
|
|
||||||
|
// keyShape
|
||||||
shapes.keyShape = this.drawKeyShape(model, shapeMap, diffData);
|
shapes.keyShape = this.drawKeyShape(model, shapeMap, diffData);
|
||||||
if (data.labelShape) {
|
|
||||||
shapes = {
|
// haloShape
|
||||||
...shapes,
|
if (data.haloShape && this.drawHaloShape) {
|
||||||
...this.drawLabelShape(model, shapeMap, diffData),
|
shapes.haloShape = this.drawHaloShape(model, shapeMap, diffData);
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// labelShape
|
||||||
|
if (data.labelShape) {
|
||||||
|
shapes.labelShape = this.drawLabelShape(model, shapeMap, diffData);
|
||||||
|
}
|
||||||
|
|
||||||
|
// labelBackgroundShape
|
||||||
|
if (data.labelBackgroundShape) {
|
||||||
|
shapes.labelBackgroundShape = this.drawLabelBackgroundShape(
|
||||||
|
model,
|
||||||
|
shapeMap,
|
||||||
|
diffData,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// iconShape
|
||||||
if (data.iconShape) {
|
if (data.iconShape) {
|
||||||
shapes.iconShape = this.drawIconShape(model, shapeMap, diffData);
|
shapes.iconShape = this.drawIconShape(model, shapeMap, diffData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// badgeShape
|
||||||
|
if (data.badgeShapes) {
|
||||||
|
const badgeShapes = this.drawBadgeShapes(
|
||||||
|
model,
|
||||||
|
shapeMap,
|
||||||
|
diffData,
|
||||||
|
diffState,
|
||||||
|
);
|
||||||
|
shapes = {
|
||||||
|
...shapes,
|
||||||
|
...badgeShapes,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherShapes
|
||||||
if (data.otherShapes && this.drawOtherShapes) {
|
if (data.otherShapes && this.drawOtherShapes) {
|
||||||
shapes = {
|
shapes = {
|
||||||
...shapes,
|
...shapes,
|
||||||
@ -58,6 +94,6 @@ export class SphereNode extends BaseNode3D {
|
|||||||
'keyShape',
|
'keyShape',
|
||||||
this.mergedStyles.keyShape,
|
this.mergedStyles.keyShape,
|
||||||
shapeMap,
|
shapeMap,
|
||||||
);
|
).shape;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -102,7 +102,7 @@ export default {
|
|||||||
...DEFAULT_SHAPE_STYLE,
|
...DEFAULT_SHAPE_STYLE,
|
||||||
lineWidth: 1,
|
lineWidth: 1,
|
||||||
stroke: edgeMainStroke,
|
stroke: edgeMainStroke,
|
||||||
lineAppendWidth: 2,
|
increasedLineWidthForHitTesting: 2,
|
||||||
},
|
},
|
||||||
labelShape: {
|
labelShape: {
|
||||||
...DEFAULT_TEXT_STYLE,
|
...DEFAULT_TEXT_STYLE,
|
||||||
|
@ -1,75 +1,107 @@
|
|||||||
import { DEFAULT_SHAPE_STYLE, DEFAULT_TEXT_STYLE } from '../../constant';
|
import { DEFAULT_SHAPE_STYLE, DEFAULT_TEXT_STYLE } from '../../constant';
|
||||||
import { ThemeSpecification } from '../../types/theme';
|
import { ThemeSpecification } from '../../types/theme';
|
||||||
|
|
||||||
const subjectColor = 'rgb(95, 149, 255)';
|
const subjectColor = 'rgb(34,126,255)';
|
||||||
const textColor = 'rgb(0, 0, 0)';
|
const textColor = 'rgba(0,0,0,0.85)';
|
||||||
|
|
||||||
const activeFill = 'rgb(247, 250, 255)';
|
const nodeColor = 'rgb(34,126,255)';
|
||||||
const nodeMainFill = 'rgb(239, 244, 255)';
|
const edgeColor = 'rgb(153, 173, 209)';
|
||||||
const comboFill = 'rgb(253, 253, 253)';
|
const comboFill = 'rgb(253, 253, 253)';
|
||||||
const disabledFill = 'rgb(250, 250, 250)';
|
const disabledFill = 'rgb(240, 240, 240)';
|
||||||
|
|
||||||
const edgeMainStroke = 'rgb(224, 224, 224)';
|
const edgeMainStroke = 'rgb(153, 173, 209)';
|
||||||
const edgeInactiveStroke = 'rgb(234, 234, 234)';
|
const edgeDisableStroke = 'rgb(217, 217, 217)';
|
||||||
const edgeDisablesStroke = 'rgb(245, 245, 245)';
|
const edgeInactiveStroke = 'rgb(210, 218, 233)';
|
||||||
const inactiveStroke = 'rgb(191, 213, 255)';
|
|
||||||
|
|
||||||
const highlightStroke = '#4572d9';
|
const nodeStroke = 'rgba(0,0,0,0.85)';
|
||||||
const highlightFill = 'rgb(223, 234, 255)';
|
const haloStroke = 'rgb(0, 0, 0)';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
node: {
|
node: {
|
||||||
palette: [],
|
palette: [
|
||||||
|
'#227EFF',
|
||||||
|
'#AD5CFF',
|
||||||
|
'#00B8B8',
|
||||||
|
'#FA822D',
|
||||||
|
'#F252AF',
|
||||||
|
'#1EB8F5',
|
||||||
|
'#108A44',
|
||||||
|
'#F4B106',
|
||||||
|
'#5241A8',
|
||||||
|
'#95CF21',
|
||||||
|
],
|
||||||
styles: [
|
styles: [
|
||||||
{
|
{
|
||||||
default: {
|
default: {
|
||||||
keyShape: {
|
keyShape: {
|
||||||
...DEFAULT_SHAPE_STYLE,
|
...DEFAULT_SHAPE_STYLE,
|
||||||
r: 10,
|
r: 16,
|
||||||
fill: nodeMainFill,
|
fill: nodeColor,
|
||||||
stroke: subjectColor,
|
lineWidth: 0,
|
||||||
lineWidth: 1,
|
zIndex: 0,
|
||||||
},
|
},
|
||||||
labelShape: {
|
labelShape: {
|
||||||
...DEFAULT_TEXT_STYLE,
|
...DEFAULT_TEXT_STYLE,
|
||||||
fill: '#000',
|
fill: '#000',
|
||||||
position: 'bottom',
|
position: 'bottom',
|
||||||
offsetY: 4,
|
offsetY: 4,
|
||||||
|
zIndex: 2,
|
||||||
|
},
|
||||||
|
labelBackgroundShape: {
|
||||||
|
padding: [4, 4, 4, 4],
|
||||||
|
lineWidth: 0,
|
||||||
|
fill: '#fff',
|
||||||
|
opacity: 0.75,
|
||||||
|
zIndex: -1,
|
||||||
},
|
},
|
||||||
iconShape: {
|
iconShape: {
|
||||||
...DEFAULT_TEXT_STYLE,
|
...DEFAULT_TEXT_STYLE,
|
||||||
fill: '#333',
|
fill: '#fff',
|
||||||
img: 'https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*wAmHQJbNVdwAAAAAAAAAAABkARQnAQ',
|
fontSize: 16,
|
||||||
width: 15,
|
zIndex: 1,
|
||||||
height: 15,
|
},
|
||||||
|
anchorShapes: {
|
||||||
|
lineWidth: 1,
|
||||||
|
stroke: 'rgba(0, 0, 0, 0.65)',
|
||||||
|
zIndex: 2,
|
||||||
|
r: 3,
|
||||||
|
},
|
||||||
|
badgeShapes: {
|
||||||
|
color: 'rgb(140, 140, 140)',
|
||||||
|
textColor: '#fff',
|
||||||
|
zIndex: 2,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
selected: {
|
selected: {
|
||||||
keyShape: {
|
keyShape: {
|
||||||
fill: nodeMainFill,
|
stroke: nodeStroke,
|
||||||
stroke: subjectColor,
|
lineWidth: 3,
|
||||||
lineWidth: 4,
|
|
||||||
shadowColor: subjectColor,
|
|
||||||
shadowBlur: 10,
|
|
||||||
},
|
},
|
||||||
labelShape: {
|
labelShape: {
|
||||||
fontWeight: 500,
|
fontWeight: 500,
|
||||||
},
|
},
|
||||||
|
haloShape: {
|
||||||
|
stroke: haloStroke,
|
||||||
|
opacity: 0.06,
|
||||||
|
lineWidth: 20,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
active: {
|
active: {
|
||||||
keyShape: {
|
keyShape: {
|
||||||
fill: activeFill,
|
stroke: nodeStroke,
|
||||||
stroke: subjectColor,
|
|
||||||
shadowColor: subjectColor,
|
|
||||||
lineWidth: 2,
|
lineWidth: 2,
|
||||||
shadowBlur: 10,
|
},
|
||||||
|
haloShape: {
|
||||||
|
stroke: haloStroke,
|
||||||
|
opacity: 0.06,
|
||||||
|
lineWidth: 4,
|
||||||
|
zIndex: -1,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
highlight: {
|
highlight: {
|
||||||
keyShape: {
|
keyShape: {
|
||||||
fill: highlightFill,
|
stroke: nodeStroke,
|
||||||
stroke: highlightStroke,
|
lineWidth: 3,
|
||||||
lineWidth: 2,
|
|
||||||
},
|
},
|
||||||
labelShape: {
|
labelShape: {
|
||||||
fontWeight: 500,
|
fontWeight: 500,
|
||||||
@ -77,23 +109,37 @@ export default {
|
|||||||
},
|
},
|
||||||
inactive: {
|
inactive: {
|
||||||
keyShape: {
|
keyShape: {
|
||||||
fill: activeFill,
|
opacity: 0.25,
|
||||||
stroke: inactiveStroke,
|
},
|
||||||
lineWidth: 1,
|
labelShape: {
|
||||||
|
opacity: 0.25,
|
||||||
|
},
|
||||||
|
iconShape: {
|
||||||
|
opacity: 0.25,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
disable: {
|
disable: {
|
||||||
keyShape: {
|
keyShape: {
|
||||||
fill: disabledFill,
|
fill: disabledFill,
|
||||||
stroke: edgeMainStroke,
|
lineWidth: 0,
|
||||||
lineWidth: 1,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
edge: {
|
edge: {
|
||||||
palette: [],
|
palette: [
|
||||||
|
'#63A4FF',
|
||||||
|
'#CD9CFF',
|
||||||
|
'#2DEFEF',
|
||||||
|
'#FFBDA1',
|
||||||
|
'#F49FD0',
|
||||||
|
'#80DBFF',
|
||||||
|
'#41CB7C',
|
||||||
|
'#FFD362',
|
||||||
|
'#A192E8',
|
||||||
|
'#CEFB75',
|
||||||
|
],
|
||||||
styles: [
|
styles: [
|
||||||
{
|
{
|
||||||
default: {
|
default: {
|
||||||
@ -101,42 +147,57 @@ export default {
|
|||||||
...DEFAULT_SHAPE_STYLE,
|
...DEFAULT_SHAPE_STYLE,
|
||||||
lineWidth: 1,
|
lineWidth: 1,
|
||||||
stroke: edgeMainStroke,
|
stroke: edgeMainStroke,
|
||||||
lineAppendWidth: 2,
|
increasedLineWidthForHitTesting: 2,
|
||||||
},
|
},
|
||||||
labelShape: {
|
labelShape: {
|
||||||
...DEFAULT_TEXT_STYLE,
|
...DEFAULT_TEXT_STYLE,
|
||||||
fill: textColor,
|
fill: textColor,
|
||||||
position: 'middle',
|
position: 'middle',
|
||||||
textBaseline: 'middle',
|
textBaseline: 'middle',
|
||||||
|
zIndex: 2,
|
||||||
|
},
|
||||||
|
labelBackgroundShape: {
|
||||||
|
padding: [4, 4, 4, 4],
|
||||||
|
lineWidth: 0,
|
||||||
|
fill: '#fff',
|
||||||
|
opacity: 0.75,
|
||||||
|
zIndex: 1,
|
||||||
},
|
},
|
||||||
iconShape: {
|
iconShape: {
|
||||||
...DEFAULT_TEXT_STYLE,
|
...DEFAULT_TEXT_STYLE,
|
||||||
fill: '#333',
|
fill: 'rgb(140, 140, 140)',
|
||||||
img: 'https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*wAmHQJbNVdwAAAAAAAAAAABkARQnAQ',
|
fontSize: 16,
|
||||||
width: 15,
|
zIndex: 2,
|
||||||
height: 15,
|
offsetX: -10,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
selected: {
|
selected: {
|
||||||
keyShape: {
|
keyShape: {
|
||||||
stroke: subjectColor,
|
|
||||||
lineWidth: 2,
|
lineWidth: 2,
|
||||||
shadowColor: subjectColor,
|
|
||||||
shadowBlur: 10,
|
|
||||||
},
|
},
|
||||||
labelShape: {
|
labelShape: {
|
||||||
fontWeight: 500,
|
fontWeight: 500,
|
||||||
},
|
},
|
||||||
|
haloShape: {
|
||||||
|
stroke: haloStroke,
|
||||||
|
opacity: 0.06,
|
||||||
|
lineWidth: 12,
|
||||||
|
zIndex: -1,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
active: {
|
active: {
|
||||||
keyShape: {
|
keyShape: {
|
||||||
stroke: subjectColor,
|
|
||||||
lineWidth: 1,
|
lineWidth: 1,
|
||||||
},
|
},
|
||||||
|
haloShape: {
|
||||||
|
stroke: haloStroke,
|
||||||
|
opacity: 0.06,
|
||||||
|
lineWidth: 12,
|
||||||
|
zIndex: -1,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
highlight: {
|
highlight: {
|
||||||
keyShape: {
|
keyShape: {
|
||||||
stroke: subjectColor,
|
|
||||||
lineWidth: 2,
|
lineWidth: 2,
|
||||||
},
|
},
|
||||||
labelShape: {
|
labelShape: {
|
||||||
@ -151,7 +212,7 @@ export default {
|
|||||||
},
|
},
|
||||||
disable: {
|
disable: {
|
||||||
keyShape: {
|
keyShape: {
|
||||||
stroke: edgeDisablesStroke,
|
stroke: edgeDisableStroke,
|
||||||
lineWidth: 1,
|
lineWidth: 1,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -176,11 +237,9 @@ export default {
|
|||||||
},
|
},
|
||||||
selected: {
|
selected: {
|
||||||
keyShape: {
|
keyShape: {
|
||||||
stroke: subjectColor,
|
stroke: nodeStroke,
|
||||||
fill: comboFill,
|
fill: comboFill,
|
||||||
shadowColor: subjectColor,
|
|
||||||
lineWidth: 2,
|
lineWidth: 2,
|
||||||
shadowBlur: 10,
|
|
||||||
},
|
},
|
||||||
labelShape: {
|
labelShape: {
|
||||||
fontWeight: 500,
|
fontWeight: 500,
|
||||||
@ -188,14 +247,13 @@ export default {
|
|||||||
},
|
},
|
||||||
active: {
|
active: {
|
||||||
keyShape: {
|
keyShape: {
|
||||||
stroke: subjectColor,
|
stroke: nodeStroke,
|
||||||
lineWidth: 1,
|
lineWidth: 1,
|
||||||
fill: activeFill,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
highlight: {
|
highlight: {
|
||||||
keyShape: {
|
keyShape: {
|
||||||
stroke: highlightStroke,
|
stroke: nodeStroke,
|
||||||
fill: comboFill,
|
fill: comboFill,
|
||||||
lineWidth: 2,
|
lineWidth: 2,
|
||||||
},
|
},
|
||||||
@ -212,7 +270,6 @@ export default {
|
|||||||
},
|
},
|
||||||
disable: {
|
disable: {
|
||||||
keyShape: {
|
keyShape: {
|
||||||
stroke: edgeInactiveStroke,
|
|
||||||
fill: disabledFill,
|
fill: disabledFill,
|
||||||
lineWidth: 1,
|
lineWidth: 1,
|
||||||
},
|
},
|
||||||
|
@ -1,7 +1,12 @@
|
|||||||
import { isArray } from '@antv/util';
|
import { isArray } from '@antv/util';
|
||||||
import { ItemStyleSets, ThemeSpecification } from '../../types/theme';
|
import {
|
||||||
|
NodeStyleSets,
|
||||||
|
EdgeStyleSets,
|
||||||
|
ThemeSpecification,
|
||||||
|
} from '../../types/theme';
|
||||||
import { mergeStyles } from '../../util/shape';
|
import { mergeStyles } from '../../util/shape';
|
||||||
import BaseThemeSolver, { ThemeSpecificationMap } from './base';
|
import BaseThemeSolver, { ThemeSpecificationMap } from './base';
|
||||||
|
import { GraphData } from 'types';
|
||||||
|
|
||||||
interface SpecThemeSolverOptions {
|
interface SpecThemeSolverOptions {
|
||||||
base: 'light' | 'dark';
|
base: 'light' | 'dark';
|
||||||
@ -9,17 +14,17 @@ interface SpecThemeSolverOptions {
|
|||||||
node?: {
|
node?: {
|
||||||
dataTypeField?: string;
|
dataTypeField?: string;
|
||||||
palette: string[] | { [dataType: string]: string };
|
palette: string[] | { [dataType: string]: string };
|
||||||
getStyleSets: (palette) => ItemStyleSets;
|
getStyleSets: (palette) => NodeStyleSets;
|
||||||
};
|
};
|
||||||
edge?: {
|
edge?: {
|
||||||
dataTypeField?: string;
|
dataTypeField?: string;
|
||||||
palette: string[] | { [dataType: string]: string };
|
palette: string[] | { [dataType: string]: string };
|
||||||
getStyleSets: (palette) => ItemStyleSets;
|
getStyleSets: (palette) => EdgeStyleSets;
|
||||||
};
|
};
|
||||||
combo?: {
|
combo?: {
|
||||||
dataTypeField?: string;
|
dataTypeField?: string;
|
||||||
palette: string[] | { [dataType: string]: string };
|
palette: string[] | { [dataType: string]: string };
|
||||||
getStyleSets: (palette) => ItemStyleSets;
|
getStyleSets: (palette) => NodeStyleSets;
|
||||||
};
|
};
|
||||||
canvas?: {
|
canvas?: {
|
||||||
[cssName: string]: unknown;
|
[cssName: string]: unknown;
|
||||||
@ -40,14 +45,28 @@ export default class SpecThemeSolver extends BaseThemeSolver {
|
|||||||
if (specification) {
|
if (specification) {
|
||||||
['node', 'edge', 'combo'].forEach((itemType) => {
|
['node', 'edge', 'combo'].forEach((itemType) => {
|
||||||
if (!specification[itemType]) return;
|
if (!specification[itemType]) return;
|
||||||
const { palette, dataTypeField, getStyleSets } =
|
let {
|
||||||
specification[itemType];
|
palette = mergedSpec[itemType].palette,
|
||||||
|
dataTypeField,
|
||||||
|
getStyleSets,
|
||||||
|
} = specification[itemType];
|
||||||
|
|
||||||
|
if (dataTypeField && !getStyleSets) {
|
||||||
|
getStyleSets = (paletteProps) => {
|
||||||
|
return paletteProps.map((color) => ({
|
||||||
|
default: {
|
||||||
|
keyShape:
|
||||||
|
itemType === 'edge' ? { stroke: color } : { fill: color },
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// merge the custom part spec and the built-in spec
|
// merge the custom part spec and the built-in spec
|
||||||
const {
|
const {
|
||||||
styles: [baseStyles],
|
styles: [baseStyles],
|
||||||
} = baseSpec[itemType];
|
} = baseSpec[itemType];
|
||||||
const incomingStyles = getStyleSets(palette);
|
const incomingStyles = getStyleSets?.(palette) || {};
|
||||||
let mergedStyles;
|
let mergedStyles;
|
||||||
if (isArray(incomingStyles)) {
|
if (isArray(incomingStyles)) {
|
||||||
mergedStyles = incomingStyles.map((incomingStyle) => {
|
mergedStyles = incomingStyles.map((incomingStyle) => {
|
||||||
|
@ -2,14 +2,15 @@ import { Node as GNode, PlainObject } from '@antv/graphlib';
|
|||||||
import { AnimateAttr } from './animate';
|
import { AnimateAttr } from './animate';
|
||||||
import { Padding } from './common';
|
import { Padding } from './common';
|
||||||
import {
|
import {
|
||||||
|
BadgePosition,
|
||||||
Encode,
|
Encode,
|
||||||
IItem,
|
IItem,
|
||||||
|
ItemShapeStyles,
|
||||||
LabelBackground,
|
LabelBackground,
|
||||||
ShapeAttrEncode,
|
ShapeAttrEncode,
|
||||||
ShapesEncode,
|
ShapesEncode,
|
||||||
ShapeStyle,
|
ShapeStyle,
|
||||||
} from './item';
|
} from './item';
|
||||||
import { AnchorPoint } from './node';
|
|
||||||
|
|
||||||
export type ComboLabelPosition =
|
export type ComboLabelPosition =
|
||||||
| 'bottom'
|
| 'bottom'
|
||||||
@ -32,28 +33,56 @@ export interface ComboUserModelData extends PlainObject {
|
|||||||
export interface ComboModelData extends ComboUserModelData {
|
export interface ComboModelData extends ComboUserModelData {
|
||||||
visible?: boolean;
|
visible?: boolean;
|
||||||
label?: string;
|
label?: string;
|
||||||
}
|
anchorPoints?: number[][];
|
||||||
|
|
||||||
export interface ComboLabelShapeStyle extends ShapeStyle {
|
|
||||||
position?: ComboLabelPosition;
|
|
||||||
offsetX?: number;
|
|
||||||
offsetY?: number;
|
|
||||||
background?: LabelBackground;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Displayed data, only for drawing and not received by users. */
|
|
||||||
export interface ComboDisplayModelData extends ComboModelData {
|
|
||||||
keyShape?: ShapeStyle;
|
|
||||||
labelShape?: ComboLabelShapeStyle;
|
|
||||||
iconShape?: ShapeStyle;
|
|
||||||
otherShapes?: {
|
|
||||||
[shapeId: string]: ShapeStyle;
|
|
||||||
};
|
|
||||||
anchorPoints?: AnchorPoint[];
|
|
||||||
fixSize?: number | number[];
|
fixSize?: number | number[];
|
||||||
padding?: Padding;
|
padding?: Padding;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ComboShapeStyles extends ItemShapeStyles {
|
||||||
|
keyShape?: ShapeStyle & {
|
||||||
|
padding?: number | number[];
|
||||||
|
};
|
||||||
|
labelShape?: ShapeStyle & {
|
||||||
|
position?: ComboLabelPosition;
|
||||||
|
offsetX?: number;
|
||||||
|
offsetY?: number;
|
||||||
|
};
|
||||||
|
labelBackgroundShape?: ShapeStyle & {
|
||||||
|
padding?: number | number[];
|
||||||
|
};
|
||||||
|
// common badge styles
|
||||||
|
badgeShapes?: ShapeStyle & {
|
||||||
|
color?: string;
|
||||||
|
textColor?: string;
|
||||||
|
// individual styles and their position
|
||||||
|
[key: number]: ShapeStyle & {
|
||||||
|
position?: BadgePosition;
|
||||||
|
color?: string;
|
||||||
|
textColor?: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
// common badge styles
|
||||||
|
anchorShapes?: ShapeStyle & {
|
||||||
|
color?: string;
|
||||||
|
textColor?: string;
|
||||||
|
size?: number;
|
||||||
|
offsetX?: number;
|
||||||
|
offsetY?: number;
|
||||||
|
// individual styles and their position
|
||||||
|
[key: number]: ShapeStyle & {
|
||||||
|
position?: BadgePosition;
|
||||||
|
color?: string;
|
||||||
|
textColor?: string;
|
||||||
|
size?: number;
|
||||||
|
offsetX?: number;
|
||||||
|
offsetY?: number;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Displayed data, only for drawing and not received by users. */
|
||||||
|
export type ComboDisplayModelData = ComboModelData & ComboShapeStyles;
|
||||||
|
|
||||||
/** User input model. */
|
/** User input model. */
|
||||||
export type ComboUserModel = GNode<ComboUserModelData>;
|
export type ComboUserModel = GNode<ComboUserModelData>;
|
||||||
|
|
||||||
@ -72,7 +101,7 @@ interface ComboLabelShapeAttrEncode extends ShapeAttrEncode {
|
|||||||
}
|
}
|
||||||
export interface ComboShapesEncode extends ShapesEncode {
|
export interface ComboShapesEncode extends ShapesEncode {
|
||||||
labelShape?: ComboLabelShapeAttrEncode;
|
labelShape?: ComboLabelShapeAttrEncode;
|
||||||
anchorPoints?: AnchorPoint[] | Encode<AnchorPoint[]>;
|
anchorPoints?: number[][] | Encode<number[][]>;
|
||||||
fixSize?: number | number[] | Encode<number | number[]>;
|
fixSize?: number | number[] | Encode<number | number[]>;
|
||||||
padding?: Padding | Encode<Padding>;
|
padding?: Padding | Encode<Padding>;
|
||||||
}
|
}
|
||||||
|
@ -1,38 +1,77 @@
|
|||||||
import { DisplayObject } from '@antv/g';
|
import { DisplayObject } from '@antv/g';
|
||||||
import { Edge as GEdge, PlainObject } from '@antv/graphlib';
|
import { Edge as GEdge, PlainObject } from '@antv/graphlib';
|
||||||
import { AnimateAttr } from './animate';
|
|
||||||
import {
|
import {
|
||||||
|
BadgePosition,
|
||||||
Encode,
|
Encode,
|
||||||
IItem,
|
IItem,
|
||||||
|
ItemShapeStyles,
|
||||||
LabelBackground,
|
LabelBackground,
|
||||||
ShapeAttrEncode,
|
ShapeAttrEncode,
|
||||||
ShapesEncode,
|
ShapesEncode,
|
||||||
ShapeStyle,
|
ShapeStyle,
|
||||||
} from './item';
|
} from './item';
|
||||||
|
|
||||||
export type EdgeUserModelData = PlainObject;
|
export interface EdgeUserModelData extends PlainObject {
|
||||||
export interface EdgeModelData extends EdgeUserModelData {
|
/**
|
||||||
visible?: boolean;
|
* Anchor index to link to the source / target node.
|
||||||
label?: string;
|
*/
|
||||||
}
|
|
||||||
|
|
||||||
export interface EdgeLabelShapeStyle extends ShapeStyle {
|
|
||||||
position?: EdgeLabelPosition;
|
|
||||||
offsetX?: number;
|
|
||||||
offsetY?: number;
|
|
||||||
background?: LabelBackground;
|
|
||||||
autoRotate?: boolean;
|
|
||||||
}
|
|
||||||
export interface EdgeDisplayModelData extends EdgeModelData {
|
|
||||||
keyShape?: ShapeStyle;
|
|
||||||
labelShape?: EdgeLabelShapeStyle;
|
|
||||||
iconShape?: ShapeStyle;
|
|
||||||
otherShapes?: {
|
|
||||||
[shapeId: string]: ShapeStyle;
|
|
||||||
};
|
|
||||||
sourceAnchor?: number;
|
sourceAnchor?: number;
|
||||||
targetAnchor?: number;
|
targetAnchor?: number;
|
||||||
|
/**
|
||||||
|
* Edge type, e.g. 'line'.
|
||||||
|
*/
|
||||||
|
type?: string;
|
||||||
|
/**
|
||||||
|
* Subject color for the keyShape and arrows.
|
||||||
|
* More styles should be configured in edge mapper.
|
||||||
|
*/
|
||||||
|
color?: string;
|
||||||
|
/**
|
||||||
|
* The text to show on the edge.
|
||||||
|
* More styles should be configured in edge mapper.
|
||||||
|
*/
|
||||||
|
label?: string;
|
||||||
|
/**
|
||||||
|
* Whether show the edge by default.
|
||||||
|
*/
|
||||||
|
visible?: boolean;
|
||||||
|
/**
|
||||||
|
* The icon to show on the edge.
|
||||||
|
* More styles should be configured in edge mapper.
|
||||||
|
*/
|
||||||
|
icon?: {
|
||||||
|
type: 'icon' | 'text';
|
||||||
|
text?: string;
|
||||||
|
img?: string;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* The badges to show on the edge.
|
||||||
|
* More styles should be configured in edge mapper.
|
||||||
|
*/
|
||||||
|
badge?: {
|
||||||
|
type: 'icon' | 'text';
|
||||||
|
text: string;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
export interface EdgeModelData extends EdgeUserModelData {}
|
||||||
|
|
||||||
|
export interface EdgeShapeStyles extends ItemShapeStyles {
|
||||||
|
labelShape?: ShapeStyle & {
|
||||||
|
position?: 'start' | 'middle' | 'end';
|
||||||
|
offsetX?: number;
|
||||||
|
offsetY?: number;
|
||||||
|
autoRotate?: boolean;
|
||||||
|
};
|
||||||
|
labelBackgroundShape?: ShapeStyle & {
|
||||||
|
padding?: number | number[];
|
||||||
|
};
|
||||||
|
badgeShape?: ShapeStyle & {
|
||||||
|
color?: string;
|
||||||
|
textColor?: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export type EdgeDisplayModelData = EdgeModelData & EdgeShapeStyles;
|
||||||
|
|
||||||
/** User input data. */
|
/** User input data. */
|
||||||
export type EdgeUserModel = GEdge<EdgeUserModelData>;
|
export type EdgeUserModel = GEdge<EdgeUserModelData>;
|
||||||
@ -54,8 +93,8 @@ interface EdgeLabelShapeAttrEncode extends ShapeAttrEncode {
|
|||||||
|
|
||||||
export interface EdgeShapesEncode extends ShapesEncode {
|
export interface EdgeShapesEncode extends ShapesEncode {
|
||||||
labelShape?: EdgeLabelShapeAttrEncode;
|
labelShape?: EdgeLabelShapeAttrEncode;
|
||||||
sourceAnchor?: number;
|
labelBackgroundShape?: LabelBackground | Encode<LabelBackground>;
|
||||||
targetAnchor?: number;
|
badgeShape?: ShapeAttrEncode | Encode<ShapeStyle>;
|
||||||
}
|
}
|
||||||
export interface EdgeEncode extends EdgeShapesEncode {
|
export interface EdgeEncode extends EdgeShapesEncode {
|
||||||
type?: string | Encode<string>;
|
type?: string | Encode<string>;
|
||||||
|
@ -40,16 +40,6 @@ import {
|
|||||||
TorusGeometryProps,
|
TorusGeometryProps,
|
||||||
} from '@antv/g-plugin-3d';
|
} from '@antv/g-plugin-3d';
|
||||||
|
|
||||||
export interface ShapeStyle {
|
|
||||||
[shapeAttr: string]: unknown;
|
|
||||||
animate?: AnimateAttr;
|
|
||||||
x?: number;
|
|
||||||
y?: number;
|
|
||||||
width?: number;
|
|
||||||
height?: number;
|
|
||||||
r?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type GShapeStyle = CircleStyleProps &
|
export type GShapeStyle = CircleStyleProps &
|
||||||
RectStyleProps &
|
RectStyleProps &
|
||||||
EllipseStyleProps &
|
EllipseStyleProps &
|
||||||
@ -63,6 +53,11 @@ export type GShapeStyle = CircleStyleProps &
|
|||||||
CubeGeometryProps &
|
CubeGeometryProps &
|
||||||
PlaneGeometryProps;
|
PlaneGeometryProps;
|
||||||
|
|
||||||
|
export type ShapeStyle = Partial<
|
||||||
|
GShapeStyle & {
|
||||||
|
animate?: AnimateAttr;
|
||||||
|
}
|
||||||
|
>;
|
||||||
export interface Encode<T> {
|
export interface Encode<T> {
|
||||||
fields: string[];
|
fields: string[];
|
||||||
formatter: (values: NodeUserModel | EdgeUserModel | ComboUserModel) => T;
|
formatter: (values: NodeUserModel | EdgeUserModel | ComboUserModel) => T;
|
||||||
@ -82,8 +77,8 @@ export interface LabelBackground {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface ShapesEncode {
|
export interface ShapesEncode {
|
||||||
keyShape?: ShapeAttrEncode;
|
keyShape?: ShapeAttrEncode | Encode<ShapeStyle>;
|
||||||
iconShape?: ShapeAttrEncode;
|
iconShape?: ShapeAttrEncode | Encode<ShapeStyle>;
|
||||||
otherShapes?: {
|
otherShapes?: {
|
||||||
[shapeId: string]: {
|
[shapeId: string]: {
|
||||||
[shapeAtrr: string]: unknown | Encode<unknown>;
|
[shapeAtrr: string]: unknown | Encode<unknown>;
|
||||||
@ -126,12 +121,35 @@ export type State = {
|
|||||||
value: boolean | string;
|
value: boolean | string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export enum BadgePosition {
|
||||||
|
rightTop = 'rightTop',
|
||||||
|
right = 'right',
|
||||||
|
rightBottom = 'rightBottom',
|
||||||
|
bottomRight = 'bottomRight',
|
||||||
|
bottom = 'bottom',
|
||||||
|
bottomLeft = 'bottomLeft',
|
||||||
|
leftBottom = 'leftBottom',
|
||||||
|
left = 'left',
|
||||||
|
leftTop = 'leftTop',
|
||||||
|
topLeft = 'topLeft',
|
||||||
|
top = 'top',
|
||||||
|
topRight = 'topRight',
|
||||||
|
}
|
||||||
|
export type IBadgePosition = `${BadgePosition}`;
|
||||||
|
|
||||||
/** Shape styles for an item. */
|
/** Shape styles for an item. */
|
||||||
export type ItemShapeStyles = {
|
export type ItemShapeStyles = {
|
||||||
|
// labelShape, labelBackgroundShape, badgeShapes, overwrote by node / edge / combo
|
||||||
|
// anchorShapes, overwrote by node / combo
|
||||||
keyShape?: ShapeStyle;
|
keyShape?: ShapeStyle;
|
||||||
labelShape?: ShapeStyle;
|
iconShape?: Partial<
|
||||||
iconShape?: ShapeStyle;
|
TextStyleProps &
|
||||||
[shapeId: string]: ShapeStyle;
|
ImageStyleProps & {
|
||||||
|
offsetX?: number;
|
||||||
|
offsetY?: number;
|
||||||
|
}
|
||||||
|
>;
|
||||||
|
haloShape?: ShapeStyle;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
import { DisplayObject } from '@antv/g';
|
import { DisplayObject, Point } from '@antv/g';
|
||||||
import { Node as GNode, PlainObject } from '@antv/graphlib';
|
import { Node as GNode, PlainObject } from '@antv/graphlib';
|
||||||
import { AnimateAttr } from './animate';
|
import { AnimateAttr } from './animate';
|
||||||
import {
|
import {
|
||||||
|
BadgePosition,
|
||||||
Encode,
|
Encode,
|
||||||
IItem,
|
IItem,
|
||||||
|
ItemShapeStyles,
|
||||||
LabelBackground,
|
LabelBackground,
|
||||||
ShapeAttrEncode,
|
ShapeAttrEncode,
|
||||||
ShapesEncode,
|
ShapesEncode,
|
||||||
@ -14,32 +16,104 @@ export type NodeLabelPosition = 'bottom' | 'center' | 'top' | 'left' | 'right';
|
|||||||
|
|
||||||
/** Data in user input model. */
|
/** Data in user input model. */
|
||||||
export interface NodeUserModelData extends PlainObject {
|
export interface NodeUserModelData extends PlainObject {
|
||||||
parentId?: string;
|
/**
|
||||||
}
|
* Node position.
|
||||||
|
*/
|
||||||
/** Data in inner model. */
|
x?: number;
|
||||||
export interface NodeModelData extends NodeUserModelData {
|
y?: number;
|
||||||
visible?: boolean;
|
z?: number;
|
||||||
|
/**
|
||||||
|
* Node type, e.g. 'circle'.
|
||||||
|
*/
|
||||||
|
type?: string;
|
||||||
|
/**
|
||||||
|
* Subject color for the keyShape and anchor points.
|
||||||
|
* More styles should be configured in node mapper.
|
||||||
|
*/
|
||||||
|
color?: string;
|
||||||
|
/**
|
||||||
|
* The text to show on the node.
|
||||||
|
* More styles should be configured in node mapper.
|
||||||
|
*/
|
||||||
label?: string;
|
label?: string;
|
||||||
|
/**
|
||||||
|
* Whether show the node by default.
|
||||||
|
*/
|
||||||
|
visible?: boolean;
|
||||||
|
/**
|
||||||
|
* Reserved for combo.
|
||||||
|
*/
|
||||||
|
parentId?: string;
|
||||||
|
/**
|
||||||
|
* The icon to show on the node.
|
||||||
|
* More styles should be configured in node mapper.
|
||||||
|
*/
|
||||||
|
icon?: {
|
||||||
|
type: 'icon' | 'text';
|
||||||
|
text?: string;
|
||||||
|
img?: string;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* The ratio position of the keyShape for related edges linking into.
|
||||||
|
* More styles should be configured in node mapper.
|
||||||
|
*/
|
||||||
|
anchorPoints?: number[][];
|
||||||
|
/**
|
||||||
|
* The badges to show on the node.
|
||||||
|
* More styles should be configured in node mapper.
|
||||||
|
*/
|
||||||
|
badges?: {
|
||||||
|
type: 'icon' | 'text';
|
||||||
|
text: string;
|
||||||
|
position: BadgePosition;
|
||||||
|
}[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NodeLabelShapeStyle extends ShapeStyle {
|
/** Data in inner model. Same format to the user data. */
|
||||||
position?: NodeLabelPosition;
|
export interface NodeModelData extends NodeUserModelData {}
|
||||||
|
|
||||||
|
export interface NodeShapeStyles extends ItemShapeStyles {
|
||||||
|
// keyShape, iconShape, haloShape are defined in ItemShapeStyles
|
||||||
|
labelShape?: ShapeStyle & {
|
||||||
|
position?: 'top' | 'bottom' | 'left' | 'right' | 'center';
|
||||||
offsetX?: number;
|
offsetX?: number;
|
||||||
offsetY?: number;
|
offsetY?: number;
|
||||||
background?: LabelBackground;
|
};
|
||||||
|
labelBackgroundShape?: ShapeStyle & {
|
||||||
|
padding?: number | number[];
|
||||||
|
};
|
||||||
|
badgeShapes?: ShapeStyle & {
|
||||||
|
// common badge styles
|
||||||
|
color?: string;
|
||||||
|
textColor?: string;
|
||||||
|
// individual styles and their position
|
||||||
|
[key: number]: ShapeStyle & {
|
||||||
|
position?: BadgePosition;
|
||||||
|
color?: string;
|
||||||
|
textColor?: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
anchorShapes?: ShapeStyle & {
|
||||||
|
// common badge styles
|
||||||
|
color?: string;
|
||||||
|
textColor?: string;
|
||||||
|
size?: number;
|
||||||
|
offsetX?: number;
|
||||||
|
offsetY?: number;
|
||||||
|
// individual styles and their position
|
||||||
|
[key: number]: ShapeStyle & {
|
||||||
|
position?: BadgePosition;
|
||||||
|
color?: string;
|
||||||
|
textColor?: string;
|
||||||
|
size?: number;
|
||||||
|
offsetX?: number;
|
||||||
|
offsetY?: number;
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Data in display model. */
|
/** Data in display model. */
|
||||||
export interface NodeDisplayModelData extends NodeModelData {
|
export type NodeDisplayModelData = NodeModelData & NodeShapeStyles;
|
||||||
keyShape?: ShapeStyle;
|
|
||||||
labelShape?: NodeLabelShapeStyle;
|
|
||||||
iconShape?: ShapeStyle;
|
|
||||||
otherShapes?: {
|
|
||||||
[shapeId: string]: ShapeStyle;
|
|
||||||
};
|
|
||||||
anchorPoints?: AnchorPoint[];
|
|
||||||
}
|
|
||||||
|
|
||||||
/** User input model. */
|
/** User input model. */
|
||||||
export type NodeUserModel = GNode<NodeUserModelData>;
|
export type NodeUserModel = GNode<NodeUserModelData>;
|
||||||
@ -50,24 +124,17 @@ export type NodeModel = GNode<NodeModelData>;
|
|||||||
/** Displayed model, only for drawing and not received by users. */
|
/** Displayed model, only for drawing and not received by users. */
|
||||||
export type NodeDisplayModel = GNode<NodeDisplayModelData>;
|
export type NodeDisplayModel = GNode<NodeDisplayModelData>;
|
||||||
|
|
||||||
/** Anchor points, for linking edges and drawing circles. */
|
|
||||||
export interface AnchorPoint {
|
|
||||||
position?: [number, number]; // range from 0 to 1
|
|
||||||
show?: boolean;
|
|
||||||
[shapeAttr: string]: unknown;
|
|
||||||
animate: AnimateAttr;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface NodeLabelShapeAttrEncode extends ShapeAttrEncode {
|
interface NodeLabelShapeAttrEncode extends ShapeAttrEncode {
|
||||||
// TODO: extends Text shape attr, import from G
|
// TODO: extends Text shape attr, import from G
|
||||||
position?: NodeLabelPosition | Encode<NodeLabelPosition>;
|
position?: NodeLabelPosition | Encode<NodeLabelPosition>;
|
||||||
offsetX?: number | Encode<number>;
|
offsetX?: number | Encode<number>;
|
||||||
offsetY?: number | Encode<number>;
|
offsetY?: number | Encode<number>;
|
||||||
background?: LabelBackground | Encode<LabelBackground>;
|
|
||||||
}
|
}
|
||||||
export interface NodeShapesEncode extends ShapesEncode {
|
export interface NodeShapesEncode extends ShapesEncode {
|
||||||
labelShape?: NodeLabelShapeAttrEncode;
|
labelShape?: NodeLabelShapeAttrEncode | Encode<ShapeStyle>;
|
||||||
anchorPoints?: AnchorPoint[] | Encode<AnchorPoint[]>;
|
labelBackgroundShape?: ShapeAttrEncode[] | Encode<ShapeStyle[]>;
|
||||||
|
anchorShapes?: ShapeAttrEncode[] | Encode<ShapeStyle[]>;
|
||||||
|
badgeShapes?: ShapeAttrEncode[] | Encode<ShapeStyle[]>;
|
||||||
}
|
}
|
||||||
export interface NodeEncode extends NodeShapesEncode {
|
export interface NodeEncode extends NodeShapesEncode {
|
||||||
type?: string | Encode<string>;
|
type?: string | Encode<string>;
|
||||||
@ -77,6 +144,8 @@ export interface NodeShapeMap {
|
|||||||
keyShape: DisplayObject;
|
keyShape: DisplayObject;
|
||||||
labelShape?: DisplayObject;
|
labelShape?: DisplayObject;
|
||||||
iconShape?: DisplayObject;
|
iconShape?: DisplayObject;
|
||||||
|
|
||||||
|
// TODO other badge shapes
|
||||||
[otherShapeId: string]: DisplayObject;
|
[otherShapeId: string]: DisplayObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
import { ItemShapeStyles, ShapeStyle } from './item';
|
import { ComboShapeStyles } from './combo';
|
||||||
|
import { EdgeShapeStyles } from './edge';
|
||||||
|
import { NodeShapeStyles } from './node';
|
||||||
|
|
||||||
export interface ThemeOption {}
|
export interface ThemeOption {}
|
||||||
/**
|
/**
|
||||||
@ -41,33 +43,53 @@ export type ThemeObjectOptionsOf<T extends ThemeRegistry = {}> = {
|
|||||||
: never;
|
: never;
|
||||||
}[Extract<keyof T, string>];
|
}[Extract<keyof T, string>];
|
||||||
|
|
||||||
export type ItemStyleSets =
|
|
||||||
| ItemStyleSet[]
|
|
||||||
| { [dataTypeValue: string]: ItemStyleSet };
|
|
||||||
|
|
||||||
/** Default and stateStyle for an item */
|
/** Default and stateStyle for an item */
|
||||||
export type ItemStyleSet = {
|
export type NodeStyleSet = {
|
||||||
default?: ItemShapeStyles;
|
default?: NodeShapeStyles;
|
||||||
[stateName: string]: ItemShapeStyles;
|
seledted?: NodeShapeStyles;
|
||||||
|
[stateName: string]: NodeShapeStyles;
|
||||||
|
};
|
||||||
|
export type EdgeStyleSet = {
|
||||||
|
default?: EdgeShapeStyles;
|
||||||
|
[stateName: string]: EdgeShapeStyles;
|
||||||
|
};
|
||||||
|
export type ComboStyleSet = {
|
||||||
|
default?: ComboShapeStyles;
|
||||||
|
[stateName: string]: ComboShapeStyles;
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface ItemThemeSpecifications {
|
export type NodeStyleSets =
|
||||||
|
| NodeStyleSet[]
|
||||||
|
| { [dataTypeValue: string]: NodeStyleSet };
|
||||||
|
export type EdgeStyleSets =
|
||||||
|
| EdgeStyleSet[]
|
||||||
|
| { [dataTypeValue: string]: EdgeStyleSet };
|
||||||
|
export type ComboStyleSets =
|
||||||
|
| ComboStyleSet[]
|
||||||
|
| { [dataTypeValue: string]: ComboStyleSet };
|
||||||
|
|
||||||
|
export interface NodeThemeSpecifications {
|
||||||
dataTypeField?: string;
|
dataTypeField?: string;
|
||||||
palette?: string[] | { [dataTypeValue: string]: string };
|
palette?: string[] | { [dataTypeValue: string]: string };
|
||||||
styles?:
|
styles?: NodeStyleSets;
|
||||||
| ItemStyleSet[]
|
}
|
||||||
| {
|
export interface EdgeThemeSpecifications {
|
||||||
[dataTypeValue: string]: ItemStyleSet;
|
dataTypeField?: string;
|
||||||
};
|
palette?: string[] | { [dataTypeValue: string]: string };
|
||||||
|
styles?: EdgeStyleSets;
|
||||||
|
}
|
||||||
|
export interface ComboThemeSpecifications {
|
||||||
|
dataTypeField?: string;
|
||||||
|
palette?: string[] | { [dataTypeValue: string]: string };
|
||||||
|
styles?: ComboStyleSets;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Theme specification
|
* Theme specification
|
||||||
*/
|
*/
|
||||||
export interface ThemeSpecification {
|
export interface ThemeSpecification {
|
||||||
node?: ItemThemeSpecifications;
|
node?: NodeThemeSpecifications;
|
||||||
edge?: ItemThemeSpecifications;
|
edge?: EdgeThemeSpecifications;
|
||||||
combo?: ItemThemeSpecifications;
|
combo?: ComboThemeSpecifications;
|
||||||
canvas?: {
|
canvas?: {
|
||||||
[cssName: string]: unknown;
|
[cssName: string]: unknown;
|
||||||
};
|
};
|
||||||
|
61
packages/g6/src/util/mapper.ts
Normal file
61
packages/g6/src/util/mapper.ts
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
import { NodeDisplayModelData } from 'types/node';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default mapper to transform simple styles in inner data.
|
||||||
|
*/
|
||||||
|
export const DEFAULT_MAPPER = {
|
||||||
|
node: (innerNodeModel) => {
|
||||||
|
const { id, data } = innerNodeModel;
|
||||||
|
const { color, label, icon, badges, anchorPoints } = data;
|
||||||
|
const resultData: NodeDisplayModelData = { ...data, keyShape: {} };
|
||||||
|
if (color) {
|
||||||
|
resultData.keyShape.fill = color;
|
||||||
|
}
|
||||||
|
if (label) {
|
||||||
|
resultData.labelShape = {
|
||||||
|
text: label,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (icon) {
|
||||||
|
resultData.iconShape = icon;
|
||||||
|
}
|
||||||
|
if (badges) {
|
||||||
|
resultData.badgeShapes = badges;
|
||||||
|
}
|
||||||
|
if (anchorPoints) {
|
||||||
|
resultData.anchorShapes = anchorPoints.map((point) => ({
|
||||||
|
position: point,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
data: resultData,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
edge: (innerEdgeModel) => {
|
||||||
|
const { id, source, target, data } = innerEdgeModel;
|
||||||
|
const { color, label, icon, badge } = data;
|
||||||
|
const resultData: NodeDisplayModelData = { ...data, keyShape: {} };
|
||||||
|
if (color) {
|
||||||
|
resultData.keyShape.stroke = color;
|
||||||
|
}
|
||||||
|
if (label) {
|
||||||
|
resultData.labelShape = {
|
||||||
|
text: label,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (icon) {
|
||||||
|
resultData.iconShape = icon;
|
||||||
|
}
|
||||||
|
if (badge) {
|
||||||
|
resultData.badgeShape = badge;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
source,
|
||||||
|
target,
|
||||||
|
data: resultData,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
combo: (innerComboModel) => innerComboModel,
|
||||||
|
};
|
9
packages/g6/src/util/math.ts
Normal file
9
packages/g6/src/util/math.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
/**
|
||||||
|
* Whether the value is begween the range of [min, max]
|
||||||
|
* @param {number} value the value to be judged
|
||||||
|
* @param {number} min the min of the range
|
||||||
|
* @param {number} max the max of the range
|
||||||
|
* @return {boolean} bool the result boolean
|
||||||
|
*/
|
||||||
|
export const isBetween = (value: number, min: number, max: number) =>
|
||||||
|
value >= min && value <= max;
|
195
packages/g6/src/util/point.ts
Normal file
195
packages/g6/src/util/point.ts
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
import { Point } from '../types/common';
|
||||||
|
import { isBetween } from './math';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the nearest on in points to the curPoint.
|
||||||
|
* @param points
|
||||||
|
* @param curPoint
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const getNearestPoint = (
|
||||||
|
points: Point[],
|
||||||
|
curPoint: Point,
|
||||||
|
): {
|
||||||
|
index: number;
|
||||||
|
nearestPoint: Point;
|
||||||
|
} => {
|
||||||
|
let index = 0;
|
||||||
|
let nearestPoint = points[0];
|
||||||
|
let minDistance = distance(points[0], curPoint);
|
||||||
|
for (let i = 0; i < points.length; i++) {
|
||||||
|
const point = points[i];
|
||||||
|
const dis = distance(point, curPoint);
|
||||||
|
if (dis < minDistance) {
|
||||||
|
nearestPoint = point;
|
||||||
|
minDistance = dis;
|
||||||
|
index = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
index,
|
||||||
|
nearestPoint,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get distance by two points.
|
||||||
|
* @param p1 first point
|
||||||
|
* @param p2 second point
|
||||||
|
*/
|
||||||
|
export const distance = (p1: Point, p2: Point): number => {
|
||||||
|
const vx = p1.x - p2.x;
|
||||||
|
const vy = p1.y - p2.y;
|
||||||
|
return Math.sqrt(vx * vx + vy * vy);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get point and circle intersect point.
|
||||||
|
* @param {ICircle} circle Circle's center x,y and radius r
|
||||||
|
* @param {Point} point Point x,y
|
||||||
|
* @return {Point} calculated intersect point
|
||||||
|
*/
|
||||||
|
export const getCircleIntersectByPoint = (
|
||||||
|
circleProps: { x: number; y: number; r: number },
|
||||||
|
point: Point,
|
||||||
|
): Point | null => {
|
||||||
|
const { x: cx, y: cy, r } = circleProps;
|
||||||
|
const { x, y } = point;
|
||||||
|
|
||||||
|
const dx = x - cx;
|
||||||
|
const dy = y - cy;
|
||||||
|
if (dx * dx + dy * dy < r * r) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const angle = Math.atan(dy / dx);
|
||||||
|
return {
|
||||||
|
x: cx + Math.abs(r * Math.cos(angle)) * Math.sign(dx),
|
||||||
|
y: cy + Math.abs(r * Math.sin(angle)) * Math.sign(dy),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get point and ellipse inIntersect.
|
||||||
|
* @param {Object} ellipse ellipse center x,y and radius rx,ry
|
||||||
|
* @param {Object} point Point x,y
|
||||||
|
* @return {object} calculated intersect point
|
||||||
|
*/
|
||||||
|
export const getEllipseIntersectByPoint = (
|
||||||
|
ellipseProps: {
|
||||||
|
rx: number;
|
||||||
|
ry: number;
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
},
|
||||||
|
point: Point,
|
||||||
|
): Point => {
|
||||||
|
const { rx: a, ry: b, x: cx, y: cy } = ellipseProps;
|
||||||
|
|
||||||
|
const dx = point.x - cx;
|
||||||
|
const dy = point.y - cy;
|
||||||
|
// The angle will be in range [-PI, PI]
|
||||||
|
let angle = Math.atan2(dy / b, dx / a);
|
||||||
|
|
||||||
|
if (angle < 0) {
|
||||||
|
// transfer to [0, 2*PI]
|
||||||
|
angle += 2 * Math.PI;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
x: cx + a * Math.cos(angle),
|
||||||
|
y: cy + b * Math.sin(angle),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Point and rectangular intersection point.
|
||||||
|
* @param {IRect} rect rect
|
||||||
|
* @param {Point} point point
|
||||||
|
* @return {PointPoint} rst;
|
||||||
|
*/
|
||||||
|
export const getRectIntersectByPoint = (
|
||||||
|
rectProps: { x: number; y: number; width: number; height: number },
|
||||||
|
point: Point,
|
||||||
|
): Point | null => {
|
||||||
|
const { x, y, width, height } = rectProps;
|
||||||
|
const cx = x + width / 2;
|
||||||
|
const cy = y + height / 2;
|
||||||
|
const points: Point[] = [];
|
||||||
|
const center: Point = {
|
||||||
|
x: cx,
|
||||||
|
y: cy,
|
||||||
|
};
|
||||||
|
points.push({
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
});
|
||||||
|
points.push({
|
||||||
|
x: x + width,
|
||||||
|
y,
|
||||||
|
});
|
||||||
|
points.push({
|
||||||
|
x: x + width,
|
||||||
|
y: y + height,
|
||||||
|
});
|
||||||
|
points.push({
|
||||||
|
x,
|
||||||
|
y: y + height,
|
||||||
|
});
|
||||||
|
points.push({
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
});
|
||||||
|
let rst: Point | null = null;
|
||||||
|
for (let i = 1; i < points.length; i++) {
|
||||||
|
rst = getLineIntersect(points[i - 1], points[i], center, point);
|
||||||
|
if (rst) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rst;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the intersect point of two lines.
|
||||||
|
* @param {Point} p0 The start point of the first line.
|
||||||
|
* @param {Point} p1 The end point of the first line.
|
||||||
|
* @param {Point} p2 The start point of the second line.
|
||||||
|
* @param {Point} p3 The end point of the second line.
|
||||||
|
* @return {Point} Calculated intersect point.
|
||||||
|
*/
|
||||||
|
export const getLineIntersect = (
|
||||||
|
p0: Point,
|
||||||
|
p1: Point,
|
||||||
|
p2: Point,
|
||||||
|
p3: Point,
|
||||||
|
): Point | null => {
|
||||||
|
const tolerance = 0.0001;
|
||||||
|
|
||||||
|
const E: Point = {
|
||||||
|
x: p2.x - p0.x,
|
||||||
|
y: p2.y - p0.y,
|
||||||
|
};
|
||||||
|
const D0: Point = {
|
||||||
|
x: p1.x - p0.x,
|
||||||
|
y: p1.y - p0.y,
|
||||||
|
};
|
||||||
|
const D1: Point = {
|
||||||
|
x: p3.x - p2.x,
|
||||||
|
y: p3.y - p2.y,
|
||||||
|
};
|
||||||
|
const kross: number = D0.x * D1.y - D0.y * D1.x;
|
||||||
|
const sqrKross: number = kross * kross;
|
||||||
|
const invertKross: number = 1 / kross;
|
||||||
|
const sqrLen0: number = D0.x * D0.x + D0.y * D0.y;
|
||||||
|
const sqrLen1: number = D1.x * D1.x + D1.y * D1.y;
|
||||||
|
if (sqrKross > tolerance * sqrLen0 * sqrLen1) {
|
||||||
|
const s = (E.x * D1.y - E.y * D1.x) * invertKross;
|
||||||
|
const t = (E.x * D0.y - E.y * D0.x) * invertKross;
|
||||||
|
if (!isBetween(s, 0, 1) || !isBetween(t, 0, 1)) return null;
|
||||||
|
return {
|
||||||
|
x: p0.x + s * D0.x,
|
||||||
|
y: p0.y + s * D0.y,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
@ -17,8 +17,15 @@ import { clone, isArray, isNumber } from '@antv/util';
|
|||||||
import { DEFAULT_LABEL_BG_PADDING } from '../constant';
|
import { DEFAULT_LABEL_BG_PADDING } from '../constant';
|
||||||
import { Point } from '../types/common';
|
import { Point } from '../types/common';
|
||||||
import { EdgeShapeMap } from '../types/edge';
|
import { EdgeShapeMap } from '../types/edge';
|
||||||
import { GShapeStyle, SHAPE_TYPE, ItemShapeStyles } from '../types/item';
|
import {
|
||||||
|
GShapeStyle,
|
||||||
|
SHAPE_TYPE,
|
||||||
|
ItemShapeStyles,
|
||||||
|
ShapeStyle,
|
||||||
|
} from '../types/item';
|
||||||
import { NodeShapeMap } from '../types/node';
|
import { NodeShapeMap } from '../types/node';
|
||||||
|
import { isArrayOverlap } from './array';
|
||||||
|
import { isBetween } from './math';
|
||||||
|
|
||||||
export const ShapeTagMap = {
|
export const ShapeTagMap = {
|
||||||
circle: Circle,
|
circle: Circle,
|
||||||
@ -46,20 +53,30 @@ export const upsertShape = (
|
|||||||
id: string,
|
id: string,
|
||||||
style: GShapeStyle,
|
style: GShapeStyle,
|
||||||
shapeMap: { [shapeId: string]: DisplayObject },
|
shapeMap: { [shapeId: string]: DisplayObject },
|
||||||
): DisplayObject => {
|
): {
|
||||||
|
updateStyles: ShapeStyle;
|
||||||
|
shape: DisplayObject;
|
||||||
|
} => {
|
||||||
let shape = shapeMap[id];
|
let shape = shapeMap[id];
|
||||||
|
let updateStyles = {};
|
||||||
if (!shape) {
|
if (!shape) {
|
||||||
shape = createShape(type, style, id);
|
shape = createShape(type, style, id);
|
||||||
|
updateStyles = style;
|
||||||
} else if (shape.nodeName !== type) {
|
} else if (shape.nodeName !== type) {
|
||||||
shape.remove();
|
shape.remove();
|
||||||
shape = createShape(type, style, id);
|
shape = createShape(type, style, id);
|
||||||
|
updateStyles = style;
|
||||||
} else {
|
} else {
|
||||||
|
const oldStyles = shape.attributes;
|
||||||
Object.keys(style).forEach((key) => {
|
Object.keys(style).forEach((key) => {
|
||||||
|
if (oldStyles[key] !== style[key]) {
|
||||||
|
updateStyles[key] = style[key];
|
||||||
shape.style[key] = style[key];
|
shape.style[key] = style[key];
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
shapeMap[id] = shape;
|
shapeMap[id] = shape;
|
||||||
return shape;
|
return { shape, updateStyles };
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getGroupSucceedMap = (
|
export const getGroupSucceedMap = (
|
||||||
@ -136,6 +153,8 @@ export const formatPadding = (value, defaultArr = DEFAULT_LABEL_BG_PADDING) => {
|
|||||||
return [value[0], value[0], value[0], value[0]];
|
return [value[0], value[0], value[0], value[0]];
|
||||||
case 2:
|
case 2:
|
||||||
return value.concat(value);
|
return value.concat(value);
|
||||||
|
case 3:
|
||||||
|
return value.concat([value[0]]);
|
||||||
default:
|
default:
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
@ -167,8 +186,8 @@ const merge2Styles = (
|
|||||||
styleMap1: ItemShapeStyles,
|
styleMap1: ItemShapeStyles,
|
||||||
styleMap2: ItemShapeStyles,
|
styleMap2: ItemShapeStyles,
|
||||||
) => {
|
) => {
|
||||||
if (!styleMap1) return clone(styleMap2);
|
if (!styleMap1) return { ...styleMap2 };
|
||||||
else if (!styleMap2) return clone(styleMap1);
|
else if (!styleMap2) return { ...styleMap1 };
|
||||||
const mergedStyle = clone(styleMap1);
|
const mergedStyle = clone(styleMap1);
|
||||||
Object.keys(styleMap2).forEach((shapeId) => {
|
Object.keys(styleMap2).forEach((shapeId) => {
|
||||||
const style = styleMap2[shapeId];
|
const style = styleMap2[shapeId];
|
||||||
@ -404,12 +423,23 @@ export const getLineIntersect = (
|
|||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const FEILDS_AFFECT_BBOX = {
|
||||||
|
circle: ['r', 'lineWidth'],
|
||||||
|
rect: ['width', 'height', 'lineWidth'],
|
||||||
|
image: ['width', 'height', 'lineWidth'],
|
||||||
|
ellipse: ['rx', 'ry', 'lineWidth'],
|
||||||
|
text: ['fontSize', 'fontWeight'],
|
||||||
|
polygon: ['points', 'lineWidth'],
|
||||||
|
line: ['x1', 'x2', 'y1', 'y2', 'lineWidth'],
|
||||||
|
polyline: ['points', 'lineWidth'],
|
||||||
|
path: ['points', 'lineWidth'],
|
||||||
|
};
|
||||||
/**
|
/**
|
||||||
* Whether the value is begween the range of [min, max]
|
* Will the fields in style affect the bbox.
|
||||||
* @param {number} value the value to be judged
|
* @param type shape type
|
||||||
* @param {number} min the min of the range
|
* @param style style object
|
||||||
* @param {number} max the max of the range
|
* @returns
|
||||||
* @return {boolean} bool the result boolean
|
|
||||||
*/
|
*/
|
||||||
const isBetween = (value: number, min: number, max: number) =>
|
export const isStyleAffectBBox = (type: SHAPE_TYPE, style: ShapeStyle) => {
|
||||||
value >= min && value <= max;
|
return isArrayOverlap(Object.keys(style), FEILDS_AFFECT_BBOX[type]);
|
||||||
|
};
|
||||||
|
@ -10,7 +10,12 @@ import {
|
|||||||
} from '@antv/g-plugin-3d';
|
} from '@antv/g-plugin-3d';
|
||||||
import { EdgeShapeMap } from '../types/edge';
|
import { EdgeShapeMap } from '../types/edge';
|
||||||
import { NodeShapeMap } from '../types/node';
|
import { NodeShapeMap } from '../types/node';
|
||||||
import { GShapeStyle, SHAPE_TYPE, SHAPE_TYPE_3D } from '../types/item';
|
import {
|
||||||
|
GShapeStyle,
|
||||||
|
SHAPE_TYPE,
|
||||||
|
SHAPE_TYPE_3D,
|
||||||
|
ShapeStyle,
|
||||||
|
} from '../types/item';
|
||||||
import { ShapeTagMap, createShape } from './shape';
|
import { ShapeTagMap, createShape } from './shape';
|
||||||
|
|
||||||
const GeometryTagMap = {
|
const GeometryTagMap = {
|
||||||
@ -103,20 +108,30 @@ export const upsertShape3D = (
|
|||||||
style: GShapeStyle,
|
style: GShapeStyle,
|
||||||
shapeMap: { [shapeId: string]: DisplayObject },
|
shapeMap: { [shapeId: string]: DisplayObject },
|
||||||
device: any,
|
device: any,
|
||||||
): DisplayObject => {
|
): {
|
||||||
|
updateStyles: ShapeStyle;
|
||||||
|
shape: DisplayObject;
|
||||||
|
} => {
|
||||||
let shape = shapeMap[id];
|
let shape = shapeMap[id];
|
||||||
|
let updateStyles = {};
|
||||||
if (!shape) {
|
if (!shape) {
|
||||||
shape = createShape3D(type, style, id, device);
|
shape = createShape3D(type, style, id, device);
|
||||||
|
updateStyles = style;
|
||||||
} else if (shape.nodeName !== type) {
|
} else if (shape.nodeName !== type) {
|
||||||
shape.remove();
|
shape.remove();
|
||||||
shape = createShape3D(type, style, id, device);
|
shape = createShape3D(type, style, id, device);
|
||||||
|
updateStyles = style;
|
||||||
} else {
|
} else {
|
||||||
|
const oldStyles = shape.attributes;
|
||||||
Object.keys(style).forEach((key) => {
|
Object.keys(style).forEach((key) => {
|
||||||
|
if (oldStyles[key] !== style[key]) {
|
||||||
|
updateStyles[key] = style[key];
|
||||||
shape.style[key] = style[key];
|
shape.style[key] = style[key];
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
shapeMap[id] = shape;
|
shapeMap[id] = shape;
|
||||||
return shape;
|
return { shape, updateStyles };
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2,7 +2,6 @@ import * as graphs from './intergration/index';
|
|||||||
|
|
||||||
const SelectGraph = document.getElementById('select') as HTMLSelectElement;
|
const SelectGraph = document.getElementById('select') as HTMLSelectElement;
|
||||||
let firstKey;
|
let firstKey;
|
||||||
console.log('firstKey', firstKey);
|
|
||||||
const Options = Object.keys(graphs).map((key, index) => {
|
const Options = Object.keys(graphs).map((key, index) => {
|
||||||
const option = document.createElement('option');
|
const option = document.createElement('option');
|
||||||
if (index === 0) {
|
if (index === 0) {
|
||||||
@ -18,10 +17,9 @@ SelectGraph.replaceChildren(...Options);
|
|||||||
SelectGraph.onchange = (e) => {
|
SelectGraph.onchange = (e) => {
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
const { value } = e.target;
|
const { value } = e.target;
|
||||||
console.log(value);
|
|
||||||
history.pushState({ value }, '', `?name=${value}`);
|
history.pushState({ value }, '', `?name=${value}`);
|
||||||
const container = document.getElementById('container');
|
const container = document.getElementById('container');
|
||||||
container.replaceChildren('');
|
container?.replaceChildren('');
|
||||||
graphs[value]();
|
graphs[value]();
|
||||||
};
|
};
|
||||||
// 初始化
|
// 初始化
|
||||||
|
@ -1370,10 +1370,6 @@ describe('lasso-select behavior with brushStyle', () => {
|
|||||||
canvas: { x: 200, y: 150 },
|
canvas: { x: 200, y: 150 },
|
||||||
shiftKey: true,
|
shiftKey: true,
|
||||||
});
|
});
|
||||||
console.log(
|
|
||||||
'graph.transientCanvas.getRoot().childNodes',
|
|
||||||
graph.transientCanvas.getRoot().childNodes,
|
|
||||||
);
|
|
||||||
expect(graph.transientCanvas.getRoot().childNodes.length).toBe(2);
|
expect(graph.transientCanvas.getRoot().childNodes.length).toBe(2);
|
||||||
|
|
||||||
// TODO: wait for correct removeBehaviors
|
// TODO: wait for correct removeBehaviors
|
||||||
|
857
packages/g6/tests/unit/edge-spec.ts
Normal file
857
packages/g6/tests/unit/edge-spec.ts
Normal file
@ -0,0 +1,857 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
|
||||||
|
import { DisplayObject } from '@antv/g';
|
||||||
|
import { clone } from '@antv/util';
|
||||||
|
import G6, {
|
||||||
|
EdgeDisplayModel,
|
||||||
|
Graph,
|
||||||
|
IGraph,
|
||||||
|
NodeDisplayModel,
|
||||||
|
} from '../../src/index';
|
||||||
|
import { LineEdge } from '../../src/stdlib/item/edge';
|
||||||
|
import { CircleNode } from '../../src/stdlib/item/node';
|
||||||
|
import { NodeModelData, NodeShapeMap } from '../../src/types/node';
|
||||||
|
import { extend } from '../../src/util/extend';
|
||||||
|
import { upsertShape } from '../../src/util/shape';
|
||||||
|
|
||||||
|
const container = document.createElement('div');
|
||||||
|
document.querySelector('body').appendChild(container);
|
||||||
|
|
||||||
|
let graph: IGraph<any>;
|
||||||
|
|
||||||
|
describe('edge item', () => {
|
||||||
|
it('new graph with two nodes and one edge', (done) => {
|
||||||
|
graph = new G6.Graph({
|
||||||
|
container,
|
||||||
|
width: 500,
|
||||||
|
height: 500,
|
||||||
|
type: 'graph',
|
||||||
|
modes: {
|
||||||
|
default: ['drag-node'],
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
nodes: [
|
||||||
|
{
|
||||||
|
id: 'node1',
|
||||||
|
data: {
|
||||||
|
x: 100,
|
||||||
|
y: 100,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'node2',
|
||||||
|
data: { x: 300, y: 300 },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
edges: [
|
||||||
|
{
|
||||||
|
id: 'edge1',
|
||||||
|
source: 'node1',
|
||||||
|
target: 'node2',
|
||||||
|
data: {},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
graph.on('afterrender', () => {
|
||||||
|
const edgeItem = graph.itemController.itemMap['edge1'];
|
||||||
|
expect(edgeItem).not.toBe(undefined);
|
||||||
|
expect(edgeItem.shapeMap.labelShape).toBe(undefined);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('update edge label', (done) => {
|
||||||
|
const padding = [4, 16, 4, 8];
|
||||||
|
graph.updateData('edge', {
|
||||||
|
id: 'edge1',
|
||||||
|
data: {
|
||||||
|
labelShape: {
|
||||||
|
text: 'edge-label',
|
||||||
|
},
|
||||||
|
labelBackgroundShape: {
|
||||||
|
radius: 10,
|
||||||
|
padding,
|
||||||
|
fill: '#f00',
|
||||||
|
},
|
||||||
|
iconShape: {
|
||||||
|
text: 'A',
|
||||||
|
fill: '#f00',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const edgeItem = graph.itemController.itemMap['edge1'];
|
||||||
|
expect(edgeItem.shapeMap.labelShape).not.toBe(undefined);
|
||||||
|
expect(edgeItem.shapeMap.labelShape.attributes.text).toBe('edge-label');
|
||||||
|
const fill = edgeItem.shapeMap.labelShape.attributes.fill;
|
||||||
|
expect(fill).toBe('rgba(0,0,0,0.85)');
|
||||||
|
expect(edgeItem.shapeMap.labelShape.attributes.transform).toBe(
|
||||||
|
'rotate(45)',
|
||||||
|
);
|
||||||
|
expect(edgeItem.shapeMap.labelBackgroundShape.attributes.transform).toBe(
|
||||||
|
'rotate(45)',
|
||||||
|
);
|
||||||
|
let labelBounds = edgeItem.shapeMap.labelShape.getGeometryBounds();
|
||||||
|
expect(edgeItem.shapeMap.labelBackgroundShape.attributes.width).toBe(
|
||||||
|
labelBounds.max[0] - labelBounds.min[0] + padding[1] + padding[3],
|
||||||
|
);
|
||||||
|
expect(edgeItem.shapeMap.labelBackgroundShape.attributes.height).toBe(
|
||||||
|
labelBounds.max[1] - labelBounds.min[1] + padding[0] + padding[2],
|
||||||
|
);
|
||||||
|
|
||||||
|
graph.updateData('edge', {
|
||||||
|
id: 'edge1',
|
||||||
|
data: {
|
||||||
|
labelShape: {
|
||||||
|
fill: '#00f',
|
||||||
|
position: 'start',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(edgeItem.shapeMap.labelShape.attributes.fill).toBe('#00f');
|
||||||
|
expect(
|
||||||
|
edgeItem.shapeMap.labelShape.attributes.x -
|
||||||
|
edgeItem.shapeMap.labelBackgroundShape.attributes.x,
|
||||||
|
).toBe(padding[3]);
|
||||||
|
labelBounds = edgeItem.shapeMap.labelShape.getGeometryBounds();
|
||||||
|
const labelWidth = labelBounds.max[0] - labelBounds.min[0];
|
||||||
|
const labelHeight = labelBounds.max[1] - labelBounds.min[1];
|
||||||
|
const labelBgBounds =
|
||||||
|
edgeItem.shapeMap.labelBackgroundShape.getGeometryBounds();
|
||||||
|
const labelBgWidth = labelBgBounds.max[0] - labelBgBounds.min[0];
|
||||||
|
const labelBgHeight = labelBgBounds.max[1] - labelBgBounds.min[1];
|
||||||
|
expect(labelBgWidth - labelWidth).toBe(padding[1] + padding[3]);
|
||||||
|
expect(labelBgHeight - labelHeight).toBe(padding[0] + padding[2]);
|
||||||
|
|
||||||
|
graph.updateData('edge', {
|
||||||
|
id: 'edge1',
|
||||||
|
data: {
|
||||||
|
labelShape: undefined,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(edgeItem.shapeMap.labelShape).toBe(undefined);
|
||||||
|
expect(edgeItem.shapeMap.labelBackgroundShape).toBe(undefined);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
it('update edge icon', (done) => {
|
||||||
|
// add image icon to follow the label at path's center
|
||||||
|
graph.updateData('edge', {
|
||||||
|
id: 'edge1',
|
||||||
|
data: {
|
||||||
|
labelShape: {
|
||||||
|
text: 'abcddd',
|
||||||
|
fill: '#f00',
|
||||||
|
position: 'center',
|
||||||
|
},
|
||||||
|
iconShape: {
|
||||||
|
text: '',
|
||||||
|
img: 'https://gw.alipayobjects.com/zos/basement_prod/012bcf4f-423b-4922-8c24-32a89f8c41ce.svg',
|
||||||
|
// text: 'A',
|
||||||
|
fill: '#f00',
|
||||||
|
fontSize: 20,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const edgeItem = graph.itemController.itemMap['edge1'];
|
||||||
|
let { labelShape, iconShape, labelBackgroundShape } = edgeItem.shapeMap;
|
||||||
|
expect(iconShape.attributes.x + iconShape.attributes.width + 6).toBe(
|
||||||
|
labelBackgroundShape.getGeometryBounds().min[0] +
|
||||||
|
labelBackgroundShape.attributes.x,
|
||||||
|
);
|
||||||
|
expect(iconShape.attributes.transform).toBe(
|
||||||
|
labelBackgroundShape.attributes.transform,
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
Math.floor(iconShape.attributes.y + iconShape.attributes.height / 2),
|
||||||
|
).toBeCloseTo(
|
||||||
|
Math.floor(
|
||||||
|
labelBackgroundShape.getGeometryBounds().center[1] +
|
||||||
|
labelBackgroundShape.attributes.y,
|
||||||
|
),
|
||||||
|
0.01,
|
||||||
|
);
|
||||||
|
|
||||||
|
// update icon to be a text
|
||||||
|
graph.updateData('edge', {
|
||||||
|
id: 'edge1',
|
||||||
|
data: {
|
||||||
|
iconShape: {
|
||||||
|
text: 'A',
|
||||||
|
fill: '#f00',
|
||||||
|
fontWeight: 800,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
labelShape = edgeItem.shapeMap['labelShape'];
|
||||||
|
iconShape = edgeItem.shapeMap['iconShape'];
|
||||||
|
labelBackgroundShape = edgeItem.shapeMap['labelBackgroundShape'];
|
||||||
|
expect(iconShape.attributes.x + iconShape.attributes.fontSize + 6).toBe(
|
||||||
|
labelBackgroundShape.getGeometryBounds().min[0] +
|
||||||
|
labelBackgroundShape.attributes.x,
|
||||||
|
);
|
||||||
|
expect(iconShape.attributes.transform).toBe(
|
||||||
|
labelBackgroundShape.attributes.transform,
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
Math.floor(iconShape.attributes.y + iconShape.attributes.fontSize / 2),
|
||||||
|
).toBeCloseTo(
|
||||||
|
Math.floor(
|
||||||
|
labelBackgroundShape.getGeometryBounds().center[1] +
|
||||||
|
labelBackgroundShape.attributes.y,
|
||||||
|
),
|
||||||
|
0.01,
|
||||||
|
);
|
||||||
|
|
||||||
|
// move label to the start, and the icon follows
|
||||||
|
graph.updateData('edge', {
|
||||||
|
id: 'edge1',
|
||||||
|
data: {
|
||||||
|
labelShape: {
|
||||||
|
position: 'start',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
labelShape = edgeItem.shapeMap['labelShape'];
|
||||||
|
iconShape = edgeItem.shapeMap['iconShape'];
|
||||||
|
labelBackgroundShape = edgeItem.shapeMap['labelBackgroundShape'];
|
||||||
|
expect(iconShape.attributes.x + iconShape.attributes.fontSize + 6).toBe(
|
||||||
|
labelBackgroundShape.getGeometryBounds().min[0] +
|
||||||
|
labelBackgroundShape.attributes.x,
|
||||||
|
);
|
||||||
|
expect(iconShape.attributes.transform).toBe(
|
||||||
|
labelShape.attributes.transform,
|
||||||
|
);
|
||||||
|
expect(iconShape.attributes.y + iconShape.attributes.fontSize / 2).toBe(
|
||||||
|
labelBackgroundShape.getGeometryBounds().center[1] +
|
||||||
|
labelBackgroundShape.attributes.y,
|
||||||
|
);
|
||||||
|
graph.destroy();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('edge mapper', () => {
|
||||||
|
const data = {
|
||||||
|
nodes: [
|
||||||
|
{
|
||||||
|
id: 'node1',
|
||||||
|
data: { x: 100, y: 200 },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'node2',
|
||||||
|
data: { x: 100, y: 300 },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'node3',
|
||||||
|
data: { x: 200, y: 300 },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
edges: [
|
||||||
|
{
|
||||||
|
id: 'edge1',
|
||||||
|
source: 'node1',
|
||||||
|
target: 'node2',
|
||||||
|
data: { buStatus: true, buType: 1, buName: 'edge-1' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'edge2',
|
||||||
|
source: 'node1',
|
||||||
|
target: 'node3',
|
||||||
|
data: { buStatus: false, buType: 0, buName: 'edge-2' },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
const graphConfig = {
|
||||||
|
container,
|
||||||
|
width: 500,
|
||||||
|
height: 500,
|
||||||
|
type: 'graph',
|
||||||
|
};
|
||||||
|
it('function mapper', (done) => {
|
||||||
|
const graph = new G6.Graph({
|
||||||
|
...graphConfig,
|
||||||
|
edge: (innerModel) => {
|
||||||
|
const { x, y, buStatus } = innerModel.data;
|
||||||
|
return {
|
||||||
|
...innerModel,
|
||||||
|
data: {
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
keyShape: {
|
||||||
|
stroke: buStatus ? '#0f0' : '#f00',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
} as any);
|
||||||
|
graph.read(clone(data));
|
||||||
|
graph.on('afterrender', () => {
|
||||||
|
const edge1 = graph.itemController.itemMap['edge1'];
|
||||||
|
expect(edge1.shapeMap.keyShape.attributes.stroke).toBe('#0f0');
|
||||||
|
let edge2 = graph.itemController.itemMap['edge2'];
|
||||||
|
expect(edge2.shapeMap.keyShape.attributes.stroke).toBe('#f00');
|
||||||
|
|
||||||
|
// update user data
|
||||||
|
graph.updateData('edge', {
|
||||||
|
id: 'edge2',
|
||||||
|
data: {
|
||||||
|
buStatus: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
edge2 = graph.itemController.itemMap['edge2'];
|
||||||
|
expect(edge2.shapeMap.keyShape.attributes.stroke).toBe('#0f0');
|
||||||
|
|
||||||
|
graph.destroy();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('value and encode mapper', (done) => {
|
||||||
|
const graph = new G6.Graph({
|
||||||
|
...graphConfig,
|
||||||
|
edge: {
|
||||||
|
keyShape: {
|
||||||
|
stroke: {
|
||||||
|
fields: ['buStatus'],
|
||||||
|
formatter: (innerModel) =>
|
||||||
|
innerModel.data.buStatus ? '#0f0' : '#f00',
|
||||||
|
},
|
||||||
|
lineWidth: 5,
|
||||||
|
lineDash: {
|
||||||
|
fields: ['buStatus', 'buType'],
|
||||||
|
formatter: (innerModel) =>
|
||||||
|
innerModel.data.buStatus || innerModel.data.buType
|
||||||
|
? undefined
|
||||||
|
: [5, 5],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
labelShape: {
|
||||||
|
text: {
|
||||||
|
fields: ['buName', 'buType'],
|
||||||
|
formatter: (innerModel) =>
|
||||||
|
`${innerModel.data.buName}-${innerModel.data.buType}`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as any);
|
||||||
|
graph.read(clone(data));
|
||||||
|
graph.on('afterrender', () => {
|
||||||
|
const edge1 = graph.itemController.itemMap['edge1'];
|
||||||
|
expect(edge1.shapeMap.keyShape.attributes.stroke).toBe('#0f0');
|
||||||
|
expect(edge1.shapeMap.keyShape.attributes.lineWidth).toBe(5);
|
||||||
|
expect(edge1.shapeMap.keyShape.attributes.lineDash).toBe('');
|
||||||
|
expect(edge1.shapeMap.labelShape.attributes.text).toBe('edge-1-1');
|
||||||
|
let edge2 = graph.itemController.itemMap['edge2'];
|
||||||
|
expect(edge2.shapeMap.keyShape.attributes.stroke).toBe('#f00');
|
||||||
|
expect(edge2.shapeMap.keyShape.attributes.lineWidth).toBe(5);
|
||||||
|
expect(JSON.stringify(edge2.shapeMap.keyShape.attributes.lineDash)).toBe(
|
||||||
|
'[5,5]',
|
||||||
|
);
|
||||||
|
expect(edge2.shapeMap.labelShape.attributes.text).toBe('edge-2-0');
|
||||||
|
|
||||||
|
// update user data
|
||||||
|
graph.updateData('edge', {
|
||||||
|
id: 'edge2',
|
||||||
|
data: {
|
||||||
|
buStatus: true,
|
||||||
|
buName: 'newedge2name',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
edge2 = graph.itemController.itemMap['edge2'];
|
||||||
|
expect(edge2.shapeMap.keyShape.attributes.stroke).toBe('#0f0');
|
||||||
|
expect(edge2.shapeMap.keyShape.attributes.lineDash).toBe('');
|
||||||
|
expect(edge2.shapeMap.labelShape.attributes.text).toBe('newedge2name-0');
|
||||||
|
|
||||||
|
graph.destroy();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('state', () => {
|
||||||
|
it('edge state', (done) => {
|
||||||
|
const graph = new Graph({
|
||||||
|
container,
|
||||||
|
width: 500,
|
||||||
|
height: 500,
|
||||||
|
type: 'graph',
|
||||||
|
data: {
|
||||||
|
nodes: [
|
||||||
|
{
|
||||||
|
id: 'node1',
|
||||||
|
data: { x: 100, y: 200 },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'node2',
|
||||||
|
data: { x: 100, y: 300 },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'node3',
|
||||||
|
data: { x: 200, y: 300 },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
edges: [
|
||||||
|
{
|
||||||
|
id: 'edge1',
|
||||||
|
source: 'node1',
|
||||||
|
target: 'node2',
|
||||||
|
data: {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'edge2',
|
||||||
|
source: 'node1',
|
||||||
|
target: 'node3',
|
||||||
|
data: {},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
edgeState: {
|
||||||
|
selected: {
|
||||||
|
keyShape: {
|
||||||
|
stroke: '#0f0',
|
||||||
|
lineWidth: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
highlight: {
|
||||||
|
keyShape: {
|
||||||
|
stroke: '#00f',
|
||||||
|
opacity: 0.5,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
graph.on('afterrender', () => {
|
||||||
|
expect(graph.findIdByState('edge', 'selected').length).toBe(0);
|
||||||
|
graph.setItemState('edge1', 'selected', true);
|
||||||
|
expect(graph.findIdByState('edge', 'selected').length).toBe(1);
|
||||||
|
expect(graph.findIdByState('edge', 'selected')[0]).toBe('edge1');
|
||||||
|
expect(
|
||||||
|
graph.itemController.itemMap['edge1'].shapeMap.keyShape.style.lineWidth,
|
||||||
|
).toBe(2);
|
||||||
|
expect(
|
||||||
|
graph.itemController.itemMap['edge1'].shapeMap.keyShape.style.stroke,
|
||||||
|
).toBe('#0f0');
|
||||||
|
graph.setItemState('edge1', 'selected', false);
|
||||||
|
expect(graph.findIdByState('edge', 'selected').length).toBe(0);
|
||||||
|
expect(
|
||||||
|
graph.itemController.itemMap['edge1'].shapeMap.keyShape.style.lineWidth,
|
||||||
|
).toBe(1);
|
||||||
|
|
||||||
|
// set multiple edges state
|
||||||
|
graph.setItemState(['edge1', 'edge2'], 'selected', true);
|
||||||
|
expect(graph.findIdByState('edge', 'selected').length).toBe(2);
|
||||||
|
expect(
|
||||||
|
graph.itemController.itemMap['edge1'].shapeMap.keyShape.style.lineWidth,
|
||||||
|
).toBe(2);
|
||||||
|
expect(
|
||||||
|
graph.itemController.itemMap['edge1'].shapeMap.keyShape.style.stroke,
|
||||||
|
).toBe('#0f0');
|
||||||
|
expect(
|
||||||
|
graph.itemController.itemMap['edge2'].shapeMap.keyShape.style.lineWidth,
|
||||||
|
).toBe(2);
|
||||||
|
expect(
|
||||||
|
graph.itemController.itemMap['edge2'].shapeMap.keyShape.style.stroke,
|
||||||
|
).toBe('#0f0');
|
||||||
|
graph.setItemState('edge1', 'selected', false);
|
||||||
|
expect(graph.findIdByState('edge', 'selected').length).toBe(1);
|
||||||
|
expect(graph.findIdByState('edge', 'selected')[0]).toBe('edge2');
|
||||||
|
expect(
|
||||||
|
graph.itemController.itemMap['edge1'].shapeMap.keyShape.style.lineWidth,
|
||||||
|
).toBe(1);
|
||||||
|
graph.setItemState(['edge1', 'edge2'], 'selected', false);
|
||||||
|
expect(graph.findIdByState('edge', 'selected').length).toBe(0);
|
||||||
|
expect(
|
||||||
|
graph.itemController.itemMap['edge2'].shapeMap.keyShape.style.lineWidth,
|
||||||
|
).toBe(1);
|
||||||
|
|
||||||
|
// // set multiple states
|
||||||
|
graph.setItemState(['edge2', 'edge1'], ['selected', 'highlight'], true);
|
||||||
|
expect(graph.findIdByState('edge', 'selected').length).toBe(2);
|
||||||
|
expect(graph.findIdByState('edge', 'highlight').length).toBe(2);
|
||||||
|
// should be merged styles from selected and highlight
|
||||||
|
expect(
|
||||||
|
graph.itemController.itemMap['edge1'].shapeMap.keyShape.style.lineWidth,
|
||||||
|
).toBe(2);
|
||||||
|
expect(
|
||||||
|
graph.itemController.itemMap['edge1'].shapeMap.keyShape.style.stroke,
|
||||||
|
).toBe('#00f');
|
||||||
|
expect(
|
||||||
|
graph.itemController.itemMap['edge1'].shapeMap.keyShape.style.opacity,
|
||||||
|
).toBe(0.5);
|
||||||
|
expect(
|
||||||
|
graph.itemController.itemMap['edge2'].shapeMap.keyShape.style.lineWidth,
|
||||||
|
).toBe(2);
|
||||||
|
expect(
|
||||||
|
graph.itemController.itemMap['edge2'].shapeMap.keyShape.style.stroke,
|
||||||
|
).toBe('#00f');
|
||||||
|
expect(
|
||||||
|
graph.itemController.itemMap['edge2'].shapeMap.keyShape.style.opacity,
|
||||||
|
).toBe(0.5);
|
||||||
|
|
||||||
|
// clear states
|
||||||
|
graph.clearItemState(['edge1', 'edge2']);
|
||||||
|
expect(graph.findIdByState('edge', 'selected').length).toBe(0);
|
||||||
|
expect(graph.findIdByState('edge', 'highlight').length).toBe(0);
|
||||||
|
expect(
|
||||||
|
graph.itemController.itemMap['edge1'].shapeMap.keyShape.style.lineWidth,
|
||||||
|
).toBe(1);
|
||||||
|
expect(
|
||||||
|
graph.itemController.itemMap['edge1'].shapeMap.keyShape.style.opacity,
|
||||||
|
).toBe(1);
|
||||||
|
|
||||||
|
graph.destroy();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('edge state with assigned style', (done) => {
|
||||||
|
const graph = new Graph({
|
||||||
|
container,
|
||||||
|
width: 500,
|
||||||
|
height: 500,
|
||||||
|
type: 'graph',
|
||||||
|
data: {
|
||||||
|
nodes: [
|
||||||
|
{
|
||||||
|
id: 'node1',
|
||||||
|
data: { x: 100, y: 200 },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'node2',
|
||||||
|
data: { x: 100, y: 300 },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'node3',
|
||||||
|
data: { x: 200, y: 300 },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
edges: [
|
||||||
|
{
|
||||||
|
id: 'edge1',
|
||||||
|
source: 'node1',
|
||||||
|
target: 'node2',
|
||||||
|
data: {
|
||||||
|
keyShape: {
|
||||||
|
stroke: '#f00',
|
||||||
|
lineDash: [2, 2],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'edge2',
|
||||||
|
source: 'node1',
|
||||||
|
target: 'node3',
|
||||||
|
data: {
|
||||||
|
keyShape: {
|
||||||
|
stroke: '#f00',
|
||||||
|
lineDash: [2, 2],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
edgeState: {
|
||||||
|
selected: {
|
||||||
|
keyShape: {
|
||||||
|
stroke: '#0f0',
|
||||||
|
lineWidth: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
highlight: {
|
||||||
|
keyShape: {
|
||||||
|
stroke: '#00f',
|
||||||
|
opacity: 0.5,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
graph.on('afterrender', () => {
|
||||||
|
expect(graph.findIdByState('edge', 'selected').length).toBe(0);
|
||||||
|
graph.setItemState('edge1', 'selected', true);
|
||||||
|
expect(graph.findIdByState('edge', 'selected').length).toBe(1);
|
||||||
|
expect(graph.findIdByState('edge', 'selected')[0]).toBe('edge1');
|
||||||
|
expect(
|
||||||
|
graph.itemController.itemMap['edge1'].shapeMap.keyShape.style.lineWidth,
|
||||||
|
).toBe(2);
|
||||||
|
expect(
|
||||||
|
graph.itemController.itemMap['edge1'].shapeMap.keyShape.style.stroke,
|
||||||
|
).toBe('#0f0');
|
||||||
|
expect(
|
||||||
|
JSON.stringify(
|
||||||
|
graph.itemController.itemMap['edge1'].shapeMap.keyShape.style
|
||||||
|
.lineDash,
|
||||||
|
),
|
||||||
|
).toBe('[2,2]');
|
||||||
|
graph.setItemState('edge1', 'selected', false);
|
||||||
|
expect(graph.findIdByState('edge', 'selected').length).toBe(0);
|
||||||
|
expect(
|
||||||
|
graph.itemController.itemMap['edge1'].shapeMap.keyShape.style.lineWidth,
|
||||||
|
).toBe(1);
|
||||||
|
expect(
|
||||||
|
graph.itemController.itemMap['edge1'].shapeMap.keyShape.style.stroke,
|
||||||
|
).toBe('#f00');
|
||||||
|
expect(
|
||||||
|
JSON.stringify(
|
||||||
|
graph.itemController.itemMap['edge1'].shapeMap.keyShape.style
|
||||||
|
.lineDash,
|
||||||
|
),
|
||||||
|
).toBe('[2,2]');
|
||||||
|
|
||||||
|
// set multiple edges state
|
||||||
|
graph.setItemState(['edge1', 'edge2'], 'selected', true);
|
||||||
|
expect(graph.findIdByState('edge', 'selected').length).toBe(2);
|
||||||
|
expect(
|
||||||
|
graph.itemController.itemMap['edge1'].shapeMap.keyShape.style.lineWidth,
|
||||||
|
).toBe(2);
|
||||||
|
expect(
|
||||||
|
graph.itemController.itemMap['edge1'].shapeMap.keyShape.style.stroke,
|
||||||
|
).toBe('#0f0');
|
||||||
|
expect(
|
||||||
|
JSON.stringify(
|
||||||
|
graph.itemController.itemMap['edge1'].shapeMap.keyShape.style
|
||||||
|
.lineDash,
|
||||||
|
),
|
||||||
|
).toBe('[2,2]');
|
||||||
|
expect(
|
||||||
|
graph.itemController.itemMap['edge2'].shapeMap.keyShape.style.lineWidth,
|
||||||
|
).toBe(2);
|
||||||
|
expect(
|
||||||
|
graph.itemController.itemMap['edge2'].shapeMap.keyShape.style.stroke,
|
||||||
|
).toBe('#0f0');
|
||||||
|
expect(
|
||||||
|
JSON.stringify(
|
||||||
|
graph.itemController.itemMap['edge1'].shapeMap.keyShape.style
|
||||||
|
.lineDash,
|
||||||
|
),
|
||||||
|
).toBe('[2,2]');
|
||||||
|
graph.setItemState('edge1', 'selected', false);
|
||||||
|
expect(graph.findIdByState('edge', 'selected').length).toBe(1);
|
||||||
|
expect(graph.findIdByState('edge', 'selected')[0]).toBe('edge2');
|
||||||
|
expect(
|
||||||
|
graph.itemController.itemMap['edge1'].shapeMap.keyShape.style.lineWidth,
|
||||||
|
).toBe(1);
|
||||||
|
expect(
|
||||||
|
graph.itemController.itemMap['edge1'].shapeMap.keyShape.style.stroke,
|
||||||
|
).toBe('#f00');
|
||||||
|
graph.setItemState(['edge1', 'edge2'], 'selected', false);
|
||||||
|
expect(graph.findIdByState('edge', 'selected').length).toBe(0);
|
||||||
|
expect(
|
||||||
|
graph.itemController.itemMap['edge2'].shapeMap.keyShape.style.lineWidth,
|
||||||
|
).toBe(1);
|
||||||
|
expect(
|
||||||
|
graph.itemController.itemMap['edge1'].shapeMap.keyShape.style.stroke,
|
||||||
|
).toBe('#f00');
|
||||||
|
|
||||||
|
// set multiple states
|
||||||
|
graph.setItemState(['edge2', 'edge1'], ['selected', 'highlight'], true);
|
||||||
|
expect(graph.findIdByState('edge', 'selected').length).toBe(2);
|
||||||
|
expect(graph.findIdByState('edge', 'highlight').length).toBe(2);
|
||||||
|
// should be merged styles from selected and highlight
|
||||||
|
expect(
|
||||||
|
graph.itemController.itemMap['edge1'].shapeMap.keyShape.style.lineWidth,
|
||||||
|
).toBe(2);
|
||||||
|
expect(
|
||||||
|
graph.itemController.itemMap['edge1'].shapeMap.keyShape.style.stroke,
|
||||||
|
).toBe('#00f');
|
||||||
|
expect(
|
||||||
|
graph.itemController.itemMap['edge1'].shapeMap.keyShape.style.opacity,
|
||||||
|
).toBe(0.5);
|
||||||
|
expect(
|
||||||
|
graph.itemController.itemMap['edge2'].shapeMap.keyShape.style.lineWidth,
|
||||||
|
).toBe(2);
|
||||||
|
expect(
|
||||||
|
graph.itemController.itemMap['edge2'].shapeMap.keyShape.style.stroke,
|
||||||
|
).toBe('#00f');
|
||||||
|
expect(
|
||||||
|
graph.itemController.itemMap['edge2'].shapeMap.keyShape.style.opacity,
|
||||||
|
).toBe(0.5);
|
||||||
|
|
||||||
|
// clear states
|
||||||
|
graph.clearItemState(['edge1', 'edge2']);
|
||||||
|
expect(graph.findIdByState('edge', 'selected').length).toBe(0);
|
||||||
|
expect(graph.findIdByState('edge', 'highlight').length).toBe(0);
|
||||||
|
expect(
|
||||||
|
graph.itemController.itemMap['edge1'].shapeMap.keyShape.style.lineWidth,
|
||||||
|
).toBe(1);
|
||||||
|
expect(
|
||||||
|
graph.itemController.itemMap['edge1'].shapeMap.keyShape.style.opacity,
|
||||||
|
).toBe(1);
|
||||||
|
|
||||||
|
graph.destroy();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
class CustomNode extends CircleNode {
|
||||||
|
public defaultStyles = {
|
||||||
|
keyShape: {
|
||||||
|
r: 25,
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
fill: '#ff0',
|
||||||
|
lineWidth: 0,
|
||||||
|
stroke: '#0f0',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
public drawLabelShape(
|
||||||
|
model: NodeDisplayModel,
|
||||||
|
shapeMap: NodeShapeMap,
|
||||||
|
diffData?: { oldData: NodeModelData; newData: NodeModelData },
|
||||||
|
) {
|
||||||
|
const { labelShape: propsLabelStyle } = model.data;
|
||||||
|
const labelStyle = Object.assign(
|
||||||
|
{},
|
||||||
|
this.defaultStyles.labelShape,
|
||||||
|
propsLabelStyle,
|
||||||
|
);
|
||||||
|
const labelShape = this.upsertShape(
|
||||||
|
'text',
|
||||||
|
'labelShape',
|
||||||
|
{
|
||||||
|
...labelStyle,
|
||||||
|
text: model.id,
|
||||||
|
},
|
||||||
|
shapeMap,
|
||||||
|
).shape;
|
||||||
|
return labelShape;
|
||||||
|
}
|
||||||
|
public drawOtherShapes(
|
||||||
|
model: NodeDisplayModel,
|
||||||
|
shapeMap: NodeShapeMap,
|
||||||
|
diffData?: { oldData: NodeModelData; newData: NodeModelData },
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
extraShape: upsertShape(
|
||||||
|
'circle',
|
||||||
|
'extraShape',
|
||||||
|
{
|
||||||
|
r: 4,
|
||||||
|
fill: '#0f0',
|
||||||
|
x: -20,
|
||||||
|
y: 0,
|
||||||
|
},
|
||||||
|
shapeMap,
|
||||||
|
).shape,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class CustomEdge extends LineEdge {
|
||||||
|
public afterDraw(
|
||||||
|
model: EdgeDisplayModel,
|
||||||
|
shapeMap: { [shapeId: string]: DisplayObject<any, any> },
|
||||||
|
shapesChanged?: string[],
|
||||||
|
): { [otherShapeId: string]: DisplayObject } {
|
||||||
|
const { keyShape } = shapeMap;
|
||||||
|
const point = keyShape.getPoint(0.3);
|
||||||
|
return {
|
||||||
|
buShape: this.upsertShape(
|
||||||
|
'rect',
|
||||||
|
'buShape',
|
||||||
|
{
|
||||||
|
width: 6,
|
||||||
|
height: 6,
|
||||||
|
x: point.x,
|
||||||
|
y: point.y,
|
||||||
|
fill: '#0f0',
|
||||||
|
...model.data?.otherShapes?.buShape, // merged style from mappers and states
|
||||||
|
},
|
||||||
|
shapeMap,
|
||||||
|
).shape,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const CustomGraph = extend(Graph, {
|
||||||
|
nodes: {
|
||||||
|
'custom-node2': CustomNode,
|
||||||
|
},
|
||||||
|
edges: {
|
||||||
|
'custom-edge2': CustomEdge,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
it('custom edge with setState', (done) => {
|
||||||
|
const graph = new CustomGraph({
|
||||||
|
container,
|
||||||
|
width: 500,
|
||||||
|
height: 500,
|
||||||
|
type: 'graph',
|
||||||
|
data: {
|
||||||
|
nodes: [
|
||||||
|
{
|
||||||
|
id: 'node1',
|
||||||
|
data: { x: 100, y: 200, type: 'custom-node2' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'node2',
|
||||||
|
data: { x: 100, y: 300, type: 'circle-node' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'node3',
|
||||||
|
data: { x: 200, y: 300, labelShape: undefined },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
edges: [
|
||||||
|
{
|
||||||
|
id: 'edge1',
|
||||||
|
source: 'node1',
|
||||||
|
target: 'node2',
|
||||||
|
data: { type: 'custom-edge2' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'edge2',
|
||||||
|
source: 'node1',
|
||||||
|
target: 'node3',
|
||||||
|
data: {},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
node: {
|
||||||
|
// affect the nodes without type field in their data object, which means configurations in the user data has higher priority than that in the mapper
|
||||||
|
type: 'custom-node2',
|
||||||
|
// affect the nodes without labelShape field in their data object, which means configurations in the user data has higher priority than that in the mapper
|
||||||
|
labelShape: {},
|
||||||
|
},
|
||||||
|
edgeState: {
|
||||||
|
selected: {
|
||||||
|
keyShape: {
|
||||||
|
lineWidth: 2,
|
||||||
|
stroke: '#00f',
|
||||||
|
},
|
||||||
|
otherShapes: {
|
||||||
|
buShape: {
|
||||||
|
fill: '#fff',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
graph.on('afterrender', () => {
|
||||||
|
const edge1 = graph.itemController.itemMap['edge1'];
|
||||||
|
graph.setItemState('edge1', 'selected', true);
|
||||||
|
expect(edge1.shapeMap.keyShape.style.stroke).toBe('#00f');
|
||||||
|
expect(edge1.shapeMap.keyShape.style.lineWidth).toBe(2);
|
||||||
|
expect(edge1.shapeMap.buShape.style.fill).toBe('#fff');
|
||||||
|
|
||||||
|
const edge2 = graph.itemController.itemMap['edge2'];
|
||||||
|
graph.setItemState('edge2', 'selected', true);
|
||||||
|
expect(edge2.shapeMap.keyShape.style.stroke).toBe('#00f');
|
||||||
|
expect(edge2.shapeMap.keyShape.style.lineWidth).toBe(2);
|
||||||
|
|
||||||
|
// update node type
|
||||||
|
graph.updateData('edge', {
|
||||||
|
id: 'edge2',
|
||||||
|
data: {
|
||||||
|
type: 'custom-edge2',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(edge2.shapeMap.buShape).not.toBe(undefined);
|
||||||
|
expect(edge2.shapeMap.keyShape.style.stroke).toBe('#00f');
|
||||||
|
expect(edge2.shapeMap.keyShape.style.lineWidth).toBe(2);
|
||||||
|
expect(edge2.shapeMap.buShape.style.fill).toBe('#fff');
|
||||||
|
|
||||||
|
graph.clearItemState(['edge2'], ['selected']);
|
||||||
|
expect(edge2.shapeMap.keyShape.style.stroke).not.toBe('#00f');
|
||||||
|
expect(edge2.shapeMap.keyShape.style.lineWidth).toBe(1);
|
||||||
|
expect(edge2.shapeMap.buShape.style.fill).toBe('#0f0');
|
||||||
|
|
||||||
|
graph.destroy();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -76,7 +76,6 @@ describe('node item', () => {
|
|||||||
|
|
||||||
graph.on('afterrender', (e) => {
|
graph.on('afterrender', (e) => {
|
||||||
graph.on('node:click', (e) => {
|
graph.on('node:click', (e) => {
|
||||||
console.log('nodeclick');
|
|
||||||
graph.setItemState(e.itemId, 'selected', true);
|
graph.setItemState(e.itemId, 'selected', true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -13,15 +13,71 @@ document.querySelector('body').appendChild(container);
|
|||||||
|
|
||||||
const data: GraphData = {
|
const data: GraphData = {
|
||||||
nodes: [
|
nodes: [
|
||||||
{ id: 'node1', data: { x: 100, y: 200, dt: 'a' } },
|
{
|
||||||
{ id: 'node2', data: { x: 200, y: 250, dt: 'b' } },
|
id: 'node1',
|
||||||
{ id: 'node3', data: { x: 300, y: 200, dt: 'c' } },
|
data: {
|
||||||
{ id: 'node4', data: { x: 300, y: 250 } },
|
x: 100,
|
||||||
|
y: 200,
|
||||||
|
dt: 'a',
|
||||||
|
labelBackgroundShape: {
|
||||||
|
radius: 8,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'node2',
|
||||||
|
data: {
|
||||||
|
x: 200,
|
||||||
|
y: 250,
|
||||||
|
dt: 'b',
|
||||||
|
badges: [
|
||||||
|
{
|
||||||
|
type: 'icon',
|
||||||
|
text: 'a',
|
||||||
|
position: 'rightTop',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'node3',
|
||||||
|
data: {
|
||||||
|
x: 300,
|
||||||
|
y: 200,
|
||||||
|
dt: 'c',
|
||||||
|
labelBackgroundShape: {
|
||||||
|
radius: 8,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'node4',
|
||||||
|
data: {
|
||||||
|
x: 300,
|
||||||
|
y: 250,
|
||||||
|
anchorPoints: [
|
||||||
|
[0.5, 0],
|
||||||
|
[0.5, 1],
|
||||||
|
[0, 0.5],
|
||||||
|
[1, 0.5],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
edges: [
|
edges: [
|
||||||
{ id: 'edge1', source: 'node1', target: 'node2', data: { edt: '1' } },
|
{ id: 'edge1', source: 'node1', target: 'node2', data: { edt: '1' } },
|
||||||
{ id: 'edge2', source: 'node1', target: 'node3', data: { edt: '2' } },
|
{ id: 'edge2', source: 'node1', target: 'node3', data: { edt: '2' } },
|
||||||
{ id: 'edge3', source: 'node1', target: 'node4', data: {} },
|
{
|
||||||
|
id: 'edge3',
|
||||||
|
source: 'node1',
|
||||||
|
target: 'node4',
|
||||||
|
data: {
|
||||||
|
edt: 'xxx',
|
||||||
|
labelBackgroundShape: {
|
||||||
|
radius: 8,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -44,6 +100,21 @@ describe('theme', () => {
|
|||||||
formatter: (model) => model.id,
|
formatter: (model) => model.id,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
iconShape: {
|
||||||
|
img: 'https://gw.alipayobjects.com/zos/basement_prod/012bcf4f-423b-4922-8c24-32a89f8c41ce.svg',
|
||||||
|
},
|
||||||
|
anchorShapes: {
|
||||||
|
fields: ['anchorPoints'],
|
||||||
|
formatter: (model) => {
|
||||||
|
return model.data.anchorPoints?.map((point, i) => ({
|
||||||
|
position: point,
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
},
|
||||||
|
badgeShapes: {
|
||||||
|
fields: ['badges'],
|
||||||
|
formatter: (model) => model.data.badges,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
edge: {
|
edge: {
|
||||||
labelShape: {
|
labelShape: {
|
||||||
@ -58,6 +129,7 @@ describe('theme', () => {
|
|||||||
const node = graph.itemController.itemMap['node1'];
|
const node = graph.itemController.itemMap['node1'];
|
||||||
const { keyShape: nodeKeyShape, labelShape: nodeLabelShape } =
|
const { keyShape: nodeKeyShape, labelShape: nodeLabelShape } =
|
||||||
node.shapeMap;
|
node.shapeMap;
|
||||||
|
expect(node.shapeMap.haloShape).toBe(undefined);
|
||||||
expect(nodeKeyShape.style.fill).toBe(
|
expect(nodeKeyShape.style.fill).toBe(
|
||||||
LightTheme.node.styles[0].default.keyShape.fill,
|
LightTheme.node.styles[0].default.keyShape.fill,
|
||||||
);
|
);
|
||||||
@ -67,6 +139,7 @@ describe('theme', () => {
|
|||||||
const edge = graph.itemController.itemMap['edge1'];
|
const edge = graph.itemController.itemMap['edge1'];
|
||||||
const { keyShape: edgeKeyShape, labelShape: edgeLabelShape } =
|
const { keyShape: edgeKeyShape, labelShape: edgeLabelShape } =
|
||||||
edge.shapeMap;
|
edge.shapeMap;
|
||||||
|
expect(edge.shapeMap.haloShape).toBe(undefined);
|
||||||
expect(edgeKeyShape.style.stroke).toBe(
|
expect(edgeKeyShape.style.stroke).toBe(
|
||||||
LightTheme.edge.styles[0].default.keyShape.stroke,
|
LightTheme.edge.styles[0].default.keyShape.stroke,
|
||||||
);
|
);
|
||||||
@ -76,8 +149,12 @@ describe('theme', () => {
|
|||||||
|
|
||||||
// set state, should response with default theme state style
|
// set state, should response with default theme state style
|
||||||
graph.setItemState('node1', 'selected', true);
|
graph.setItemState('node1', 'selected', true);
|
||||||
|
expect(node.shapeMap.haloShape).not.toBe(undefined);
|
||||||
|
expect(node.shapeMap.haloShape.style.lineWidth).not.toBe(
|
||||||
|
LightTheme.node.styles[0].selected.haloShape.lineWith,
|
||||||
|
);
|
||||||
expect(nodeKeyShape.style.fill).toBe(
|
expect(nodeKeyShape.style.fill).toBe(
|
||||||
LightTheme.node.styles[0].selected.keyShape.fill,
|
LightTheme.node.styles[0].default.keyShape.fill, // no change
|
||||||
);
|
);
|
||||||
expect(nodeLabelShape.style.fontWeight).toBe(
|
expect(nodeLabelShape.style.fontWeight).toBe(
|
||||||
LightTheme.node.styles[0].selected.labelShape.fontWeight,
|
LightTheme.node.styles[0].selected.labelShape.fontWeight,
|
||||||
@ -91,20 +168,31 @@ describe('theme', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
graph.setItemState('edge1', 'selected', true);
|
graph.setItemState('edge1', 'selected', true);
|
||||||
|
expect(edge.shapeMap.haloShape).not.toBe(undefined);
|
||||||
|
expect(edge.shapeMap.haloShape.style.lineWidth).not.toBe(
|
||||||
|
LightTheme.edge.styles[0].selected.haloShape.lineWith,
|
||||||
|
);
|
||||||
expect(edgeKeyShape.style.stroke).toBe(
|
expect(edgeKeyShape.style.stroke).toBe(
|
||||||
LightTheme.edge.styles[0].selected.keyShape.stroke,
|
LightTheme.edge.styles[0].default.keyShape.stroke, // no change
|
||||||
);
|
);
|
||||||
expect(edgeKeyShape.style.lineWidth).toBe(
|
expect(edgeKeyShape.style.lineWidth).toBe(
|
||||||
LightTheme.edge.styles[0].selected.keyShape.lineWidth,
|
LightTheme.edge.styles[0].selected.keyShape.lineWidth,
|
||||||
);
|
);
|
||||||
console.log(
|
|
||||||
'xssx',
|
|
||||||
edgeLabelShape.style.fontWeight,
|
|
||||||
LightTheme.edge.styles[0].default.labelShape.fontWeight,
|
|
||||||
);
|
|
||||||
expect(edgeLabelShape.style.fill).toBe(
|
expect(edgeLabelShape.style.fill).toBe(
|
||||||
LightTheme.edge.styles[0].default.labelShape.fill,
|
LightTheme.edge.styles[0].default.labelShape.fill,
|
||||||
); // no change in theme def
|
);
|
||||||
|
|
||||||
|
const node4 = graph.itemController.itemMap['node4'];
|
||||||
|
expect(node4.shapeMap.anchorShape0).not.toBe(undefined);
|
||||||
|
expect(node4.shapeMap.anchorShape1).not.toBe(undefined);
|
||||||
|
expect(node4.shapeMap.anchorShape2).not.toBe(undefined);
|
||||||
|
expect(node4.shapeMap.anchorShape3).not.toBe(undefined);
|
||||||
|
|
||||||
|
graph.setItemState('node4', 'selected', true);
|
||||||
|
expect(node4.shapeMap.anchorShape0).not.toBe(undefined);
|
||||||
|
expect(node4.shapeMap.anchorShape1).not.toBe(undefined);
|
||||||
|
expect(node4.shapeMap.anchorShape2).not.toBe(undefined);
|
||||||
|
expect(node4.shapeMap.anchorShape3).not.toBe(undefined);
|
||||||
|
|
||||||
graph.destroy();
|
graph.destroy();
|
||||||
done();
|
done();
|
||||||
@ -271,17 +359,11 @@ describe('theme', () => {
|
|||||||
|
|
||||||
// node setState with state in builtin theme
|
// node setState with state in builtin theme
|
||||||
graph.setItemState('node1', 'selected', true);
|
graph.setItemState('node1', 'selected', true);
|
||||||
expect(nodeKeyShape1.style.fill).toBe(
|
|
||||||
LightTheme.node.styles[0].selected.keyShape.fill,
|
|
||||||
);
|
|
||||||
expect(nodeLabelShape1.style.fontWeight).toBe(
|
expect(nodeLabelShape1.style.fontWeight).toBe(
|
||||||
LightTheme.node.styles[0].selected.labelShape.fontWeight,
|
LightTheme.node.styles[0].selected.labelShape.fontWeight,
|
||||||
);
|
);
|
||||||
// edge setState with state in builtin theme
|
// edge setState with state in builtin theme
|
||||||
graph.setItemState('edge1', 'selected', true);
|
graph.setItemState('edge1', 'selected', true);
|
||||||
expect(edgeKeyShape1.style.stroke).toBe(
|
|
||||||
LightTheme.edge.styles[0].selected.keyShape.stroke,
|
|
||||||
);
|
|
||||||
expect(edgeLabelShape1.style.fill).toBe('#0f0'); // not assigned in selected theme
|
expect(edgeLabelShape1.style.fill).toBe('#0f0'); // not assigned in selected theme
|
||||||
expect(edgeLabelShape1.style.fontWeight).toBe(
|
expect(edgeLabelShape1.style.fontWeight).toBe(
|
||||||
LightTheme.edge.styles[0].selected.labelShape.fontWeight,
|
LightTheme.edge.styles[0].selected.labelShape.fontWeight,
|
||||||
@ -323,9 +405,6 @@ describe('theme', () => {
|
|||||||
// clear node's one state
|
// clear node's one state
|
||||||
graph.clearItemState('node1', ['state2']);
|
graph.clearItemState('node1', ['state2']);
|
||||||
expect(nodeKeyShape1.style.fill).toBe('#ff0');
|
expect(nodeKeyShape1.style.fill).toBe('#ff0');
|
||||||
expect(nodeKeyShape1.style.stroke).toBe(
|
|
||||||
LightTheme.node.styles[0].default.keyShape.stroke,
|
|
||||||
);
|
|
||||||
// clear edge's one state, state1 + state3 is kept
|
// clear edge's one state, state1 + state3 is kept
|
||||||
graph.clearItemState('edge1', ['state2']);
|
graph.clearItemState('edge1', ['state2']);
|
||||||
expect(edgeKeyShape1.style.stroke).toBe('#ff0');
|
expect(edgeKeyShape1.style.stroke).toBe('#ff0');
|
||||||
@ -483,17 +562,11 @@ describe('theme', () => {
|
|||||||
|
|
||||||
// node setState with state in builtin theme
|
// node setState with state in builtin theme
|
||||||
graph.setItemState('node1', 'selected', true);
|
graph.setItemState('node1', 'selected', true);
|
||||||
expect(nodeKeyShape1.style.fill).toBe(
|
|
||||||
LightTheme.node.styles[0].selected.keyShape.fill,
|
|
||||||
);
|
|
||||||
expect(nodeLabelShape1.style.fontWeight).toBe(
|
expect(nodeLabelShape1.style.fontWeight).toBe(
|
||||||
LightTheme.node.styles[0].selected.labelShape.fontWeight,
|
LightTheme.node.styles[0].selected.labelShape.fontWeight,
|
||||||
);
|
);
|
||||||
// edge setState with state in builtin theme
|
// edge setState with state in builtin theme
|
||||||
graph.setItemState('edge1', 'selected', true);
|
graph.setItemState('edge1', 'selected', true);
|
||||||
expect(edgeKeyShape1.style.stroke).toBe(
|
|
||||||
LightTheme.edge.styles[0].selected.keyShape.stroke,
|
|
||||||
);
|
|
||||||
expect(edgeLabelShape1.style.fill).toBe('#0f0'); // not assigned in selected theme
|
expect(edgeLabelShape1.style.fill).toBe('#0f0'); // not assigned in selected theme
|
||||||
expect(edgeLabelShape1.style.fontWeight).toBe(
|
expect(edgeLabelShape1.style.fontWeight).toBe(
|
||||||
LightTheme.edge.styles[0].selected.labelShape.fontWeight,
|
LightTheme.edge.styles[0].selected.labelShape.fontWeight,
|
||||||
@ -535,9 +608,6 @@ describe('theme', () => {
|
|||||||
// clear node's one state
|
// clear node's one state
|
||||||
graph.clearItemState('node1', ['state2']);
|
graph.clearItemState('node1', ['state2']);
|
||||||
expect(nodeKeyShape1.style.fill).toBe('#ff0');
|
expect(nodeKeyShape1.style.fill).toBe('#ff0');
|
||||||
expect(nodeKeyShape1.style.stroke).toBe(
|
|
||||||
LightTheme.node.styles[0].default.keyShape.stroke,
|
|
||||||
);
|
|
||||||
|
|
||||||
// clear edge's one state, state1 + state3 is kept
|
// clear edge's one state, state1 + state3 is kept
|
||||||
graph.clearItemState('edge1', ['state2']);
|
graph.clearItemState('edge1', ['state2']);
|
||||||
@ -572,7 +642,24 @@ describe('theme', () => {
|
|||||||
shapeMap: NodeShapeMap,
|
shapeMap: NodeShapeMap,
|
||||||
diffData?: { oldData: NodeModelData; newData: NodeModelData },
|
diffData?: { oldData: NodeModelData; newData: NodeModelData },
|
||||||
) {
|
) {
|
||||||
const extraShape = upsertShape(
|
const { labelShape: labelStyle } = this.mergedStyles;
|
||||||
|
return this.upsertShape(
|
||||||
|
'text',
|
||||||
|
'labelShape',
|
||||||
|
{
|
||||||
|
...labelStyle,
|
||||||
|
text: model.id,
|
||||||
|
},
|
||||||
|
shapeMap,
|
||||||
|
).shape;
|
||||||
|
}
|
||||||
|
public drawOtherShapes(
|
||||||
|
model: NodeDisplayModel,
|
||||||
|
shapeMap: NodeShapeMap,
|
||||||
|
diffData?: { oldData: NodeModelData; newData: NodeModelData },
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
extraShape: this.upsertShape(
|
||||||
'circle',
|
'circle',
|
||||||
'extraShape',
|
'extraShape',
|
||||||
{
|
{
|
||||||
@ -582,18 +669,8 @@ describe('theme', () => {
|
|||||||
y: 0,
|
y: 0,
|
||||||
},
|
},
|
||||||
shapeMap,
|
shapeMap,
|
||||||
);
|
).shape,
|
||||||
const { labelShape: labelStyle } = this.mergedStyles;
|
};
|
||||||
const labelShape = upsertShape(
|
|
||||||
'text',
|
|
||||||
'labelShape',
|
|
||||||
{
|
|
||||||
...labelStyle,
|
|
||||||
text: model.id,
|
|
||||||
},
|
|
||||||
shapeMap,
|
|
||||||
);
|
|
||||||
return { labelShape, extraShape };
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
class CustomEdge extends LineEdge {
|
class CustomEdge extends LineEdge {
|
||||||
@ -605,7 +682,7 @@ describe('theme', () => {
|
|||||||
const { keyShape } = shapeMap;
|
const { keyShape } = shapeMap;
|
||||||
const point = keyShape.getPoint(0.3);
|
const point = keyShape.getPoint(0.3);
|
||||||
return {
|
return {
|
||||||
buShape: upsertShape(
|
buShape: this.upsertShape(
|
||||||
'rect',
|
'rect',
|
||||||
'buShape',
|
'buShape',
|
||||||
{
|
{
|
||||||
@ -616,7 +693,7 @@ describe('theme', () => {
|
|||||||
fill: '#0f0',
|
fill: '#0f0',
|
||||||
},
|
},
|
||||||
shapeMap,
|
shapeMap,
|
||||||
),
|
).shape,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -645,6 +722,7 @@ describe('theme', () => {
|
|||||||
formatter: (model) => model.id,
|
formatter: (model) => model.id,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
otherShapes: {},
|
||||||
},
|
},
|
||||||
edge: {
|
edge: {
|
||||||
type: 'theme-spec-custom-edge',
|
type: 'theme-spec-custom-edge',
|
||||||
|
Loading…
Reference in New Issue
Block a user