mirror of
https://gitee.com/antv/g6.git
synced 2024-11-29 18:28:19 +08:00
fix: when update styles of node, it's react node does't update. (#6208)
* feat: shape upsert support hook callback * fix: fix g node doesnot update when node props change * test: update test case --------- Co-authored-by: antv <antv@antfin.com>
This commit is contained in:
parent
7cb9d269e0
commit
b0453225a8
@ -1,4 +1,4 @@
|
||||
import type { NodeData } from '@antv/g6';
|
||||
import type { Graph as G6Graph, NodeData } from '@antv/g6';
|
||||
import { ExtensionCategory, register } from '@antv/g6';
|
||||
import { GNode, Group, Image, Rect, Text } from '../../src';
|
||||
import { Graph } from '../../src/graph';
|
||||
@ -14,8 +14,9 @@ type Datum = {
|
||||
failure: number;
|
||||
};
|
||||
|
||||
const Node = ({ data, size }: { data: NodeData; size: [number, number] }) => {
|
||||
const Node = ({ graph, data, size }: { graph: G6Graph; data: NodeData; size: [number, number] }) => {
|
||||
const [width, height] = size;
|
||||
const { lineWidth = 1 } = graph.getElementRenderStyle(data.id);
|
||||
|
||||
const { name, type, status, success, time, failure } = data.data as Datum;
|
||||
const color = status === 'success' ? '#30BF78' : '#F4664A';
|
||||
@ -51,7 +52,14 @@ const Node = ({ data, size }: { data: NodeData; size: [number, number] }) => {
|
||||
|
||||
return (
|
||||
<Group>
|
||||
<Rect width={width} height={height} stroke={color} fill={'white'} radius={radius}>
|
||||
<Rect
|
||||
width={width}
|
||||
height={height}
|
||||
stroke={color}
|
||||
fill={'white'}
|
||||
radius={radius}
|
||||
lineWidth={lineWidth ? lineWidth : 1}
|
||||
>
|
||||
<Rect width={width} height={20} fill={color} radius={[radius, radius, 0, 0]}>
|
||||
<Image
|
||||
src={
|
||||
@ -102,10 +110,12 @@ export const GNodeDemo = () => {
|
||||
type: 'g',
|
||||
style: {
|
||||
size: [180, 60],
|
||||
component: (data: NodeData) => <Node data={data} size={[180, 60]} />,
|
||||
component: function (this: G6Graph, data: NodeData) {
|
||||
return <Node graph={this} data={data} size={[180, 60]} />;
|
||||
},
|
||||
},
|
||||
},
|
||||
behaviors: ['drag-element', 'zoom-canvas', 'drag-canvas'],
|
||||
behaviors: ['drag-element', 'zoom-canvas', 'drag-canvas', 'click-select'],
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
@ -1,5 +1,5 @@
|
||||
import type { DisplayObjectConfig } from '@antv/g';
|
||||
import { ElementEvent, Group } from '@antv/g';
|
||||
import { Group } from '@antv/g';
|
||||
import type { BaseNodeStyleProps } from '@antv/g6';
|
||||
import { Rect } from '@antv/g6';
|
||||
import { render } from '@antv/react-g';
|
||||
@ -29,15 +29,13 @@ export class GNode extends Rect {
|
||||
const { component } = attributes;
|
||||
const [width, height] = this.getSize();
|
||||
|
||||
const dom = this.upsert('key', Group, { width, height }, container)!;
|
||||
|
||||
dom.isMutationObserved = true;
|
||||
dom.addEventListener(ElementEvent.MOUNTED, () => {
|
||||
// component 已经被回调机制自动创建为 ReactNode
|
||||
// component has been automatically created as ReactNode by the callback mechanism
|
||||
render(component as unknown as ReactNode, dom);
|
||||
return this.upsert('key', Group, { width, height }, container, {
|
||||
afterCreate: (dom) => {
|
||||
render(component as unknown as ReactNode, dom);
|
||||
},
|
||||
afterUpdate: (dom) => {
|
||||
render(component as unknown as ReactNode, dom);
|
||||
},
|
||||
});
|
||||
|
||||
return dom;
|
||||
}
|
||||
}
|
||||
|
62
packages/g6/__tests__/unit/elements/shape.spec.ts
Normal file
62
packages/g6/__tests__/unit/elements/shape.spec.ts
Normal file
@ -0,0 +1,62 @@
|
||||
import type { BaseShapeStyleProps } from '@/src';
|
||||
import { BaseShape } from '@/src';
|
||||
import { Circle } from '@antv/g';
|
||||
|
||||
describe('element shape', () => {
|
||||
it('upsert hooks', () => {
|
||||
interface ShapeStyleProps extends BaseShapeStyleProps {
|
||||
shape: any;
|
||||
}
|
||||
|
||||
const beforeCreate = jest.fn();
|
||||
const afterCreate = jest.fn();
|
||||
const beforeUpdate = jest.fn();
|
||||
const afterUpdate = jest.fn();
|
||||
const beforeDestroy = jest.fn();
|
||||
const afterDestroy = jest.fn();
|
||||
|
||||
class Shape extends BaseShape<ShapeStyleProps> {
|
||||
render() {
|
||||
this.upsert('circle', Circle, this.attributes.shape, this, {
|
||||
beforeCreate,
|
||||
afterCreate,
|
||||
beforeUpdate,
|
||||
afterUpdate,
|
||||
beforeDestroy,
|
||||
afterDestroy,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const shape = new Shape({
|
||||
style: {
|
||||
shape: { r: 10 },
|
||||
},
|
||||
});
|
||||
|
||||
expect(beforeCreate).toHaveBeenCalledTimes(1);
|
||||
expect(afterCreate).toHaveBeenCalledTimes(1);
|
||||
expect(beforeUpdate).toHaveBeenCalledTimes(0);
|
||||
expect(afterUpdate).toHaveBeenCalledTimes(0);
|
||||
expect(beforeDestroy).toHaveBeenCalledTimes(0);
|
||||
expect(afterDestroy).toHaveBeenCalledTimes(0);
|
||||
|
||||
shape.update({ shape: { r: 20 } });
|
||||
|
||||
expect(beforeCreate).toHaveBeenCalledTimes(1);
|
||||
expect(afterCreate).toHaveBeenCalledTimes(1);
|
||||
expect(beforeUpdate).toHaveBeenCalledTimes(1);
|
||||
expect(afterUpdate).toHaveBeenCalledTimes(1);
|
||||
expect(beforeDestroy).toHaveBeenCalledTimes(0);
|
||||
expect(afterDestroy).toHaveBeenCalledTimes(0);
|
||||
|
||||
shape.update({ shape: false });
|
||||
|
||||
expect(beforeCreate).toHaveBeenCalledTimes(1);
|
||||
expect(afterCreate).toHaveBeenCalledTimes(1);
|
||||
expect(beforeUpdate).toHaveBeenCalledTimes(1);
|
||||
expect(afterUpdate).toHaveBeenCalledTimes(1);
|
||||
expect(beforeDestroy).toHaveBeenCalledTimes(1);
|
||||
expect(afterDestroy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
@ -46,6 +46,7 @@ export abstract class BaseShape<StyleProps extends BaseShapeStyleProps> extends
|
||||
* @param Ctor - <zh> 图形类型 | <en> shape type
|
||||
* @param style - <zh> 图形样式 | <en> shape style
|
||||
* @param container - <zh> 容器 | <en> container
|
||||
* @param hooks - <zh> 钩子函数 | <en> hooks
|
||||
* @returns <zh> 图形实例 | <en> shape instance
|
||||
*/
|
||||
protected upsert<T extends DisplayObject>(
|
||||
@ -53,30 +54,41 @@ export abstract class BaseShape<StyleProps extends BaseShapeStyleProps> extends
|
||||
Ctor: { new (...args: any[]): T },
|
||||
style: T['attributes'] | false,
|
||||
container: DisplayObject,
|
||||
hooks?: UpsertHooks,
|
||||
): T | undefined {
|
||||
const target = this.shapeMap[className] as T | undefined;
|
||||
// remove
|
||||
// 如果 style 为 false,则删除图形 / remove shape if style is false
|
||||
if (style === false) {
|
||||
if (target) {
|
||||
hooks?.beforeDestroy?.(target);
|
||||
container.removeChild(target);
|
||||
delete this.shapeMap[className];
|
||||
hooks?.afterDestroy?.(target);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// create
|
||||
if (!target || target.destroyed || !(target instanceof Ctor)) {
|
||||
target?.destroy();
|
||||
if (target) {
|
||||
hooks?.beforeDestroy?.(target);
|
||||
target?.destroy();
|
||||
hooks?.afterDestroy?.(target);
|
||||
}
|
||||
|
||||
hooks?.beforeCreate?.();
|
||||
const instance = new Ctor({ className, style });
|
||||
container.appendChild(instance);
|
||||
this.shapeMap[className] = instance;
|
||||
hooks?.afterCreate?.(instance);
|
||||
return instance;
|
||||
}
|
||||
|
||||
// update
|
||||
hooks?.beforeUpdate?.(target);
|
||||
updateStyle(target, style);
|
||||
hooks?.afterUpdate?.(target);
|
||||
|
||||
return target;
|
||||
}
|
||||
@ -248,3 +260,52 @@ function releaseAnimation(target: DisplayObject, animation: IAnimation) {
|
||||
if (index > -1) target.activeAnimations.splice(index, 1);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* <zh/> 图形 upsert 方法生命周期钩子
|
||||
*
|
||||
* <en/> Shape upsert method lifecycle hooks
|
||||
*/
|
||||
export interface UpsertHooks {
|
||||
/**
|
||||
* <zh/> 图形创建前
|
||||
*
|
||||
* <en/> Before creating the shape
|
||||
*/
|
||||
beforeCreate?: () => void;
|
||||
/**
|
||||
* <zh/> 图形创建后
|
||||
*
|
||||
* <en/> After creating the shape
|
||||
* @param instance - <zh/> 图形实例 | <en/> shape instance
|
||||
*/
|
||||
afterCreate?: (instance: DisplayObject) => void;
|
||||
/**
|
||||
* <zh/> 图形更新前
|
||||
*
|
||||
* <en/> Before updating the shape
|
||||
* @param instance - <zh/> 图形实例 | <en/> shape instance
|
||||
*/
|
||||
beforeUpdate?: (instance: DisplayObject) => void;
|
||||
/**
|
||||
* <zh/> 图形更新后
|
||||
*
|
||||
* <en/> After updating the shape
|
||||
* @param instance - <zh/> 图形实例 | <en/> shape instance
|
||||
*/
|
||||
afterUpdate?: (instance: DisplayObject) => void;
|
||||
/**
|
||||
* <zh/> 图形销毁前
|
||||
*
|
||||
* <en/> Before destroying the shape
|
||||
* @param instance - <zh/> 图形实例 | <en/> shape instance
|
||||
*/
|
||||
beforeDestroy?: (instance: DisplayObject) => void;
|
||||
/**
|
||||
* <zh/> 图形销毁后
|
||||
*
|
||||
* <en/> After destroying the shape
|
||||
* @param instance - <zh/> 图形实例 | <en/> shape instance
|
||||
*/
|
||||
afterDestroy?: (instance: DisplayObject) => void;
|
||||
}
|
||||
|
@ -159,6 +159,7 @@ export type {
|
||||
LabelStyleProps,
|
||||
PolygonStyleProps,
|
||||
} from './elements/shapes';
|
||||
export type { UpsertHooks } from './elements/shapes/base-shape';
|
||||
export type { ContourLabelStyleProps, ContourStyleProps } from './elements/shapes/contour';
|
||||
export type { BaseLayoutOptions, WebWorkerLayoutOptions } from './layouts/types';
|
||||
export type { CategoricalPalette } from './palettes/types';
|
||||
|
Loading…
Reference in New Issue
Block a user