orgchart 4.0.1 → 5.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "orgchart",
3
- "version": "4.0.1",
3
+ "version": "5.0.0",
4
4
  "description": "Simple and direct organization chart(tree-like hierarchy) plugin based on pure DOM and jQuery.",
5
5
  "main": "./dist/js/jquery.orgchart.min.js",
6
6
  "style": [
@@ -44,11 +44,11 @@
44
44
  "jspdf": "^2.5.1"
45
45
  },
46
46
  "devDependencies": {
47
- "browser-sync": "^2.26.7",
47
+ "browser-sync": "^3.0.2",
48
48
  "chai": "^4.2.0",
49
49
  "cypress": "^13.2.0",
50
50
  "del": "^3.0.0",
51
- "gulp": "^4.0.2",
51
+ "gulp": "^5.0.0",
52
52
  "gulp-clean-css": "^3.10.0",
53
53
  "gulp-csslint": "^1.0.1",
54
54
  "gulp-eslint": "^4.0.2",
@@ -83,6 +83,7 @@
83
83
  list-style: none;
84
84
  padding-left: 0;
85
85
  margin: 0;
86
+ justify-content: center;
86
87
  }
87
88
 
88
89
  .orgchart .hierarchy {
@@ -100,6 +101,62 @@
100
101
  box-sizing: border-box;
101
102
  }
102
103
 
104
+ /* 本姓人与上一层父母的连线,借助伪元素::before实现 */
105
+ /* 处理同一层中特殊的首节点 */
106
+ .orgchart .couple:first-child:not(:only-child)::before {
107
+ width: calc(100% - 69px); /* half width of hierarchy - half width of node - node's padding - node's border width */
108
+ left: 69px;
109
+ }
110
+
111
+ /* 处理同一层中特殊的末节点 */
112
+ .orgchart .couple:last-child:not(:only-child)::before {
113
+ width: calc(50% + 71px);
114
+ left: 0px;
115
+ }
116
+
117
+ .orgchart .hierarchy.couple:only-child::before {
118
+ width: 142px; /* half of node's width + link's width */
119
+ left: calc(50% - 71px); /* half width of node - half of link width */
120
+ }
121
+
122
+ /* 在family tree中,一个夫妻组合里,本姓人只有一个,外姓人可能有多个 */
123
+ /* 我们通过水平的连线来表示他们存在婚姻关系,借助伪元素::after实现 */
124
+ .orgchart .hierarchy.spouse:not(.couple)::after {
125
+ content: "";
126
+ position: absolute;
127
+ border-top: 2px solid rgba(217, 83, 79, 0.8);
128
+ box-sizing: border-box;
129
+ z-index: 0;
130
+ top: 24px;
131
+ width: var(--ft-width);
132
+ left: var(--ft-left-offset);
133
+ }
134
+
135
+ /* 一夫一妻情况下的连线 */
136
+ .orgchart .couple {
137
+ white-space: nowrap;
138
+ }
139
+
140
+ .orgchart .couple:has(> .nodes) > .node:first-child::after {
141
+ content: "";
142
+ position: absolute;
143
+ top: 24px;
144
+ left: calc(100% + 1px);
145
+ width: 2px;
146
+ height: 33px;
147
+ background-color: rgba(217, 83, 79, 0.8);
148
+ }
149
+ .orgchart .couple::after {
150
+ content: "";
151
+ position: absolute;
152
+ border-top: 2px solid rgba(217, 83, 79, 0.8);
153
+ box-sizing: border-box;
154
+ z-index: 0;
155
+ top: 24px;
156
+ width: 20px;
157
+ left: calc(50% - 10px); /* half of hierarchy - node's padding width - node's border with */
158
+ }
159
+
103
160
  .orgchart .hierarchy:first-child::before,
104
161
  .orgchart .hierarchy.isSiblingsCollapsed.left-sibs::before {
105
162
  left: calc(50% - 1px);
@@ -111,7 +168,7 @@
111
168
  width: calc(50% + 1px);
112
169
  }
113
170
 
