Skip to content

Commit ebeab13

Browse files
Aarebeccaantv
and
antv
authored
refactor: use unified util to create plugin canvas (#6524)
* refactor(plugins): remove plugin utils under plugins dir * refactor: use unified util to create plugin canvas * refactor(plugins): legend draw in independent canvas * test: update snapshots * refactor: remove useless method * fix: fix comments --------- Co-authored-by: antv <[email protected]>
1 parent 4fd2afd commit ebeab13

File tree

19 files changed

+321
-1782
lines changed

19 files changed

+321
-1782
lines changed

packages/g6/__tests__/demos/plugin-legend.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export const pluginLegend: TestCase = async (context) => {
2222
}),
2323
},
2424
layout: { type: 'd3-force' },
25-
behaviors: ['drag-canvas', 'drag-element'],
25+
behaviors: ['drag-canvas', 'drag-element', 'zoom-canvas'],
2626
node: {
2727
type: (item: any) => {
2828
if (item.data.cluster === 'a') return 'diamond';

packages/g6/__tests__/snapshots/plugins/legend/click-again.svg

+1-298
Loading

packages/g6/__tests__/snapshots/plugins/legend/click.svg

+1-298
Loading

packages/g6/__tests__/snapshots/plugins/legend/mouseenter.svg

+1-298
Loading

packages/g6/__tests__/snapshots/plugins/legend/mouseleave.svg

+1-298
Loading

packages/g6/__tests__/snapshots/plugins/legend/normal.svg

+1-298
Loading
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { createPluginContainer, insertDOM } from '@/src/plugins/utils/dom';
2+
3+
describe('plugin dom utils', () => {
4+
it('createPluginContainer', () => {
5+
const el = createPluginContainer('test');
6+
expect(el.getAttribute('class')).toBe('g6-test');
7+
expect(el.style.position).toBe('absolute');
8+
expect(el.style.display).toBe('block');
9+
expect(el.style.inset).toBe('0px');
10+
expect(el.style.height).toBe('100%');
11+
expect(el.style.width).toBe('100%');
12+
expect(el.style.overflow).toBe('hidden');
13+
expect(el.style.pointerEvents).toBe('none');
14+
});
15+
16+
it('createPluginContainer cover=false', () => {
17+
const el = createPluginContainer('test', false);
18+
expect(el.getAttribute('class')).toBe('g6-test');
19+
expect(el.style.position).toBe('absolute');
20+
expect(el.style.display).toBe('block');
21+
expect(el.style.height).not.toBe('100%');
22+
expect(el.style.width).not.toBe('100%');
23+
expect(el.style.overflow).not.toBe('hidden');
24+
expect(el.style.pointerEvents).not.toBe('none');
25+
});
26+
27+
it('createPluginContainer with style', () => {
28+
const el = createPluginContainer('test', false, { color: 'red' });
29+
expect(el.getAttribute('class')).toBe('g6-test');
30+
expect(el.style.color).toBe('red');
31+
});
32+
33+
it('insertDOM', () => {
34+
insertDOM('g6-test', 'div', { color: 'red' }, 'test', document.body);
35+
36+
let el = document.getElementById('g6-test')!;
37+
expect(el).toBeTruthy();
38+
expect(el.style.color).toBe('red');
39+
expect(el.innerHTML).toBe('test');
40+
41+
insertDOM('g6-test', 'div', { color: 'red' }, 'new html', document.body);
42+
43+
el = document.getElementById('g6-test')!;
44+
expect(el.innerHTML).toBe('new html');
45+
46+
el = insertDOM('g6-test');
47+
expect(el.tagName.toLowerCase()).toBe('div');
48+
expect(el.innerHTML).toBe('');
49+
expect(el.parentNode).toBe(document.body);
50+
});
51+
});
+2-50
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { createPluginContainer, insertDOM, sizeOf } from '@/src/utils/dom';
1+
import { sizeOf } from '@/src/utils/dom';
22

3-
describe('sizeOf', () => {
3+
describe('dom', () => {
44
it('should return the size of the graph container', () => {
55
// Create a mock container element
66
const container = document.createElement('div');
@@ -13,52 +13,4 @@ describe('sizeOf', () => {
1313
// Assert the result
1414
expect(result).toEqual([500, 300]);
1515
});
16-
17-
it('createPluginContainer', () => {
18-
const el = createPluginContainer('test');
19-
expect(el.getAttribute('class')).toBe('g6-test');
20-
expect(el.style.position).toBe('absolute');
21-
expect(el.style.display).toBe('block');
22-
expect(el.style.inset).toBe('0px');
23-
expect(el.style.height).toBe('100%');
24-
expect(el.style.width).toBe('100%');
25-
expect(el.style.overflow).toBe('hidden');
26-
expect(el.style.pointerEvents).toBe('none');
27-
});
28-
29-
it('createPluginContainer cover=false', () => {
30-
const el = createPluginContainer('test', false);
31-
expect(el.getAttribute('class')).toBe('g6-test');
32-
expect(el.style.position).toBe('absolute');
33-
expect(el.style.display).toBe('block');
34-
expect(el.style.height).not.toBe('100%');
35-
expect(el.style.width).not.toBe('100%');
36-
expect(el.style.overflow).not.toBe('hidden');
37-
expect(el.style.pointerEvents).not.toBe('none');
38-
});
39-
40-
it('createPluginContainer with style', () => {
41-
const el = createPluginContainer('test', false, { color: 'red' });
42-
expect(el.getAttribute('class')).toBe('g6-test');
43-
expect(el.style.color).toBe('red');
44-
});
45-
46-
it('insertDOM', () => {
47-
insertDOM('g6-test', 'div', { color: 'red' }, 'test', document.body);
48-
49-
let el = document.getElementById('g6-test')!;
50-
expect(el).toBeTruthy();
51-
expect(el.style.color).toBe('red');
52-
expect(el.innerHTML).toBe('test');
53-
54-
insertDOM('g6-test', 'div', { color: 'red' }, 'new html', document.body);
55-
56-
el = document.getElementById('g6-test')!;
57-
expect(el.innerHTML).toBe('new html');
58-
59-
el = insertDOM('g6-test');
60-
expect(el.tagName.toLowerCase()).toBe('div');
61-
expect(el.innerHTML).toBe('');
62-
expect(el.parentNode).toBe(document.body);
63-
});
6416
});

packages/g6/src/plugins/background/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { omit } from '@antv/util';
22
import type { RuntimeContext } from '../../runtime/types';
3-
import { createPluginContainer } from '../../utils/dom';
43
import type { BasePluginOptions } from '../base-plugin';
54
import { BasePlugin } from '../base-plugin';
5+
import { createPluginContainer } from '../utils/dom';
66

77
/**
88
* <zh/> 背景配置项

packages/g6/src/plugins/contextmenu/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import type { RuntimeContext } from '../../runtime/types';
22
import type { Element } from '../../types';
33
import type { IElementEvent } from '../../types/event';
4-
import { createPluginContainer, insertDOM } from '../../utils/dom';
54
import type { BasePluginOptions } from '../base-plugin';
65
import { BasePlugin } from '../base-plugin';
6+
import { createPluginContainer, insertDOM } from '../utils/dom';
77
import type { Item } from './util';
88
import { CONTEXTMENU_CSS, getContentFromItems } from './util';
99
/**

packages/g6/src/plugins/grid-line.ts

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import { GraphEvent } from '../constants';
22
import type { RuntimeContext } from '../runtime/types';
33
import type { Point } from '../types';
4-
import { createPluginContainer, insertBefore } from '../utils/dom';
54
import { ViewportEvent } from '../utils/event';
65
import { add, mod } from '../utils/vector';
76
import { BasePlugin, BasePluginOptions } from './base-plugin';
7+
import { createPluginContainer } from './utils/dom';
88

99
/**
1010
* <zh/> 网格线配置项
@@ -98,15 +98,17 @@ export class GridLine extends BasePlugin<GridLineOptions> {
9898
stroke: '#eee',
9999
};
100100

101-
private $element: HTMLElement = createPluginContainer('grid-line');
101+
private $element: HTMLElement = createPluginContainer('grid-line', true, {
102+
zIndex: '-1',
103+
});
102104

103105
private offset: Point = [0, 0];
104106

105107
constructor(context: RuntimeContext, options: GridLineOptions) {
106108
super(context, Object.assign({}, GridLine.defaultOptions, options));
107109

108110
const $container = this.context.canvas.getContainer()!;
109-
insertBefore($container, this.$element);
111+
$container.prepend(this.$element);
110112

111113
this.updateStyle();
112114
this.bindEvents();

packages/g6/src/plugins/legend.ts

+67-55
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1-
import { Category, Layout, Selection } from '@antv/component';
1+
import { Category, Selection } from '@antv/component';
22
import { CategoryStyleProps } from '@antv/component/lib/ui/legend/types';
3+
import { Canvas } from '@antv/g';
34
import { get, isFunction } from '@antv/util';
45
import { GraphEvent } from '../constants';
56
import type { RuntimeContext } from '../runtime/types';
67
import type { ElementDatum, ElementType, ID, State } from '../types';
78
import type { CardinalPlacement } from '../types/placement';
89
import type { BasePluginOptions } from './base-plugin';
910
import { BasePlugin } from './base-plugin';
11+
import { createPluginCanvas } from './utils/canvas';
1012

1113
interface Datum extends Record<string, any> {
1214
id?: string;
@@ -40,6 +42,24 @@ export interface LegendOptions extends BasePluginOptions, Omit<CategoryStyleProp
4042
* @defaultValue 'bottom'
4143
*/
4244
position?: CardinalPlacement;
45+
/**
46+
* <zh/> 图例挂载的容器,无则挂载到 Graph 所在容器
47+
*
48+
* <en/> The container where the legend is mounted, if not, it will be mounted to the container where the Graph is located
49+
*/
50+
container?: HTMLElement | string;
51+
/**
52+
* <zh/> 图例画布类名,传入外置容器时不生效
53+
*
54+
* <en/> The class name of the legend canvas, which does not take effect when an external container is passed in
55+
*/
56+
className?: string;
57+
/**
58+
* <zh/> 图例的容器样式,传入外置容器时不生效
59+
*
60+
* <en/> The style of the legend container, which does not take effect when an external container is passed in
61+
*/
62+
containerStyle?: Partial<CSSStyleDeclaration>;
4363
/**
4464
* <zh/> 节点分类标识
4565
*
@@ -80,16 +100,20 @@ export class Legend extends BasePlugin<LegendOptions> {
80100
colPadding: 10,
81101
itemMarkerSize: 16,
82102
itemLabelFontSize: 16,
103+
width: 240,
104+
height: 160,
83105
};
84106
private typePrefix = '__data__';
85-
private element: Layout | null = null;
86107
private draw = false;
87108
private fieldMap = {
88109
node: new Map<string, ID[]>(),
89110
edge: new Map<string, ID[]>(),
90111
combo: new Map<string, ID[]>(),
91112
};
92113
private selectedItems: string[] = [];
114+
private category?: Category;
115+
private container?: HTMLElement;
116+
private canvas?: Canvas;
93117

94118
constructor(context: RuntimeContext, options: LegendOptions) {
95119
super(context, Object.assign({}, Legend.defaultOptions, options));
@@ -110,8 +134,11 @@ export class Legend extends BasePlugin<LegendOptions> {
110134
}
111135

112136
private clear() {
113-
this.element?.destroy();
114-
this.element = null;
137+
this.canvas?.destroy();
138+
this.container?.remove();
139+
this.canvas = undefined;
140+
this.container = undefined;
141+
115142
this.draw = false;
116143
}
117144

@@ -184,10 +211,9 @@ export class Legend extends BasePlugin<LegendOptions> {
184211
* <en/> Refresh the status of the legend element
185212
*/
186213
public updateElement() {
187-
if (!this.element) return;
188-
const category = this.element.getChildByIndex(0) as Category;
214+
if (!this.category) return;
189215

190-
category.update({
216+
this.category.update({
191217
itemMarkerOpacity: ({ id }) => {
192218
if (!this.selectedItems.length || this.selectedItems.includes(id)) return 1;
193219
return 0.5;
@@ -224,7 +250,7 @@ export class Legend extends BasePlugin<LegendOptions> {
224250

225251
private getMarkerData = (field: string | ((item: ElementDatum) => string), elementType: ElementType) => {
226252
if (!field) return [];
227-
const { model, element, graph } = this.context;
253+
const { model, element } = this.context;
228254
const { nodes, edges, combos } = model.getData();
229255
const items: { [key: string]: Datum } = {};
230256

@@ -239,7 +265,8 @@ export class Legend extends BasePlugin<LegendOptions> {
239265
combo: 'rect',
240266
};
241267

242-
/** 用于将 G6 element 转换为 componets 支持的类型 */
268+
// 用于将 G6 element 转换为 components 支持的类型
269+
// Used to convert G6 element to types supported by components
243270
const markerMapping: { [key: string]: string } = {
244271
circle: 'circle',
245272
ellipse: 'circle', // 待 components 支持 ellipse
@@ -303,69 +330,52 @@ export class Legend extends BasePlugin<LegendOptions> {
303330
return Object.values(items);
304331
};
305332

306-
/**
307-
* <zh/> 图例布局
308-
*
309-
* <en/> Legend layout
310-
* @param position - <zh/> 图例位置| <en/> Legend position
311-
* @returns <zh/> 图例布局样式| <en/> Legend layout style
312-
*/
313-
public layout = (position: CardinalPlacement) => {
314-
const preset = {
315-
flexDirection: 'row',
316-
alignItems: 'flex-end',
317-
justifyContent: 'center',
318-
};
319-
let { flexDirection, alignItems, justifyContent } = preset;
333+
private upsertCanvas() {
334+
if (this.canvas) return this.canvas;
320335

321-
const layout = {
322-
top: ['row', 'flex-start', 'center'],
323-
bottom: ['row', 'flex-end', 'center'],
324-
left: ['column', 'flex-start', 'center'],
325-
right: ['column', 'flex-end', 'center'],
326-
};
336+
const graphCanvas = this.context.canvas;
337+
const [canvasWidth, canvasHeight] = graphCanvas.getSize();
327338

328-
if (position in layout) {
329-
[flexDirection, alignItems, justifyContent] = layout[position];
330-
}
331-
return {
332-
display: 'flex',
333-
flexDirection,
334-
justifyContent,
335-
alignItems,
336-
};
337-
};
339+
const { width = canvasWidth, height = canvasHeight, position, container, containerStyle, className } = this.options;
340+
const [$container, canvas] = createPluginCanvas({
341+
width,
342+
height,
343+
graphCanvas,
344+
container,
345+
containerStyle,
346+
placement: position,
347+
className: 'legend',
348+
});
349+
350+
this.container = $container;
351+
if (className) $container.classList.add(className);
352+
this.canvas = canvas;
353+
354+
return this.canvas;
355+
}
338356

339357
private createElement = () => {
340358
if (this.draw) {
341359
this.updateElement();
342360
return;
343361
}
344-
const { canvas } = this.context;
345-
const [canvasWidth, canvasHeight] = canvas.getSize();
346362
const {
347-
width = canvasWidth,
348-
height = canvasHeight,
363+
width,
364+
height,
349365
nodeField,
350366
edgeField,
351367
comboField,
352368
trigger,
353369
position,
370+
container,
371+
containerStyle,
372+
className,
354373
...rest
355374
} = this.options;
356375
const nodeItems = this.getMarkerData(nodeField, 'node');
357376
const edgeItems = this.getMarkerData(edgeField, 'edge');
358377
const comboItems = this.getMarkerData(comboField, 'combo');
359378
const items = [...nodeItems, ...comboItems, ...edgeItems];
360-
const layout = this.layout(position);
361-
362-
const layoutWrapper = new Layout({
363-
style: {
364-
width,
365-
height,
366-
...layout,
367-
},
368-
});
369379

370380
const categoryStyle = Object.assign(
371381
{
@@ -386,9 +396,11 @@ export class Legend extends BasePlugin<LegendOptions> {
386396
className: 'legend',
387397
style: categoryStyle,
388398
});
389-
layoutWrapper.appendChild(category);
390-
canvas.appendChild(layoutWrapper as any);
391-
this.element = layoutWrapper;
399+
this.category = category;
400+
401+
const canvas = this.upsertCanvas();
402+
canvas.appendChild(category);
403+
392404
this.draw = true;
393405
};
394406

0 commit comments

Comments
 (0)