react-apextree 1.0.0

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/LICENSE ADDED
@@ -0,0 +1,80 @@
1
+ ## 📄 License Options for react-apextree
2
+
3
+ react-apextree is offered under a **dual-license model** to support individuals, startups, and commercial products of all sizes.
4
+
5
+ ---
6
+
7
+ ### 🔓 Community License (Free)
8
+
9
+ For individuals, non-profits, educators, and small businesses with **less than $2 million USD in annual revenue**.
10
+
11
+ ✅ What's allowed:
12
+
13
+ - Personal, educational, or non-profit use
14
+ - Commercial use by small orgs (< $2M annual revenue)
15
+ - Modifications and redistribution (with attribution)
16
+
17
+ 🚫 Not allowed:
18
+
19
+ - Use by companies or entities over $2M/year revenue
20
+ - Use in competing charting products
21
+ - Sublicensing under different terms
22
+
23
+ ➡ By using react-apextree under this license, you confirm that **you qualify as a Small Organization**.
24
+
25
+ ---
26
+
27
+ ### 💼 Commercial License (Paid)
28
+
29
+ Required if **you or your affiliated organization earns $2 million USD or more per year**.
30
+
31
+ ✅ What's included:
32
+
33
+ - Use in internal tools and commercial applications
34
+ - Modifications and app-level distribution
35
+ - 12-month subscription with updates & support
36
+
37
+ 🚫 Not allowed:
38
+
39
+ - Redistribution in toolkits, SDKs, or platforms
40
+ - Use by unlicensed developers
41
+ - Competing charting products
42
+
43
+ ---
44
+
45
+ ### 🔄 OEM / Redistribution License (Paid)
46
+
47
+ Required if you are **embedding react-apextree into a product or platform used by other people**, such as:
48
+
49
+ - No-code dashboards
50
+ - Developer platforms
51
+ - Embedded BI tools
52
+ - White-labeled apps or SDKs
53
+
54
+ ✅ What's included:
55
+
56
+ - Redistribution rights for 1 application or product
57
+ - 12-month subscription with updates & support
58
+
59
+ ✅ OEM **not required** if your app simply renders static charts and users **cannot** configure or interact with them.
60
+
61
+ ---
62
+
63
+ ### ⚠️ License Acceptance
64
+
65
+ By installing react-apextree (e.g., via `npm install react-apextree`), you are agreeing to the applicable license based on your usage:
66
+
67
+ - Community License (if under $2M revenue)
68
+ - Commercial License (if over $2M revenue)
69
+ - OEM License (if redistributing to third-party users)
70
+
71
+ ---
72
+
73
+ ### 🛠 Need a License or Have Questions?
74
+
75
+ 📧 Contact us at [sales@apexcharts.com](mailto:sales@apexcharts.com)
76
+ 📚 Read full license agreements here: [https://apexcharts.com/license](https://apexcharts.com/license)
77
+
78
+ ---
79
+
80
+ Thank you for supporting ApexCharts! Your licensing helps keep it free and open for individuals and small teams.
package/README.md ADDED
@@ -0,0 +1,244 @@
1
+ # react-apextree
2
+
3
+ React wrapper for [ApexTree](https://github.com/apexcharts/apextree) - a JavaScript library for creating organizational and hierarchical charts.
4
+
5
+ <img width="811" alt="ApexTree Example" src="https://github.com/apexcharts/tree/assets/17950663/e09212ec-6322-4c68-ac12-9afc524d2abd">
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install react-apextree apextree
11
+ ```
12
+
13
+ > **Note:** `apextree` is a peer dependency and must be installed alongside `react-apextree`.
14
+
15
+ ## Basic Usage
16
+
17
+ ```tsx
18
+ import { ApexTreeChart } from "react-apextree";
19
+
20
+ const data = {
21
+ id: "1",
22
+ name: "CEO",
23
+ children: [
24
+ {
25
+ id: "2",
26
+ name: "CTO",
27
+ children: [
28
+ { id: "3", name: "Dev Lead" },
29
+ { id: "4", name: "QA Lead" },
30
+ ],
31
+ },
32
+ {
33
+ id: "5",
34
+ name: "CFO",
35
+ },
36
+ ],
37
+ };
38
+
39
+ function App() {
40
+ return (
41
+ <ApexTreeChart
42
+ data={data}
43
+ width={800}
44
+ height={600}
45
+ direction="top"
46
+ nodeWidth={120}
47
+ nodeHeight={80}
48
+ />
49
+ );
50
+ }
51
+ ```
52
+
53
+ ## Using Imperative Methods
54
+
55
+ Access methods like `changeLayout`, `collapse`, `expand`, and `fitScreen` via ref:
56
+
57
+ ```tsx
58
+ import { useRef } from "react";
59
+ import { ApexTreeChart, ApexTreeRef } from "react-apextree";
60
+
61
+ function App() {
62
+ const treeRef = useRef<ApexTreeRef>(null);
63
+
64
+ const handleChangeLayout = () => {
65
+ treeRef.current?.changeLayout("left");
66
+ };
67
+
68
+ const handleCollapse = (nodeId: string) => {
69
+ treeRef.current?.collapse(nodeId);
70
+ };
71
+
72
+ const handleExpand = (nodeId: string) => {
73
+ treeRef.current?.expand(nodeId);
74
+ };
75
+
76
+ const handleFitScreen = () => {
77
+ treeRef.current?.fitScreen();
78
+ };
79
+
80
+ return (
81
+ <div>
82
+ <button onClick={handleChangeLayout}>Change Layout</button>
83
+ <button onClick={handleFitScreen}>Fit Screen</button>
84
+
85
+ <ApexTreeChart ref={treeRef} data={data} width={800} height={600} />
86
+ </div>
87
+ );
88
+ }
89
+ ```
90
+
91
+ ## Custom Node Templates
92
+
93
+ ```tsx
94
+ <ApexTreeChart
95
+ data={data}
96
+ width={800}
97
+ height={600}
98
+ contentKey="data"
99
+ nodeWidth={150}
100
+ nodeHeight={100}
101
+ nodeTemplate={(content) => `
102
+ <div style="display: flex; flex-direction: column; align-items: center; height: 100%;">
103
+ <img
104
+ src="${content.imageURL}"
105
+ style="width: 50px; height: 50px; border-radius: 50%;"
106
+ />
107
+ <div style="font-weight: bold;">${content.name}</div>
108
+ </div>
109
+ `}
110
+ />
111
+ ```
112
+
113
+ ## Props
114
+
115
+ | Prop | Type | Default | Description |
116
+ | ---------------------- | ---------------------------------------- | ------------ | ----------------------------------- |
117
+ | `data` | `NodeData` | **required** | Tree data structure |
118
+ | `width` | `number \| string` | `400` | Width of the container |
119
+ | `height` | `number \| string` | `400` | Height of the container |
120
+ | `direction` | `'top' \| 'bottom' \| 'left' \| 'right'` | `'top'` | Direction of tree growth |
121
+ | `contentKey` | `string` | `'name'` | Key for node content |
122
+ | `siblingSpacing` | `number` | `50` | Spacing between siblings |
123
+ | `childrenSpacing` | `number` | `50` | Spacing between parent and children |
124
+ | `nodeWidth` | `number` | `50` | Width of nodes |
125
+ | `nodeHeight` | `number` | `30` | Height of nodes |
126
+ | `nodeTemplate` | `(content: unknown) => string` | - | Custom HTML template for nodes |
127
+ | `nodeStyle` | `string` | - | CSS styles for nodes |
128
+ | `nodeBGColor` | `string` | `'#FFFFFF'` | Node background color |
129
+ | `nodeBGColorHover` | `string` | `'#FFFFFF'` | Node background color on hover |
130
+ | `borderWidth` | `number` | `1` | Node border width |
131
+ | `borderStyle` | `string` | `'solid'` | Node border style |
132
+ | `borderRadius` | `string` | `'5px'` | Node border radius |
133
+ | `borderColor` | `string` | `'#BCBCBC'` | Node border color |
134
+ | `borderColorHover` | `string` | `'#5C6BC0'` | Node border color on hover |
135
+ | `edgeWidth` | `number` | `1` | Edge line width |
136
+ | `edgeColor` | `string` | `'#BCBCBC'` | Edge line color |
137
+ | `edgeColorHover` | `string` | `'#5C6BC0'` | Edge line color on hover |
138
+ | `fontSize` | `string` | `'14px'` | Font size |
139
+ | `fontFamily` | `string` | - | Font family |
140
+ | `fontWeight` | `string` | `'400'` | Font weight |
141
+ | `fontColor` | `string` | `'#000000'` | Font color |
142
+ | `highlightOnHover` | `boolean` | `true` | Enable highlight on hover |
143
+ | `enableToolbar` | `boolean` | `false` | Show toolbar |
144
+ | `enableExpandCollapse` | `boolean` | `false` | Enable expand/collapse buttons |
145
+ | `enableTooltip` | `boolean` | `false` | Enable tooltips |
146
+ | `tooltipTemplate` | `(content: unknown) => string` | - | Custom tooltip template |
147
+ | `groupLeafNodes` | `boolean` | `false` | Stack leaf nodes |
148
+ | `onNodeClick` | `(node: NodeData) => void` | - | Node click handler |
149
+ | `className` | `string` | - | CSS class for container |
150
+ | `style` | `CSSProperties` | - | Inline styles for container |
151
+ | `canvasStyle` | `string` | - | CSS styles for canvas |
152
+
153
+ ## Ref Methods
154
+
155
+ | Method | Description |
156
+ | -------------------------- | --------------------------------- |
157
+ | `changeLayout(direction?)` | Change tree direction |
158
+ | `collapse(nodeId)` | Collapse a node |
159
+ | `expand(nodeId)` | Expand a node |
160
+ | `fitScreen()` | Fit tree to screen |
161
+ | `getGraph()` | Get the underlying graph instance |
162
+
163
+ ## Data Structure
164
+
165
+ ```ts
166
+ interface NodeData<T = unknown> {
167
+ id: string; // unique identifier
168
+ name?: string; // display name (or use contentKey)
169
+ data?: T; // custom data for templates
170
+ options?: NodeOptions; // per-node styling
171
+ children?: NodeData<T>[];
172
+ }
173
+
174
+ interface NodeOptions {
175
+ nodeBGColor?: string;
176
+ nodeBGColorHover?: string;
177
+ borderColor?: string;
178
+ borderColorHover?: string;
179
+ fontSize?: string;
180
+ fontFamily?: string;
181
+ fontWeight?: string | number;
182
+ fontColor?: string;
183
+ }
184
+ ```
185
+
186
+ ## TypeScript Support
187
+
188
+ Full TypeScript support with exported types:
189
+
190
+ ```ts
191
+ import type {
192
+ ApexTreeProps,
193
+ ApexTreeRef,
194
+ NodeData,
195
+ NodeOptions,
196
+ TreeDirection,
197
+ GraphInstance,
198
+ } from "react-apextree";
199
+ ```
200
+
201
+ ## License
202
+
203
+ React-Apextree uses the same dual-license model as ApexCharts. See [LICENSE](./LICENSE) for details.
204
+
205
+ - **Free** for individuals, non-profits, and small businesses (< $2M revenue)
206
+ - **Commercial license** required for larger organizations
207
+
208
+ ## Links
209
+
210
+ - [ApexTree Documentation](https://github.com/apexcharts/apextree)
211
+ - [ApexCharts](https://apexcharts.com)
212
+ - [License Information](https://apexcharts.com/license)
213
+
214
+ ## Development
215
+
216
+ ### Running the Demo
217
+
218
+ The package includes a demo app showcasing various features:
219
+
220
+ ```bash
221
+ # clone the repo
222
+ git clone https://github.com/apexcharts/react-apextree.git
223
+ cd react-apextree
224
+
225
+ # install dependencies
226
+ npm install
227
+ cd demo && npm install && cd ..
228
+
229
+ # build the library
230
+ npm run build
231
+
232
+ # run the demo
233
+ npm run demo
234
+ ```
235
+
236
+ Then open http://localhost:5173 to see the examples.
237
+
238
+ ### Available Scripts
239
+
240
+ - `npm run build` - Build the library
241
+ - `npm run dev` - Build in watch mode
242
+ - `npm run typecheck` - Run TypeScript type checking
243
+ - `npm run demo` - Run the demo app
244
+ - `npm run demo:build` - Build the demo app
package/dist/index.cjs ADDED
@@ -0,0 +1,2 @@
1
+ 'use strict';var react=require('react'),x=require('apextree'),jsxRuntime=require('react/jsx-runtime');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var x__default=/*#__PURE__*/_interopDefault(x);function u(c){let{className:e,style:d,data:a,...i}=c,l={};for(let[r,n]of Object.entries(i))n!==void 0&&(l[r]=n);return l}var C=react.forwardRef(function(e,d){let{data:a,className:i,style:l}=e,r=react.useRef(null),n=react.useRef(null),f=react.useMemo(()=>u(e),[e.width,e.height,e.direction,e.contentKey,e.siblingSpacing,e.childrenSpacing,e.containerClassName,e.canvasStyle,e.nodeWidth,e.nodeHeight,e.nodeTemplate,e.nodeStyle,e.nodeClassName,e.nodeBGColor,e.nodeBGColorHover,e.borderWidth,e.borderStyle,e.borderRadius,e.borderColor,e.borderColorHover,e.edgeWidth,e.edgeColor,e.edgeColorHover,e.fontSize,e.fontFamily,e.fontWeight,e.fontColor,e.enableTooltip,e.tooltipId,e.tooltipTemplate,e.tooltipMaxWidth,e.tooltipMinWidth,e.tooltipBorderColor,e.tooltipBGColor,e.tooltipFontColor,e.tooltipFontSize,e.tooltipPadding,e.tooltipOffset,e.highlightOnHover,e.enableToolbar,e.enableExpandCollapse,e.expandCollapseButtonBGColor,e.expandCollapseButtonBorderColor,e.groupLeafNodes,e.groupLeafNodesSpacing,e.onNodeClick]);return react.useImperativeHandle(d,()=>({changeLayout:t=>{var o;(o=n.current)==null||o.changeLayout(t);},collapse:t=>{var o;(o=n.current)==null||o.collapse(t);},expand:t=>{var o;(o=n.current)==null||o.expand(t);},fitScreen:()=>{var t;(t=n.current)==null||t.fitScreen();},getGraph:()=>n.current}),[]),react.useEffect(()=>{if(!r.current||!a)return;r.current.innerHTML="";let t=new x__default.default(r.current,f);n.current=t.render(a);},[a,f]),jsxRuntime.jsx("div",{ref:r,className:i,style:l})});exports.ApexTreeChart=C;//# sourceMappingURL=index.cjs.map
2
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils.ts","../src/ApexTreeChart.tsx"],"names":["buildOptions","props","_className","_style","_data","options","cleanOptions","key","value","ApexTreeChart","forwardRef","ref","data","className","style","containerRef","useRef","graphRef","useMemo","useImperativeHandle","direction","_a","nodeId","useEffect","tree","ApexTree","jsx"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { ApexTreeProps } from './types';\n\n/**\n * extracts ApexTree options from React props\n * filters out react-specific props (className, style, data)\n */\nexport function buildOptions<T>(props: ApexTreeProps<T>): Record<string, unknown> {\n const {\n // exclude react-specific props\n className: _className,\n style: _style,\n data: _data,\n // everything else becomes options\n ...options\n } = props;\n\n // remove undefined values to let ApexTree use its defaults\n const cleanOptions: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(options)) {\n if (value !== undefined) {\n cleanOptions[key] = value;\n }\n }\n\n return cleanOptions;\n}\n\n/**\n * shallow comparison of two objects\n * used to detect option changes\n */\nexport function shallowEqual(\n obj1: Record<string, unknown>,\n obj2: Record<string, unknown>\n): boolean {\n const keys1 = Object.keys(obj1);\n const keys2 = Object.keys(obj2);\n\n if (keys1.length !== keys2.length) {\n return false;\n }\n\n for (const key of keys1) {\n if (obj1[key] !== obj2[key]) {\n return false;\n }\n }\n\n return true;\n}\n","import {\n forwardRef,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n} from 'react';\nimport ApexTree from 'apextree';\nimport type { ApexTreeProps, ApexTreeRef, GraphInstance } from './types';\nimport { buildOptions } from './utils';\n\n/**\n * react wrapper component for ApexTree\n */\nexport const ApexTreeChart = forwardRef<ApexTreeRef, ApexTreeProps>(\n function ApexTreeChart(props, ref) {\n const { data, className, style } = props;\n\n const containerRef = useRef<HTMLDivElement>(null);\n const graphRef = useRef<GraphInstance | null>(null);\n\n // memoize options to prevent unnecessary re-renders\n const options = useMemo(() => buildOptions(props), [\n props.width,\n props.height,\n props.direction,\n props.contentKey,\n props.siblingSpacing,\n props.childrenSpacing,\n props.containerClassName,\n props.canvasStyle,\n props.nodeWidth,\n props.nodeHeight,\n props.nodeTemplate,\n props.nodeStyle,\n props.nodeClassName,\n props.nodeBGColor,\n props.nodeBGColorHover,\n props.borderWidth,\n props.borderStyle,\n props.borderRadius,\n props.borderColor,\n props.borderColorHover,\n props.edgeWidth,\n props.edgeColor,\n props.edgeColorHover,\n props.fontSize,\n props.fontFamily,\n props.fontWeight,\n props.fontColor,\n props.enableTooltip,\n props.tooltipId,\n props.tooltipTemplate,\n props.tooltipMaxWidth,\n props.tooltipMinWidth,\n props.tooltipBorderColor,\n props.tooltipBGColor,\n props.tooltipFontColor,\n props.tooltipFontSize,\n props.tooltipPadding,\n props.tooltipOffset,\n props.highlightOnHover,\n props.enableToolbar,\n props.enableExpandCollapse,\n props.expandCollapseButtonBGColor,\n props.expandCollapseButtonBorderColor,\n props.groupLeafNodes,\n props.groupLeafNodesSpacing,\n props.onNodeClick,\n ]);\n\n // expose imperative methods via ref\n useImperativeHandle(ref, () => ({\n changeLayout: (direction) => {\n graphRef.current?.changeLayout(direction);\n },\n collapse: (nodeId) => {\n graphRef.current?.collapse(nodeId);\n },\n expand: (nodeId) => {\n graphRef.current?.expand(nodeId);\n },\n fitScreen: () => {\n graphRef.current?.fitScreen();\n },\n getGraph: () => graphRef.current,\n }), []);\n\n // render tree when data or options change\n useEffect(() => {\n if (!containerRef.current || !data) {\n return;\n }\n\n // clear previous content\n containerRef.current.innerHTML = '';\n\n // create new tree instance and render\n const tree = new ApexTree(containerRef.current, options);\n graphRef.current = tree.render(data as any) as GraphInstance;\n }, [data, options]);\n\n return (\n <div\n ref={containerRef}\n className={className}\n style={style}\n />\n );\n }\n);"]}
@@ -0,0 +1,107 @@
1
+ import * as react from 'react';
2
+ import { CSSProperties } from 'react';
3
+
4
+ type TreeDirection = 'top' | 'bottom' | 'left' | 'right';
5
+ /**
6
+ * node data structure expected by ApexTree
7
+ */
8
+ interface NodeData<T = unknown> {
9
+ readonly id: string;
10
+ readonly name?: string;
11
+ readonly data?: T;
12
+ readonly options?: NodeOptions;
13
+ readonly children?: Array<NodeData<T>>;
14
+ }
15
+ /**
16
+ * per-node styling options
17
+ */
18
+ interface NodeOptions {
19
+ nodeBGColor?: string;
20
+ nodeBGColorHover?: string;
21
+ borderColor?: string;
22
+ borderColorHover?: string;
23
+ fontSize?: string;
24
+ fontFamily?: string;
25
+ fontWeight?: string | number;
26
+ fontColor?: string;
27
+ }
28
+ /**
29
+ * graph instance returned by ApexTree.render()
30
+ */
31
+ interface GraphInstance {
32
+ changeLayout: (direction?: TreeDirection) => void;
33
+ collapse: (nodeId: string) => void;
34
+ expand: (nodeId: string) => void;
35
+ fitScreen: () => void;
36
+ }
37
+ /**
38
+ * imperative methods exposed via ref
39
+ */
40
+ interface ApexTreeRef {
41
+ changeLayout: (direction?: TreeDirection) => void;
42
+ collapse: (nodeId: string) => void;
43
+ expand: (nodeId: string) => void;
44
+ fitScreen: () => void;
45
+ getGraph: () => GraphInstance | null;
46
+ }
47
+ /**
48
+ * props for the ApexTree React component
49
+ */
50
+ interface ApexTreeProps<T = unknown> {
51
+ data: NodeData<T>;
52
+ className?: string;
53
+ style?: CSSProperties;
54
+ width?: number | string;
55
+ height?: number | string;
56
+ direction?: TreeDirection;
57
+ siblingSpacing?: number;
58
+ childrenSpacing?: number;
59
+ containerClassName?: string;
60
+ canvasStyle?: string;
61
+ contentKey?: string;
62
+ nodeWidth?: number;
63
+ nodeHeight?: number;
64
+ nodeTemplate?: (content: unknown) => string;
65
+ nodeStyle?: string;
66
+ nodeClassName?: string;
67
+ nodeBGColor?: string;
68
+ nodeBGColorHover?: string;
69
+ borderWidth?: number;
70
+ borderStyle?: string;
71
+ borderRadius?: string;
72
+ borderColor?: string;
73
+ borderColorHover?: string;
74
+ edgeWidth?: number;
75
+ edgeColor?: string;
76
+ edgeColorHover?: string;
77
+ fontSize?: string;
78
+ fontFamily?: string;
79
+ fontWeight?: string;
80
+ fontColor?: string;
81
+ enableTooltip?: boolean;
82
+ tooltipId?: string;
83
+ tooltipTemplate?: (content: unknown) => string;
84
+ tooltipMaxWidth?: number;
85
+ tooltipMinWidth?: number;
86
+ tooltipBorderColor?: string;
87
+ tooltipBGColor?: string;
88
+ tooltipFontColor?: string;
89
+ tooltipFontSize?: string;
90
+ tooltipPadding?: number;
91
+ tooltipOffset?: number;
92
+ highlightOnHover?: boolean;
93
+ enableToolbar?: boolean;
94
+ enableExpandCollapse?: boolean;
95
+ expandCollapseButtonBGColor?: string;
96
+ expandCollapseButtonBorderColor?: string;
97
+ groupLeafNodes?: boolean;
98
+ groupLeafNodesSpacing?: number;
99
+ onNodeClick?: (node: NodeData<T>) => void;
100
+ }
101
+
102
+ /**
103
+ * react wrapper component for ApexTree
104
+ */
105
+ declare const ApexTreeChart: react.ForwardRefExoticComponent<ApexTreeProps<unknown> & react.RefAttributes<ApexTreeRef>>;
106
+
107
+ export { ApexTreeChart, type ApexTreeProps, type ApexTreeRef, type GraphInstance, type NodeData, type NodeOptions, type TreeDirection };
@@ -0,0 +1,107 @@
1
+ import * as react from 'react';
2
+ import { CSSProperties } from 'react';
3
+
4
+ type TreeDirection = 'top' | 'bottom' | 'left' | 'right';
5
+ /**
6
+ * node data structure expected by ApexTree
7
+ */
8
+ interface NodeData<T = unknown> {
9
+ readonly id: string;
10
+ readonly name?: string;
11
+ readonly data?: T;
12
+ readonly options?: NodeOptions;
13
+ readonly children?: Array<NodeData<T>>;
14
+ }
15
+ /**
16
+ * per-node styling options
17
+ */
18
+ interface NodeOptions {
19
+ nodeBGColor?: string;
20
+ nodeBGColorHover?: string;
21
+ borderColor?: string;
22
+ borderColorHover?: string;
23
+ fontSize?: string;
24
+ fontFamily?: string;
25
+ fontWeight?: string | number;
26
+ fontColor?: string;
27
+ }
28
+ /**
29
+ * graph instance returned by ApexTree.render()
30
+ */
31
+ interface GraphInstance {
32
+ changeLayout: (direction?: TreeDirection) => void;
33
+ collapse: (nodeId: string) => void;
34
+ expand: (nodeId: string) => void;
35
+ fitScreen: () => void;
36
+ }
37
+ /**
38
+ * imperative methods exposed via ref
39
+ */
40
+ interface ApexTreeRef {
41
+ changeLayout: (direction?: TreeDirection) => void;
42
+ collapse: (nodeId: string) => void;
43
+ expand: (nodeId: string) => void;
44
+ fitScreen: () => void;
45
+ getGraph: () => GraphInstance | null;
46
+ }
47
+ /**
48
+ * props for the ApexTree React component
49
+ */
50
+ interface ApexTreeProps<T = unknown> {
51
+ data: NodeData<T>;
52
+ className?: string;
53
+ style?: CSSProperties;
54
+ width?: number | string;
55
+ height?: number | string;
56
+ direction?: TreeDirection;
57
+ siblingSpacing?: number;
58
+ childrenSpacing?: number;
59
+ containerClassName?: string;
60
+ canvasStyle?: string;
61
+ contentKey?: string;
62
+ nodeWidth?: number;
63
+ nodeHeight?: number;
64
+ nodeTemplate?: (content: unknown) => string;
65
+ nodeStyle?: string;
66
+ nodeClassName?: string;
67
+ nodeBGColor?: string;
68
+ nodeBGColorHover?: string;
69
+ borderWidth?: number;
70
+ borderStyle?: string;
71
+ borderRadius?: string;
72
+ borderColor?: string;
73
+ borderColorHover?: string;
74
+ edgeWidth?: number;
75
+ edgeColor?: string;
76
+ edgeColorHover?: string;
77
+ fontSize?: string;
78
+ fontFamily?: string;
79
+ fontWeight?: string;
80
+ fontColor?: string;
81
+ enableTooltip?: boolean;
82
+ tooltipId?: string;
83
+ tooltipTemplate?: (content: unknown) => string;
84
+ tooltipMaxWidth?: number;
85
+ tooltipMinWidth?: number;
86
+ tooltipBorderColor?: string;
87
+ tooltipBGColor?: string;
88
+ tooltipFontColor?: string;
89
+ tooltipFontSize?: string;
90
+ tooltipPadding?: number;
91
+ tooltipOffset?: number;
92
+ highlightOnHover?: boolean;
93
+ enableToolbar?: boolean;
94
+ enableExpandCollapse?: boolean;
95
+ expandCollapseButtonBGColor?: string;
96
+ expandCollapseButtonBorderColor?: string;
97
+ groupLeafNodes?: boolean;
98
+ groupLeafNodesSpacing?: number;
99
+ onNodeClick?: (node: NodeData<T>) => void;
100
+ }
101
+
102
+ /**
103
+ * react wrapper component for ApexTree
104
+ */
105
+ declare const ApexTreeChart: react.ForwardRefExoticComponent<ApexTreeProps<unknown> & react.RefAttributes<ApexTreeRef>>;
106
+
107
+ export { ApexTreeChart, type ApexTreeProps, type ApexTreeRef, type GraphInstance, type NodeData, type NodeOptions, type TreeDirection };
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ import {forwardRef,useRef,useMemo,useImperativeHandle,useEffect}from'react';import x from'apextree';import {jsx}from'react/jsx-runtime';function u(c){let{className:e,style:d,data:a,...i}=c,l={};for(let[r,n]of Object.entries(i))n!==void 0&&(l[r]=n);return l}var C=forwardRef(function(e,d){let{data:a,className:i,style:l}=e,r=useRef(null),n=useRef(null),f=useMemo(()=>u(e),[e.width,e.height,e.direction,e.contentKey,e.siblingSpacing,e.childrenSpacing,e.containerClassName,e.canvasStyle,e.nodeWidth,e.nodeHeight,e.nodeTemplate,e.nodeStyle,e.nodeClassName,e.nodeBGColor,e.nodeBGColorHover,e.borderWidth,e.borderStyle,e.borderRadius,e.borderColor,e.borderColorHover,e.edgeWidth,e.edgeColor,e.edgeColorHover,e.fontSize,e.fontFamily,e.fontWeight,e.fontColor,e.enableTooltip,e.tooltipId,e.tooltipTemplate,e.tooltipMaxWidth,e.tooltipMinWidth,e.tooltipBorderColor,e.tooltipBGColor,e.tooltipFontColor,e.tooltipFontSize,e.tooltipPadding,e.tooltipOffset,e.highlightOnHover,e.enableToolbar,e.enableExpandCollapse,e.expandCollapseButtonBGColor,e.expandCollapseButtonBorderColor,e.groupLeafNodes,e.groupLeafNodesSpacing,e.onNodeClick]);return useImperativeHandle(d,()=>({changeLayout:t=>{var o;(o=n.current)==null||o.changeLayout(t);},collapse:t=>{var o;(o=n.current)==null||o.collapse(t);},expand:t=>{var o;(o=n.current)==null||o.expand(t);},fitScreen:()=>{var t;(t=n.current)==null||t.fitScreen();},getGraph:()=>n.current}),[]),useEffect(()=>{if(!r.current||!a)return;r.current.innerHTML="";let t=new x(r.current,f);n.current=t.render(a);},[a,f]),jsx("div",{ref:r,className:i,style:l})});export{C as ApexTreeChart};//# sourceMappingURL=index.js.map
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils.ts","../src/ApexTreeChart.tsx"],"names":["buildOptions","props","_className","_style","_data","options","cleanOptions","key","value","ApexTreeChart","forwardRef","ref","data","className","style","containerRef","useRef","graphRef","useMemo","useImperativeHandle","direction","_a","nodeId","useEffect","tree","ApexTree","jsx"],"mappings":"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","file":"index.js","sourcesContent":["import type { ApexTreeProps } from './types';\n\n/**\n * extracts ApexTree options from React props\n * filters out react-specific props (className, style, data)\n */\nexport function buildOptions<T>(props: ApexTreeProps<T>): Record<string, unknown> {\n const {\n // exclude react-specific props\n className: _className,\n style: _style,\n data: _data,\n // everything else becomes options\n ...options\n } = props;\n\n // remove undefined values to let ApexTree use its defaults\n const cleanOptions: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(options)) {\n if (value !== undefined) {\n cleanOptions[key] = value;\n }\n }\n\n return cleanOptions;\n}\n\n/**\n * shallow comparison of two objects\n * used to detect option changes\n */\nexport function shallowEqual(\n obj1: Record<string, unknown>,\n obj2: Record<string, unknown>\n): boolean {\n const keys1 = Object.keys(obj1);\n const keys2 = Object.keys(obj2);\n\n if (keys1.length !== keys2.length) {\n return false;\n }\n\n for (const key of keys1) {\n if (obj1[key] !== obj2[key]) {\n return false;\n }\n }\n\n return true;\n}\n","import {\n forwardRef,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n} from 'react';\nimport ApexTree from 'apextree';\nimport type { ApexTreeProps, ApexTreeRef, GraphInstance } from './types';\nimport { buildOptions } from './utils';\n\n/**\n * react wrapper component for ApexTree\n */\nexport const ApexTreeChart = forwardRef<ApexTreeRef, ApexTreeProps>(\n function ApexTreeChart(props, ref) {\n const { data, className, style } = props;\n\n const containerRef = useRef<HTMLDivElement>(null);\n const graphRef = useRef<GraphInstance | null>(null);\n\n // memoize options to prevent unnecessary re-renders\n const options = useMemo(() => buildOptions(props), [\n props.width,\n props.height,\n props.direction,\n props.contentKey,\n props.siblingSpacing,\n props.childrenSpacing,\n props.containerClassName,\n props.canvasStyle,\n props.nodeWidth,\n props.nodeHeight,\n props.nodeTemplate,\n props.nodeStyle,\n props.nodeClassName,\n props.nodeBGColor,\n props.nodeBGColorHover,\n props.borderWidth,\n props.borderStyle,\n props.borderRadius,\n props.borderColor,\n props.borderColorHover,\n props.edgeWidth,\n props.edgeColor,\n props.edgeColorHover,\n props.fontSize,\n props.fontFamily,\n props.fontWeight,\n props.fontColor,\n props.enableTooltip,\n props.tooltipId,\n props.tooltipTemplate,\n props.tooltipMaxWidth,\n props.tooltipMinWidth,\n props.tooltipBorderColor,\n props.tooltipBGColor,\n props.tooltipFontColor,\n props.tooltipFontSize,\n props.tooltipPadding,\n props.tooltipOffset,\n props.highlightOnHover,\n props.enableToolbar,\n props.enableExpandCollapse,\n props.expandCollapseButtonBGColor,\n props.expandCollapseButtonBorderColor,\n props.groupLeafNodes,\n props.groupLeafNodesSpacing,\n props.onNodeClick,\n ]);\n\n // expose imperative methods via ref\n useImperativeHandle(ref, () => ({\n changeLayout: (direction) => {\n graphRef.current?.changeLayout(direction);\n },\n collapse: (nodeId) => {\n graphRef.current?.collapse(nodeId);\n },\n expand: (nodeId) => {\n graphRef.current?.expand(nodeId);\n },\n fitScreen: () => {\n graphRef.current?.fitScreen();\n },\n getGraph: () => graphRef.current,\n }), []);\n\n // render tree when data or options change\n useEffect(() => {\n if (!containerRef.current || !data) {\n return;\n }\n\n // clear previous content\n containerRef.current.innerHTML = '';\n\n // create new tree instance and render\n const tree = new ApexTree(containerRef.current, options);\n graphRef.current = tree.render(data as any) as GraphInstance;\n }, [data, options]);\n\n return (\n <div\n ref={containerRef}\n className={className}\n style={style}\n />\n );\n }\n);"]}
package/package.json ADDED
@@ -0,0 +1,70 @@
1
+ {
2
+ "name": "react-apextree",
3
+ "version": "1.0.0",
4
+ "description": "React wrapper for ApexTree - a JavaScript library for creating organizational and hierarchical charts",
5
+ "author": "ApexCharts",
6
+ "license": "See LICENSE in LICENSE",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/apexcharts/react-apextree"
10
+ },
11
+ "homepage": "https://github.com/apexcharts/react-apextree#readme",
12
+ "bugs": {
13
+ "url": "https://github.com/apexcharts/react-apextree/issues"
14
+ },
15
+ "keywords": [
16
+ "react",
17
+ "apextree",
18
+ "tree",
19
+ "chart",
20
+ "organizational-chart",
21
+ "org-chart",
22
+ "hierarchy",
23
+ "diagram",
24
+ "svg"
25
+ ],
26
+ "type": "module",
27
+ "main": "./dist/index.cjs",
28
+ "module": "./dist/index.js",
29
+ "types": "./dist/index.d.ts",
30
+ "exports": {
31
+ ".": {
32
+ "import": {
33
+ "types": "./dist/index.d.ts",
34
+ "default": "./dist/index.js"
35
+ },
36
+ "require": {
37
+ "types": "./dist/index.d.cts",
38
+ "default": "./dist/index.cjs"
39
+ }
40
+ }
41
+ },
42
+ "files": [
43
+ "dist",
44
+ "LICENSE",
45
+ "README.md"
46
+ ],
47
+ "sideEffects": false,
48
+ "scripts": {
49
+ "build": "tsup",
50
+ "dev": "tsup --watch",
51
+ "typecheck": "tsc --noEmit",
52
+ "prepublishOnly": "npm run build",
53
+ "demo": "npm run --prefix demo dev",
54
+ "demo:build": "npm run --prefix demo build"
55
+ },
56
+ "peerDependencies": {
57
+ "apextree": ">=1.6.1",
58
+ "react": ">=17.0.0",
59
+ "react-dom": ">=17.0.0"
60
+ },
61
+ "devDependencies": {
62
+ "@types/react": "^19.2.7",
63
+ "@types/react-dom": "^19.2.3",
64
+ "apextree": "^1.6.1",
65
+ "react": "^19.2.3",
66
+ "react-dom": "^19.2.3",
67
+ "tsup": "^8.5.1",
68
+ "typescript": "^5.9.3"
69
+ }
70
+ }