Skip to content

Commit

Permalink
feat(gi-sdk): basic graph application rendering using gi-sdk (#540)
Browse files Browse the repository at this point in the history
* feat(gi-sdk): use gisdk to render graph application

* chore: update deps
  • Loading branch information
yvonneyx authored Jul 25, 2024
1 parent 75b7f39 commit e927ccb
Show file tree
Hide file tree
Showing 58 changed files with 520 additions and 969 deletions.
5 changes: 2 additions & 3 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,8 @@ module.exports = {
},
rules: {
'no-console': 'error',
noImplicitAny: 0, // 允许 any
'no-unused-vars': ['warn', { varsIgnorePattern: '^_' }],
'@typescript-eslint/no-unused-vars': ['warn', { varsIgnorePattern: '^_' }],
'@typescript-eslint/no-explicit-any': 1,
'@typescript-eslint/no-unused-vars': ['warn', { ignoreRestSiblings: true }],
'jsdoc/check-param-names': 1,
'jsdoc/require-description': 1,
'jsdoc/require-jsdoc': 1,
Expand Down
3 changes: 0 additions & 3 deletions packages/gi-sdk/.eslintrc.js

This file was deleted.

45 changes: 19 additions & 26 deletions packages/gi-sdk/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@antv/gi-sdk",
"version": "3.0.0",
"version": "3.0.0-alpha.0",
"description": "the react toolkit for graph analysis based on g6",
"main": "lib/index.js",
"module": "es/index.js",
Expand All @@ -10,50 +10,43 @@
"url": "https://github.com/antvis/Graphin.git"
},
"files": [
"src",
"es",
"lib",
"dist"
"lib"
],
"scripts": {
"dev": "vite",
"watch": "pnpm lib:es --w",
"build": "run-s clean lib",
"build:lib": "run-s clean lib",
"clean": "rimraf lib es dist",
"ci": "run-s type-check lint build",
"clean": "rimraf lib es",
"dev": "vite",
"lib:cjs": "tsc -p tsconfig.build.json --target ES5 --module commonjs --outDir lib",
"lib:es": "tsc -p tsconfig.build.json --target ES5 --module ESNext --outDir es",
"lib": "run-p lib:*",
"lib:cjs": "tsc -p tsconfig.json --target ES5 --module commonjs --outDir lib",
"lib:es": "tsc -p tsconfig.json --target ES5 --module ESNext --outDir es",
"lint": "eslint --cache --ext .js,.jsx,.ts,.tsx --fix --format=pretty ./src && pnpm run lint:prettier",
"lint:prettier": "pnpm run prettier && git diff && prettier --version && prettier --check \"src/**/**.{js,jsx,tsx,ts,less,md,json}\" --end-of-line auto",
"prettier": "prettier --write \"**/**.{js,jsx,tsx,ts,less,md,json}\""
"lint": "eslint ./src --quiet && prettier ./src --check",
"prepublishOnly": "npm run ci",
"type-check": "tsc --noEmit",
"watch": "pnpm lib:es --w"
},
"author": "",
"license": "ISC",
"dependencies": {
"@ant-design/icons": "^4.8.1",
"@antv/graphin": "workspace:*",
"@emotion/react": "^11.11.3",
"@antv/g6": "^5.0.0",
"@antv/graphin": "^3.0.0",
"antd": "^5.13.2",
"i18next": "^23.8.2",
"lodash-es": "^4.17.21",
"react-i18next": "^14.0.1",
"react-transition-group": "^4.4.5",
"size-sensor": "^1.0.1",
"valtio": "^1.13.0"
"react-transition-group": "^4.4.5"
},
"devDependencies": {
"@antv/graphin": "^3.0.0",
"lodash-es": "^4.17.21",
"less": "^4.2.0",
"react": ">=16.9.0",
"react-dom": ">=16.9.0"
},
"peerDependencies": {
"@antv/graphin": "^2.7.27",
"lodash-es": "^4.17.21",
"react": ">=16.9.0",
"react-dom": ">=16.9.0"
},
"publishConfig": {
"access": "public"
"registry": "https://registry.npmjs.org/",
"access": "public",
"tag": "alpha"
}
}
212 changes: 90 additions & 122 deletions packages/gi-sdk/public/main.tsx
Original file line number Diff line number Diff line change
@@ -1,65 +1,29 @@
import React from 'react';
import { Button } from 'antd';
import { get } from 'lodash-es';
import React, { useEffect, useMemo, useState } from 'react';
import ReactDOM from 'react-dom';
import GISDK, { Widgets, useModel, useGraph } from '../src';

