@bencamus/vue3-tree-chart 0.1.0 → 0.1.1

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/package.json CHANGED
@@ -1,13 +1,14 @@
1
1
  {
2
2
  "name": "@bencamus/vue3-tree-chart",
3
- "version": "0.1.0",
4
- "main": "dist/vue3-tree-chart.umd.js",
5
- "module": "dist/vue3-tree-chart.esm.js",
6
- "types": "dist/vue3-tree-chart.d.ts",
7
- "files": ["dist"],
3
+ "version": "0.1.1",
4
+ "main": "src/index.ts",
5
+ "module": "src/index.ts",
6
+ "types": "src/index.ts",
7
+ "style": "src/styles/vue3-tree-chart.css",
8
+ "files": ["src"],
8
9
  "scripts": {
9
10
  "build": "vite build",
10
- "prepublishOnly": "npm run build",
11
+ "prepublishOnly": "echo 'No build required for source-only package'",
11
12
  "test": "echo \"Error: no test specified\" && exit 1"
12
13
  },
13
14
  "devDependencies": {
@@ -16,12 +17,13 @@
16
17
  "typescript": "~5.7.2",
17
18
  "vite": "^6.2.0",
18
19
  "less": "^4.2.2",
19
- "@types/node": "^20.0.0"
20
+ "@types/node": "^20.0.0",
21
+ "vite-plugin-less": "^2.0.0"
20
22
  },
21
23
  "peerDependencies": {
22
24
  "vue": "^3.0.0",
23
25
  "@bencamus/tree-chart-core": "^0.1.0"
24
26
  },
25
27
  "license": "MIT",
26
- "description": "Vue 3 component for tree chart visualization."
28
+ "description": "Vue 3 component for tree chart visualization (source code)."
27
29
  }
