Skip to content

Commit

Permalink
feat(graphs): dendrogram (#2702)
Browse files Browse the repository at this point in the history
* feat: dendrogram

* fix: use lodash

* docs: update config

* refactor: dendrogram

* chore: revert version update

* fix: remove unnecessary null checks
  • Loading branch information
yvonneyx authored Sep 23, 2024
1 parent 2da717a commit ff0bea6
Show file tree
Hide file tree
Showing 12 changed files with 163 additions and 15 deletions.
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,7 @@
"@typescript-eslint/parser": "^4.1.1",
"tslib": "2.6.1",
"react-dom": "^18.0.1",
"react": "^18.0.1",
"dumi": "2.4.10"
"react": "^18.0.1"
}
},
"gitHooks": {
Expand Down
2 changes: 1 addition & 1 deletion packages/graphs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
},
"dependencies": {
"@ant-design/charts-util": "workspace:*",
"@antv/g6": "^5.0.21",
"@antv/g6": "^5.0.22",
"@antv/g6-extension-react": "^0.1.6",
"@antv/graphin": "^3.0.2",
"lodash": "^4.17.21",
Expand Down
31 changes: 31 additions & 0 deletions packages/graphs/src/components/dendrogram/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import type { Graph } from '@antv/g6';
import React, {
ForwardRefExoticComponent,
PropsWithChildren,
PropsWithoutRef,
RefAttributes,
forwardRef,
useMemo,
} from 'react';
import { BaseGraph } from '../../core/base-graph';
import { COMMON_OPTIONS } from '../../core/constants';
import { mergeOptions } from '../../core/utils/options';
import { DEFAULT_OPTIONS, getDendrogramOptions } from './options';
import type { DendrogramOptions } from './types';

export const Dendrogram: ForwardRefExoticComponent<
PropsWithoutRef<PropsWithChildren<DendrogramOptions>> & RefAttributes<Graph>
> = forwardRef<Graph, PropsWithChildren<DendrogramOptions>>(({ children, ...props }, ref) => {
const options = useMemo(() => {
const { direction = 'horizontal', compact = false, ...restProps } = props;
return mergeOptions(COMMON_OPTIONS, DEFAULT_OPTIONS, getDendrogramOptions({ direction, compact }), restProps);
}, [props]);

return (
<BaseGraph {...options} ref={ref}>
{children}
</BaseGraph>
);
});

export type { DendrogramOptions };
61 changes: 61 additions & 0 deletions packages/graphs/src/components/dendrogram/options.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { isEmpty } from 'lodash';
import type { DendrogramOptions } from './types';

export const DEFAULT_OPTIONS: DendrogramOptions = {
node: {
type: 'circle',
style: {
labelText: (d) => d.id,
},
},
};

export const getDendrogramOptions = ({
direction,
compact,
}: Pick<DendrogramOptions, 'direction' | 'compact'>): DendrogramOptions => {
const isLeafNode = (d) => isEmpty(d.children);

const layoutType = compact ? 'compact-box' : 'dendrogram';
if (direction === 'vertical') {
return {
node: {
style: {
labelBackground: true,
labelPlacement: 'right',
labelTransform: (d) => (isLeafNode(d) ? 'rotate(90deg) translate(18px)' : 'translate(18px)'),
ports: [{ placement: 'top' }, { placement: 'bottom' }],
},
},
edge: { type: 'cubic-vertical' },
layout: { type: layoutType, direction: 'TB', nodeSep: 40, rankSep: 140, getVGap: () => 80, getHGap: () => 20 },
};
} else if (direction === 'horizontal') {
return {
node: {
style: {
labelBackground: true,
labelPlacement: (d) => (isLeafNode(d) ? 'right' : 'left'),
ports: [{ placement: 'left' }, { placement: 'right' }],
},
},
edge: { type: 'cubic-horizontal' },
layout: { type: layoutType, direction: 'LR', nodeSep: 40, rankSep: 200, getVGap: () => 5, getHGap: () => 100 },
};
} else {
return {
node: { style: { labelBackground: true } },
edge: { type: 'cubic-radial' },
layout: {
type: layoutType,
direction: 'RL',
radial: true,
nodeSep: 40,
rankSep: 200,
getVGap: () => 30,
getHGap: () => 60,
},
transforms: (prev) => [...prev, 'place-radial-labels'],
};
}
};
17 changes: 17 additions & 0 deletions packages/graphs/src/components/dendrogram/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import type { GraphOptions } from '../../types';

export interface DendrogramOptions extends GraphOptions {
/**
* The direction of the dendrogram.
* - `'vertical'`: vertical direction (top to bottom).
* - `'horizontal'`: horizontal direction (left to right).
* - `'radial'`: radial direction (clockwise).
* @default 'horizontal'
*/
direction?: 'vertical' | 'horizontal' | 'radial';
/**
* Whether to compact the layout.
* @default false
*/
compact?: boolean;
}
2 changes: 2 additions & 0 deletions packages/graphs/src/components/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
export { Dendrogram } from './dendrogram';
export type { DendrogramOptions } from './dendrogram';
export { FlowGraph } from './flow-graph';
export { HierarchicalGraph } from './hierarchical-graph';
export { MindMap } from './mind-map';
Expand Down
4 changes: 2 additions & 2 deletions packages/graphs/src/core/base-graph.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { ChartLoading, ErrorBoundary } from '@ant-design/charts-util';
import type { Graph, GraphOptions as G6GraphOptions } from '@antv/g6';
import type { GraphOptions as G6GraphOptions, Graph } from '@antv/g6';
import { Graphin } from '@antv/graphin';
import { isEmpty } from 'lodash';
import React, {
forwardRef,
ForwardRefExoticComponent,
PropsWithChildren,
PropsWithoutRef,
RefAttributes,
forwardRef,
useImperativeHandle,
useRef,
} from 'react';
Expand Down
11 changes: 10 additions & 1 deletion packages/graphs/src/core/constants/options.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
import type { GraphOptions } from '../../types';

export const COMMON_OPTIONS: GraphOptions = {
behaviors: ['drag-canvas', 'zoom-canvas'],
behaviors: [
{
key: 'zoom-canvas',
type: 'zoom-canvas',
},
{
key: 'drag-canvas',
type: 'drag-canvas',
},
],
};
18 changes: 10 additions & 8 deletions packages/graphs/src/core/utils/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,16 @@ export function mergeOptions(...options: GraphOptions[]): ParsedGraphOptions {
if (['component', 'data'].includes(key)) {
merged[key] = currValue;
} else if (typeof currValue === 'function') {
merged[key] = function (datum) {
if (['plugins', 'behaviors', 'transforms'].includes(key)) return currValue(prevValue || []);

const value = currValue.call(this, datum);
if (isPlainObject(value) && value !== null) return mergeOptions(prevValue, value);
return value;
};
} else if (isPlainObject(currValue) && isPlainObject(prevValue) && currValue !== null && prevValue !== null) {
if (['plugins', 'behaviors', 'transforms'].includes(key)) {
merged[key] = currValue(prevValue || []);
} else {
merged[key] = function (datum) {
const value = currValue.call(this, datum);
if (isPlainObject(value)) return mergeOptions(prevValue, value);
return value;
};
}
} else if (isPlainObject(currValue) && isPlainObject(prevValue)) {
merged[key] = mergeOptions(prevValue, currValue);
} else {
merged[key] = currValue;
Expand Down
3 changes: 2 additions & 1 deletion packages/graphs/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import * as G6 from '@antv/g6';
import './preset';

export { FlowGraph, HierarchicalGraph, MindMap, NetworkGraph } from './components';
export { Dendrogram, FlowGraph, HierarchicalGraph, MindMap, NetworkGraph } from './components';
export type { DendrogramOptions } from './components';
export { MindMapNode, OrganizationChartNode, PlainNode } from './core/nodes';
export { measureTextSize } from './core/utils/measure-text';
export { getNodeSide } from './core/utils/node';
Expand Down
25 changes: 25 additions & 0 deletions packages/graphs/tests/demos/dendrogram.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Dendrogram as DendrogramComponent, DendrogramOptions } from '@ant-design/graphs';
import { treeToGraphData } from '@antv/g6';
import React from 'react';
import data from '../datasets/algorithm-category.json';

export const Dendrogram = () => {
const options: DendrogramOptions = {
autoFit: 'view',
data: treeToGraphData(data),
direction: 'vertical',
compact: true,
behaviors: (prev) => [
...prev,
{
key: 'hover-activate',
type: 'hover-activate',
degree: Infinity,
direction: 'in',
inactiveState: 'inactive',
},
],
};

return <DendrogramComponent {...options} />;
};
1 change: 1 addition & 0 deletions packages/graphs/tests/demos/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export { Dendrogram } from './dendrogram';
export { HierarchicalGraph } from './hierarchical-graph';
export { MindMap } from './mind-map';
export { MindMap2 } from './mind-map2';
Expand Down

0 comments on commit ff0bea6

Please sign in to comment.