15 KiB
title | order |
---|---|
Custom Combo | 2 |
G6 provides two types of Built-in Combos: circle, [rect](/en/docs/manual/middle/elements/combos/built-in/rect. Besides, the custom machanism allows the users to extend the built-in Combos to design their own type of nodes by G6.registerCombo('comboName', options, expendedComboName)
. A combo with complex graphics shapes, complex interactions, fantastic animations can be implemented easily.
In this document, we will introduce the custom combo mechanism by two examples:
1. Extend the Rect Combo;
2. Extend the Circle Combo.
The API of Register Combo
As stated in Shape, there are two points should be satisfied when customize a combo:
- Controll the life cycle of the combo;
- Analyze the input data and show it by graphics.
The API and the methods which can be rewritten when extending a built-in combo are shown below:
G6.regitserCombo(
'comboName',
{
/**
* Draw the shapes of the Combo.
* Do not need the label shape, it will be added by the extended class
* @param {Object} cfg The configurations of the combo
* @param {G.Group} group Graphics group, the container of the shapes of the combo
* @return {G.Shape} The keyShape of the combo. It can be obtained by combo.get('keyShape')
* More details about keyShape can be found in Middle-Graph Elements-Graphis Shape and keyShape
*/
drawShape(cfg, group) {},
/**
* The extra operations after drawing the combo. There is no operation in this function by default
* @param {Object} cfg The configurations of the combo
* @param {G.Group} group Graphics group, the container of the shapes of the combo
*/
afterDraw(cfg, group) {},
/**
* The operations after updating the combo.
* Control the update logic of the new graphic shapes expect keyShape here
* @override
* @param {Object} cfg The configurations of the combo
* @param {Combo} combo The combo item
*/
afterUpdate(cfg, combo) {},
/**
* Response the combo states change.
* Should be rewritten when you want to response the state changes by animation.
* Responsing the state changes by styles can be configured, which is described in the document Middle-Behavior & Event-State
* @param {String} name The name of the state
* @param {Object} value The value of the state
* @param {Combo} combo The combo item
*/
setState(name, value, combo) {},
},
// the type name of the extended Combo, options: 'circle' or 'rect'
extendComboName,
);
Attention
Since the updating logic of Combo is special (upate the size and position according to the children automatically), registering a combo is kind of different from regitering a node or an edge:
- It is not recommended to customize a Combo without extending a built-in Combo, you should extend the built-in 'circle' or 'rect' Combo;
- Do not add text shape for label in
drawShape
, it will be added and updated automatically by the base class; - Different from registering a node or an edge, it is not recommended to rewritten
update
anddraw
, or the updating logic will be abnormal; - The rewirtten
drawShape
should return the same type of keyShape as the keyShape of the extended Combo. Means that return a circle shape if you are extending the circle Combo, rect shape if you are extending the rect Combo; - The updating logic of new shapes expect the keyShape and the label should be defined in
afterUpdate
; setState
should be override when you want to response the state changes by animation. Responsing the state changes by simple styles can be achieved by Configure Styles for State.
1. Extend the Rect Combo
Demo.
Illustration of Built-in Rect Combo
As shown in the figure below, the position logic of built-in rect Combo:
- The area boxed by the grey dashed rectangle is the area of the Combo's children to be positioned. innerWidth and innerHeight are the width and the height of the area respectively;
- The padding around the grey dashed area can be configured, and the real drawing width/height of the keyShape is equal to the innerWidth/innerHeight plus padding values;
- The shapes inside the combo uses the self coordinate system with origin (0, 0) centered at the center of the dashed area;
- The top and bottom padding, left and right padding are different, which leads to result that the (x, y) of the keyShape rect's left-top is not simply equal to (-width / 2, -height / 2), but calculated as shown in the figure;
- The default label of the rect Combo is positioned on the left-top inside the keyShape rect with refY to the top border and refX to the left border. The
position
,refX
, andrefY
can be configured while using the Combo.
Illustration of Built-in Rect Combo
Render the Combo
Now, we are going to register a Combo as shown below (the figure below shows an empty combo):
According to the Illustration of Built-in Rect Combo, please be caution about the x
, y
, width
, height
of the shapes when extending the rect Combo.
G6.registerCombo(
'cRect',
{
drawShape: function drawShape(cfg, group) {
const self = this;
// Get the padding from the configuration
cfg.padding = cfg.padding || [50, 20, 20, 20];
// Get the shape's style, where the style.width and style.height correspond to the width and height in the figure of Illustration of Built-in Rect Combo
const style = self.getShapeStyle(cfg);
// Add a rect shape as the keyShape which is the same as the extended rect Combo
const rect = group.addShape('rect', {
attrs: {
...style,
x: -style.width / 2 - (cfg.padding[3] - cfg.padding[1]) / 2,
y: -style.height / 2 - (cfg.padding[0] - cfg.padding[2]) / 2,
width: style.width,
height: style.height,
},
draggable: true,
name: 'combo-keyShape', // must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
});
// Add the circle on the right
group.addShape('circle', {
attrs: {
...style,
fill: '#fff',
opacity: 1,
// cfg.style.width and cfg.style.heigth correspond to the innerWidth and innerHeight in the figure of Illustration of Built-in Rect Combo
x: cfg.style.width / 2 + cfg.padding[1],
y: (cfg.padding[2] - cfg.padding[0]) / 2,
r: 5,
},
draggable: true,
name: 'combo-circle-shape', // must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
});
return rect;
},
// Define the updating logic of the right circle
afterUpdate: function afterUpdate(cfg, combo) {
const group = combo.get('group');
// Find the circle shape in the graphics group of the Combo by name
const circle = group.find((ele) => ele.get('name') === 'combo-circle-shape');
// Update the position of the right circle
circle.attr({
// cfg.style.width and cfg.style.heigth correspond to the innerWidth and innerHeight in the figure of Illustration of Built-in Rect Combo
x: cfg.style.width / 2 + cfg.padding[1],
y: (cfg.padding[2] - cfg.padding[0]) / 2,
});
},
},
'rect',
);
Attention: you need to assign name
and draggable
for the shapes added in the custom node, where the value of name
must be unique in a custom node/edge/combo type. draggable: true
means that the shape is allowed to response the drag events. Only when draggable: true
, the interaction behavior 'drag-node'
can be responsed on this shape. In the codes above, if you only assign draggable: true
to the keyShape
but not the right circle shape, the drag events will only be responsed on the keyShape
.
Use the Custom Combo
The following code uses the 'cRect'
Combo:
const data = {
nodes: [
{ id: 'node1', x: 250, y: 100, comboId: 'combo1' },
{ id: 'node2', x: 300, y: 100, comboId: 'combo1' },
],
combos: [
{ id: 'combo1', label: 'Combo 1', parentId: 'combo2' },
{ id: 'combo2', label: 'Combo 2' },
{ id: 'combo3', label: 'Combo 3' },
],
};
const graph = new G6.Graph({
container: 'mountNode',
width: 800,
height: 800,
// Configure the combos globally
defaultCombo: {
// The type of the combos. You can also assign type in the data of combos
type: 'cRect',
// ... Other global configurations for combos
},
});
graph.data(data);
graph.render();
2. Extend the Circle Combo
Demo.
Illustration of Built-in Circle Combo
As shown in the figure below, the position logic of built-in circle Combo is much more simple thant rect Combo, where the (x, y) is the center of the circle, and the padding
is a number:
- The area boxed by the grey dashed circle is the area of the Combo's children to be positioned. innerR is the radius of the area;
- The padding around the grey dashed area can be configured, and the real drawing radius of the keyShape R = innerR + padding;
- The shapes inside the combo uses the self coordinate system with origin (0, 0) centered at the center of the circle;
- The padding around the circle is even;
- The default label of the circle Combo is positioned on the top outside the keyShape circle with refY to the top border. The
position
,refX
, andrefY
can be configured while using the Combo.
Illustration of Built-in Rect Combo
Render the Combo
Now, we are going to register a Combo as shown below (the figure below shows an empty combo):
// The symbols for the marker inside the combo
const collapseIcon = (x, y, r) => {
return [
['M', x - r, y],
['a', r, r, 0, 1, 0, r * 2, 0],
['a', r, r, 0, 1, 0, -r * 2, 0],
['M', x - r + 4, y],
['L', x - r + 2 * r - 4, y],
];
};
const expandIcon = (x, y, r) => {
return [
['M', x - r, y],
['a', r, r, 0, 1, 0, r * 2, 0],
['a', r, r, 0, 1, 0, -r * 2, 0],
['M', x - r + 4, y],
['L', x - r + 2 * r - 4, y],
['M', x - r + r, y - r + 4],
['L', x, y + r - 4],
];
};
G6.registerCombo(
'cCircle',
{
drawShape: function draw(cfg, group) {
const self = this;
// Get the shape style, where the style.r corresponds to the R in the Illustration of Built-in Rect Combo
const style = self.getShapeStyle(cfg);
// Add a circle shape as keyShape which is the same as the extended 'circle' type Combo
const circle = group.addShape('circle', {
attrs: {
...style,
x: 0,
y: 0,
r: style.r,
},
draggable: true,
name: 'combo-keyShape', // must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
});
// Add the marker on the bottom
const marker = group.addShape('marker', {
attrs: {
...style,
fill: '#fff',
opacity: 1,
x: 0,
y: style.r,
r: 10,
symbol: collapseIcon,
},
draggable: true,
name: 'combo-marker-shape', // must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
});
return circle;
},
// Define the updating logic for the marker
afterUpdate: function afterUpdate(cfg, combo) {
const self = this;
// Get the shape style, where the style.r corresponds to the R in the Illustration of Built-in Rect Combo
const style = self.getShapeStyle(cfg);
const group = combo.get('group');
// Find the marker shape in the graphics group of the Combo
const marker = group.find((ele) => ele.get('name') === 'combo-marker-shape');
// Update the marker shape
marker.attr({
x: 0,
y: style.r,
// The property 'collapsed' in the combo data represents the collapsing state of the Combo
// Update the symbol according to 'collapsed'
symbol: cfg.collapsed ? expandIcon : collapseIcon,
});
},
},
'circle',
);
Attention: you need to assign name
and draggable
for the shapes added in the custom node, where the name
can be not unique with any value you want. draggable: true
means that the shape is allowed to response the drag events. Only when draggable: true
, the interaction behavior 'drag-node'
can be responsed on this shape. In the codes above, if you only assign draggable: true
to the keyShape
but not the bottom marker shape, the drag events will only be responsed on the keyShape
.
Use the Custom Combo
The following code uses the 'cCircle'
Combo:
const data = {
nodes: [
{ id: 'node1', x: 250, y: 100, comboId: 'combo1' },
{ id: 'node2', x: 300, y: 100, comboId: 'combo1' },
],
combos: [
{ id: 'combo1', label: 'Combo 1', parentId: 'combo2' },
{ id: 'combo2', label: 'Combo 2' },
{ id: 'combo3', label: 'Combo 3' },
],
};
const graph = new G6.Graph({
container: 'mountNode',
width: 800,
height: 800,
// Configure the combos globally
defaultCombo: {
// The type of the combos. You can also assign type in the data of combos
type: 'cCircle',
labelCfg: {
refY: 2,
},
// ... Other global configurations for combos
},
modes: {
default: [
// The behavior to collapse/expand the Combo by double click
// It modifies the property 'collapsed' of the combo data
'collapse-expand-combo',
],
},
});
graph.data(data);
graph.render();
Custom Behavior
In the code above, we configured 'collapse-expand-combo'
for the graph, which means allowing user to double click a combo to make it collapsed or expanded. To expand/collapse the combo when clicking the marker instead of double clicking the combo, remove 'collapse-expand-combo'
configuration, and append the following code:
// collapse/expand when click the marker
graph.on('combo:click', (e) => {
if (e.target.get('name') === 'combo-marker-shape') {
// Collapse or expand the combo
graph.collapseExpandCombo(e.item);
if (graph.get('layout')) graph.layout();
// If there is a layout configured on the graph, relayout
else graph.refreshPositions(); // Refresh positions for items otherwise
}
});