Skip to content

Commit

Permalink
feat: add fishbone diagram (#2773)
Browse files Browse the repository at this point in the history
* chore: update deps

* chore: merge

* chore: merge

* refactor: update graph style

* feat: fishbone diagram

* docs: add fishbone site
  • Loading branch information
yvonneyx authored Nov 14, 2024
1 parent d8a2a24 commit 5a88e5e
Show file tree
Hide file tree
Showing 36 changed files with 13,878 additions and 16,885 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ node_modules
/npm-debug.log*
/yarn-error.log
/yarn.lock
# /pnpm-lock.yaml
/pnpm-lock.yaml
/package-lock.json
/pnpm-debug.log
/**/pnpm-debug.log
Expand Down
16 changes: 8 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@
"release:beta": "pnpm run before:release && pnpm publish --tag beta --no-git-checks -r --filter @ant-design/*"
},
"devDependencies": {
"@babel/core": "^7.25.2",
"@babel/core": "^7.26.0",
"@babel/polyfill": "^7.12.1",
"@babel/preset-env": "^7.25.4",
"@babel/preset-typescript": "^7.24.7",
"@changesets/cli": "^2.27.8",
"@swc/jest": "^0.2.36",
"@babel/preset-env": "^7.26.0",
"@babel/preset-typescript": "^7.26.0",
"@changesets/cli": "^2.27.9",
"@swc/jest": "^0.2.37",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react-hooks": "^7.0.2",
"@types/enzyme": "^3.10.18",
Expand Down Expand Up @@ -54,15 +54,15 @@
"npm-run-all": "^4.1.5",
"prettier": "^2.8.8",
"prettier-plugin-organize-imports": "^4.1.0",
"prettier-plugin-packagejson": "^2.5.2",
"prettier-plugin-packagejson": "^2.5.3",
"pretty-quick": "^3.3.1",
"react-dev-utils": "^12.0.1",
"rimraf": "^3.0.2",
"style-loader": "^3.3.4",
"ts-loader": "^9.5.1",
"typescript": "^5.6.2",
"typescript": "^5.6.3",
"vfile-reporter": "^7.0.5",
"webpack": "^5.94.0",
"webpack": "^5.96.1",
"webpack-bundle-analyzer": "^4.10.2",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^5.1.0",
Expand Down
8 changes: 5 additions & 3 deletions packages/charts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,11 @@
"prepublishOnly": "pnpm run build"
},
"dependencies": {
"@ant-design/graphs": "^2.0.0",
"@ant-design/plots": "^2.1.3",
"lodash": "^4.17.21"
"@ant-design/graphs": "workspace:^",
"@ant-design/plots": "workspace:^",
"lodash": "^4.17.21",
"react": "^18.3.1",
"react-dom": "^18.3.1"
},
"peerDependencies": {
"react": ">=16.8.4",
Expand Down
16 changes: 9 additions & 7 deletions packages/graphs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,20 +49,22 @@
},
"dependencies": {
"@ant-design/charts-util": "workspace:*",
"@antv/g6": "^5.0.24",
"@antv/g6": "^5.0.30",
"@antv/g6-extension-react": "^0.1.7",
"@antv/graphin": "^3.0.2",
"@antv/graphin": "^3.0.4",
"lodash": "^4.17.21",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"styled-components": "^6.1.13"
},
"devDependencies": {
"@antv/algorithm": "^0.1.26",
"@types/jest": "^26.0.0",
"@types/lodash": "^4.17.7",
"antd": "^5.20.5",
"react-router-dom": "^6.26.1",
"@types/jest": "^26.0.24",
"@types/lodash": "^4.17.13",
"antd": "^5.22.0",
"react-router-dom": "^6.28.0",
"tsconfig-paths-webpack-plugin": "^4.1.0",
"vite": "^5.4.3"
"vite": "^5.4.11"
},
"peerDependencies": {
"react": ">=16.8.4",
Expand Down
28 changes: 28 additions & 0 deletions packages/graphs/src/components/fishbone/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
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, getFishboneOptions } from './options';
import type { FishboneOptions } from './types';