const data = {
nodes: [
{ id: 'node0', data: {} },
{ id: 'node1', data: {} },
{ id: 'node2', data: {} },
{ id: 'node3', data: {} },
{ id: 'node4', data: {} },
{ id: 'node5', data: {} },
{ id: 'node6', data: {} },
{ id: 'node7', data: {} },
{ id: 'node8', data: {} },
{ id: 'node9', data: {} },
{ id: 'node10', data: {} },
{ id: 'node11', data: {} },
{ id: 'node12', data: {} },
{ id: 'node13', data: {} },
{ id: 'node14', data: {} },
{ id: 'node15', data: {} },
{ id: 'node16', data: {} },
],
edges: [
{ id: 'edge0', source: 'node0', target: 'node1' },
{ id: 'edge1', source: 'node0', target: 'node2' },
{ id: 'edge2', source: 'node0', target: 'node3' },
{ id: 'edge3', source: 'node0', target: 'node4' },
{ id: 'edge4', source: 'node0', target: 'node5' },
{ id: 'edge5', source: 'node1', target: 'node6' },
{ id: 'edge6', source: 'node1', target: 'node7' },
{ id: 'edge7', source: 'node2', target: 'node8' },
{ id: 'edge8', source: 'node2', target: 'node9' },
{ id: 'edge9', source: 'node2', target: 'node10' },
{ id: 'edge10', source: 'node2', target: 'node11' },
{ id: 'edge11', source: 'node2', target: 'node12' },
{ id: 'edge12', source: 'node2', target: 'node13' },
{ id: 'edge13', source: 'node3', target: 'node14' },
{ id: 'edge14', source: 'node3', target: 'node15' },
{ id: 'edge15', source: 'node3', target: 'node16' },
],
};
import { GISDK, registerWidget, useEventSubscribe, useGlobalModel, useGraph, useWidgets } from '@antv/gi-sdk';
import type { WidgetItem } from '@antv/gi-sdk';
import type { GraphinProps } from '@antv/graphin';
import { Button, Spin } from 'antd';
import { CanvasEvent, NodeEvent } from '@antv/g6';
import type { IPointerEvent, Node } from '@antv/g6';

const style = {
fontSize: 32,
color: 'green',
};

