react-kggraph 0.0.18 → 0.0.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -10,6 +10,8 @@
10
10
  - 🔍 支持节点搜索、高亮路径分析
11
11
  - 📦 支持知识图谱展开、节点详情查看
12
12
  - 📱 响应式设计,支持缩放、拖拽等交互
13
+ - ⚙️ 支持 ActionBar 工具栏配置(位置、按钮列表)
14
+ - 🖱️ 支持右键菜单(actionCircle)自定义
13
15
 
14
16
  ## 安装
15
17
 
@@ -22,7 +24,7 @@ npm install react-kggraph
22
24
  react-kggraph 依赖以下 peerDependencies,请确保已安装:
23
25
 
24
26
  ```bash
25
- npm install react react-dom antd
27
+ npm install react react-dom antd @ant-design/icons
26
28
  ```
27
29
 
28
30
  ## 使用
@@ -46,9 +48,7 @@ const App = () => {
46
48
 
47
49
  return (
48
50
  <div style={{ width: '100%', height: '600px' }}>
49
- <CytoscapeReact
50
- data={data}
51
- />
51
+ <CytoscapeReact data={data} />
52
52
  </div>
53
53
  );
54
54
  };
@@ -57,8 +57,8 @@ const App = () => {
57
57
  ### 完整示例
58
58
 
59
59
  ```tsx
60
- import { CytoscapeReact, SearchType } from 'react-kggraph';
61
- import 'react-kggraph/style.css';
60
+ import { CytoscapeReact, SearchType, actionList } from 'react-kggraph';
61
+ import { MinusCircleOutlined } from '@ant-design/icons';
62
62
 
63
63
  const App = () => {
64
64
  const data = {
@@ -108,6 +108,18 @@ const App = () => {
108
108
  graphInfo={{ kgId: 3, searchNodes: ['刘培', '许冉'] }}
109
109
  stepNextApi={stepNextApi}
110
110
  knowledgeCardApi={knowledgeCardApi}
111
+ // ActionBar 配置
112
+ actionBar={{
113
+ position: 'top-left',
114
+ actionList: actionList.filter(item => ['queryStatistics', 'layout'].includes(item.key)),
115
+ }}
116
+ // 右键菜单配置
117
+ actionCircle={[
118
+ { ids: 'contract', label: '展收实体' },
119
+ { ids: 'expanded', label: '展收属性' },
120
+ { ids: 'nodeInfo', label: '节点信息' },
121
+ { ids: 'customNew', label: '自定义菜单', onClick: (node) => console.log('自定义', node) },
122
+ ]}
111
123
  />
112
124
  </div>
113
125
  );
@@ -118,17 +130,147 @@ const App = () => {
118
130
 
119
131
  | 属性 | 类型 | 必填 | 说明 |
120
132
  |------|------|------|------|
121
- | data | `GraphData \| any[]` | | 图谱数据 |
122
- | getAllGraphData | `(params?: any) => Promise<any>` | ❌ | 获取完整图谱数据的回调函数 |
133
+ | data | `GraphData \| any[]` | | 图谱数据 |
134
+ | getAllGraphData | `(params?: any) => void` | ❌ | 获取完整图谱数据的回调函数 |
123
135
  | searchType | `SearchType` | ❌ | 搜索类型 |
124
136
  | highPathAnalysis | `string[]` | ❌ | 高亮路径分析的节点ID数组 |
125
137
  | graphInfo | `GraphInfo` | ❌ | 图谱配置信息 |
126
138
  | stepNextApi | `(params: any) => Promise<any>` | ❌ | 下一步API(用于知识图谱展开) |
127
139
  | knowledgeCardApi | `(params: any) => Promise<any>` | ❌ | 知识卡片API |
128
140
  | colors | `GraphColors` | ❌ | 颜色配置 |
141
+ | actionBar | `ActionBarConfig` | ❌ | ActionBar 工具栏配置 |
142
+ | actionCircle | `ActionCircleItem[]` | ❌ | 右键菜单配置 |
129
143
 
130
- ## 类型定义
144
+ ## ActionBar 配置
145
+
146
+ ActionBar 工具栏支持自定义位置和按钮列表。
147
+
148
+ ### ActionBarConfig
149
+
150
+ ```typescript
151
+ interface ActionBarConfig {
152
+ /** 位置配置 */
153
+ position?: ActionBarPosition;
154
+ /** 按钮列表配置 */
155
+ actionList?: ActionBarItem[];
156
+ }
157
+
158
+ type ActionBarPosition = 'top' | 'bottom' | 'left' | 'right' | 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' | {
159
+ top?: string | number;
160
+ right?: string | number;
161
+ bottom?: string | number;
162
+ left?: string | number;
163
+ };
164
+ ```
165
+
166
+ ### ActionBarItem
167
+
168
+ ```typescript
169
+ interface ActionBarItem {
170
+ key: string; // 唯一标识
171
+ title: string; // 显示文本
172
+ icon?: React.ReactNode[]; // 图标(支持多个 antd icon)
173
+ onClick?: (params?: any) => void; // 点击事件
174
+ }
175
+ ```
176
+
177
+ ### 默认按钮列表
178
+
179
+ 组件内置以下默认按钮:
180
+
181
+ | key | title | 说明 |
182
+ |-----|-------|------|
183
+ | queryStatistics | 查询统计 | 统计面板 |
184
+ | layout | 布局 | 布局切换 |
185
+ | graphDisplaySettings | 图显示设置 | 图形显示配置 |
186
+ | analysis | 分析 | 分析功能 |
187
+ | select | 选择 | 选择功能 |
188
+ | downloadData | 下载数据 | 导出数据 |
189
+ | hideAttributes | 隐藏属性 | 隐藏节点属性 |
190
+ | textPosition | 文字位置 | 文字位置调整 |
191
+ | clearCanvas | 清空画布 | 清空画布 |
192
+ | nodeZoom | 节点缩放 | 节点缩放控制 |
193
+ | canvasZoom | 画布缩放 | 画布缩放控制 |
194
+
195
+ ### ActionBar 示例
196
+
197
+ ```tsx
198
+ // 方式1:使用预定义 actionList
199
+ import { actionList } from 'react-kggraph';
200
+ import { MinusCircleOutlined } from '@ant-design/icons';
201
+
202
+ <CytoscapeReact
203
+ actionBar={{
204
+ position: 'top-left',
205
+ actionList: actionList.filter(item => ['queryStatistics', 'layout'].includes(item.key)),
206
+ }}
207
+ />
208
+
209
+ // 方式2:自定义按钮
210
+ <CytoscapeReact
211
+ actionBar={{
212
+ position: 'bottom-right',
213
+ actionList: [
214
+ { key: 'customBtn', title: '自定义按钮', icon: [<MinusCircleOutlined key="icon" />], onClick: () => console.log('点击') },
215
+ ],
216
+ }}
217
+ />
218
+ ```
219
+
220
+ ## 右键菜单配置(actionCircle)
221
+
222
+ 右键菜单支持过滤、追加自定义菜单项。
223
+
224
+ ### ActionCircleItem
225
+
226
+ ```typescript
227
+ interface ActionCircleItem {
228
+ ids: string; // 唯一标识
229
+ label: string; // 显示文本
230
+ onClick?: (node: any) => void; // 点击事件回调
231
+ }
232
+ ```
233
+
234
+ ### 默认菜单项
235
+
236
+ | ids | label | 说明 |
237
+ |-----|-------|------|
238
+ | contract | 展收实体 | 收缩实体节点 |
239
+ | expanded | 展收属性 | 展开节点属性 |
240
+ | nodeInfo | 节点信息 | 查看节点详情 |
241
+ | knowledgeCard | 知识卡片 | 查看知识卡片 |
242
+ | selRelate | 选中关联 | 高亮关联节点 |
243
+ | pathAnalysis | 路径分析 | 路径分析功能 |
131
244
 
245
+ ### actionCircle 示例
246
+
247
+ ```tsx
248
+ // 方式1:筛选默认菜单
249
+ <CytoscapeReact
250
+ actionCircle={[
251
+ { ids: 'contract', label: '展收实体' },
252
+ { ids: 'expanded', label: '展收属性' },
253
+ { ids: 'nodeInfo', label: '节点信息' },
254
+ ]}
255
+ />
256
+
257
+ // 方式2:覆盖默认 action
258
+ <CytoscapeReact
259
+ actionCircle={[
260
+ { ids: 'nodeInfo', label: '节点信息', onClick: (node) => console.log('自定义', node) },
261
+ ]}
262
+ />
263
+
264
+ // 方式3:追加自定义菜单项
265
+ <CytoscapeReact
266
+ actionCircle={[
267
+ { ids: 'contract', label: '展收实体' },
268
+ { ids: 'customNew', label: '自定义菜单', onClick: (node) => console.log('新增功能', node) },
269
+ ]}
270
+ />
271
+ ```
272
+
273
+ ## 类型定义
132
274
 
133
275
  ### GraphData
134
276
 
package/lib/index.es11.js CHANGED
@@ -1,75 +1,73 @@
1
- const i = ({ cyRef: n, setContextMenu: r, onPathAnalysis: l, setPathNodes: s }) => [
2
- {
3
- label: "展收实体",
4
- ids: "contract",
5
- action: (e) => {
6
- const o = n.current;
7
- console.log("新增属性", o);
8
- }
9
- },
10
- {
11
- label: "展收属性",
12
- ids: "expanded",
13
- action: (e) => {
14
- if (console.log("展开", e), !n.current) {
15
- console.error("Cytoscape instance is not available");
16
- return;
1
+ const f = ({ cyRef: n, setContextMenu: a, onPathAnalysis: t, setPathNodes: i, actionCircle: l }) => {
2
+ const d = [
3
+ {
4
+ label: "展收实体",
5
+ ids: "contract",
6
+ onClick: (e) => {
7
+ const o = n.current;
8
+ console.log("新增属性", o);
17
9
  }
18
- console.log("New node and edge added");
19
- }
20
- },
21
- {
22
- label: "节点信息",
23
- ids: "nodeInfo",
24
- action: (e) => {
25
- console.log("节点信息:", e);
26
- }
27
- },
28
- {
29
- label: "知识卡片",
30
- ids: "knowledgeCard",
31
- action: (e) => {
32
- const o = n.current;
33
- console.log("知识卡片", o);
34
- }
35
- },
36
- // {
37
- // label: '新增关系',
38
- // action: (node) => {
39
- // const cy = cyRef.current;
40
- // console.log('新增关系', cy)
41
- // },
42
- // },
43
- {
44
- label: "选中关联",
45
- ids: "selRelate",
46
- action: (e) => {
47
- const o = n.current;
48
- if (!o || !e || e.removed()) {
49
- console.error("节点或Cytoscape实例无效"), r((t) => ({ ...t, visible: !1 }));
50
- return;
10
+ },
11
+ {
12
+ label: "展收属性",
13
+ ids: "expanded",
14
+ onClick: (e) => {
15
+ if (console.log("展开", e), !n.current) {
16
+ console.error("Cytoscape instance is not available");
17
+ return;
18
+ }
19
+ }
20
+ },
21
+ {
22
+ label: "节点信息",
23
+ ids: "nodeInfo",
24
+ onClick: (e) => {
25
+ console.log("节点信息:", e);
26
+ }
27
+ },
28
+ {
29
+ label: "知识卡片",
30
+ ids: "knowledgeCard",
31
+ onClick: (e) => {
32
+ const o = n.current;
33
+ console.log("知识卡片", o);
34
+ }
35
+ },
36
+ {
37
+ label: "选中关联",
38
+ ids: "selRelate",
39
+ onClick: (e) => {
40
+ const o = n.current;
41
+ if (!o || !e || typeof e.removed != "function" || e.removed()) {
42
+ console.error("节点或Cytoscape实例无效"), a((r) => ({ ...r, visible: !1 }));
43
+ return;
44
+ }
45
+ const s = e, c = s.neighborhood().nodes().add(s);
46
+ c.edgesWith(c), o.nodes().not(c).removeClass("correlationStyle"), c.addClass("correlationStyle"), a((r) => ({ ...r, visible: !1 }));
47
+ }
48
+ },
49
+ {
50
+ label: "路径分析",
51
+ ids: "pathAnalysis",
52
+ onClick: (e) => {
53
+ n.current, console.log(11, e.data("name")), t == null || t(), i([e.data("name"), ""]);
51
54
  }
52
- const d = e, c = d.neighborhood().nodes().add(d);
53
- c.edgesWith(c), o.nodes().not(c).removeClass("correlationStyle"), c.addClass("correlationStyle"), r((t) => ({ ...t, visible: !1 }));
54
- }
55
- },
56
- {
57
- label: "路径分析",
58
- action: (e) => {
59
- n.current, console.log(11, e.data("name")), l == null || l(), s([e.data("name"), ""]);
60
55
  }
61
- }
62
- // {
63
- // label: '删除节点',
64
- // action: (node) => {
65
- // const cy = cyRef.current;
66
- // console.log('删除节点cy::::', cy)
67
- // if (node && !node.removed()) {
68
- // node.remove();
69
- // }
70
- // },
71
- // },
72
- ];
56
+ ];
57
+ if (!l || l.length === 0)
58
+ return d;
59
+ const u = new Set(l.map((e) => e.ids)), b = d.filter((e) => u.has(e.ids)).map((e) => {
60
+ const o = l.find((s) => s.ids === e.ids);
61
+ return o != null && o.onClick ? { ...e, onClick: o.onClick } : e;
62
+ }), C = new Set(d.map((e) => e.ids)), g = l.filter((e) => !C.has(e.ids)).map((e) => ({
63
+ ids: e.ids,
64
+ label: e.label,
65
+ onClick: e.onClick || ((o) => {
66
+ console.log(e.label, o);
67
+ })
68
+ }));
69
+ return [...b, ...g];
70
+ };
73
71
  export {
74
- i as menuItemsConfig
72
+ f as menuItemsConfig
75
73
  };
package/lib/index.es13.js CHANGED
@@ -1,26 +1,20 @@
1
1
  import { jsxs as d, jsx as e } from "react/jsx-runtime";
2
- import { useMemo as b } from "react";
2
+ import { useMemo as C } from "react";
3
3
  /* empty css */
4
- const L = ({
5
- items: h = [
6
- // { label: '知识卡片' },
7
- // { label: '选中关联' },
8
- // { label: '路径分析' },
9
- // { label: '展收实体' },
10
- // { label: '展收属性' },
11
- // { label: '节点配置' },
12
- ],
4
+ const S = ({
5
+ items: h = [],
13
6
  centerImage: f,
14
7
  size: c = 200,
15
- onClick: $
8
+ onClick: $,
9
+ targetNode: p
16
10
  }) => {
17
- const t = c / 2, a = c / 2 - 10, s = 30, o = 360 / h.length, p = b(() => h.map((n, r) => {
18
- const l = (r * o - 90) * (Math.PI / 180), i = ((r + 1) * o - 90) * (Math.PI / 180), m = ((r + 0.5) * o - 90) * (Math.PI / 180), y = t + a * Math.cos(l), v = t + a * Math.sin(l), w = t + a * Math.cos(i), A = t + a * Math.sin(i), N = t + s * Math.cos(i), k = t + s * Math.sin(i), u = t + s * Math.cos(l), x = t + s * Math.sin(l), g = o > 180 ? 1 : 0, I = `M ${u} ${x} L ${y} ${v} A ${a} ${a} 0 ${g} 1 ${w} ${A} L ${N} ${k} A ${s} ${s} 0 ${g} 0 ${u} ${x}`, M = (a + s) / 2 + 5, P = t + M * Math.cos(m), R = t + M * Math.sin(m);
11
+ const t = c / 2, a = c / 2 - 10, s = 30, o = 360 / h.length, y = C(() => h.map((n, r) => {
12
+ const l = (r * o - 90) * (Math.PI / 180), i = ((r + 1) * o - 90) * (Math.PI / 180), m = ((r + 0.5) * o - 90) * (Math.PI / 180), v = t + a * Math.cos(l), w = t + a * Math.sin(l), A = t + a * Math.cos(i), N = t + a * Math.sin(i), k = t + s * Math.cos(i), I = t + s * Math.sin(i), u = t + s * Math.cos(l), x = t + s * Math.sin(l), g = o > 180 ? 1 : 0, P = `M ${u} ${x} L ${v} ${w} A ${a} ${a} 0 ${g} 1 ${A} ${N} L ${k} ${I} A ${s} ${s} 0 ${g} 0 ${u} ${x}`, M = (a + s) / 2 + 5, R = t + M * Math.cos(m), b = t + M * Math.sin(m);
19
13
  return {
20
14
  ...n,
21
- path: I,
22
- textX: P,
23
- textY: R,
15
+ path: P,
16
+ textX: R,
17
+ textY: b,
24
18
  rotation: (r + 0.5) * o - 90
25
19
  };
26
20
  }), [h, o, t, a, s]);
@@ -28,7 +22,7 @@ const L = ({
28
22
  /* @__PURE__ */ d("svg", { width: c, height: c, className: "radial-svg", children: [
29
23
  /* @__PURE__ */ e("defs", { children: /* @__PURE__ */ e("filter", { id: "shadow", x: "-20%", y: "-20%", width: "140%", height: "140%", children: /* @__PURE__ */ e("feDropShadow", { dx: "0", dy: "2", stdDeviation: "3", floodOpacity: "0.3" }) }) }),
30
24
  /* @__PURE__ */ e("circle", { cx: t, cy: t, r: a, fill: "#f5f5f5", stroke: "#ddd", strokeWidth: "1" }),
31
- p.map((n, r) => /* @__PURE__ */ d("g", { onClick: () => $(n), className: "sector-group", children: [
25
+ y.map((n, r) => /* @__PURE__ */ d("g", { onClick: () => $(n, p), className: "sector-group", children: [
32
26
  /* @__PURE__ */ e(
33
27
  "path",
34
28
  {
@@ -68,5 +62,5 @@ const L = ({
68
62
  ] });
69
63
  };
70
64
  export {
71
- L as default
65
+ S as default
72
66
  };