Skip to content

Commit

Permalink
refactor: use unified util to create plugin canvas (#6524)
Browse files Browse the repository at this point in the history
* 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]>
  • Loading branch information
Aarebecca and antv authored Nov 15, 2024
1 parent 4fd2afd commit ebeab13
Show file tree
Hide file tree
Showing 19 changed files with 321 additions and 1,782 deletions.
2 changes: 1 addition & 1 deletion packages/g6/__tests__/demos/plugin-legend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export const pluginLegend: TestCase = async (context) => {
}),
},
layout: { type: 'd3-force' },
behaviors: ['drag-canvas', 'drag-element'],
behaviors: ['drag-canvas', 'drag-element', 'zoom-canvas'],
node: {
type: (item: any) => {
if (item.data.cluster === 'a') return 'diamond';
Expand Down
299 changes: 1 addition & 298 deletions packages/g6/__tests__/snapshots/plugins/legend/click-again.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
299 changes: 1 addition & 298 deletions packages/g6/__tests__/snapshots/plugins/legend/click.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
299 changes: 1 addition & 298 deletions packages/g6/__tests__/snapshots/plugins/legend/mouseenter.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
299 changes: 1 addition & 298 deletions packages/g6/__tests__/snapshots/plugins/legend/mouseleave.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
299 changes: 1 addition & 298 deletions packages/g6/__tests__/snapshots/plugins/legend/normal.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
51 changes: 51 additions & 0 deletions packages/g6/__tests__/unit/plugins/utils/dom.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { createPluginContainer, insertDOM } from '@/src/plugins/utils/dom';

describe('plugin dom utils', () => {
it('createPluginContainer', () => {
const el = createPluginContainer('test');
expect(el.getAttribute('class')).toBe('g6-test');
expect(el.style.position).toBe('absolute');
expect(el.style.display).toBe('block');
expect(el.style.inset).toBe('0px');
expect(el.style.height).toBe('100%');
expect(el.style.width).toBe('100%');
expect(el.style.overflow).toBe('hidden');
expect(el.style.pointerEvents).toBe('none');
});

it('createPluginContainer cover=false', () => {
const el = createPluginContainer('test', false);
expect(el.getAttribute('class')).toBe('g6-test');
expect(el.style.position).toBe('absolute');
expect(el.style.display).toBe('block');
expect(el.style.height).not.toBe('100%');
expect(el.style.width).not.toBe('100%');
expect(el.style.overflow).not.toBe('hidden');
expect(el.style.pointerEvents).not.toBe('none');
});

it('createPluginContainer with style', () => {
const el = createPluginContainer('test', false, { color: 'red' });
expect(el.getAttribute('class')).toBe('g6-test');
expect(el.style.color).toBe('red');
});

it('insertDOM', () => {
insertDOM('g6-test', 'div', { color: 'red' }, 'test', document.body);

let el = document.getElementById('g6-test')!;
expect(el).toBeTruthy();
expect(el.style.color).toBe('red');
expect(el.innerHTML).toBe('test');

insertDOM('g6-test', 'div', { color: 'red' }, 'new html', document.body);

el = document.getElementById('g6-test')!;
expect(el.innerHTML).toBe('new html');

el = insertDOM('g6-test');
expect(el.tagName.toLowerCase()).toBe('div');
expect(el.innerHTML).toBe('');
expect(el.parentNode).toBe(document.body);
});
});
52 changes: 2 additions & 50 deletions packages/g6/__tests__/unit/utils/dom.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { createPluginContainer, insertDOM, sizeOf } from '@/src/utils/dom';
import { sizeOf } from '@/src/utils/dom';

describe('sizeOf', () => {
describe('dom', () => {
it('should return the size of the graph container', () => {
// Create a mock container element
const container = document.createElement('div');
Expand All @@ -13,52 +13,4 @@ describe('sizeOf', () => {
// Assert the result
expect(result).toEqual([500, 300]);
});

it('createPluginContainer', () => {
const el = createPluginContainer('test');
expect(el.getAttribute('class')).toBe('g6-test');
expect(el.style.position).toBe('absolute');
expect(el.style.display).toBe('block');
expect(el.style.inset).toBe('0px');
expect(el.style.height).toBe('100%');
expect(el.style.width).toBe('100%');
expect(el.style.overflow).toBe('hidden');
expect(el.style.pointerEvents).toBe('none');
});

it('createPluginContainer cover=false', () => {
const el = createPluginContainer('test', false);
expect(el.getAttribute('class')).toBe('g6-test');
expect(el.style.position).toBe('absolute');
expect(el.style.display).toBe('block');
expect(el.style.height).not.toBe('100%');
expect(el.style.width).not.toBe('100%');
expect(el.style.overflow).not.toBe('hidden');
expect(el.style.pointerEvents).not.toBe('none');
});

it('createPluginContainer with style', () => {
const el = createPluginContainer('test', false, { color: 'red' });
expect(el.getAttribute('class')).toBe('g6-test');
expect(el.style.color).toBe('red');
});

it('insertDOM', () => {
insertDOM('g6-test', 'div', { color: 'red' }, 'test', document.body);

let el = document.getElementById('g6-test')!;
expect(el).toBeTruthy();
expect(el.style.color).toBe('red');
expect(el.innerHTML).toBe('test');

insertDOM('g6-test', 'div', { color: 'red' }, 'new html', document.body);

el = document.getElementById('g6-test')!;
expect(el.innerHTML).toBe('new html');

el = insertDOM('g6-test');
expect(el.tagName.toLowerCase()).toBe('div');
expect(el.innerHTML).toBe('');
expect(el.parentNode).toBe(document.body);
});
});
2 changes: 1 addition & 1 deletion packages/g6/src/plugins/background/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { omit } from '@antv/util';
import type { RuntimeContext } from '../../runtime/types';
import { createPluginContainer } from '../../utils/dom';
import type { BasePluginOptions } from '../base-plugin';
import { BasePlugin } from '../base-plugin';
import { createPluginContainer } from '../utils/dom';

/**
* <zh/> 背景配置项
Expand Down
2 changes: 1 addition & 1 deletion packages/g6/src/plugins/contextmenu/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import type { RuntimeContext } from '../../runtime/types';
import type { Element } from '../../types';
import type { IElementEvent } from '../../types/event';
import { createPluginContainer, insertDOM } from '../../utils/dom';
import type { BasePluginOptions } from '../base-plugin';
import { BasePlugin } from '../base-plugin';
import { createPluginContainer, insertDOM } from '../utils/dom';
import type { Item } from './util';
import { CONTEXTMENU_CSS, getContentFromItems } from './util';
/**
Expand Down
8 changes: 5 additions & 3 deletions packages/g6/src/plugins/grid-line.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { GraphEvent } from '../constants';
import type { RuntimeContext } from '../runtime/types';
import type { Point } from '../types';
import { createPluginContainer, insertBefore } from '../utils/dom';
import { ViewportEvent } from '../utils/event';
import { add, mod } from '../utils/vector';
import { BasePlugin, BasePluginOptions } from './base-plugin';
import { createPluginContainer } from './utils/dom';

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

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

private offset: Point = [0, 0];

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

const $container = this.context.canvas.getContainer()!;
insertBefore($container, this.$element);
$container.prepend(this.$element);

this.updateStyle();
this.bindEvents();
Expand Down
122 changes: 67 additions & 55 deletions packages/g6/src/plugins/legend.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { Category, Layout, Selection } from '@antv/component';
import { Category, Selection } from '@antv/component';
import { CategoryStyleProps } from '@antv/component/lib/ui/legend/types';
import { Canvas } from '@antv/g';
import { get, isFunction } from '@antv/util';
import { GraphEvent } from '../constants';
import type { RuntimeContext } from '../runtime/types';
import type { ElementDatum, ElementType, ID, State } from '../types';
import type { CardinalPlacement } from '../types/placement';
import type { BasePluginOptions } from './base-plugin';
import { BasePlugin } from './base-plugin';
import { createPluginCanvas } from './utils/canvas';

interface Datum extends Record<string, any> {
id?: string;
Expand Down Expand Up @@ -40,6 +42,24 @@ export interface LegendOptions extends BasePluginOptions, Omit<CategoryStyleProp
* @defaultValue 'bottom'
*/
position?: CardinalPlacement;
/**
* <zh/> 图例挂载的容器,无则挂载到 Graph 所在容器
*
* <en/> The container where the legend is mounted, if not, it will be mounted to the container where the Graph is located
*/
container?: HTMLElement | string;
/**
* <zh/> 图例画布类名,传入外置容器时不生效
*
* <en/> The class name of the legend canvas, which does not take effect when an external container is passed in
*/
className?: string;
/**
* <zh/> 图例的容器样式,传入外置容器时不生效
*
* <en/> The style of the legend container, which does not take effect when an external container is passed in
*/
containerStyle?: Partial<CSSStyleDeclaration>;
/**
* <zh/> 节点分类标识
*
Expand Down Expand Up @@ -80,16 +100,20 @@ export class Legend extends BasePlugin<LegendOptions> {
colPadding: 10,
itemMarkerSize: 16,
itemLabelFontSize: 16,
width: 240,
height: 160,
};
private typePrefix = '__data__';
private element: Layout | null = null;
private draw = false;
private fieldMap = {
node: new Map<string, ID[]>(),
edge: new Map<string, ID[]>(),
combo: new Map<string, ID[]>(),
};
private selectedItems: string[] = [];
private category?: Category;
private container?: HTMLElement;
private canvas?: Canvas;

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

private clear() {
this.element?.destroy();
this.element = null;
this.canvas?.destroy();
this.container?.remove();
this.canvas = undefined;
this.container = undefined;

this.draw = false;
}

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

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

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

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

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

/**
* <zh/> 图例布局
*
* <en/> Legend layout
* @param position - <zh/> 图例位置| <en/> Legend position
* @returns <zh/> 图例布局样式| <en/> Legend layout style
*/
public layout = (position: CardinalPlacement) => {
const preset = {
flexDirection: 'row',
alignItems: 'flex-end',
justifyContent: 'center',
};
let { flexDirection, alignItems, justifyContent } = preset;
private upsertCanvas() {
if (this.canvas) return this.canvas;

const layout = {
top: ['row', 'flex-start', 'center'],
bottom: ['row', 'flex-end', 'center'],
left: ['column', 'flex-start', 'center'],
right: ['column', 'flex-end', 'center'],
};
const graphCanvas = this.context.canvas;
const [canvasWidth, canvasHeight] = graphCanvas.getSize();

if (position in layout) {
[flexDirection, alignItems, justifyContent] = layout[position];
}
return {
display: 'flex',
flexDirection,
justifyContent,
alignItems,
};
};
const { width = canvasWidth, height = canvasHeight, position, container, containerStyle, className } = this.options;
const [$container, canvas] = createPluginCanvas({
width,
height,
graphCanvas,
container,
containerStyle,
placement: position,
className: 'legend',
});

this.container = $container;
if (className) $container.classList.add(className);
this.canvas = canvas;

return this.canvas;
}

private createElement = () => {
if (this.draw) {
this.updateElement();
return;
}
const { canvas } = this.context;
const [canvasWidth, canvasHeight] = canvas.getSize();
const {
width = canvasWidth,
height = canvasHeight,
width,
height,
nodeField,
edgeField,
comboField,
trigger,
position,
container,
containerStyle,
className,
...rest
} = this.options;
const nodeItems = this.getMarkerData(nodeField, 'node');
const edgeItems = this.getMarkerData(edgeField, 'edge');
const comboItems = this.getMarkerData(comboField, 'combo');
const items = [...nodeItems, ...comboItems, ...edgeItems];
const layout = this.layout(position);

const layoutWrapper = new Layout({
style: {
width,
height,
...layout,
},
});

const categoryStyle = Object.assign(
{
Expand All @@ -386,9 +396,11 @@ export class Legend extends BasePlugin<LegendOptions> {
className: 'legend',
style: categoryStyle,
});
layoutWrapper.appendChild(category);
canvas.appendChild(layoutWrapper as any);
this.element = layoutWrapper;
this.category = category;

const canvas = this.upsertCanvas();
canvas.appendChild(category);

this.draw = true;
};

Expand Down
Loading

0 comments on commit ebeab13

Please sign in to comment.