@@ -0,0 +1,247 @@
1
+ <template>
2
+ <div class="tree-container" ref="container">
3
+ <svg class="svg vue-tree" ref="svg" :style="initialTransformStyle"></svg>
4
+ <div class="dom-container" ref="domContainer" :style="initialTransformStyle">
5
+ <transition-group name="tree-node-item" tag="div">
6
+ <div class="node-slot" v-for="(node, index) of nodeDataList" @click="onClickNode(index)" :key="node.data._key"
7
+ :style="{
8
+ left: formatDimension(
9
+ direction === Direction.VERTICAL ? node.x : node.y
10
+ ),
11
+ top: formatDimension(
12
+ direction === Direction.VERTICAL ? node.y : node.x
13
+ ),
14
+ width: formatDimension(config.nodeWidth),
15
+ height: formatDimension(config.nodeHeight),
16
+ }">
17
+ <slot name="node" v-bind:node="node.data" v-bind:collapsed="node.data._collapsed">
18
+ <span>{{ node.data.value }}</span>
19
+ </slot>
20
+ </div>
21
+ </transition-group>
22
+ </div>
23
+ </div>
24
+ </template>
25
+
26
+ <script lang="ts">
27
+ import { defineComponent, ref, onMounted, onBeforeUnmount, watch } from 'vue';
28
+ import TreeChartCore, {
29
+ DEFAULT_NODE_WIDTH,
30
+ DEFAULT_NODE_HEIGHT,
31
+ DEFAULT_LEVEL_HEIGHT,
32
+ TreeLinkStyle,
33
+ Direction,
34
+ } from "../../../tree-chart-core/index.ts";
35
+
36
+ interface TreeConfig {
37
+ nodeWidth?: number;
38
+ nodeHeight?: number;
39
+ levelHeight?: number;
40
+ focusToNode?: boolean;
41
+ initiallyCollapsed?: boolean;
42
+ useMobileZoom?: boolean;
43
+ useMouseZoom?: boolean;
44
+ collapseDepth?: number;
45
+ }
46
+
47
+ interface NodeData {
48
+ x: number;
49
+ y: number;
50
+ data: {
51
+ _key: string;
52
+ _collapsed: boolean;
53
+ value: string;
54
+ [key: string]: any;
55
+ };
56
+ }
57
+
58
+ const formatDimension = (dimension: number | string): string => {
59
+ if (typeof dimension === "number") return `${dimension}px`;
60
+ if (typeof dimension === "string" && dimension.indexOf("px") !== -1) {
61
+ return dimension;
62
+ }
63
+ return `${dimension}px`;
64
+ };
65
+
66
+ export default defineComponent({
67
+ name: "VueTree",
68
+ props: {
69
+ config: {
70
+ type: Object as () => TreeConfig,
71
+ default: () => ({
72
+ nodeWidth: DEFAULT_NODE_WIDTH,
73
+ nodeHeight: DEFAULT_NODE_HEIGHT,
74
+ levelHeight: DEFAULT_LEVEL_HEIGHT,
75
+ focusToNode: false,
76
+ initiallyCollapsed: false,
77
+ useMobileZoom: false,
78
+ useMouseZoom: false,
79
+ collapseDepth: 0,
80
+ }),
81
+ },
82
+ linkStyle: {
83
+ type: String as () => TreeLinkStyle,
84
+ default: TreeLinkStyle.CURVE,
85
+ },
86
+ direction: {
87
+ type: String as () => Direction,
88
+ default: Direction.VERTICAL,
89
+ },
90
+ collapseEnabled: {
91
+ type: Boolean,
92
+ default: true,
93
+ },
94
+ dataset: {
95
+ type: [Object, Array] as () => any,
96
+ required: true,
97
+ },
98
+ },
99
+ setup(props) {
100
+ const container = ref<HTMLElement | null>(null);
101
+ const svg = ref<SVGSVGElement | null>(null);
102
+ const domContainer = ref<HTMLElement | null>(null);
103
+ const treeChartCore = ref<TreeChartCore | null>(null);
104
+ const nodeDataList = ref<NodeData[]>([]);
105
+ const initialTransformStyle = ref<Record<string, string>>({});
106
+
107
+ const init = () => {
108
+ if (!svg.value || !domContainer.value || !container.value) return;
109
+
110
+ treeChartCore.value = new TreeChartCore({
111
+ svgElement: svg.value,
112
+ domElement: domContainer.value,
113
+ treeContainer: container.value,
114
+ dataset: props.dataset,
115
+ direction: props.direction,
116
+ treeConfig: props.config,
117
+ collapseEnabled: props.collapseEnabled,
118
+ linkStyle: props.linkStyle,
119
+ });
120
+
121
+ treeChartCore.value.init();
122
+ nodeDataList.value = treeChartCore.value.getNodeDataList();
123
+ initialTransformStyle.value = treeChartCore.value.getInitialTransformStyle();
124
+ };
125
+
126
+ const zoomIn = () => treeChartCore.value?.zoomIn();
127
+ const zoomOut = () => treeChartCore.value?.zoomOut();
128
+ const restoreScale = () => treeChartCore.value?.setScale(1);
129
+ const expandNodeByLevelAndPosition = (level: number, position: number) => {
130
+ treeChartCore.value?.expandNodeByLevelAndPosition(level, position);
131
+ nodeDataList.value = treeChartCore.value?.getNodeDataList() || [];
132
+ };
133
+ const restorePosition = () => treeChartCore.value?.restorePosition();
134
+ const onClickNode = (index: number) => {
135
+ treeChartCore.value?.onClickNode(index);
136
+ nodeDataList.value = treeChartCore.value?.getNodeDataList() || [];
137
+ if (props.config.focusToNode) {
138
+ focusToNode(index);
139
+ }
140
+ };
141
+ const focusToNode = (index: number) => treeChartCore.value?.focusToNode(index);
142
+
143
+ onMounted(() => {
144
+ init();
145
+ });
146
+
147
+ onBeforeUnmount(() => {
148
+ treeChartCore.value?.destroy();
149
+ });
150
+
151
+ watch(
152
+ () => props.dataset,
153
+ (newDataset) => {
154
+ treeChartCore.value?.updateDataset(newDataset);
155
+ nodeDataList.value = treeChartCore.value?.getNodeDataList() || [];
156
+ },
157
+ { deep: true }
158
+ );
159
+
160
+ return {
161
+ container,
162
+ svg,
163
+ domContainer,
164
+ nodeDataList,
165
+ initialTransformStyle,
166
+ formatDimension,
167
+ Direction,
168
+ zoomIn,
169
+ zoomOut,
170
+ restoreScale,
171
+ expandNodeByLevelAndPosition,
172
+ restorePosition,
173
+ onClickNode,
174
+ focusToNode,
175
+ };
176
+ },
177
+ });
178
+ </script>
179
+
180
+ <style>
181
+ .tree-container .node {
182
+ fill: grey !important;
183
+ }
184
+
185
+ .tree-container .link {
186
+ stroke-width: 2px !important;
187
+ fill: transparent !important;
188
+ stroke: #cecece !important;
189
+ }
190
+
191
+ .tree-container .link-hidden {
192
+ display: none;
193
+ stroke-width: 2px !important;
194
+ fill: transparent !important;
195
+ stroke: #FC2020FF !important;
196
+ }
197
+
198
+ .tree-node-item-enter,
199
+ .tree-node-item-leave-to {
200
+ opacity: 0;
201
+ }
202
+
203
+ .tree-node-item-enter-active,
204
+ .tree-node-item-leave-active {
205
+
206
+ }
207
+
208
+ .tree-container {
209
+ touch-action: none;
210
+ position: relative;
211
+ overflow: hidden;
212
+ }
213
+
214
+ .tree-container .vue-tree {
215
+ position: relative;
216
+ }
217
+
218
+ .tree-container > svg,
219
+ .tree-container .dom-container {
220
+ width: 100%;
221
+ height: 100%;
222
+ position: absolute;
223
+ left: 0;
224
+ top: 0;
225
+ overflow: visible;
226
+ transform-origin: 0 50%;
227
+ }
228
+
229
+ .tree-container .dom-container {
230
+ z-index: 1;
231
+ pointer-events: none;
232
+ }
233
+
234
+ .node-slot {
235
+ cursor: pointer;
236
+ pointer-events: all;
237
+ position: absolute;
238
+ background-color: transparent;
239
+ box-sizing: border-box;
240
+ transform: translate(-50%, -50%);
241
+ display: flex;
242
+ align-items: center;
243
+ justify-content: center;
244
+ box-sizing: content-box;
245
+ transition-timing-function: ease-in-out;
246
+ }
247
+ </style>
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ import VueTree from '@/components/VueTree.vue';
2
+ export default VueTree;
@@ -1 +0,0 @@
1
- .tree-container .node{fill:gray!important}.tree-container .link{stroke-width:2px!important;fill:transparent!important;stroke:#cecece!important}.tree-container .link-hidden{display:none;stroke-width:2px!important;fill:transparent!important;stroke:#fc2020!important}.tree-node-item-enter[data-v-c304c1c5],.tree-node-item-leave-to[data-v-c304c1c5]{opacity:0}.tree-container[data-v-c304c1c5]{touch-action:none;position:relative;overflow:hidden}.tree-container .vue-tree[data-v-c304c1c5]{position:relative}.tree-container>svg[data-v-c304c1c5],.tree-container .dom-container[data-v-c304c1c5]{width:100%;height:100%;position:absolute;left:0;top:0;overflow:visible;transform-origin:0 50%}.tree-container .dom-container[data-v-c304c1c5]{z-index:1;pointer-events:none}.node-slot[data-v-c304c1c5]{cursor:pointer;pointer-events:all;position:absolute;background-color:transparent;box-sizing:border-box;transform:translate(-50%,-50%);display:flex;align-items:center;justify-content:center;box-sizing:content-box;transition-timing-function:ease-in-out}
@@ -1,156 +0,0 @@
1
- import { defineComponent as S, ref as d, onMounted as k, onBeforeUnmount as I, watch as _, createElementBlock as c, openBlock as f, createElementVNode as D, normalizeStyle as m, createVNode as A, TransitionGroup as V, withCtx as H, Fragment as O, renderList as $, renderSlot as b, toDisplayString as B } from "vue";
2
- import z, { Direction as T, TreeLinkStyle as U, DEFAULT_LEVEL_HEIGHT as w, DEFAULT_NODE_HEIGHT as F, DEFAULT_NODE_WIDTH as P } from "@bencamus/tree-chart-core";
3
- const R = (e) => typeof e == "number" ? `${e}px` : typeof e == "string" && e.indexOf("px") !== -1 ? e : `${e}px`, G = S({
4
- name: "VueTree",
5
- props: {
6
- config: {
7
- type: Object,
8
- default: () => ({
9
- nodeWidth: P,
10
- nodeHeight: F,
11
- levelHeight: w,
12
- focusToNode: !1,
13
- initiallyCollapsed: !1,
14
- useMobileZoom: !1,
15
- useMouseZoom: !1,
16
- collapseDepth: 0
17
- })
18
- },
19
- linkStyle: {
20
- type: String,
21
- default: U.CURVE
22
- },
23
- direction: {
24
- type: String,
25
- default: T.VERTICAL
26
- },
27
- collapseEnabled: {
28
- type: Boolean,
29
- default: !0
30
- },
31
- dataset: {
32
- type: [Object, Array],
33
- required: !0
34
- }
35
- },
36
- setup(e) {
37
- const r = d(null), i = d(null), s = d(null), o = d(null), u = d([]), n = d({}), v = () => {
38
- !i.value || !s.value || !r.value || (o.value = new z({
39
- svgElement: i.value,
40
- domElement: s.value,
41
- treeContainer: r.value,
42
- dataset: e.dataset,
43
- direction: e.direction,
44
- treeConfig: e.config,
45
- collapseEnabled: e.collapseEnabled,
46
- linkStyle: e.linkStyle
47
- }), o.value.init(), u.value = o.value.getNodeDataList(), n.value = o.value.getInitialTransformStyle());
48
- }, y = () => {
49
- var t;
50
- return (t = o.value) == null ? void 0 : t.zoomIn();
51
- }, C = () => {
52
- var t;
53
- return (t = o.value) == null ? void 0 : t.zoomOut();
54
- }, E = () => {
55
- var t;
56
- return (t = o.value) == null ? void 0 : t.setScale(1);
57
- }, L = (t, a) => {
58
- var l, g;
59
- (l = o.value) == null || l.expandNodeByLevelAndPosition(t, a), u.value = ((g = o.value) == null ? void 0 : g.getNodeDataList()) || [];
60
- }, N = () => {
61
- var t;
62
- return (t = o.value) == null ? void 0 : t.restorePosition();
63
- }, h = (t) => {
64
- var a, l;
65
- (a = o.value) == null || a.onClickNode(t), u.value = ((l = o.value) == null ? void 0 : l.getNodeDataList()) || [], e.config.focusToNode && p(t);
66
- }, p = (t) => {
67
- var a;
68
- return (a = o.value) == null ? void 0 : a.focusToNode(t);
69
- };
70
- return k(() => {
71
- v();
72
- }), I(() => {
73
- var t;
74
- (t = o.value) == null || t.destroy();
75
- }), _(
76
- () => e.dataset,
77
- (t) => {
78
- var a, l;
79
- (a = o.value) == null || a.updateDataset(t), u.value = ((l = o.value) == null ? void 0 : l.getNodeDataList()) || [];
80
- },
81
- { deep: !0 }
82
- ), {
83
- container: r,
84
- svg: i,
85
- domContainer: s,
86
- nodeDataList: u,
87
- initialTransformStyle: n,
88
- formatDimension: R,
89
- Direction: T,
90
- zoomIn: y,
91
- zoomOut: C,
92
- restoreScale: E,
93
- expandNodeByLevelAndPosition: L,
94
- restorePosition: N,
95
- onClickNode: h,
96
- focusToNode: p
97
- };
98
- }
99
- }), M = (e, r) => {
100
- const i = e.__vccOpts || e;
101
- for (const [s, o] of r)
102
- i[s] = o;
103
- return i;
104
- }, W = {
105
- class: "tree-container",
106
- ref: "container"
107
- }, j = ["onClick"];
108
- function Z(e, r, i, s, o, u) {
109
- return f(), c("div", W, [
110
- (f(), c("svg", {
111
- class: "svg vue-tree",
112
- ref: "svg",
113
- style: m(e.initialTransformStyle)
114
- }, null, 4)),
115
- D("div", {
116
- class: "dom-container",
117
- ref: "domContainer",
118
- style: m(e.initialTransformStyle)
119
- }, [
120
- A(V, {
121
- name: "tree-node-item",
122
- tag: "div"
123
- }, {
124
- default: H(() => [
125
- (f(!0), c(O, null, $(e.nodeDataList, (n, v) => (f(), c("div", {
126
- class: "node-slot",
127
- onClick: (y) => e.onClickNode(v),
128
- key: n.data._key,
129
- style: m({
130
- left: e.formatDimension(
131
- e.direction === e.Direction.VERTICAL ? n.x : n.y
132
- ),
133
- top: e.formatDimension(
134
- e.direction === e.Direction.VERTICAL ? n.y : n.x
135
- ),
136
- width: e.formatDimension(e.config.nodeWidth),
137
- height: e.formatDimension(e.config.nodeHeight)
138
- })
139
- }, [
140
- b(e.$slots, "node", {
141
- node: n.data,
142
- collapsed: n.data._collapsed
143
- }, () => [
144
- D("span", null, B(n.data.value), 1)
145
- ], !0)
146
- ], 12, j))), 128))
147
- ]),
148
- _: 3
149
- })
150
- ], 4)
151
- ], 512);
152
- }
153
- const K = /* @__PURE__ */ M(G, [["render", Z], ["__scopeId", "data-v-c304c1c5"]]);
154
- export {
155
- K as default
156
- };
@@ -1 +0,0 @@
1
- (function(e,a){typeof exports=="object"&&typeof module<"u"?module.exports=a(require("vue"),require("@bencamus/tree-chart-core")):typeof define=="function"&&define.amd?define(["vue","@bencamus/tree-chart-core"],a):(e=typeof globalThis<"u"?globalThis:e||self,e.Vue3TreeChart=a(e.Vue,e.TreeChartCore))})(this,function(e,a){"use strict";const g=t=>typeof t=="number"?`${t}px`:typeof t=="string"&&t.indexOf("px")!==-1?t:`${t}px`,D=e.defineComponent({name:"VueTree",props:{config:{type:Object,default:()=>({nodeWidth:a.DEFAULT_NODE_WIDTH,nodeHeight:a.DEFAULT_NODE_HEIGHT,levelHeight:a.DEFAULT_LEVEL_HEIGHT,focusToNode:!1,initiallyCollapsed:!1,useMobileZoom:!1,useMouseZoom:!1,collapseDepth:0})},linkStyle:{type:String,default:a.TreeLinkStyle.CURVE},direction:{type:String,default:a.Direction.VERTICAL},collapseEnabled:{type:Boolean,default:!0},dataset:{type:[Object,Array],required:!0}},setup(t){const c=e.ref(null),s=e.ref(null),d=e.ref(null),n=e.ref(null),f=e.ref([]),l=e.ref({}),u=()=>{!s.value||!d.value||!c.value||(n.value=new a({svgElement:s.value,domElement:d.value,treeContainer:c.value,dataset:t.dataset,direction:t.direction,treeConfig:t.config,collapseEnabled:t.collapseEnabled,linkStyle:t.linkStyle}),n.value.init(),f.value=n.value.getNodeDataList(),l.value=n.value.getInitialTransformStyle())},m=()=>{var o;return(o=n.value)==null?void 0:o.zoomIn()},L=()=>{var o;return(o=n.value)==null?void 0:o.zoomOut()},N=()=>{var o;return(o=n.value)==null?void 0:o.setScale(1)},S=(o,i)=>{var r,y;(r=n.value)==null||r.expandNodeByLevelAndPosition(o,i),f.value=((y=n.value)==null?void 0:y.getNodeDataList())||[]},h=()=>{var o;return(o=n.value)==null?void 0:o.restorePosition()},V=o=>{var i,r;(i=n.value)==null||i.onClickNode(o),f.value=((r=n.value)==null?void 0:r.getNodeDataList())||[],t.config.focusToNode&&p(o)},p=o=>{var i;return(i=n.value)==null?void 0:i.focusToNode(o)};return e.onMounted(()=>{u()}),e.onBeforeUnmount(()=>{var o;(o=n.value)==null||o.destroy()}),e.watch(()=>t.dataset,o=>{var i,r;(i=n.value)==null||i.updateDataset(o),f.value=((r=n.value)==null?void 0:r.getNodeDataList())||[]},{deep:!0}),{container:c,svg:s,domContainer:d,nodeDataList:f,initialTransformStyle:l,formatDimension:g,Direction:a.Direction,zoomIn:m,zoomOut:L,restoreScale:N,expandNodeByLevelAndPosition:S,restorePosition:h,onClickNode:V,focusToNode:p}}}),E=(t,c)=>{const s=t.__vccOpts||t;for(const[d,n]of c)s[d]=n;return s},k={class:"tree-container",ref:"container"},T=["onClick"];function v(t,c,s,d,n,f){return e.openBlock(),e.createElementBlock("div",k,[(e.openBlock(),e.createElementBlock("svg",{class:"svg vue-tree",ref:"svg",style:e.normalizeStyle(t.initialTransformStyle)},null,4)),e.createElementVNode("div",{class:"dom-container",ref:"domContainer",style:e.normalizeStyle(t.initialTransformStyle)},[e.createVNode(e.TransitionGroup,{name:"tree-node-item",tag:"div"},{default:e.withCtx(()=>[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(t.nodeDataList,(l,u)=>(e.openBlock(),e.createElementBlock("div",{class:"node-slot",onClick:m=>t.onClickNode(u),key:l.data._key,style:e.normalizeStyle({left:t.formatDimension(t.direction===t.Direction.VERTICAL?l.x:l.y),top:t.formatDimension(t.direction===t.Direction.VERTICAL?l.y:l.x),width:t.formatDimension(t.config.nodeWidth),height:t.formatDimension(t.config.nodeHeight)})},[e.renderSlot(t.$slots,"node",{node:l.data,collapsed:l.data._collapsed},()=>[e.createElementVNode("span",null,e.toDisplayString(l.data.value),1)],!0)],12,T))),128))]),_:3})],4)],512)}return E(D,[["render",v],["__scopeId","data-v-c304c1c5"]])});