1 | import { Component, ViewEncapsulation, ChangeDetectionStrategy, Inject, forwardRef, ChangeDetectorRef, Input, EventEmitter, ElementRef, Output, ContentChildren, NgModule } from '@angular/core';
|
2 | import { trigger, state, style, transition, animate } from '@angular/animations';
|
3 | import { CommonModule } from '@angular/common';
|
4 | import { PrimeTemplate, SharedModule } from 'primeng/api';
|
5 | import { Subject } from 'rxjs';
|
6 |
|
7 | class OrganizationChartNode {
|
8 | constructor(chart, cd) {
|
9 | this.cd = cd;
|
10 | this.chart = chart;
|
11 | this.subscription = this.chart.selectionSource$.subscribe(() => {
|
12 | this.cd.markForCheck();
|
13 | });
|
14 | }
|
15 | get leaf() {
|
16 | return this.node.leaf == false ? false : !(this.node.children && this.node.children.length);
|
17 | }
|
18 | get colspan() {
|
19 | return (this.node.children && this.node.children.length) ? this.node.children.length * 2 : null;
|
20 | }
|
21 | onNodeClick(event, node) {
|
22 | this.chart.onNodeClick(event, node);
|
23 | }
|
24 | toggleNode(event, node) {
|
25 | node.expanded = !node.expanded;
|
26 | if (node.expanded)
|
27 | this.chart.onNodeExpand.emit({ originalEvent: event, node: this.node });
|
28 | else
|
29 | this.chart.onNodeCollapse.emit({ originalEvent: event, node: this.node });
|
30 | event.preventDefault();
|
31 | }
|
32 | isSelected() {
|
33 | return this.chart.isSelected(this.node);
|
34 | }
|
35 | ngOnDestroy() {
|
36 | this.subscription.unsubscribe();
|
37 | }
|
38 | }
|
39 | OrganizationChartNode.decorators = [
|
40 | { type: Component, args: [{
|
41 | selector: '[pOrganizationChartNode]',
|
42 | template: `
|
43 | <tbody *ngIf="node">
|
44 | <tr>
|
45 | <td [attr.colspan]="colspan">
|
46 | <div [class]="node.styleClass" [ngClass]="{'p-organizationchart-node-content': true, 'p-organizationchart-selectable-node': chart.selectionMode && node.selectable !== false,'p-highlight':isSelected()}"
|
47 | (click)="onNodeClick($event,node)">
|
48 | <div *ngIf="!chart.getTemplateForNode(node)">{{node.label}}</div>
|
49 | <div *ngIf="chart.getTemplateForNode(node)">
|
50 | <ng-container *ngTemplateOutlet="chart.getTemplateForNode(node); context: {$implicit: node}"></ng-container>
|
51 | </div>
|
52 | <a *ngIf="!leaf" tabindex="0" class="p-node-toggler" (click)="toggleNode($event, node)" (keydown.enter)="toggleNode($event, node)">
|
53 | <i class="p-node-toggler-icon pi" [ngClass]="{'pi-chevron-down': node.expanded, 'pi-chevron-up': !node.expanded}"></i>
|
54 | </a>
|
55 | </div>
|
56 | </td>
|
57 | </tr>
|
58 | <tr [ngClass]="!leaf&&node.expanded ? 'p-organizationchart-node-visible' : 'p-organizationchart-node-hidden'" class="p-organizationchart-lines" [@childState]="'in'">
|
59 | <td [attr.colspan]="colspan">
|
60 | <div class="p-organizationchart-line-down"></div>
|
61 | </td>
|
62 | </tr>
|
63 | <tr [ngClass]="!leaf&&node.expanded ? 'p-organizationchart-node-visible' : 'p-organizationchart-node-hidden'" class="p-organizationchart-lines" [@childState]="'in'">
|
64 | <ng-container *ngIf="node.children && node.children.length === 1">
|
65 | <td [attr.colspan]="colspan">
|
66 | <div class="p-organizationchart-line-down"></div>
|
67 | </td>
|
68 | </ng-container>
|
69 | <ng-container *ngIf="node.children && node.children.length > 1">
|
70 | <ng-template ngFor let-child [ngForOf]="node.children" let-first="first" let-last="last">
|
71 | <td class="p-organizationchart-line-left" [ngClass]="{'p-organizationchart-line-top':!first}"> </td>
|
72 | <td class="p-organizationchart-line-right" [ngClass]="{'p-organizationchart-line-top':!last}"> </td>
|
73 | </ng-template>
|
74 | </ng-container>
|
75 | </tr>
|
76 | <tr [ngClass]="!leaf&&node.expanded ? 'p-organizationchart-node-visible' : 'p-organizationchart-node-hidden'" class="p-organizationchart-nodes" [@childState]="'in'">
|
77 | <td *ngFor="let child of node.children" colspan="2">
|
78 | <table class="p-organizationchart-table" pOrganizationChartNode [node]="child"></table>
|
79 | </td>
|
80 | </tr>
|
81 | </tbody>
|
82 | `,
|
83 | animations: [
|
84 | trigger('childState', [
|
85 | state('in', style({ opacity: 1 })),
|
86 | transition('void => *', [
|
87 | style({ opacity: 0 }),
|
88 | animate(150)
|
89 | ]),
|
90 | transition('* => void', [
|
91 | animate(150, style({ opacity: 0 }))
|
92 | ])
|
93 | ])
|
94 | ],
|
95 | encapsulation: ViewEncapsulation.None,
|
96 | changeDetection: ChangeDetectionStrategy.OnPush,
|
97 | styles: [".p-organizationchart-table{border-collapse:separate;border-spacing:0;margin:0 auto}.p-organizationchart-table>tbody>tr>td{padding:0 .75rem;text-align:center;vertical-align:top}.p-organizationchart-node-content{display:inline-block;position:relative}.p-organizationchart-node-content .p-node-toggler{-moz-user-select:none;-ms-user-select:none;-webkit-user-select:none;bottom:-.75rem;cursor:pointer;height:1.5rem;left:50%;margin-left:-.75rem;position:absolute;user-select:none;width:1.5rem;z-index:2}.p-organizationchart-node-content .p-node-toggler .p-node-toggler-icon{position:relative;top:.25rem}.p-organizationchart-line-down{height:20px;margin:0 auto;width:1px}.p-organizationchart-line-left,.p-organizationchart-line-right{border-radius:0}.p-organizationchart-selectable-node{cursor:pointer}.p-organizationchart .p-organizationchart-node-hidden{display:none}.p-organizationchart-preservespace .p-organizationchart-node-hidden{display:inherit;visibility:hidden}"]
|
98 | },] }
|
99 | ];
|
100 | OrganizationChartNode.ctorParameters = () => [
|
101 | { type: undefined, decorators: [{ type: Inject, args: [forwardRef(() => OrganizationChart),] }] },
|
102 | { type: ChangeDetectorRef }
|
103 | ];
|
104 | OrganizationChartNode.propDecorators = {
|
105 | node: [{ type: Input }],
|
106 | root: [{ type: Input }],
|
107 | first: [{ type: Input }],
|
108 | last: [{ type: Input }]
|
109 | };
|
110 | class OrganizationChart {
|
111 | constructor(el, cd) {
|
112 | this.el = el;
|
113 | this.cd = cd;
|
114 | this.preserveSpace = true;
|
115 | this.selectionChange = new EventEmitter();
|
116 | this.onNodeSelect = new EventEmitter();
|
117 | this.onNodeUnselect = new EventEmitter();
|
118 | this.onNodeExpand = new EventEmitter();
|
119 | this.onNodeCollapse = new EventEmitter();
|
120 | this.selectionSource = new Subject();
|
121 | this.selectionSource$ = this.selectionSource.asObservable();
|
122 | }
|
123 | get selection() {
|
124 | return this._selection;
|
125 | }
|
126 | set selection(val) {
|
127 | this._selection = val;
|
128 | if (this.initialized)
|
129 | this.selectionSource.next();
|
130 | }
|
131 | get root() {
|
132 | return this.value && this.value.length ? this.value[0] : null;
|
133 | }
|
134 | ngAfterContentInit() {
|
135 | if (this.templates.length) {
|
136 | this.templateMap = {};
|
137 | }
|
138 | this.templates.forEach((item) => {
|
139 | this.templateMap[item.getType()] = item.template;
|
140 | });
|
141 | this.initialized = true;
|
142 | }
|
143 | getTemplateForNode(node) {
|
144 | if (this.templateMap)
|
145 | return node.type ? this.templateMap[node.type] : this.templateMap['default'];
|
146 | else
|
147 | return null;
|
148 | }
|
149 | onNodeClick(event, node) {
|
150 | let eventTarget = event.target;
|
151 | if (eventTarget.className && (eventTarget.className.indexOf('p-node-toggler') !== -1 || eventTarget.className.indexOf('p-node-toggler-icon') !== -1)) {
|
152 | return;
|
153 | }
|
154 | else if (this.selectionMode) {
|
155 | if (node.selectable === false) {
|
156 | return;
|
157 | }
|
158 | let index = this.findIndexInSelection(node);
|
159 | let selected = (index >= 0);
|
160 | if (this.selectionMode === 'single') {
|
161 | if (selected) {
|
162 | this.selection = null;
|
163 | this.onNodeUnselect.emit({ originalEvent: event, node: node });
|
164 | }
|
165 | else {
|
166 | this.selection = node;
|
167 | this.onNodeSelect.emit({ originalEvent: event, node: node });
|
168 | }
|
169 | }
|
170 | else if (this.selectionMode === 'multiple') {
|
171 | if (selected) {
|
172 | this.selection = this.selection.filter((val, i) => i != index);
|
173 | this.onNodeUnselect.emit({ originalEvent: event, node: node });
|
174 | }
|
175 | else {
|
176 | this.selection = [...this.selection || [], node];
|
177 | this.onNodeSelect.emit({ originalEvent: event, node: node });
|
178 | }
|
179 | }
|
180 | this.selectionChange.emit(this.selection);
|
181 | this.selectionSource.next();
|
182 | }
|
183 | }
|
184 | findIndexInSelection(node) {
|
185 | let index = -1;
|
186 | if (this.selectionMode && this.selection) {
|
187 | if (this.selectionMode === 'single') {
|
188 | index = (this.selection == node) ? 0 : -1;
|
189 | }
|
190 | else if (this.selectionMode === 'multiple') {
|
191 | for (let i = 0; i < this.selection.length; i++) {
|
192 | if (this.selection[i] == node) {
|
193 | index = i;
|
194 | break;
|
195 | }
|
196 | }
|
197 | }
|
198 | }
|
199 | return index;
|
200 | }
|
201 | isSelected(node) {
|
202 | return this.findIndexInSelection(node) != -1;
|
203 | }
|
204 | }
|
205 | OrganizationChart.decorators = [
|
206 | { type: Component, args: [{
|
207 | selector: 'p-organizationChart',
|
208 | template: `
|
209 | <div [ngStyle]="style" [class]="styleClass" [ngClass]="{'p-organizationchart p-component': true, 'p-organizationchart-preservespace': preserveSpace}">
|
210 | <table class="p-organizationchart-table" pOrganizationChartNode [node]="root" *ngIf="root"></table>
|
211 | </div>
|
212 | `,
|
213 | changeDetection: ChangeDetectionStrategy.OnPush
|
214 | },] }
|
215 | ];
|
216 | OrganizationChart.ctorParameters = () => [
|
217 | { type: ElementRef },
|
218 | { type: ChangeDetectorRef }
|
219 | ];
|
220 | OrganizationChart.propDecorators = {
|
221 | value: [{ type: Input }],
|
222 | style: [{ type: Input }],
|
223 | styleClass: [{ type: Input }],
|
224 | selectionMode: [{ type: Input }],
|
225 | preserveSpace: [{ type: Input }],
|
226 | selection: [{ type: Input }],
|
227 | selectionChange: [{ type: Output }],
|
228 | onNodeSelect: [{ type: Output }],
|
229 | onNodeUnselect: [{ type: Output }],
|
230 | onNodeExpand: [{ type: Output }],
|
231 | onNodeCollapse: [{ type: Output }],
|
232 | templates: [{ type: ContentChildren, args: [PrimeTemplate,] }]
|
233 | };
|
234 | class OrganizationChartModule {
|
235 | }
|
236 | OrganizationChartModule.decorators = [
|
237 | { type: NgModule, args: [{
|
238 | imports: [CommonModule],
|
239 | exports: [OrganizationChart, SharedModule],
|
240 | declarations: [OrganizationChart, OrganizationChartNode]
|
241 | },] }
|
242 | ];
|
243 |
|
244 |
|
245 |
|
246 |
|
247 |
|
248 | export { OrganizationChart, OrganizationChartModule, OrganizationChartNode };
|
249 |
|