@logixode/force-graph-lib 0.1.1 โ†’ 0.1.2

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.
Files changed (2) hide show
  1. package/README.md +250 -66
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -2,117 +2,301 @@
2
2
 
3
3
  A TypeScript library for creating interactive force-directed graphs with advanced features and optimizations for handling large datasets.
4
4
 
5
+ ## Table of Contents
6
+
7
+ - [Features](#features)
8
+ - [Installation](#installation)
9
+ - [Usage in TypeScript](#usage-in-typescript)
10
+ - [Usage in Vue.js](#usage-in-vuejs)
11
+ - [Detail for Types](#detail-for-types)
12
+ - [API Reference](#api-reference)
13
+ - [License](#license)
14
+
5
15
  ## Features
6
16
 
7
- - ๐ŸŽฏ Dynamic label threshold control
8
- - ๐Ÿ”„ Multiple layout algorithms (Force-directed and Circle Pack)
9
- - ๐ŸŽจ Customizable styling for nodes and edges
10
- - ๐Ÿ“Š Efficient handling of large datasets
11
- - ๐Ÿ”„ Pagination support for incremental data loading
12
- - ๐Ÿ”„ Graph refresh and reset capabilities
13
- - โšก Non-blocking calculations using Web Workers
17
+ - ๐ŸŽจ Highly customizable styling for nodes, links, and groups.
18
+ - ๐ŸŽฏ Dynamic label visibility control based on zoom level.
19
+ - ๐Ÿ”„ Incremental data loading and dynamic data updates.
20
+ - โš™๏ธ Fine-grained control over the force simulation (e.g., collision, clustering).
21
+ - ๐Ÿš€ Optimized for large datasets with features like dynamic cooldown time.
22
+ - ๐Ÿ–ผ๏ธ Group visualization with borders and labels.
23
+ - ๐Ÿ” Zoom and pan, including programmatic focus on specific nodes or coordinates.
24
+ - โœ‹ Persist node positions after dragging.
25
+ - ๐Ÿ’ง Responsive design that adapts to container size changes.
26
+ - ๐Ÿ”ง Methods to query and manipulate graph data and state.
27
+ - ๐Ÿงน Full cleanup and resource destruction.
14
28
 
15
29
  ## Installation
16
30
 
17
31
  ```bash
18
- npm install force-graph-lib
32
+ npm install @logixode/force-graph-lib
33
+ ```
34
+
35
+ ## Usage in TypeScript
36
+
37
+ Here's an example of how to use the library in a TypeScript project.
38
+
39
+ **HTML File**
40
+
41
+ ```html
42
+ <div id="graph-container" style="width: 800px; height: 600px; border: 1px solid #ccc;"></div>
19
43
  ```
20
44
 
21
- ## Usage
45
+ **TypeScript File (`main.ts`)**
22
46
 
23
47
  ```typescript
24
- import { ForceGraph } from 'force-graph-lib'
48
+ import { ForceGraph } from '@logixode/force-graph-lib'
49
+ import type { GraphData, GraphOptions, NodeData } from '@logixode/force-graph-lib'
50
+
51
+ // 1. Get the container element
52
+ const container = document.getElementById('graph-container') as HTMLElement
53
+
54
+ // 2. Define initial data
55
+ const initialData: GraphData = {
56
+ nodes: [
57
+ { id: '1', label: 'Topic Node', type: 'topic' },
58
+ { id: '2', label: 'Post A', type: 'post' },
59
+ { id: '3', label: 'Post B', type: 'post' },
60
+ ],
61
+ links: [
62
+ { source: '1', target: '2' },
63
+ { source: '1', target: '3' },
64
+ ],
65
+ }
66
+
67
+ // 3. Define graph options
68
+ const options: GraphOptions = {
69
+ width: container.clientWidth,
70
+ height: container.clientHeight,
71
+ keepDragPosition: true,
72
+ labelThreshold: 1.2,
73
+
74
+ // Node styling
75
+ nodeSize: (node: NodeData) => (node.type === 'topic' ? 5 : 2),
76
+ nodeLabel: (node: NodeData) => node.label as string,
77
+ nodeColor: (node: NodeData) => (node.type === 'topic' ? '#FA8F21' : '#1877F2'),
78
+ nodeBorderWidth: 0.5,
79
+ nodeBorderColor: 'white',
80
+
81
+ // Link styling
82
+ linkWidth: 0.4,
83
+ linkCurvature: 0.1,
84
+
85
+ // Simulation forces
86
+ collide: (node: NodeData) => (node.type === 'topic' ? 15 : 5),
87
+ cluster: (node: NodeData) => node.type, // Group nodes by their 'type'
88
+ }
89
+
90
+ // 4. Initialize the graph
91
+ const graph = new ForceGraph(container, initialData, options)
25
92
 
26
- // Initialize the graph
27
- const container = document.getElementById('graph-container')
28
- const graph = new ForceGraph(
29
- container,
30
- {
93
+ // 5. Interact with the graph
94
+ setTimeout(() => {
95
+ const newData = {
31
96
  nodes: [
32
- { id: '1', name: 'Node 1' },
33
- { id: '2', name: 'Node 2' },
97
+ { id: '4', label: 'Post C', type: 'post' },
98
+ { id: '5', label: 'Repost', type: 'repost' },
34
99
  ],
35
- links: [{ source: '1', target: '2' }],
36
- },
37
- {
38
- labelThreshold: 1.2,
39
- nodeSize: (node) => node.size || 5,
40
- nodeLabel: (node) => node.name,
41
- nodeIcon: 'โ—',
42
- },
43
- )
44
-
45
- // Add more data incrementally
46
- graph.addData({
47
- nodes: [{ id: '3', name: 'Node 3' }],
48
- links: [{ source: '2', target: '3' }],
100
+ links: [
101
+ { source: '2', target: '4' },
102
+ { source: '1', target: '5' },
103
+ ],
104
+ }
105
+ graph.addData(newData)
106
+ graph.focusPosition({ id: '4' })
107
+ }, 2000)
108
+
109
+ // Handle window resizing
110
+ window.addEventListener('resize', () => {
111
+ graph.setOptions({
112
+ width: container.clientWidth,
113
+ height: container.clientHeight,
114
+ })
115
+ graph.reinitialize()
49
116
  })
117
+ ```
50
118
 
51
- // Change layout
52
- graph.setLayout('circlepack')
119
+ ## Usage in Vue.js
53
120
 
54
- // Update label threshold
55
- graph.setLabelThreshold(1.5)
121
+ Here is a basic example of integrating the library within a Vue 3 component using the Composition API.
56
122
 
57
- // Refresh or reset the graph
58
- graph.refreshGraph()
59
- graph.resetGraph()
123
+ ```vue
124
+ <template>
125
+ <div ref="graphContainer" style="width: 100%; height: 600px;"></div>
126
+ </template>
60
127
 
61
- // Check loading state
62
- console.log(graph.isLoading())
128
+ <script setup lang="ts">
129
+ import { ref, graphContainer, onMounted, onUnmounted, watch } from 'vue'
130
+ import { ForceGraph } from '@logixode/force-graph-lib'
131
+ import type { GraphData, GraphOptions, NodeData } from '@logixode/force-graph-lib'
132
+ import { useElementSize } from '@vueuse/core'
63
133
 
64
- // Clean up
65
- graph.destroy()
66
- ```
134
+ const graphContainer = useTemplateRef('graphContainer')
135
+ const graph = ref<ForceGraph | null>(null)
136
+ const { width, height } = useElementSize(graphContainer)
67
137
 
68
- ## API Reference
138
+ const initialData: GraphData = {
139
+ nodes: [
140
+ { id: 'a', label: 'A', sentiment: 'positive' },
141
+ { id: 'b', label: 'B', sentiment: 'negative' },
142
+ { id: 'c', label: 'C', sentiment: 'positive' },
143
+ ],
144
+ links: [
145
+ { source: 'a', target: 'b' },
146
+ { source: 'c', target: 'a' },
147
+ ],
148
+ }
69
149
 
70
- ### Constructor
150
+ const graphOptions: GraphOptions = {
151
+ keepDragPosition: true,
152
+ nodeSize: 3,
153
+ nodeLabel: (node: NodeData) => node.label as string,
154
+ nodeColor: (node: NodeData) => (node.sentiment === 'positive' ? 'green' : 'red'),
155
+ cluster: (node: NodeData) => node.sentiment,
156
+ }
71
157
 
72
- ```typescript
73
- new ForceGraph(container: HTMLElement, data: GraphData, options?: GraphOptions)
158
+ onMounted(() => {
159
+ if (graphContainer.value) {
160
+ graph.value = new ForceGraph(graphContainer.value, initialData, {
161
+ ...graphOptions,
162
+ width: width.value,
163
+ height: height.value,
164
+ })
165
+ }
166
+ })
167
+
168
+ onUnmounted(() => {
169
+ graph.value?.destroy()
170
+ })
171
+
172
+ // Make graph responsive
173
+ watch([width, height], (newSize, oldSize) => {
174
+ if (
175
+ graph.value &&
176
+ (Math.abs(newSize[0] - oldSize[0]) > 10 || Math.abs(newSize[1] - oldSize[1]) > 10)
177
+ ) {
178
+ graph.value.setOptions({ width: width.value, height: height.value })
179
+ graph.value.reinitialize()
180
+ }
181
+ })
182
+ </script>
74
183
  ```
75
184
 
76
- ### GraphData Interface
185
+ ## Detail for Types
186
+
187
+ ### `GraphData` Interface
77
188
 
78
189
  ```typescript
79
190
  interface GraphData {
80
191
  nodes: Array<{
81
- id: string
192
+ id: string | number
82
193
  [key: string]: any
83
194
  }>
84
195
  links: Array<{
85
- source: string
86
- target: string
196
+ source: string | number
197
+ target: string | number
87
198
  [key: string]: any
88
199
  }>
89
200
  }
90
201
  ```
91
202
 
92
- ### GraphOptions Interface
203
+ ### `GraphOptions` Interface
93
204
 
94
205
  ```typescript
95
206
  interface GraphOptions {
207
+ width?: number
208
+ height?: number
96
209
  labelThreshold?: number
97
- layout?: 'force' | 'circlepack'
98
- nodeSize?: number | ((node: any) => number)
99
- linkWidth?: number | ((link: any) => number)
100
- nodeLabel?: string | ((node: any) => string)
101
- linkLabel?: string | ((link: any) => string)
102
- nodeIcon?: string | ((node: any) => string)
210
+ keepDragPosition?: boolean
211
+
212
+ // Node Styling
213
+ nodeSize?: number | ((node: NodeData) => number)
214
+ nodeLabel?: string | ((node: NodeData) => string)
215
+ nodeLabelColor?: string | ((node: NodeData) => string)
216
+ nodeColor?: string | ((node: NodeData) => string)
217
+ nodeBorderColor?: string | ((node: NodeData) => string)
218
+ nodeBorderWidth?: number | ((node: NodeData) => number)
219
+
220
+ // Link Styling
221
+ linkWidth?: number | ((link: LinkData) => number)
222
+ linkCurvature?: number | 'curvature' | ((link: LinkData) => number)
223
+ linkDirectionalParticles?: number | ((link: LinkData) => number)
224
+ linkDirectionalParticleSpeed?: number | ((link: LinkData) => number)
225
+ linkDirectionalParticleWidth?: number | ((link: LinkData) => number)
226
+ linkDirectionalParticleColor?: string | ((link: LinkData) => string)
227
+
228
+ // Simulation
229
+ cluster?: (node: NodeData) => any
230
+ collide?: (node: NodeData) => number
231
+
232
+ // Grouping
233
+ showGroups?: boolean
234
+ groupBy?: string | ((node: NodeData) => string | undefined)
235
+ groupBorderColor?: string | ((groupId: string) => string)
236
+ groupBorderWidth?: number
237
+ groupBorderOpacity?: number
238
+ groupLabelColor?: string | ((groupId: string) => string)
239
+ groupLabelSize?: number
240
+ groupLabelThreshold?: number
241
+ groupPadding?: number
242
+
243
+ // Events
244
+ nodeClickHandler?: (node: NodeData) => void
103
245
  }
104
246
  ```
105
247
 
248
+ ## API Reference
249
+
250
+ ### Constructor
251
+
252
+ ```typescript
253
+ new ForceGraph(container: HTMLElement, data: GraphData, options?: GraphOptions)
254
+ ```
255
+
106
256
  ### Methods
107
257
 
108
- - `addData(newData: GraphData)`: Add new nodes and edges to the graph
109
- - `setLayout(layout: 'force' | 'circlepack')`: Change the graph layout algorithm
110
- - `setLabelThreshold(threshold: number)`: Set the zoom threshold for showing labels
111
- - `updateStyles(options: Partial<GraphOptions>)`: Update graph styling options
112
- - `refreshGraph()`: Refresh the graph with current data
113
- - `resetGraph()`: Reset the graph to its initial state
114
- - `isLoading()`: Check if the graph is currently calculating layout
115
- - `destroy()`: Clean up resources and remove the graph
258
+ #### Data Management
259
+
260
+ - `addData(newData: GraphData): Promise<void>`: Asynchronously adds new nodes and links, avoiding duplicates.
261
+ - `updateData(data: GraphData): void`: Merges new data with existing data. (Prefer `addData` for incremental updates).
262
+ - `graphData(data: GraphData): ForceGraph`: Replaces the entire graph data and re-renders.
263
+ - `updateNode(id: string | number, updates: Partial<NodeData>): boolean`: Updates properties of a single node.
264
+ - `removeNode(id: string | number): boolean`: Removes a node and its associated links.
265
+ - `reset()`: Clears all graph data and re-initializes the component.
266
+
267
+ #### Getters
268
+
269
+ - `getData(): GraphData`: Returns the current graph data object.
270
+ - `getNodesData(): NodeData[]`: Returns an array of all nodes.
271
+ - `getLinksData(): LinkData[]`: Returns an array of all links.
272
+ - `getNodeById(id: string | number): NodeData | undefined`: Finds a node by its ID.
273
+ - `hasNode(id: string | number): boolean`: Checks if a node exists.
274
+ - `getDataSize(): { nodes: number; links: number }`: Returns the number of nodes and links.
275
+ - `getOptions(): GraphOptions`: Returns the current options object.
276
+
277
+ #### Rendering & Options
278
+
279
+ - `setOptions(options: Partial<GraphOptions>)`: Updates one or more options without re-initializing the entire graph.
280
+ - `setLabelThreshold(threshold: number)`: Sets the zoom level at which node labels become visible.
281
+ - `refreshGraph()`: Refreshes the graph with the current data. Useful after manual data manipulation.
282
+ - `reinitialize()`: Performs a complete re-initialization. Use when major changes like dimensions are needed.
283
+ - `applyOptions()`: Re-applies all current options to the graph.
284
+
285
+ #### Camera & View
286
+
287
+ - `focusPosition(position: { id?: string; x?: number; y?: number }): void`: Centers the view on a specific node or coordinate.
288
+
289
+ #### Grouping
290
+
291
+ - `showGroups(show: boolean): ForceGraph`: Enables or disables the group visualization.
292
+ - `setGroupBy(groupBy: string | ((node: NodeData) => string | undefined)): ForceGraph`: Sets the property or function used to group nodes.
293
+ - `setGroupOptions(options: object): ForceGraph`: Sets styling options for groups (e.g., `borderColor`, `padding`).
294
+ - `getGroups(): string[]`: Returns an array of all unique group IDs.
295
+ - `getNodesInGroup(groupId: string): NodeData[]`: Returns all nodes belonging to a specific group.
296
+
297
+ #### Lifecycle
298
+
299
+ - `destroy()`: Cleans up all resources, including the simulation and event listeners. Essential for single-page applications.
116
300
 
117
301
  ## License
118
302
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@logixode/force-graph-lib",
3
3
  "private": false,
4
- "version": "0.1.1",
4
+ "version": "0.1.2",
5
5
  "description": "Force-directed graph visualization library.",
6
6
  "author": "Rohmad Kurniadi",
7
7
  "license": "MIT",