const CustomSidebar = props => {
// const { graph } = useGraph();
const [model, setModel] = useModel();
const { panel } = model;
const CustomSidebar: React.FC = () => {
const [globalModel] = useGlobalModel();
const [, updateWidget] = useWidgets();
const isPanelOpen = globalModel?.panel;

return (
<div style={{ height: '100%' }}>
<p>sider</p>
<p>Panel {panel?.open ? <b style={style}>opened</b> : <b style={style}>closed</b>}</p>
<p>Sider</p>
<p>Panel {isPanelOpen ? <b style={style}>opened</b> : <b style={style}>closed</b>}</p>
<Button
onClick={() => {
setModel('widgets:[2]properties.count', Math.floor(Math.random() * 1000));
updateWidget({ name: 'custom-panel', properties: { count: Math.floor(Math.random() * 1000) } });
}}
>
Change panel Count
Expand All @@ -68,90 +32,94 @@ const CustomSidebar = props => {
);
};

const CustomPanel = props => {
const { properties } = props;
const [model, setModel] = useModel();
const { sider } = model;
const activeNode = get(model, 'interaction.clickNode', {});
const CustomPanel: React.FC<{ count: number }> = props => {
const { count } = props;
const [globalModel] = useGlobalModel();
const isSiderOpen = globalModel?.sider;

return (
<div>
<p style={style}>{properties?.count}...</p>
<p>{sider?.open ? 'sider opened' : 'sider closed'}</p>
<p style={style}>{count}...</p>
<p>Sider {isSiderOpen ? <b style={style}>opened</b> : <b style={style}>closed</b>}</p>
<p>
current node: <b style={style}>{activeNode.id}</b>
current node: <b style={style}>{globalModel.currentNode?.id}</b>
</p>
</div>
);
};

const CustomHeader = props => {
return <h1 style={{ height: 48, lineHeight: '48px', textAlign: 'center' }}>GI SDK</h1>;
const CustomHeader: React.FC = () => {
return <h1 style={{ height: 48, lineHeight: '48px', textAlign: 'center' }}>GI-SDK</h1>;
};

Widgets.register('CustomSidebar', CustomSidebar);
Widgets.register('CustomPanel', CustomPanel);
Widgets.register('CustomHeader', CustomHeader);

const SPEC = {
version: 'v0.1',
metadata: {
name: '测试应用',
},
dataset: {
id: '4a4fee6d-f4e8-403b-a1e6-19fc7fcad418',
metadata: {
name: '我链接的 GraphScope 数据',
},
type: 'remote',
serviceType: 'GS_SERVICE_INTIAL_GRAPH',
properties: {},
},
spec: {
graph: {
layout: {
type: 'force',
linkDistance: 50,
animated: true,
clustering: true,
nodeClusterBy: 'cluster',
clusterNodeStrength: 100,
const CustomCanvasComponent: React.FC = () => {
const [, updateGlobalModel] = useGlobalModel();
const graph = useGraph();

useEventSubscribe<IPointerEvent<Node>>(NodeEvent.CLICK, e => {
const nodeId = e.target.id;
updateGlobalModel({ currentNode: graph?.getNodeData(nodeId), panel: true });
});

useEventSubscribe(CanvasEvent.CLICK, e => {
updateGlobalModel({ currentNode: null, panel: false });
});

return null;
};

registerWidget('custom-sidebar', CustomSidebar);
registerWidget('custom-panel', CustomPanel);
registerWidget('custom-header', CustomHeader);
registerWidget('custom-canvas-component', CustomCanvasComponent);

const Demo: React.FC = () => {
const [data, setData] = useState(undefined);

useEffect(() => {
fetch('https://assets.antv.antgroup.com/g6/graph.json')
.then(res => res.json())
.then(data => setData(data));
}, []);

const graphinProps: GraphinProps = useMemo(
() => ({
options: {
autoResize: true,
data,
layout: { type: 'd3-force' },
behaviors: ['drag-canvas', 'zoom-canvas', 'drag-element', 'click-select', 'hover-activate'],
animation: false,
},
data,
}),
[data],
);

const widgets: WidgetItem[] = [
{
name: 'custom-header',
slot: 'header',
},
widgets: [
{
name: 'CustomHeader',
solt: 'header',
},
{
name: 'CustomSidebar',
solt: 'sider',
},
{
name: 'CustomPanel',
solt: 'panel',
properties: {
count: 100,
},
},
/** 内部组件,无需注册 */
{
name: 'Toolbar',
solt: 'canvas',
},
{
name: 'Minimap',
solt: 'canvas',
},
{
name: 'ContextMenu',
solt: 'canvas',
{
name: 'custom-sidebar',
slot: 'sider',
},
{
name: 'custom-panel',
slot: 'panel',
properties: {
count: 100,
},
],
},
};
},
{
name: 'custom-canvas-component',
slot: 'canvas',
},
];

if (!data) return <Spin />;

localStorage.setItem('language', 'zh-CN');
return <GISDK graph={graphinProps} widgets={widgets}></GISDK>;
};

ReactDOM.render(<GISDK {...SPEC}></GISDK>, document.getElementById('root'));
ReactDOM.render(<Demo />, document.getElementById('root'));
Loading

0 comments on commit e927ccb

Please sign in to comment.