diff --git a/packages/g6/__tests__/demos/index.ts b/packages/g6/__tests__/demos/index.ts index 1bc24598444..1c6aa2e4d88 100644 --- a/packages/g6/__tests__/demos/index.ts +++ b/packages/g6/__tests__/demos/index.ts @@ -80,6 +80,7 @@ export * from './layout-radial-prevent-overlap-unstrict'; export * from './layout-radial-sort'; export * from './plugin-contextmenu'; export * from './plugin-grid-line'; +export * from './plugin-legend'; export * from './plugin-toolbar-build-in'; export * from './plugin-toolbar-iconfont'; export * from './plugin-tooltip'; diff --git a/packages/g6/__tests__/demos/plugin-legend.ts b/packages/g6/__tests__/demos/plugin-legend.ts new file mode 100644 index 00000000000..05ae525bfb5 --- /dev/null +++ b/packages/g6/__tests__/demos/plugin-legend.ts @@ -0,0 +1,79 @@ +import { Graph } from '@/src'; +import data from '@@/dataset/cluster.json'; +import { isObject } from '@antv/util'; + +export const pluginLegend: TestCase = async (context) => { + const { nodes, edges } = data; + const findCluster = (id: string) => { + return nodes.find(({ id: node }) => node === id)?.data.cluster; + }; + const graph = new Graph({ + ...context, + data: { + nodes, + edges: edges.map(({ source, target }) => { + return { + source, + target, + id: `${source}-${target}`, + data: { + cluster: `${findCluster(source)}-${findCluster(target)}`, + }, + }; + }), + }, + layout: { type: 'd3force' }, + behaviors: ['drag-canvas', 'drag-element'], + node: { + style: { + labelText: (d) => d.id, + lineWidth: 0, + type: (item: any) => { + if (item.data.cluster === 'a') return 'diamond'; + if (item.data.cluster === 'b') return 'rect'; + if (item.data.cluster === 'c') return 'triangle'; + return 'circle'; + }, + color: (item: any) => { + if (item.data.cluster === 'a') return 'red'; + if (item.data.cluster === 'b') return 'blue'; + if (item.data.cluster === 'c') return 'green'; + return '#99add1'; + }, + }, + }, + plugins: [ + { + key: 'legend', + type: 'legend', + titleText: 'Cluster Legend', + nodeField: 'cluster', + edgeField: 'cluster', + trigger: 'click', + }, + ], + }); + + await graph.render(); + + pluginLegend.form = (panel) => { + const config = { + trigger: 'hover', + }; + return [ + panel + .add(config, 'trigger', ['hover', 'click']) + .name('Change Trigger Method') + .onChange((trigger: string) => { + graph.setPlugins((plugins) => + plugins.map((plugin) => { + if (isObject(plugin) && plugin.type === 'legend') return { ...plugin, trigger }; + return plugin; + }), + ); + }), + ]; + }; + + return graph; +}; diff --git a/packages/g6/__tests__/snapshots/plugins/legend/click-again.svg b/packages/g6/__tests__/snapshots/plugins/legend/click-again.svg new file mode 100644 index 00000000000..e9a9a60c11c --- /dev/null +++ b/packages/g6/__tests__/snapshots/plugins/legend/click-again.svg @@ -0,0 +1,1137 @@ + + + + + + + + + + + + + + + + Cluster Legend + + + + + + + + + + + + + + + + + + + + + + + + + + + a + + + + + + + + + + + + + + + + + + + + + + b + + + + + + + + + + + + + + + + + + + + + + c + + + + + + + + + + + + + + + + + + + + + + d + + + + + + + + + + + + + + + + + + + + + + a-a + + + + + + + + + + + + + + + + + + + + + + a-b + + + + + + + + + + + + + + + + + + + + + + a-c + + + + + + + + + + + + + + + + + + + + + + b-b + + + + + + + + + + + + + + + + + + + + + + b-c + + + + + + + + + + + + + + + + + + + + + + c-c + + + + + + + + + + + + + + + + + + + + + + c-d + + + + + + + + + + + + + + + + + + + + + + d-d + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + + + + + + + + + 1 + + + + + + + + + + + + 2 + + + + + + + + + + + + 3 + + + + + + + + + + + + 4 + + + + + + + + + + + + 5 + + + + + + + + + + + + 6 + + + + + + + + + + + + 7 + + + + + + + + + + + + 8 + + + + + + + + + + + + 9 + + + + + + + + + + + + 10 + + + + + + + + + + + + 11 + + + + + + + + + + + + 12 + + + + + + + + + + + + 13 + + + + + + + + + + + + 14 + + + + + + + + + + + + 15 + + + + + + + + + + + + 16 + + + + + + + + + + + + 17 + + + + + + + + + + + + 18 + + + + + + + + + + + + 19 + + + + + + + + + + + + 20 + + + + + + + + + + + + 21 + + + + + + + + + + + + 22 + + + + + + + + + + + + 23 + + + + + + + + + + + + 24 + + + + + + + + + + + + 25 + + + + + + + + + + + + 26 + + + + + + + + + + + + 27 + + + + + + + + + + + + 28 + + + + + + + + + + + + 29 + + + + + + + + + + + + 30 + + + + + + + + + + + + 31 + + + + + + + + + + + + 32 + + + + + + + + + + + + 33 + + + + + + + + \ No newline at end of file diff --git a/packages/g6/__tests__/snapshots/plugins/legend/click.svg b/packages/g6/__tests__/snapshots/plugins/legend/click.svg new file mode 100644 index 00000000000..06cebce256f --- /dev/null +++ b/packages/g6/__tests__/snapshots/plugins/legend/click.svg @@ -0,0 +1,1176 @@ + + + + + + + + + + + + + + + + Cluster Legend + + + + + + + + + + + + + + + + + + + + + + + + + + + a + + + + + + + + + + + + + + + + + + + + + + b + + + + + + + + + + + + + + + + + + + + + + c + + + + + + + + + + + + + + + + + + + + + + d + + + + + + + + + + + + + + + + + + + + + + a-a + + + + + + + + + + + + + + + + + + + + + + a-b + + + + + + + + + + + + + + + + + + + + + + a-c + + + + + + + + + + + + + + + + + + + + + + b-b + + + + + + + + + + + + + + + + + + + + + + b-c + + + + + + + + + + + + + + + + + + + + + + c-c + + + + + + + + + + + + + + + + + + + + + + c-d + + + + + + + + + + + + + + + + + + + + + + d-d + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + + + + + + + + + + + + 1 + + + + + + + + + + + + + + + 2 + + + + + + + + + + + + + + + 3 + + + + + + + + + + + + + + + 4 + + + + + + + + + + + + + + + 5 + + + + + + + + + + + + + + + 6 + + + + + + + + + + + + + + + 7 + + + + + + + + + + + + + + + 8 + + + + + + + + + + + + + + + 9 + + + + + + + + + + + + + + + 10 + + + + + + + + + + + + + + + 11 + + + + + + + + + + + + + + + 12 + + + + + + + + + + + + 13 + + + + + + + + + + + + 14 + + + + + + + + + + + + 15 + + + + + + + + + + + + 16 + + + + + + + + + + + + 17 + + + + + + + + + + + + 18 + + + + + + + + + + + + 19 + + + + + + + + + + + + 20 + + + + + + + + + + + + 21 + + + + + + + + + + + + 22 + + + + + + + + + + + + 23 + + + + + + + + + + + + 24 + + + + + + + + + + + + 25 + + + + + + + + + + + + 26 + + + + + + + + + + + + 27 + + + + + + + + + + + + 28 + + + + + + + + + + + + 29 + + + + + + + + + + + + 30 + + + + + + + + + + + + 31 + + + + + + + + + + + + 32 + + + + + + + + + + + + 33 + + + + + + + + \ No newline at end of file diff --git a/packages/g6/__tests__/snapshots/plugins/legend/mouseenter.svg b/packages/g6/__tests__/snapshots/plugins/legend/mouseenter.svg new file mode 100644 index 00000000000..e337346d4d6 --- /dev/null +++ b/packages/g6/__tests__/snapshots/plugins/legend/mouseenter.svg @@ -0,0 +1,1176 @@ + + + + + + + + + + + + + + + + Cluster Legend + + + + + + + + + + + + + + + + + + + + + + + + + + + a + + + + + + + + + + + + + + + + + + + + + + b + + + + + + + + + + + + + + + + + + + + + + c + + + + + + + + + + + + + + + + + + + + + + d + + + + + + + + + + + + + + + + + + + + + + a-a + + + + + + + + + + + + + + + + + + + + + + a-b + + + + + + + + + + + + + + + + + + + + + + a-c + + + + + + + + + + + + + + + + + + + + + + b-b + + + + + + + + + + + + + + + + + + + + + + b-c + + + + + + + + + + + + + + + + + + + + + + c-c + + + + + + + + + + + + + + + + + + + + + + c-d + + + + + + + + + + + + + + + + + + + + + + d-d + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + + + + + + + + + + + + 1 + + + + + + + + + + + + + + + 2 + + + + + + + + + + + + + + + 3 + + + + + + + + + + + + + + + 4 + + + + + + + + + + + + + + + 5 + + + + + + + + + + + + + + + 6 + + + + + + + + + + + + + + + 7 + + + + + + + + + + + + + + + 8 + + + + + + + + + + + + + + + 9 + + + + + + + + + + + + + + + 10 + + + + + + + + + + + + + + + 11 + + + + + + + + + + + + + + + 12 + + + + + + + + + + + + 13 + + + + + + + + + + + + 14 + + + + + + + + + + + + 15 + + + + + + + + + + + + 16 + + + + + + + + + + + + 17 + + + + + + + + + + + + 18 + + + + + + + + + + + + 19 + + + + + + + + + + + + 20 + + + + + + + + + + + + 21 + + + + + + + + + + + + 22 + + + + + + + + + + + + 23 + + + + + + + + + + + + 24 + + + + + + + + + + + + 25 + + + + + + + + + + + + 26 + + + + + + + + + + + + 27 + + + + + + + + + + + + 28 + + + + + + + + + + + + 29 + + + + + + + + + + + + 30 + + + + + + + + + + + + 31 + + + + + + + + + + + + 32 + + + + + + + + + + + + 33 + + + + + + + + \ No newline at end of file diff --git a/packages/g6/__tests__/snapshots/plugins/legend/mouseleave.svg b/packages/g6/__tests__/snapshots/plugins/legend/mouseleave.svg new file mode 100644 index 00000000000..01ffa144cbe --- /dev/null +++ b/packages/g6/__tests__/snapshots/plugins/legend/mouseleave.svg @@ -0,0 +1,1137 @@ + + + + + + + + + + + + + + + + Cluster Legend + + + + + + + + + + + + + + + + + + + + + + + + + + + a + + + + + + + + + + + + + + + + + + + + + + b + + + + + + + + + + + + + + + + + + + + + + c + + + + + + + + + + + + + + + + + + + + + + d + + + + + + + + + + + + + + + + + + + + + + a-a + + + + + + + + + + + + + + + + + + + + + + a-b + + + + + + + + + + + + + + + + + + + + + + a-c + + + + + + + + + + + + + + + + + + + + + + b-b + + + + + + + + + + + + + + + + + + + + + + b-c + + + + + + + + + + + + + + + + + + + + + + c-c + + + + + + + + + + + + + + + + + + + + + + c-d + + + + + + + + + + + + + + + + + + + + + + d-d + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + + + + + + + + + 1 + + + + + + + + + + + + 2 + + + + + + + + + + + + 3 + + + + + + + + + + + + 4 + + + + + + + + + + + + 5 + + + + + + + + + + + + 6 + + + + + + + + + + + + 7 + + + + + + + + + + + + 8 + + + + + + + + + + + + 9 + + + + + + + + + + + + 10 + + + + + + + + + + + + 11 + + + + + + + + + + + + 12 + + + + + + + + + + + + 13 + + + + + + + + + + + + 14 + + + + + + + + + + + + 15 + + + + + + + + + + + + 16 + + + + + + + + + + + + 17 + + + + + + + + + + + + 18 + + + + + + + + + + + + 19 + + + + + + + + + + + + 20 + + + + + + + + + + + + 21 + + + + + + + + + + + + 22 + + + + + + + + + + + + 23 + + + + + + + + + + + + 24 + + + + + + + + + + + + 25 + + + + + + + + + + + + 26 + + + + + + + + + + + + 27 + + + + + + + + + + + + 28 + + + + + + + + + + + + 29 + + + + + + + + + + + + 30 + + + + + + + + + + + + 31 + + + + + + + + + + + + 32 + + + + + + + + + + + + 33 + + + + + + + + \ No newline at end of file diff --git a/packages/g6/__tests__/snapshots/plugins/legend/normal.svg b/packages/g6/__tests__/snapshots/plugins/legend/normal.svg new file mode 100644 index 00000000000..4e198dce4b3 --- /dev/null +++ b/packages/g6/__tests__/snapshots/plugins/legend/normal.svg @@ -0,0 +1,1137 @@ + + + + + + + + + + + + + + + + Cluster Legend + + + + + + + + + + + + + + + + + + + + + + + + + + + a + + + + + + + + + + + + + + + + + + + + + + b + + + + + + + + + + + + + + + + + + + + + + c + + + + + + + + + + + + + + + + + + + + + + d + + + + + + + + + + + + + + + + + + + + + + a-a + + + + + + + + + + + + + + + + + + + + + + a-b + + + + + + + + + + + + + + + + + + + + + + a-c + + + + + + + + + + + + + + + + + + + + + + b-b + + + + + + + + + + + + + + + + + + + + + + b-c + + + + + + + + + + + + + + + + + + + + + + c-c + + + + + + + + + + + + + + + + + + + + + + c-d + + + + + + + + + + + + + + + + + + + + + + d-d + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + + + + + + + + + 1 + + + + + + + + + + + + 2 + + + + + + + + + + + + 3 + + + + + + + + + + + + 4 + + + + + + + + + + + + 5 + + + + + + + + + + + + 6 + + + + + + + + + + + + 7 + + + + + + + + + + + + 8 + + + + + + + + + + + + 9 + + + + + + + + + + + + 10 + + + + + + + + + + + + 11 + + + + + + + + + + + + 12 + + + + + + + + + + + + 13 + + + + + + + + + + + + 14 + + + + + + + + + + + + 15 + + + + + + + + + + + + 16 + + + + + + + + + + + + 17 + + + + + + + + + + + + 18 + + + + + + + + + + + + 19 + + + + + + + + + + + + 20 + + + + + + + + + + + + 21 + + + + + + + + + + + + 22 + + + + + + + + + + + + 23 + + + + + + + + + + + + 24 + + + + + + + + + + + + 25 + + + + + + + + + + + + 26 + + + + + + + + + + + + 27 + + + + + + + + + + + + 28 + + + + + + + + + + + + 29 + + + + + + + + + + + + 30 + + + + + + + + + + + + 31 + + + + + + + + + + + + 32 + + + + + + + + + + + + 33 + + + + + + + + \ No newline at end of file diff --git a/packages/g6/__tests__/unit/plugins/legend.spec.ts b/packages/g6/__tests__/unit/plugins/legend.spec.ts new file mode 100644 index 00000000000..aa10f720129 --- /dev/null +++ b/packages/g6/__tests__/unit/plugins/legend.spec.ts @@ -0,0 +1,67 @@ +import { Legend } from '@/src/plugins/legend'; +import { pluginLegend } from '@@/demos'; +import { createDemoGraph } from '@@/utils'; + +const mockEvent: any = { + __data__: { + id: 'node__0', + index: 0, + style: { + layout: 'flex', + labelText: 'a', + markerLineWidth: 3, + marker: 'diamond', + markerStroke: '#000000', + markerFill: 'red', + spacing: 4, + markerSize: 16, + labelFontSize: 16, + markerOpacity: 1, + labelOpacity: 1, + }, + }, +}; + +describe('plugin legend', () => { + it('normal', async () => { + const graph = await createDemoGraph(pluginLegend); + await expect(graph).toMatchSnapshot(__filename, 'normal'); + graph.destroy(); + }); + + it('click', async () => { + const graph = await createDemoGraph(pluginLegend); + + const legend = graph.getPluginInstance('legend'); + + legend.click(mockEvent); + await expect(graph).toMatchSnapshot(__filename, 'click'); + legend.click(mockEvent); + await expect(graph).toMatchSnapshot(__filename, 'click-again'); + graph.destroy(); + }); + + it('update trigger to hover', async () => { + const graph = await createDemoGraph(pluginLegend); + graph.setPlugins((plugins) => + plugins.map((plugin) => { + if (typeof plugin === 'object') { + return { + ...plugin, + trigger: 'hover', + position: 'top', + }; + } + return plugin; + }), + ); + + const legend = graph.getPluginInstance('legend'); + + legend.mouseenter(mockEvent); + await expect(graph).toMatchSnapshot(__filename, 'mouseenter'); + legend.mouseleave(mockEvent); + await expect(graph).toMatchSnapshot(__filename, 'mouseleave'); + graph.destroy(); + }); +}); diff --git a/packages/g6/package.json b/packages/g6/package.json index 2e9d54232e7..400f3e7b2e1 100644 --- a/packages/g6/package.json +++ b/packages/g6/package.json @@ -55,7 +55,7 @@ "type-check": "tsc --noEmit" }, "dependencies": { - "@antv/component": "^1.0.2", + "@antv/component": "^1.0.3", "@antv/event-emitter": "latest", "@antv/g": "^5.18.25", "@antv/g-canvas": "^1.11.27", diff --git a/packages/g6/src/plugins/index.ts b/packages/g6/src/plugins/index.ts index 82cb8c26e08..63f93d3b481 100644 --- a/packages/g6/src/plugins/index.ts +++ b/packages/g6/src/plugins/index.ts @@ -1,6 +1,7 @@ export { BasePlugin } from './base-plugin'; export { Contextmenu } from './contextmenu'; export { GridLine } from './grid-line'; +export { Legend } from './legend'; export { Toolbar } from './toolbar'; export { Tooltip } from './tooltip'; export { Watermark } from './watermark'; @@ -8,6 +9,7 @@ export { Watermark } from './watermark'; export type { BasePluginOptions } from './base-plugin'; export type { ContextmenuOptions } from './contextmenu'; export type { GridLineOptions } from './grid-line'; +export type { LegendOptions } from './legend'; export type { ToolbarOptions } from './toolbar'; export type { TooltipOptions } from './tooltip'; export type { WatermarkOptions } from './watermark'; diff --git a/packages/g6/src/plugins/legend.ts b/packages/g6/src/plugins/legend.ts new file mode 100644 index 00000000000..5b106402a8e --- /dev/null +++ b/packages/g6/src/plugins/legend.ts @@ -0,0 +1,321 @@ +import type { CategoryOptions } from '@antv/component'; +import { Category, Layout, Selection } from '@antv/component'; +import type { ID } from '@antv/graphlib'; +import { get, isFunction } from '@antv/util'; +import { GraphEvent } from '../constants'; +import type { RuntimeContext } from '../runtime/types'; +import type { ElementDatum, ElementType, State } from '../types'; +import type { CardinalPlacement } from '../types/placement'; +import type { BasePluginOptions } from './base-plugin'; +import { BasePlugin } from './base-plugin'; + +type Field = string | ((item: ElementDatum) => string); +type Datum = { + id?: string; + label?: string; + color?: string; + marker?: string; + elementType?: ElementType; + [key: string]: any; +}; + +export interface LegendOptions extends BasePluginOptions, Omit { + /** 触发方式 | Event type that triggers display of legend */ + trigger?: 'hover' | 'click'; + /** 位置 | Legend position */ + position?: CardinalPlacement; + /** 节点分类标识 | Node Classification Identifier */ + nodeField?: Field; + /** 边分类标识 | Edge Classification Identifier */ + edgeField?: Field; + /** Combo分类标识 | Combo Classification Identifier */ + comboField?: Field; +} + +export class Legend extends BasePlugin { + static defaultOptions: Partial = { + position: 'bottom', + trigger: 'hover', + orientation: 'horizontal', + layout: 'flex', + itemSpacing: 4, + rowPadding: 10, + colPadding: 10, + itemMarkerSize: 16, + itemLabelFontSize: 16, + }; + private typePrefix = '__data__'; + private element: Layout | null = null; + private draw = false; + private fieldMap = { + node: new Map(), + edge: new Map(), + combo: new Map(), + }; + private selectedItems: string[] = []; + + constructor(context: RuntimeContext, options: LegendOptions) { + super(context, Object.assign({}, Legend.defaultOptions, options)); + this.bindEvents(); + } + + public update(options: Partial) { + super.update(options); + this.clear(); + this.createElement(); + } + + private clear() { + this.element?.destroy(); + this.element = null; + this.draw = false; + } + + private bindEvents = () => { + const { graph } = this.context; + graph.on(GraphEvent.AFTER_DRAW, this.createElement); + }; + + private changeState = (el: Selection, state: State | State[]) => { + const { graph } = this.context; + const { typePrefix } = this; + const composeId = get(el, [typePrefix, 'id']); + const category = get(el, [typePrefix, 'style', 'labelText']); + const [type] = composeId.split('__'); + const ids = this.fieldMap[type as keyof typeof this.fieldMap].get(category) || []; + + graph.setElementState(Object.fromEntries(ids?.map((id) => [id, state]))); + }; + + public click = (el: Selection) => { + if (this.options.trigger === 'hover') return; + const composeId = get(el, [this.typePrefix, 'id']); + if (!this.selectedItems.includes(composeId)) { + this.selectedItems.push(composeId); + this.changeState(el, 'selected'); + } else { + this.selectedItems = this.selectedItems.filter((item) => item !== composeId); + this.changeState(el, []); + } + }; + + public mouseleave = (el: Selection) => { + if (this.options.trigger === 'click') return; + this.selectedItems = []; + this.changeState(el, []); + }; + + public mouseenter = (el: Selection) => { + if (this.options.trigger === 'click') return; + const composeId = get(el, [this.typePrefix, 'id']); + if (!this.selectedItems.includes(composeId)) { + this.selectedItems.push(composeId); + this.changeState(el, 'active'); + } else { + this.selectedItems = this.selectedItems.filter((item) => item !== composeId); + } + }; + + public updateElement() { + if (!this.element) return; + const category = this.element.getChildByIndex(0) as Category; + + category.update({ + itemMarkerOpacity: ({ id }) => { + if (!this.selectedItems.length || this.selectedItems.includes(id)) return 1; + return 0.5; + }, + itemLabelOpacity: ({ id }) => { + if (!this.selectedItems.length || this.selectedItems.includes(id)) return 1; + return 0.5; + }, + }); + } + + private setFieldMap = (field: string, id: ID, type: ElementType) => { + if (!field) return; + const map = this.fieldMap[type]; + if (!map) return; + if (!map.has(field)) { + map.set(field, [id]); + } else { + const ids = map.get(field); + if (ids) { + ids.push(id); + map.set(field, ids); + } + } + }; + + private getEvents = () => { + return { + mouseenter: this.mouseenter, + mouseleave: this.mouseleave, + click: this.click, + }; + }; + + private getMarkerData = (field: Field, elementType: ElementType) => { + if (!field) return []; + const { model, element, graph } = this.context; + const { nodes, edges, combos } = model.getData(); + const items: { [key: string]: Datum } = {}; + + const getField = (item: ElementDatum) => { + if (isFunction(field)) return field(item); + return field; + }; + + const defaultType = { + node: 'circle', + edge: 'line', + combo: 'rect', + }; + + /** 用于将 G6 element 转换为 componets 支持的类型 */ + const markerMapping: { [key: string]: string } = { + circle: 'circle', + ellipse: 'circle', // 待 components 支持 ellipse + image: 'bowtie', + rect: 'square', + star: 'cross', + triangle: 'triangle', + diamond: 'diamond', + cubic: 'dot', + line: 'hyphen', + polyline: 'hyphen', + quadratic: 'hv', + 'cubic-horizontal': 'hyphen', + 'cubic-vertical': 'line', + }; + + const getElementStyle = (type: ElementType, datum: ElementDatum) => { + const style = element?.getElementComputedStyle(type, datum); + return style; + }; + + const getElementModel = (data: ElementDatum[], type: ElementType) => { + data.forEach((item) => { + const { id } = item; + const value = get(item, ['data', getField(item)]); + const { color = '#1783ff', type: marker = 'circle' } = getElementStyle(type, item); + if ((id || id === 0) && value && value.replace(/\s+/g, '')) { + this.setFieldMap(value, id, type); + if (!items[value]) { + items[value] = { + id: `${type}__${id}`, + label: value, + marker: markerMapping[marker] || defaultType[type], + elementType: type, + lineWidth: 1, + stroke: color, + fill: color, + }; + } + } + }); + }; + + switch (elementType) { + case 'node': + getElementModel(nodes, 'node'); + break; + case 'edge': + getElementModel(edges, 'edge'); + break; + case 'combo': + getElementModel(combos, 'combo'); + break; + default: + return []; + } + + return Object.values(items); + }; + + public layout = (position: CardinalPlacement) => { + const preset = { + flexDirection: 'row', + alignItems: 'flex-end', + justifyContent: 'center', + }; + let { flexDirection, alignItems, justifyContent } = preset; + + const layout = { + top: ['row', 'flex-start', 'center'], + bottom: ['row', 'flex-end', 'center'], + left: ['column', 'flex-start', 'center'], + right: ['column', 'flex-end', 'center'], + }; + + if (position in layout) { + [flexDirection, alignItems, justifyContent] = layout[position]; + } + return { + display: 'flex', + flexDirection, + justifyContent, + alignItems, + }; + }; + + private createElement = () => { + if (this.draw) { + this.updateElement(); + return; + } + const { canvas } = this.context; + const [canvasWidth, canvasHeiht] = canvas.getSize(); + const { + width = canvasWidth, + height = canvasHeiht, + nodeField, + edgeField, + comboField, + trigger, + position, + ...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 = { + width, + height, + data: items, + itemMarkerLineWidth: ({ lineWidth }: Datum) => lineWidth, + itemMarker: ({ marker }: Datum) => marker, + itemMarkerStroke: ({ stroke }: Datum) => stroke, + itemMarkerFill: ({ fill }: Datum) => fill, + gridCol: nodeItems.length, + ...rest, + ...this.getEvents(), + }; + + const category = new Category({ + className: 'legend', + style: categoryStyle, + }); + layoutWrapper.appendChild(category); + canvas.appendChild(layoutWrapper); + this.element = layoutWrapper; + this.draw = true; + }; + + public destroy(): void { + this.clear(); + this.context.graph.off(GraphEvent.AFTER_DRAW, this.createElement); + super.destroy(); + } +} diff --git a/packages/g6/src/registry/build-in.ts b/packages/g6/src/registry/build-in.ts index 46d4aef7008..20c237d504b 100644 --- a/packages/g6/src/registry/build-in.ts +++ b/packages/g6/src/registry/build-in.ts @@ -38,7 +38,7 @@ import { mindmap, } from '../layouts'; import { blues, greens, oranges, spectral } from '../palettes'; -import { Contextmenu, GridLine, Toolbar, Tooltip, Watermark } from '../plugins'; +import { Contextmenu, GridLine, Legend, Toolbar, Tooltip, Watermark } from '../plugins'; import { dark, light } from '../themes'; import type { ExtensionRegistry } from './types'; @@ -117,5 +117,6 @@ export const BUILT_IN_EXTENSIONS: ExtensionRegistry = { tooltip: Tooltip, contextmenu: Contextmenu, toolbar: Toolbar, + legend: Legend, }, }; diff --git a/packages/site/examples/tool/legend/demo/legend-click.ts b/packages/site/examples/tool/legend/demo/legend-click.ts new file mode 100644 index 00000000000..0535372c498 --- /dev/null +++ b/packages/site/examples/tool/legend/demo/legend-click.ts @@ -0,0 +1,98 @@ +import { Graph } from '@antv/g6'; + +const data = { + nodes: [ + { + id: '1', + style: { + type: 'circle', + color: '#5B8FF9', + }, + data: { cluster: 'node-type1' }, + }, + { + id: '2', + style: { + type: 'rect', + color: '#5AD8A6', + }, + data: { cluster: 'node-type2' }, + }, + { + id: '3', + style: { + type: 'triangle', + color: '#5D7092', + }, + data: { cluster: 'node-type3' }, + }, + { + id: '4', + style: { + type: 'diamond', + color: '#6F5EF9', + }, + data: { cluster: 'node-type4' }, + }, + ], + edges: [ + { + id: '1-2', + source: '1', + target: '2', + style: { + type: 'quadratic', + color: '#F6BD16', + }, + data: { cluster: 'edge-type1' }, + }, + { + id: '1-4', + source: '1', + target: '4', + data: { cluster: 'edge-type2' }, + }, + { + id: '3-4', + source: '3', + target: '4', + }, + { + id: '2-4', + source: '2', + target: '4', + data: { cluster: 'edge-type3' }, + }, + ], +}; + +const graph = new Graph({ + container: 'container', + data, + node: { + style: { + labelPosition: 'bottom', + stroke: '#fff', + lineWidth: 4, + }, + }, + edge: { + style: { + stroke: '#fff', + lineWidth: 4, + }, + }, + layout: { + type: 'force', + }, + plugins: [ + { + type: 'legend', + nodeField: 'cluster', + edgeField: 'cluster', + trigger: 'click', + }, + ], +}); + +graph.render(); diff --git a/packages/site/examples/tool/legend/demo/legend.ts b/packages/site/examples/tool/legend/demo/legend.ts index 8800ae06e03..a7a0774789a 100644 --- a/packages/site/examples/tool/legend/demo/legend.ts +++ b/packages/site/examples/tool/legend/demo/legend.ts @@ -1,167 +1,88 @@ -import { Graph as BaseGraph, Extensions, extend } from '@antv/g6'; +import { Graph } from '@antv/g6'; -const Graph = extend(BaseGraph, { - plugins: { - legend: Extensions.Legend, - }, - behaviors: { - 'brush-select': Extensions.BrushSelect, - 'activate-relations': Extensions.ActivateRelations, - 'zoom-canvas': Extensions.ZoomCanvas, - }, -}); - -const container = document.getElementById('container') as HTMLElement; -const width = container.scrollWidth; -const height = (container.scrollHeight || 500) - 50; - -/** graph schema */ -const GraphSchema = { +const data = { nodes: [ { - nodeType: 'person', - properties: { - id: 'string', - name: 'string', - phoneNumber: 'number', + id: '1', + style: { + type: 'circle', + color: '#5B8FF9', }, - count: 10, + data: { cluster: 'node-type1' }, }, { - nodeType: 'company', - properties: { - id: 'string', - name: 'string', + id: '2', + style: { + type: 'rect', + color: '#5AD8A6', }, - count: 3, + data: { cluster: 'node-type2' }, + }, + { + id: '3', + style: { + type: 'triangle', + color: '#5D7092', + }, + data: { cluster: 'node-type3' }, + }, + { + id: '4', + style: { + type: 'diamond', + color: '#6F5EF9', + }, + data: { cluster: 'node-type4' }, }, ], edges: [ { - edgeType: 'friend', - sourceType: 'person', - targetType: 'person', - properties: {}, - count: 3, + source: '1', + target: '2', + data: { cluster: 'edge-type1' }, }, { - edgeType: 'employ', - sourceType: 'company', - targetType: 'person', - properties: {}, - count: 4, + source: '1', + target: '4', + data: { cluster: 'edge-type2' }, }, { - edgeType: 'legal', - sourceType: 'person', - targetType: 'company', - properties: {}, - count: 5, + source: '3', + target: '4', + }, + { + source: '2', + target: '4', + data: { cluster: 'edge-type3' }, }, ], }; -const nodeMap = new Map(); -const nodeTypeMap = new Map(); -const edgeMap = new Map(); -GraphSchema.nodes.forEach((item, index) => { - const { nodeType, count, properties } = item; - Array.from({ length: count }).map((c, i) => { - const id = `${nodeType}-${index}-${i}`; - const node = { - id, - data: { - ...properties, // can import facker - nodeType, - id, - }, - }; - nodeMap.set(id, node); - const nty = nodeTypeMap.get(nodeType) || []; - nodeTypeMap.set(nodeType, [...nty, node]); - }); -}); - -GraphSchema.edges.forEach((item, index) => { - const { edgeType, count, properties, sourceType, targetType } = item; - const sources = nodeTypeMap.get(sourceType); - const targets = nodeTypeMap.get(targetType); - Array.from({ length: count }).forEach((c, i) => { - const id = `${edgeType}-${index}-${i}`; - const edge = { - id, - data: { - ...properties, // can import facker - edgeType, - id, - }, - source: sources[i % sources.length].id, - target: targets[i % targets.length].id, - }; - edgeMap.set(id, edge); - }); -}); - -const data = { - nodes: [...nodeMap.values()], - edges: [...edgeMap.values()], -}; -const legend = { - key: 'default-legend', - type: 'legend', - size: [250, 'fit-content'], - background: 'rgba(0,0,0,0.05)', +const graph = new Graph({ + container: 'container', + data, node: { - enable: true, - padding: [20, 20], - title: 'node-legend', - typeField: 'nodeType', - rows: 1, - cols: 4, - labelStyle: { - spacing: 8, - fontSize: 20, + style: { + labelPosition: 'bottom', + stroke: '#fff', + lineWidth: 4, }, }, edge: { - enable: true, - padding: [10, 20], - title: 'edge-legend', - typeField: 'edgeType', + style: { + stroke: '#fff', + lineWidth: 4, + }, }, -}; - -new Graph({ - container, - width, - height, - data, - plugins: [legend], layout: { type: 'force', }, - theme: { - type: 'spec', - specification: { - node: { - dataTypeField: 'nodeType', - }, - edge: { - dataTypeField: 'edgeType', - }, - }, - }, - node: { - labelShape: { - text: { - fields: ['id'], - formatter: (model) => { - return model.id; - }, - }, + plugins: [ + { + type: 'legend', + nodeField: 'cluster', }, - }, - modes: { - default: ['brush-select', 'zoom-canvas', 'activate-relations', 'drag-canvas', 'drag-element'], - }, + ], }); + +graph.render(); diff --git a/packages/site/examples/tool/legend/demo/legendCustomMarker.ts b/packages/site/examples/tool/legend/demo/legendCustomMarker.ts deleted file mode 100644 index 412cae61e3b..00000000000 --- a/packages/site/examples/tool/legend/demo/legendCustomMarker.ts +++ /dev/null @@ -1,177 +0,0 @@ -import { Graph as BaseGraph, Extensions, extend } from '@antv/g6'; - -const Graph = extend(BaseGraph, { - plugins: { - legend: Extensions.Legend, - }, - behaviors: { - 'brush-select': Extensions.BrushSelect, - 'activate-relations': Extensions.ActivateRelations, - 'zoom-canvas': Extensions.ZoomCanvas, - }, -}); - -const container = document.getElementById('container') as HTMLElement; -const width = container.scrollWidth; -const height = (container.scrollHeight || 500) - 50; - -/** graph schema */ -const GraphSchema = { - nodes: [ - { - nodeType: 'person', - properties: { - id: 'string', - name: 'string', - phoneNumber: 'number', - }, - count: 10, - }, - { - nodeType: 'company', - properties: { - id: 'string', - name: 'string', - }, - count: 3, - }, - ], - edges: [ - { - edgeType: 'friend', - sourceType: 'person', - targetType: 'person', - properties: {}, - count: 3, - }, - { - edgeType: 'employ', - sourceType: 'company', - targetType: 'person', - properties: {}, - count: 4, - }, - { - edgeType: 'legal', - sourceType: 'person', - targetType: 'company', - properties: {}, - count: 5, - }, - ], -}; -const nodeMap = new Map(); -const nodeTypeMap = new Map(); -const edgeMap = new Map(); -GraphSchema.nodes.forEach((item, index) => { - const { nodeType, count, properties } = item; - Array.from({ length: count }).map((c, i) => { - const id = `${nodeType}-${index}-${i}`; - const node = { - id, - data: { - ...properties, // can import facker - nodeType, - id, - }, - }; - nodeMap.set(id, node); - const nty = nodeTypeMap.get(nodeType) || []; - nodeTypeMap.set(nodeType, [...nty, node]); - }); -}); - -GraphSchema.edges.forEach((item, index) => { - const { edgeType, count, properties, sourceType, targetType } = item; - const sources = nodeTypeMap.get(sourceType); - const targets = nodeTypeMap.get(targetType); - Array.from({ length: count }).forEach((c, i) => { - const id = `${edgeType}-${index}-${i}`; - const edge = { - id, - data: { - ...properties, // can import facker - edgeType, - id, - }, - source: sources[i % sources.length].id, - target: targets[i % targets.length].id, - }; - edgeMap.set(id, edge); - }); -}); - -const data = { - nodes: [...nodeMap.values()], - edges: [...edgeMap.values()], -}; - -const legend = { - key: 'default-legend', - type: 'legend', - size: [250, 'fit-content'], - background: 'rgba(0,0,0,0.05)', - node: { - enable: true, - padding: [20, 20], - title: 'node-legend', - typeField: 'nodeType', - rows: 1, - cols: 4, - labelStyle: { - spacing: 8, - fontSize: 20, - }, - markerStyle: { - shape: 'circle', - size: (type) => { - return type === 'person' ? 10 : 20; - }, - color: (type) => { - return type === 'person' ? '#f00' : '#0f0'; - }, - }, - }, - edge: { - enable: true, - padding: [10, 20], - title: 'edge-legend', - typeField: 'edgeType', - markerStyle: { - color: (type) => { - switch (type) { - case 'friend': - return '#00f'; - case 'employ': - return '#f0f'; - case 'legal': - return '#0ff'; - } - }, - }, - }, -}; - -new Graph({ - container, - width, - height, - data, - layout: { - type: 'force', - }, - plugins: [legend], - node: { - labelShape: { - text: { - fields: ['id'], - formatter: (model) => { - return model.id; - }, - }, - }, - }, - modes: { - default: ['brush-select', 'zoom-canvas', 'activate-relations', 'drag-canvas', 'drag-element'], - }, -}); diff --git a/packages/site/examples/tool/legend/demo/legendSVG.ts b/packages/site/examples/tool/legend/demo/legendSVG.ts deleted file mode 100644 index 2c1d2b94e0f..00000000000 --- a/packages/site/examples/tool/legend/demo/legendSVG.ts +++ /dev/null @@ -1,168 +0,0 @@ -import { Graph as BaseGraph, Extensions, extend } from '@antv/g6'; - -const Graph = extend(BaseGraph, { - plugins: { - legend: Extensions.Legend, - }, - behaviors: { - 'brush-select': Extensions.BrushSelect, - 'activate-relations': Extensions.ActivateRelations, - 'zoom-canvas': Extensions.ZoomCanvas, - }, -}); - -const container = document.getElementById('container') as HTMLElement; -const width = container.scrollWidth; -const height = (container.scrollHeight || 500) - 50; - -/** graph schema */ -const GraphSchema = { - nodes: [ - { - nodeType: 'person', - properties: { - id: 'string', - name: 'string', - phoneNumber: 'number', - }, - count: 10, - }, - { - nodeType: 'company', - properties: { - id: 'string', - name: 'string', - }, - count: 3, - }, - ], - edges: [ - { - edgeType: 'friend', - sourceType: 'person', - targetType: 'person', - properties: {}, - count: 3, - }, - { - edgeType: 'employ', - sourceType: 'company', - targetType: 'person', - properties: {}, - count: 4, - }, - { - edgeType: 'legal', - sourceType: 'person', - targetType: 'company', - properties: {}, - count: 5, - }, - ], -}; -const nodeMap = new Map(); -const nodeTypeMap = new Map(); -const edgeMap = new Map(); -GraphSchema.nodes.forEach((item, index) => { - const { nodeType, count, properties } = item; - Array.from({ length: count }).map((c, i) => { - const id = `${nodeType}-${index}-${i}`; - const node = { - id, - data: { - ...properties, // can import facker - nodeType, - id, - }, - }; - nodeMap.set(id, node); - const nty = nodeTypeMap.get(nodeType) || []; - nodeTypeMap.set(nodeType, [...nty, node]); - }); -}); - -GraphSchema.edges.forEach((item, index) => { - const { edgeType, count, properties, sourceType, targetType } = item; - const sources = nodeTypeMap.get(sourceType); - const targets = nodeTypeMap.get(targetType); - Array.from({ length: count }).forEach((c, i) => { - const id = `${edgeType}-${index}-${i}`; - const edge = { - id, - data: { - ...properties, // can import facker - edgeType, - id, - }, - source: sources[i % sources.length].id, - target: targets[i % targets.length].id, - }; - edgeMap.set(id, edge); - }); -}); - -const data = { - nodes: [...nodeMap.values()], - edges: [...edgeMap.values()], -}; - -const legend = { - key: 'default-legend', - type: 'legend', - renderer: 'svg', - size: [250, 'fit-content'], - background: 'rgba(0,0,0,0.05)', - node: { - enable: true, - padding: [20, 20], - title: 'node-legend', - typeField: 'nodeType', - rows: 1, - cols: 4, - labelStyle: { - spacing: 8, - fontSize: 20, - }, - }, - edge: { - enable: true, - padding: [10, 20], - title: 'edge-legend', - typeField: 'edgeType', - }, -}; - -new Graph({ - container, - width, - height, - data, - plugins: [legend], - layout: { - type: 'force', - }, - theme: { - type: 'spec', - specification: { - node: { - dataTypeField: 'nodeType', - }, - edge: { - dataTypeField: 'edgeType', - }, - }, - }, - node: { - labelShape: { - text: { - fields: ['id'], - formatter: (model) => { - return model.id; - }, - }, - }, - }, - modes: { - default: ['brush-select', 'zoom-canvas', 'activate-relations', 'drag-canvas', 'drag-element'], - }, -}); diff --git a/packages/site/examples/tool/legend/demo/meta.json b/packages/site/examples/tool/legend/demo/meta.json index 182a1d4994d..2a0cc215bcb 100644 --- a/packages/site/examples/tool/legend/demo/meta.json +++ b/packages/site/examples/tool/legend/demo/meta.json @@ -7,26 +7,26 @@ { "filename": "legend.ts", "title": { - "zh": "图例-点击与 hover 筛选", - "en": "Filtering legend by clicking and hovering" + "zh": "默认图例", + "en": "Default legend" }, - "screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*53oGRpdKpwsAAAAAAAAAAAAADmJ7AQ/original" + "screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*pGMMQIVmtdoAAAAAAAAAAAAADmJ7AQ/original" }, { - "filename": "legendCustomMarker.ts", + "filename": "legend-click.ts", "title": { - "zh": "图例-自定义 Marker 样式", - "en": "Customize legend marker style" + "zh": "点击图例", + "en": "Click legend" }, - "screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*40awTrXVQ1UAAAAAAAAAAAAADmJ7AQ/original" + "screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*krcoS6DXEnYAAAAAAAAAAAAADmJ7AQ/original" }, { - "filename": "legendSVG.ts", + "filename": "style.ts", "title": { - "zh": "图例-使用 SVG 绘制图例", - "en": "Render legend with SVG" + "zh": "Marker 样式", + "en": "Marker style" }, - "screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*53oGRpdKpwsAAAAAAAAAAAAADmJ7AQ/original" + "screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*cnYaQLn2zkQAAAAAAAAAAAAADmJ7AQ/original" } ] } diff --git a/packages/site/examples/tool/legend/demo/style.ts b/packages/site/examples/tool/legend/demo/style.ts new file mode 100644 index 00000000000..936976230f7 --- /dev/null +++ b/packages/site/examples/tool/legend/demo/style.ts @@ -0,0 +1,101 @@ +import { Graph } from '@antv/g6'; + +const data = { + nodes: [ + { + id: '1', + style: { + type: 'circle', + color: '#5B8FF9', + }, + data: { cluster: 'node-type1' }, + }, + { + id: '2', + style: { + type: 'rect', + color: '#5AD8A6', + }, + data: { cluster: 'node-type2' }, + }, + { + id: '3', + style: { + type: 'triangle', + color: '#5D7092', + }, + data: { cluster: 'node-type3' }, + }, + { + id: '4', + style: { + type: 'diamond', + color: '#6F5EF9', + }, + data: { cluster: 'node-type4' }, + }, + ], + edges: [ + { + id: '1-2', + source: '1', + target: '2', + style: { + type: 'quadratic', + color: '#F6BD16', + }, + data: { cluster: 'edge-type1' }, + }, + { + id: '1-4', + source: '1', + target: '4', + data: { cluster: 'edge-type2' }, + }, + { + id: '3-4', + source: '3', + target: '4', + }, + { + id: '2-4', + source: '2', + target: '4', + data: { cluster: 'edge-type3' }, + }, + ], +}; + +const graph = new Graph({ + container: 'container', + data, + node: { + style: { + labelPosition: 'bottom', + stroke: '#fff', + lineWidth: 4, + }, + }, + edge: { + style: { + stroke: '#fff', + lineWidth: 4, + }, + }, + layout: { + type: 'force', + }, + plugins: [ + { + type: 'legend', + nodeField: 'cluster', + edgeField: 'cluster', + titleText: 'Legend Title', + trigger: 'click', + position: 'top', + gridCol: 3, + }, + ], +}); + +graph.render();