export const Fishbone: ForwardRefExoticComponent<PropsWithoutRef<PropsWithChildren<FishboneOptions>> & RefAttributes<Graph>> = forwardRef<Graph, PropsWithChildren<FishboneOptions>>(({ children, ...props }, ref) => {
const { type = 'cause', ...restProps } = props;

const options = useMemo(() => mergeOptions(COMMON_OPTIONS, DEFAULT_OPTIONS, getFishboneOptions({ type }), restProps), [props]);

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

export type { FishboneOptions };
89 changes: 89 additions & 0 deletions packages/graphs/src/components/fishbone/options.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import type { ID, NodeData, SingleLayoutOptions, Size } from '@antv/g6';
import type { FishboneOptions } from "./types";
import { measureTextSize } from '../../core/utils/measure-text';

export const DEFAULT_OPTIONS: FishboneOptions = {
node: {
style: {
size: 10,
labelText: d => d.id,
labelPlacement: 'center',
}
},
edge: {
type: 'polyline',
},
layout: {
type: 'fishbone',
hGap: 40,
vGap: 60,
},
animation: false,
};

const FONT_FAMILY = 'system-ui, sans-serif';

const getNodeSize = (id: ID, depth: number): Size => {
if (depth === 0) return measureTextSize(id, [80, 48], { fontSize: 24, fontWeight: 600, fontFamily: FONT_FAMILY });
if (depth === 1) return measureTextSize(id, [80, 30], { fontSize: 18, fontFamily: FONT_FAMILY });
return [2, 30];
};

const getNodeFill = (node: NodeData): string => {
const depth = node.depth as number;
if (depth === 0) return '#EFF0F0';
if (depth === 1) return (node.style?.color as string) || '#EFF0F0';
return 'transparent';
}

export function getFishboneOptions({ type }: Pick<FishboneOptions, 'type'>): FishboneOptions {
const options: FishboneOptions = {
node: {
type: 'rect',
style: {
fill: d => getNodeFill(d),
labelFill: d => d.depth === 1 ? '#fff' : '#262626',
labelFillOpacity: 1,
labelFontSize: d => d.depth === 0 ? 24 : d.depth === 1 ? 18 : 16,
labelFontWeight: d => d.depth === 0 ? 600 : 400,
labelLineHeight: d => d.depth === 0 ? 26 : d.depth === 1 ? 20 : 18,
labelText: d => d.id,
radius: 8,
size: d => getNodeSize(d.id, d.depth!),
}
},
edge: {
type: 'polyline',
style: {
lineWidth: 3,
stroke: function (data) {
return (this.getNodeData(data.target).style!.color as string) || '#99ADD1';
},
},
},
transforms: (prev) => [
...prev,
{
type: 'assign-color-by-branch',
key: 'assign-color-by-branch',
},
{
type: 'arrange-edge-z-index',
key: 'arrange-edge-z-index',
},
]
};

options.layout ||= {} as SingleLayoutOptions;
if (type === 'decision') {
// @ts-ignore
options.node.style.labelPlacement = d => d.depth === 0 || d.depth === 1 ? 'center' : 'right';
Object.assign(options.layout!, { direction: 'LR' })
} else if (type === 'cause') {
// @ts-ignore
options.node.style.labelPlacement = d => d.depth === 0 || d.depth === 1 ? 'center' : 'left';
Object.assign(options.layout!, { direction: 'RL' })
}

return options;
}
12 changes: 12 additions & 0 deletions packages/graphs/src/components/fishbone/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { GraphOptions } from "../../types";
import type { NodeData } from '@antv/g6';

export interface FishboneOptions extends GraphOptions {
/**
* The type of the fishbone diagram.
* - `'decision'`: direction from left to right.
* - `'cause'`: direction from right to left.
* @default 'cause'
*/
type?: 'decision' | 'cause';
}
18 changes: 8 additions & 10 deletions packages/graphs/src/components/indented-tree/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,14 @@ import type { IndentedTreeOptions } from './types';
export const IndentedTree: ForwardRefExoticComponent<
PropsWithoutRef<PropsWithChildren<IndentedTreeOptions>> & RefAttributes<Graph>
> = forwardRef<Graph, PropsWithChildren<IndentedTreeOptions>>(({ children, ...props }, ref) => {
const options = useMemo(() => {
const { type = 'default', nodeMinWidth, nodeMaxWidth, direction = 'right', ...restProps } = props;
const options = mergeOptions(
COMMON_OPTIONS,
DEFAULT_OPTIONS,
getIndentedTreeOptions({ type, nodeMinWidth, nodeMaxWidth, direction }),
restProps,
);
return options;
}, [props]);
const { type = 'default', nodeMinWidth, nodeMaxWidth, direction = 'right', ...restProps } = props;

const options = useMemo(() => mergeOptions(
COMMON_OPTIONS,
DEFAULT_OPTIONS,
getIndentedTreeOptions({ type, nodeMinWidth, nodeMaxWidth, direction }),
restProps,
), [props]);

return (
<BaseGraph {...options} ref={ref}>
Expand Down
14 changes: 7 additions & 7 deletions packages/graphs/src/components/indented-tree/options.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export const DEFAULT_OPTIONS: IndentedTreeOptions = {
edge: {
type: 'indented',
style: {
lineWidth: 2,
lineWidth: 3,
},
},
transforms: (prev) => [
Expand Down Expand Up @@ -152,9 +152,9 @@ export const getIndentedTreeOptions = ({
depth === 0
? { type: 'filled', color: '#f1f4f5', style: { color: '#252525' } }
: {
type: 'underlined',
style: { textAlign: getNodeTextAlign(this as unknown as Graph, data) },
},
type: 'underlined',
style: { textAlign: getNodeTextAlign(this as unknown as Graph, data) },
},
);
return <TextNode {...props} />;
},
Expand All @@ -165,8 +165,8 @@ export const getIndentedTreeOptions = ({
return side === 'left'
? [{ placement: 'bottom' }, { placement: 'bottom-right' }]
: side === 'center'
? [{ placement: 'bottom' }]
: [{ placement: 'bottom' }, { placement: 'bottom-left' }];
? [{ placement: 'bottom' }]
: [{ placement: 'bottom' }, { placement: 'bottom-left' }];
},
},
},
Expand All @@ -175,7 +175,7 @@ export const getIndentedTreeOptions = ({
stroke: function (data) {
return (this.getNodeData(data.target).style!.color as string) || '#99ADD1';
},
radius: 16,
radius: 24,
},
},
layout: {
Expand Down
1 change: 1 addition & 0 deletions packages/graphs/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ export { IndentedTree, type IndentedTreeOptions } from './indented-tree';
export { MindMap, type MindMapOptions } from './mind-map';
export { NetworkGraph, type NetworkGraphOptions } from './network-graph';
export { OrganizationChart, type OrganizationChartOptions } from './organization-chart';
export { Fishbone, type FishboneOptions } from './fishbone';
12 changes: 6 additions & 6 deletions packages/graphs/src/components/mind-map/options.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export const DEFAULT_OPTIONS: MindMapOptions = {
edge: {
type: 'cubic-horizontal',
style: {
lineWidth: 2,
lineWidth: 3,
},
},
transforms: (prev) => [
Expand Down Expand Up @@ -103,8 +103,8 @@ export function getMindMapOptions({
depth === 0
? { type: 'filled', color: '#f1f4f5', style: { color: '#252525' } }
: depth === 1
? { type: 'filled' }
: { type: 'outlined' },
? { type: 'filled' }
: { type: 'outlined' },
);
return <TextNode {...props} />;
},
Expand Down Expand Up @@ -157,9 +157,9 @@ export function getMindMapOptions({
depth === 0
? { type: 'filled', color: '#f1f4f5', style: { color: '#252525' } }
: {
type: 'underlined',
style: side === 'left' ? { textAlign: 'right' } : side === 'center' ? { textAlign: 'center' } : {},
},
type: 'underlined',
style: side === 'left' ? { textAlign: 'right' } : side === 'center' ? { textAlign: 'center' } : {},
},
);
return <TextNode {...props} />;
},
Expand Down
2 changes: 1 addition & 1 deletion packages/graphs/src/core/base/node/text-node.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ export const TextNode: FC<TextNodeProps> = (props) => {
text = '',
font,
color = '#1783ff',
borderWidth = 2,
borderWidth = 3,
maxWidth = Infinity,
isActive = false,
isSelected = false,
Expand Down
1 change: 1 addition & 0 deletions packages/graphs/src/core/constants/options.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { GraphOptions } from '../../types';

export const COMMON_OPTIONS: GraphOptions = {
autoResize: true,
behaviors: [
{
key: 'zoom-canvas',
Expand Down
14 changes: 6 additions & 8 deletions packages/graphs/src/core/utils/tree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@ import { measureTextSize } from './measure-text';
export const getLinearTextNodeStyle = memoize(
(text: string, minWidth: number, maxWidth: number, depth: number = 0) => {
const font = {
fontWeight: 'bold',
fontSize: depth === 0 ? 20 : 16,
fontFamily: 'PingFang SC',
fontWeight: depth === 0 ? 600 : 400,
fontSize: depth === 0 ? 24 : 16,
};
const offset = depth === 0 ? [24, 36] : [12, 12];
const offset = depth === 0 ? [64, 30] : [12, 12];
const size = measureTextSize(text, offset, font, minWidth, maxWidth);
return { font, size };
},
Expand All @@ -17,11 +16,10 @@ export const getLinearTextNodeStyle = memoize(
export const getBoxedTextNodeStyle = memoize(
(text: string, minWidth: number, maxWidth: number, depth: number = 0) => {
const font = {
fontWeight: 'bold',
fontSize: depth === 0 ? 20 : depth === 1 ? 18 : 16,
fontFamily: 'PingFang SC',
fontWeight: depth === 0 || depth === 1 ? 600 : 400,
fontSize: depth === 0 ? 24 : 16,
};
const offset = depth === 0 ? [24, 36] : [12, 24];
const offset = depth === 0 ? [64, 30] : [36, 24];
const size = measureTextSize(text, offset, font, minWidth, maxWidth);
return { font, size };
},
Expand Down
2 changes: 2 additions & 0 deletions packages/graphs/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export {
MindMap,
NetworkGraph,
OrganizationChart,
Fishbone,
} from './components';
export type {
DendrogramOptions,
Expand All @@ -18,6 +19,7 @@ export type {
MindMapOptions,
NetworkGraphOptions,
OrganizationChartOptions,
FishboneOptions
} from './components';
export { CollapseExpandIcon, RCNode } from './core/base';
export type { OrganizationChartNodeProps, TextNodeProps } from './core/base/node';
Expand Down
Loading

0 comments on commit 5a88e5e

Please sign in to comment.