114
- .orgchart .hierarchy:not(.hidden):only-child::before {
171
+ .orgchart .hierarchy:not(.hidden):not(.spouse):only-child::before {
115
172
  width: 2px;
116
173
  }
117
174
 
@@ -122,17 +179,17 @@
122
179
  .orgchart .isSiblingsCollapsed.left-sibs:last-child:before,
123
180
  .orgchart .isCollapsedSibling::before,
124
181
  .orgchart .isCollapsedSibling .hierarchy::before,
125
- .orgchart .isChildrenCollapsed > .node:not(:only-child)::after,
182
+ .orgchart .hierarchy.isChildrenCollapsed:not(.couple) > .node:has(+ .nodes)::after,
126
183
  .orgchart .isCollapsedDescendant::before,
127
184
  .orgchart .isCollapsedDescendant > .node::before,
128
185
  .orgchart .isCollapsedDescendant > .node::after,
129
186
  .orgchart .isAncestorsCollapsed:only-child::before,
130
- .orgchart .isAncestorsCollapsed > .node::before {
187
+ .orgchart .isAncestorsCollapsed > .node:not(.outsider)::before {
131
188
  content: none;
132
189
  }
133
190
 
134
191
  /* excluding leaf node */
135
- .orgchart .node:not(:only-child)::after {
192
+ .orgchart .hierarchy:not(.couple) > .node:has(+ .nodes)::after {
136
193
  content: "";
137
194
  position: absolute;
138
195
  bottom: -12px; /* -(background square size + node's border width) */
@@ -155,7 +212,7 @@
155
212
  background-color: rgba(238, 217, 54, 0.5);
156
213
  }
157
214
  /* excluding root node */
158
- .orgchart > ul > li > ul li > .node::before {
215
+ .orgchart > ul > li > ul li > .node:not(.outsider)::before {
159
216
  content: "";
160
217
  position: absolute;
161
218
  top: var(--top, -12px); /* -(fallback value = background square size + border width of node) */
@@ -165,7 +222,17 @@
165
222
  background-color: rgba(217, 83, 79, 0.8);
166
223
  }
167
224
 
168
- .orgchart > ul > li > ul li.isSiblingsCollapsed > .node::before {
225
+ .orgchart > ul > li > ul li > .node.outsider::before {
226
+ content: "";
227
+ position: absolute;
228
+ top: var(--top, -12px);
229
+ left: calc(50% - 1px);
230
+ width: 0;
231
+ height: var(--height, 10px);
232
+ border-right: 2px dotted rgba(217, 83, 79, 0.8);
233
+ }
234
+
235
+ .orgchart > ul > li > ul .isSiblingsCollapsed > .node::before {
169
236
  top: var(--top-cross-point, -12px);
170
237
  height: var(--height-cross-point, 10px);
171
238
  }
@@ -179,6 +246,8 @@
179
246
  padding: 3px;
180
247
  border: 2px dashed transparent;
181
248
  text-align: center;
249
+ transition: transform 0.3s, opacity 0.3s;
250
+ z-index: 1;
182
251
  }
183
252
 
184
253
  .orgchart.l2r .node, .orgchart.r2l .node {
@@ -190,7 +259,7 @@
190
259
  background-color: rgba(238, 217, 54, 0.5);
191
260
  transition: .5s;
192
261
  cursor: default;
193
- z-index: 20;
262
+ z-index: 2;
194
263
  }
195
264
 
196
265
  .orgchart .node.focused {
@@ -420,10 +489,6 @@
420
489
  padding: 0.5rem 1rem;
421
490
  }
422
491
 
423
- .orgchart .node {
424
- transition: transform 0.3s, opacity 0.3s;
425
- }
426
-
427
492
  .orgchart .slide-down {
428
493
  opacity: 0;
429
494
  transform: translateY(40px);
@@ -479,8 +544,8 @@
479
544
  margin-bottom: 0;
480
545
  }
481
546
 
482
- .orgchart .nodes.vertical .node::before,
483
- .orgchart .nodes.vertical .node::after {
547
+ .orgchart .nodes.vertical .hierarchy .node::before,
548
+ .orgchart .nodes.vertical .hierarchy .node::after {
484
549
  content: none;
485
550
  }
486
551
 
@@ -500,6 +565,10 @@
500
565
  border-width: 0 0 2px 2px;
501
566
  }
502
567
 
568
+ .orgchart .nodes.vertical .hierarchy:only-child::before {
569
+ width: 11px;
570
+ }
571
+
503
572
  .orgchart .nodes.vertical .hierarchy::before {
504
573
  top: 0px;
505
574
  height: 26px; /* node top half's height(25px) + half width of line */
@@ -627,7 +696,7 @@
627
696
  margin-bottom: 0;
628
697
  }
629
698
 
630
- .orgchart .node.compact .node:not(:only-child)::after {
699
+ .orgchart .node.compact .node:has(+ .nodes)::after {
631
700
  content: unset;
632
701
  }
633
702
 
@@ -75,7 +75,8 @@
75
75
  if (typeof MutationObserver !== 'undefined') {
76
76
  this.triggerInitEvent();
77
77
  }
78
- var $root = $chart.append($('<ul class="nodes"><li class="hierarchy"></li></ul>')).find('.hierarchy');
78
+ var $root = Array.isArray(data) ? $chart.append($('<ul class="nodes"></ul>')).find('.nodes')
79
+ : $chart.append($('<ul class="nodes"><li class="hierarchy"></li></ul>')).find('.hierarchy');
79
80
 
80
81
  if (data instanceof $) { // ul datasource
81
82
  this.buildHierarchy($root, this.buildJsonDS(data.children()), 0, this.options);
@@ -83,7 +84,7 @@
83
84
  if (data.relationship) {
84
85
  this.buildHierarchy($root, data);
85
86
  } else {
86
- this.buildHierarchy($root, this.attachRel(data, '00'));
87
+ this.buildHierarchy($root, Array.isArray(data) ? data : this.attachRel(data, '00'));
87
88
  }
88
89
  }
89
90
 
@@ -749,6 +750,7 @@
749
750
  },
750
751
  // whether the cursor is hovering over the node
751
752
  isInAction: function ($node) {
753
+ // TODO: 展开/折叠的按钮不止4个箭头,还有toggleBtn
752
754
  return [
753
755
  this.options.icons.expandToUp,
754
756
  this.options.icons.collapseToDown,
@@ -1347,7 +1349,10 @@
1347
1349
  }
1348
1350
  // construct the content of node
1349
1351
  var $nodeDiv = $('<div' + (opts.draggable ? ' draggable="true"' : '') + (data[opts.nodeId] ? ' id="' + data[opts.nodeId] + '"' : '') + (data.parentId ? ' data-parent="' + data.parentId + '"' : '') + '>')
1350
- .addClass('node ' + (data.className || '') + (level > opts.visibleLevel ? ' slide-up' : ''));
1352
+ .addClass('node '
1353
+ + (data.className || '')
1354
+ + (data?.outsider ? 'outsider' : '')
1355
+ + (level > opts.visibleLevel ? ' slide-up' : ''));
1351
1356
  if (opts.nodeTemplate) {
1352
1357
  $nodeDiv.append(opts.nodeTemplate(data));
1353
1358
  } else {
@@ -1416,51 +1421,38 @@
1416
1421
 
1417
1422
  return $nodeDiv;
1418
1423
  },
1419
- // recursively build the tree
1420
- buildHierarchy: function ($appendTo, data) {
1424
+ // Construct the inferior nodes within a hierarchy
1425
+ buildInferiorNodes: function ($hierarchy, $nodeDiv, data, level) {
1421
1426
  var that = this;
1422
1427
  var opts = this.options;
1423
- var level = 0;
1424
- var $nodeDiv;
1425
- if (data.level) {
1426
- level = data.level;
1428
+ var isHidden = level + 1 > opts.visibleLevel || (data.collapsed !== undefined && data.collapsed);
1429
+ var $nodesLayer;
1430
+ if ((opts.verticalLevel && (level + 1) >= opts.verticalLevel) || data.hybrid) {
1431
+ $nodesLayer = $('<ul class="nodes">');
1432
+ if (isHidden && (opts.verticalLevel && (level + 1) >= opts.verticalLevel)) {
1433
+ $nodesLayer.addClass('hidden');
1434
+ }
1435
+ if (((opts.verticalLevel && level + 1 === opts.verticalLevel) || data.hybrid)
1436
+ && !$hierarchy.closest('.vertical').length) {
1437
+ $nodesLayer.addClass('vertical');
1438
+ }
1439
+ $hierarchy.append($nodesLayer);
1440
+ } else if (data.compact) {
1441
+ $nodeDiv.addClass('compact');
1427
1442
  } else {
1428
- level = data.level = $appendTo.parentsUntil('.orgchart', '.nodes').length;
1429
- }
1430
- // Construct the node
1431
- if (Object.keys(data).length > 2) {
1432
- $nodeDiv = this.createNode(data);
1433
- $appendTo.append($nodeDiv);
1434
- }
1435
- // Construct the "inferior nodes"
1436
- if (data.children && data.children.length) {
1437
- var isHidden = level + 1 > opts.visibleLevel || (data.collapsed !== undefined && data.collapsed);
1438
- var $nodesLayer;
1439
- if ((opts.verticalLevel && (level + 1) >= opts.verticalLevel) || data.hybrid) {
1440
- $nodesLayer = $('<ul class="nodes">');
1441
- if (isHidden && (opts.verticalLevel && (level + 1) >= opts.verticalLevel)) {
1442
- $nodesLayer.addClass('hidden');
1443
- }
1444
- if (((opts.verticalLevel && level + 1 === opts.verticalLevel) || data.hybrid)
1445
- && !$appendTo.closest('.vertical').length) {
1446
- $appendTo.append($nodesLayer.addClass('vertical'));
1447
- } else {
1448
- $appendTo.append($nodesLayer);
1449
- }
1450
- } else if (data.compact) {
1451
- $nodeDiv.addClass('compact');
1452
- } else {
1453
- $nodesLayer = $('<ul class="nodes' + (isHidden ? ' hidden' : '') + '">');
1454
- if (Object.keys(data).length === 2) {
1455
- $appendTo.append($nodesLayer);
1456
- } else {
1457
- if (isHidden) {
1458
- $appendTo.addClass('isChildrenCollapsed');
1459
- }
1460
- $appendTo.append($nodesLayer);
1461
- }
1443
+ $nodesLayer = $('<ul class="nodes' + (isHidden ? ' hidden' : '') + '">');
1444
+ if (isHidden) {
1445
+ $hierarchy.addClass('isChildrenCollapsed');
1462
1446
  }
1463
- // recurse through children nodes
1447
+ $hierarchy.append($nodesLayer);
1448
+ }
1449
+ // recurse through children nodes
1450
+ if (Array.isArray(data.children[0])) {
1451
+ $.each(data.children, function() {
1452
+ this.level = level + 1;
1453
+ });
1454
+ this.buildHierarchy($nodesLayer, data.children); // 构造子一层的夫妻组合(每个组合可能有多妻多夫情况)
1455
+ } else {
1464
1456
  $.each(data.children, function () {
1465
1457
  this.level = level + 1;
1466
1458
  if (data.compact) {
@@ -1473,6 +1465,69 @@
1473
1465
  });
1474
1466
  }
1475
1467
  },
1468
+ // recursively build the tree
1469
+ buildHierarchy: function ($hierarchy, data) {
1470
+ var that = this;
1471
+ var opts = this.options;
1472
+ var level = 0;
1473
+ var $nodeDiv;
1474
+ if (data.level || data[0]?.level) {
1475
+ level = data.level;
1476
+ } else {
1477
+ level = $hierarchy.parentsUntil('.orgchart', '.nodes').length;
1478
+ if (Array.isArray(data) && Array.isArray(data[0])) {
1479
+ $.each(data, function () {
1480
+ $.each(this, function () {
1481
+ this.level = level;
1482
+ });
1483
+ });
1484
+ } else {
1485
+ data.level = level;
1486
+ }
1487
+ }
1488
+ // Construct the single node in OrgChart or the multiple nodes in family tree
1489
+ if (Array.isArray(data) && Array.isArray(data[0])) { // 处理family tree的情况
1490
+ $.each(data, function () { // 构造一个家庭的hierarchy
1491
+ var _this = this;
1492
+ $.each(this, function (i) { // 构造一个夫/妻节点
1493
+ $nodeDiv = that.createNode(this);
1494
+ // if there are only two persons in a marriage, two single nodes will appear in a hierarchy
1495
+ if (_this.length === 2 && i === 1) {
1496
+ $hierarchy.find(`#${_this[0].id}`).after($nodeDiv);
1497
+ if (this.children && this.children.length && this.children[0].length) {
1498
+ that.buildInferiorNodes($hierarchy.find(`#${_this[0].id}`).parent(), $nodeDiv, this, level);
1499
+ }
1500
+ } else {
1501
+ // if there are more than two persons in a marriage, every node will be included in a single hierarchy
1502
+ var $wrapper = $(`<li class="hierarchy${_this.length > 1 ? ' spouse' : ''}${_this.length === 2 ? ' couple' : ''}${!!this.outsider === false && _this.length > 2 ? ' insider' : ''}"></li>`);
1503
+
1504
+ //在family tree中,一个多妻/多夫组合里,本姓人只有一个,外姓人可能有多个,我们通过水平的连线来表示他们是一家子
1505
+ if (i === 0) {
1506
+ $wrapper.css({'--ft-width': '50%', '--ft-left-offset': '50%'});
1507
+ } else if (i > 0 && i < _this.length - 1) {
1508
+ $wrapper.css({'--ft-width': '100%', '--ft-left-offset': '0px'});
1509
+ } else {
1510
+ $wrapper.css({'--ft-width': '50%', '--ft-left-offset': '0px'});
1511
+ }
1512
+
1513
+ $wrapper.append($nodeDiv);
1514
+ $hierarchy.append($wrapper);
1515
+ if (this.children && this.children.length && this.children[0].length) {
1516
+ that.buildInferiorNodes($wrapper, $nodeDiv, this, level);
1517
+ }
1518
+ }
1519
+ });
1520
+ });
1521
+ } else {
1522
+ if (Object.keys(data).length > 2) { // TODO: 应该用更好的方式来判断是否是供父一级节点创建的信息
1523
+ $nodeDiv = this.createNode(data);
1524
+ $hierarchy.append($nodeDiv);
1525
+ }
1526
+ if (data.children && data.children.length) {
1527
+ this.buildInferiorNodes($hierarchy, $nodeDiv, data, level);
1528
+ }
1529
+ }
1530
+ },
1476
1531
  // build the child nodes of specific node
1477
1532
  buildChildNode: function ($appendTo, data) {
1478
1533
  this.buildHierarchy($appendTo, { 'children': data });
@@ -1,128 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="utf-8">
5
- <title>Organization Chart Plugin</title>
6
- <link rel="icon" href="img/logo.png">
7
- <link rel="stylesheet" href="css/jquery.orgchart.css">
8
- <link rel="stylesheet" href="css/style.css">
9
- <style type="text/css">
10
- .hidden { display: none; }
11
- .orgchart .drill-icon {
12
- transition: opacity .5s;
13
- opacity: 0;
14
- right: -8px;
15
- top: -8px;
16
- z-index: 2;
17
- position: absolute;
18
- }
19
- .orgchart .drill-icon::before {
20
- background-color: rgba(68, 157, 68, 0.5);
21
- }
22
- .orgchart .drill-icon:hover::before { background-color: #449d44; }
23
- .orgchart .node:hover .drill-icon { opacity: 1; }
24
- </style>
25
- </head>
26
- <body>
27
- <div id="chart-container"></div>
28
-
29
- <script type="text/javascript" src="js/jquery.min.js"></script>
30
- <script type="text/javascript" src="js/jquery.orgchart.js"></script>
31
- <script type="text/javascript" src="js/jquery.mockjax.min.js"></script>
32
- <script type="text/javascript">
33
- $.mockjax({
34
- url: '/orgchart/root-node',
35
- responseTime: 1000,
36
- contentType: 'application/json',
37
- responseText: {
38
- 'className': 'root-node',
39
- 'name': 'Lao Lao',
40
- 'dept': 'president office',
41
- 'children': [
42
- { 'name': 'Bo Miao', 'dept': 'product dept' },
43
- { 'className': 'drill-down asso-rd', 'name': 'Su Miao', 'dept': 'R&D dept' },
44
- { 'name': 'Hong Miao', 'dept': 'finance dept' },
45
- { 'name': 'Chun Miao', 'dept': 'HR dept' }
46
- ]
47
- }
48
- });
49
-
50
- $.mockjax({
51
- url: '/orgchart/asso-rd',
52
- responseTime: 1000,
53
- contentType: 'application/json',
54
- responseText: {
55
- 'className': 'asso-rd drill-up',
56
- 'name': 'Su Miao',
57
- 'dept': 'R&D dept',
58
- 'children': [
59
- { 'name': 'Tie Hua', 'dept': 'backend group' },
60
- { 'className': 'drill-down asso-frontend', 'name': 'Hei Hei', 'dept': 'frontend group' }
61
- ]
62
- }
63
- });
64
-
65
- $.mockjax({
66
- url: '/orgchart/asso-frontend',
67
- responseTime: 1000,
68
- contentType: 'application/json',
69
- responseText: {
70
- 'className': 'asso-frontend drill-up',
71
- 'name': 'Hei Hei',
72
- 'dept': 'frontend group',
73
- 'children': [
74
- { 'name': 'Pang Pang', 'dept': 'frontend group' },
75
- { 'name': 'Xiang Xiang', 'dept': 'frontend group',
76
- 'children': [
77
- { 'name': 'Xiao Xiao', 'dept': 'frontend group' },
78
- { 'name': 'Dan Dan', 'dept': 'frontend group' },
79
- { 'name': 'Zai Zai', 'dept': 'frontend group' }
80
- ]
81
- }
82
- ]
83
- }
84
- });
85
-
86
- function initOrgchart(rootClass) {
87
- $('#chart-container').orgchart({
88
- 'chartClass': rootClass,
89
- 'data' : '/orgchart/' + rootClass,
90
- 'nodeContent': 'dept',
91
- 'createNode': function($node, data) {
92
- if ($node.is('.drill-down')) {
93
- var assoClass = data.className.match(/asso-\w+/)[0];
94
- var drillDownIcon = $('<i>', {
95
- 'class': 'oci oci-arrow-square-down drill-icon',
96
- 'click': function() {
97
- $('#chart-container').find('.orgchart:visible').addClass('hidden');
98
- if (!$('#chart-container').find('.orgchart.' + assoClass).length) {
99
- initOrgchart(assoClass);
100
- } else {
101
- $('#chart-container').find('.orgchart.' + assoClass).removeClass('hidden');
102
- }
103
- }
104
- });
105
- $node.append(drillDownIcon);
106
- } else if ($node.is('.drill-up')) {
107
- var assoClass = data.className.match(/asso-\w+/)[0];
108
- var drillUpIcon = $('<i>', {
109
- 'class': 'oci oci-arrow-square-up drill-icon',
110
- 'click': function() {
111
- $('#chart-container').find('.orgchart:visible').addClass('hidden').end()
112
- .find('.drill-down.' + assoClass).closest('.orgchart').removeClass('hidden');
113
- }
114
- });
115
- $node.append(drillUpIcon);
116
- }
117
- }
118
- });
119
- }
120
-
121
- $(function() {
122
-
123
- initOrgchart('root-node');
124
-
125
- });
126
- </script>
127
- </body>
128
- </html>