1 | import { EventEmitter, Component, ChangeDetectionStrategy, ViewEncapsulation, ElementRef, ChangeDetectorRef, Input, Output, ViewChild, ContentChildren, NgModule } from '@angular/core';
|
2 | import { CommonModule } from '@angular/common';
|
3 | import { ButtonModule } from 'primeng/button';
|
4 | import { PrimeTemplate, SharedModule } from 'primeng/api';
|
5 | import { DomHandler } from 'primeng/dom';
|
6 | import { ObjectUtils, FilterUtils } from 'primeng/utils';
|
7 | import { RippleModule } from 'primeng/ripple';
|
8 |
|
9 | class OrderList {
|
10 | constructor(el, cd) {
|
11 | this.el = el;
|
12 | this.cd = cd;
|
13 | this.metaKeySelection = true;
|
14 | this.controlsPosition = 'left';
|
15 | this.filterMatchMode = "contains";
|
16 | this.selectionChange = new EventEmitter();
|
17 | this.trackBy = (index, item) => item;
|
18 | this.onReorder = new EventEmitter();
|
19 | this.onSelectionChange = new EventEmitter();
|
20 | this.onFilterEvent = new EventEmitter();
|
21 | }
|
22 | get selection() {
|
23 | return this._selection;
|
24 | }
|
25 | set selection(val) {
|
26 | this._selection = val;
|
27 | }
|
28 | ngAfterContentInit() {
|
29 | this.templates.forEach((item) => {
|
30 | switch (item.getType()) {
|
31 | case 'item':
|
32 | this.itemTemplate = item.template;
|
33 | break;
|
34 | default:
|
35 | this.itemTemplate = item.template;
|
36 | break;
|
37 | }
|
38 | });
|
39 | }
|
40 | ngAfterViewChecked() {
|
41 | if (this.movedUp || this.movedDown) {
|
42 | let listItems = DomHandler.find(this.listViewChild.nativeElement, 'li.p-highlight');
|
43 | let listItem;
|
44 | if (listItems.length > 0) {
|
45 | if (this.movedUp)
|
46 | listItem = listItems[0];
|
47 | else
|
48 | listItem = listItems[listItems.length - 1];
|
49 | DomHandler.scrollInView(this.listViewChild.nativeElement, listItem);
|
50 | }
|
51 | this.movedUp = false;
|
52 | this.movedDown = false;
|
53 | }
|
54 | }
|
55 | get value() {
|
56 | return this._value;
|
57 | }
|
58 | set value(val) {
|
59 | this._value = val;
|
60 | if (this.filterValue) {
|
61 | this.filter();
|
62 | }
|
63 | }
|
64 | onItemClick(event, item, index) {
|
65 | this.itemTouched = false;
|
66 | let selectedIndex = ObjectUtils.findIndexInList(item, this.selection);
|
67 | let selected = (selectedIndex != -1);
|
68 | let metaSelection = this.itemTouched ? false : this.metaKeySelection;
|
69 | if (metaSelection) {
|
70 | let metaKey = (event.metaKey || event.ctrlKey || event.shiftKey);
|
71 | if (selected && metaKey) {
|
72 | this._selection = this._selection.filter((val, index) => index !== selectedIndex);
|
73 | }
|
74 | else {
|
75 | this._selection = (metaKey) ? this._selection ? [...this._selection] : [] : [];
|
76 | ObjectUtils.insertIntoOrderedArray(item, index, this._selection, this.value);
|
77 | }
|
78 | }
|
79 | else {
|
80 | if (selected) {
|
81 | this._selection = this._selection.filter((val, index) => index !== selectedIndex);
|
82 | }
|
83 | else {
|
84 | this._selection = this._selection ? [...this._selection] : [];
|
85 | ObjectUtils.insertIntoOrderedArray(item, index, this._selection, this.value);
|
86 | }
|
87 | }
|
88 |
|
89 | this.selectionChange.emit(this._selection);
|
90 |
|
91 | this.onSelectionChange.emit({ originalEvent: event, value: this._selection });
|
92 | }
|
93 | onFilterKeyup(event) {
|
94 | this.filterValue = event.target.value.trim().toLocaleLowerCase(this.filterLocale);
|
95 | this.filter();
|
96 | this.onFilterEvent.emit({
|
97 | originalEvent: event,
|
98 | value: this.visibleOptions
|
99 | });
|
100 | }
|
101 | filter() {
|
102 | let searchFields = this.filterBy.split(',');
|
103 | this.visibleOptions = FilterUtils.filter(this.value, searchFields, this.filterValue, this.filterMatchMode, this.filterLocale);
|
104 | }
|
105 | isItemVisible(item) {
|
106 | if (this.filterValue && this.filterValue.trim().length) {
|
107 | for (let i = 0; i < this.visibleOptions.length; i++) {
|
108 | if (item == this.visibleOptions[i]) {
|
109 | return true;
|
110 | }
|
111 | }
|
112 | }
|
113 | else {
|
114 | return true;
|
115 | }
|
116 | }
|
117 | onItemTouchEnd(event) {
|
118 | this.itemTouched = true;
|
119 | }
|
120 | isSelected(item) {
|
121 | return ObjectUtils.findIndexInList(item, this.selection) != -1;
|
122 | }
|
123 | moveUp(event) {
|
124 | if (this.selection) {
|
125 | for (let i = 0; i < this.selection.length; i++) {
|
126 | let selectedItem = this.selection[i];
|
127 | let selectedItemIndex = ObjectUtils.findIndexInList(selectedItem, this.value);
|
128 | if (selectedItemIndex != 0) {
|
129 | let movedItem = this.value[selectedItemIndex];
|
130 | let temp = this.value[selectedItemIndex - 1];
|
131 | this.value[selectedItemIndex - 1] = movedItem;
|
132 | this.value[selectedItemIndex] = temp;
|
133 | }
|
134 | else {
|
135 | break;
|
136 | }
|
137 | }
|
138 | this.movedUp = true;
|
139 | this.onReorder.emit(event);
|
140 | }
|
141 | }
|
142 | moveTop(event) {
|
143 | if (this.selection) {
|
144 | for (let i = this.selection.length - 1; i >= 0; i--) {
|
145 | let selectedItem = this.selection[i];
|
146 | let selectedItemIndex = ObjectUtils.findIndexInList(selectedItem, this.value);
|
147 | if (selectedItemIndex != 0) {
|
148 | let movedItem = this.value.splice(selectedItemIndex, 1)[0];
|
149 | this.value.unshift(movedItem);
|
150 | }
|
151 | else {
|
152 | break;
|
153 | }
|
154 | }
|
155 | this.onReorder.emit(event);
|
156 | this.listViewChild.nativeElement.scrollTop = 0;
|
157 | }
|
158 | }
|
159 | moveDown(event) {
|
160 | if (this.selection) {
|
161 | for (let i = this.selection.length - 1; i >= 0; i--) {
|
162 | let selectedItem = this.selection[i];
|
163 | let selectedItemIndex = ObjectUtils.findIndexInList(selectedItem, this.value);
|
164 | if (selectedItemIndex != (this.value.length - 1)) {
|
165 | let movedItem = this.value[selectedItemIndex];
|
166 | let temp = this.value[selectedItemIndex + 1];
|
167 | this.value[selectedItemIndex + 1] = movedItem;
|
168 | this.value[selectedItemIndex] = temp;
|
169 | }
|
170 | else {
|
171 | break;
|
172 | }
|
173 | }
|
174 | this.movedDown = true;
|
175 | this.onReorder.emit(event);
|
176 | }
|
177 | }
|
178 | moveBottom(event) {
|
179 | if (this.selection) {
|
180 | for (let i = 0; i < this.selection.length; i++) {
|
181 | let selectedItem = this.selection[i];
|
182 | let selectedItemIndex = ObjectUtils.findIndexInList(selectedItem, this.value);
|
183 | if (selectedItemIndex != (this.value.length - 1)) {
|
184 | let movedItem = this.value.splice(selectedItemIndex, 1)[0];
|
185 | this.value.push(movedItem);
|
186 | }
|
187 | else {
|
188 | break;
|
189 | }
|
190 | }
|
191 | this.onReorder.emit(event);
|
192 | this.listViewChild.nativeElement.scrollTop = this.listViewChild.nativeElement.scrollHeight;
|
193 | }
|
194 | }
|
195 | onDragStart(event, index) {
|
196 | event.dataTransfer.setData('text', 'b');
|
197 | event.target.blur();
|
198 | this.dragging = true;
|
199 | this.draggedItemIndex = index;
|
200 | }
|
201 | onDragOver(event, index) {
|
202 | if (this.dragging && this.draggedItemIndex !== index && this.draggedItemIndex + 1 !== index) {
|
203 | this.dragOverItemIndex = index;
|
204 | event.preventDefault();
|
205 | }
|
206 | }
|
207 | onDragLeave(event) {
|
208 | this.dragOverItemIndex = null;
|
209 | }
|
210 | onDrop(event, index) {
|
211 | let dropIndex = (this.draggedItemIndex > index) ? index : (index === 0) ? 0 : index - 1;
|
212 | ObjectUtils.reorderArray(this.value, this.draggedItemIndex, dropIndex);
|
213 | this.dragOverItemIndex = null;
|
214 | this.onReorder.emit(event);
|
215 | event.preventDefault();
|
216 | }
|
217 | onDragEnd(event) {
|
218 | this.dragging = false;
|
219 | }
|
220 | onListMouseMove(event) {
|
221 | if (this.dragging) {
|
222 | let offsetY = this.listViewChild.nativeElement.getBoundingClientRect().top + document.body.scrollTop;
|
223 | let bottomDiff = (offsetY + this.listViewChild.nativeElement.clientHeight) - event.pageY;
|
224 | let topDiff = (event.pageY - offsetY);
|
225 | if (bottomDiff < 25 && bottomDiff > 0)
|
226 | this.listViewChild.nativeElement.scrollTop += 15;
|
227 | else if (topDiff < 25 && topDiff > 0)
|
228 | this.listViewChild.nativeElement.scrollTop -= 15;
|
229 | }
|
230 | }
|
231 | onItemKeydown(event, item, index) {
|
232 | let listItem = event.currentTarget;
|
233 | switch (event.which) {
|
234 |
|
235 | case 40:
|
236 | var nextItem = this.findNextItem(listItem);
|
237 | if (nextItem) {
|
238 | nextItem.focus();
|
239 | }
|
240 | event.preventDefault();
|
241 | break;
|
242 |
|
243 | case 38:
|
244 | var prevItem = this.findPrevItem(listItem);
|
245 | if (prevItem) {
|
246 | prevItem.focus();
|
247 | }
|
248 | event.preventDefault();
|
249 | break;
|
250 |
|
251 | case 13:
|
252 | this.onItemClick(event, item, index);
|
253 | event.preventDefault();
|
254 | break;
|
255 | }
|
256 | }
|
257 | findNextItem(item) {
|
258 | let nextItem = item.nextElementSibling;
|
259 | if (nextItem)
|
260 | return !DomHandler.hasClass(nextItem, 'p-orderlist-item') || DomHandler.isHidden(nextItem) ? this.findNextItem(nextItem) : nextItem;
|
261 | else
|
262 | return null;
|
263 | }
|
264 | findPrevItem(item) {
|
265 | let prevItem = item.previousElementSibling;
|
266 | if (prevItem)
|
267 | return !DomHandler.hasClass(prevItem, 'p-orderlist-item') || DomHandler.isHidden(prevItem) ? this.findPrevItem(prevItem) : prevItem;
|
268 | else
|
269 | return null;
|
270 | }
|
271 | }
|
272 | OrderList.decorators = [
|
273 | { type: Component, args: [{
|
274 | selector: 'p-orderList',
|
275 | template: `
|
276 | <div [ngClass]="{'p-orderlist p-component': true, 'p-orderlist-controls-left': controlsPosition === 'left',
|
277 | 'p-orderlist-controls-right': controlsPosition === 'right'}" [ngStyle]="style" [class]="styleClass">
|
278 | <div class="p-orderlist-controls">
|
279 | <button type="button" pButton pRipple icon="pi pi-angle-up" (click)="moveUp($event)"></button>
|
280 | <button type="button" pButton pRipple icon="pi pi-angle-double-up" (click)="moveTop($event)"></button>
|
281 | <button type="button" pButton pRipple icon="pi pi-angle-down" (click)="moveDown($event)"></button>
|
282 | <button type="button" pButton pRipple icon="pi pi-angle-double-down" (click)="moveBottom($event)"></button>
|
283 | </div>
|
284 | <div class="p-orderlist-list-container">
|
285 | <div class="p-orderlist-header" *ngIf="header">
|
286 | <div class="p-orderlist-title">{{header}}</div>
|
287 | </div>
|
288 | <div class="p-orderlist-filter-container" *ngIf="filterBy">
|
289 | <div class="p-orderlist-filter">
|
290 | <input type="text" role="textbox" (keyup)="onFilterKeyup($event)" class="p-orderlist-filter-input p-inputtext p-component" [attr.placeholder]="filterPlaceholder" [attr.aria-label]="ariaFilterLabel">
|
291 | <span class="p-orderlist-filter-icon pi pi-search"></span>
|
292 | </div>
|
293 | </div>
|
294 | <ul #listelement class="p-orderlist-list" [ngStyle]="listStyle" (dragover)="onListMouseMove($event)">
|
295 | <ng-template ngFor [ngForTrackBy]="trackBy" let-item [ngForOf]="value" let-i="index" let-l="last">
|
296 | <li class="p-orderlist-droppoint" *ngIf="dragdrop && isItemVisible(item)" (dragover)="onDragOver($event, i)" (drop)="onDrop($event, i)" (dragleave)="onDragLeave($event)"
|
297 | [ngClass]="{'p-orderlist-droppoint-highlight': (i === dragOverItemIndex)}"></li>
|
298 | <li class="p-orderlist-item" tabindex="0" [ngClass]="{'p-highlight':isSelected(item)}" pRipple
|
299 | (click)="onItemClick($event,item,i)" (touchend)="onItemTouchEnd($event)" (keydown)="onItemKeydown($event,item,i)"
|
300 | [style.display]="isItemVisible(item) ? 'block' : 'none'" role="option" [attr.aria-selected]="isSelected(item)"
|
301 | [attr.draggable]="dragdrop" (dragstart)="onDragStart($event, i)" (dragend)="onDragEnd($event)">
|
302 | <ng-container *ngTemplateOutlet="itemTemplate; context: {$implicit: item, index: i}"></ng-container>
|
303 | </li>
|
304 | <li class="p-orderlist-droppoint" *ngIf="dragdrop && l" (dragover)="onDragOver($event, i + 1)" (drop)="onDrop($event, i + 1)" (dragleave)="onDragLeave($event)"
|
305 | [ngClass]="{'p-orderlist-droppoint-highlight': (i + 1 === dragOverItemIndex)}"></li>
|
306 | </ng-template>
|
307 | </ul>
|
308 | </div>
|
309 | </div>
|
310 | `,
|
311 | changeDetection: ChangeDetectionStrategy.OnPush,
|
312 | encapsulation: ViewEncapsulation.None,
|
313 | styles: [".p-orderlist,.p-orderlist-controls{display:-ms-flexbox;display:flex}.p-orderlist-controls{-ms-flex-direction:column;-ms-flex-pack:center;flex-direction:column;justify-content:center}.p-orderlist-list-container{-ms-flex:1 1 auto;flex:1 1 auto}.p-orderlist-list{list-style-type:none;margin:0;max-height:24rem;min-height:12rem;overflow:auto;padding:0}.p-orderlist-item{cursor:pointer;overflow:hidden;position:relative}.p-orderlist.p-state-disabled .p-button,.p-orderlist.p-state-disabled .p-orderlist-item{cursor:default}.p-orderlist.p-state-disabled .p-orderlist-list{overflow:hidden}.p-orderlist-filter{position:relative}.p-orderlist-filter-icon{margin-top:-.5rem;position:absolute;top:50%}.p-orderlist-filter-input{width:100%}.p-orderlist-controls-right .p-orderlist-controls{-ms-flex-order:2;order:2}.p-orderlist-controls-right .p-orderlist-list-container{-ms-flex-order:1;order:1}.p-orderlist-droppoint{height:6px}"]
|
314 | },] }
|
315 | ];
|
316 | OrderList.ctorParameters = () => [
|
317 | { type: ElementRef },
|
318 | { type: ChangeDetectorRef }
|
319 | ];
|
320 | OrderList.propDecorators = {
|
321 | header: [{ type: Input }],
|
322 | style: [{ type: Input }],
|
323 | styleClass: [{ type: Input }],
|
324 | listStyle: [{ type: Input }],
|
325 | responsive: [{ type: Input }],
|
326 | filterBy: [{ type: Input }],
|
327 | filterPlaceholder: [{ type: Input }],
|
328 | filterLocale: [{ type: Input }],
|
329 | metaKeySelection: [{ type: Input }],
|
330 | dragdrop: [{ type: Input }],
|
331 | controlsPosition: [{ type: Input }],
|
332 | ariaFilterLabel: [{ type: Input }],
|
333 | filterMatchMode: [{ type: Input }],
|
334 | selectionChange: [{ type: Output }],
|
335 | trackBy: [{ type: Input }],
|
336 | onReorder: [{ type: Output }],
|
337 | onSelectionChange: [{ type: Output }],
|
338 | onFilterEvent: [{ type: Output }],
|
339 | listViewChild: [{ type: ViewChild, args: ['listelement',] }],
|
340 | templates: [{ type: ContentChildren, args: [PrimeTemplate,] }],
|
341 | selection: [{ type: Input }],
|
342 | value: [{ type: Input }]
|
343 | };
|
344 | class OrderListModule {
|
345 | }
|
346 | OrderListModule.decorators = [
|
347 | { type: NgModule, args: [{
|
348 | imports: [CommonModule, ButtonModule, SharedModule, RippleModule],
|
349 | exports: [OrderList, SharedModule],
|
350 | declarations: [OrderList]
|
351 | },] }
|
352 | ];
|
353 |
|
354 |
|
355 |
|
356 |
|
357 |
|
358 | export { OrderList, OrderListModule };
|
359 |
|