UNPKG

7.44 kBJavaScriptView Raw
1import { Component, Input, Output, EventEmitter, forwardRef, TemplateRef } from '@angular/core';
2import { NG_VALUE_ACCESSOR } from '@angular/forms';
3import { DraggableItemService } from './draggable-item.service';
4export class SortableComponent {
5 constructor(transfer) {
6 /** class name for items wrapper */
7 this.wrapperClass = '';
8 /** style object for items wrapper */
9 this.wrapperStyle = {};
10 /** class name for item */
11 this.itemClass = '';
12 /** style object for item */
13 this.itemStyle = {};
14 /** class name for active item */
15 this.itemActiveClass = '';
16 /** style object for active item */
17 this.itemActiveStyle = {};
18 /** class name for placeholder */
19 this.placeholderClass = '';
20 /** style object for placeholder */
21 this.placeholderStyle = {};
22 /** placeholder item which will be shown if collection is empty */
23 this.placeholderItem = '';
24 /** fired on array change (reordering, insert, remove), same as <code>ngModelChange</code>.
25 * Returns new items collection as a payload.
26 */
27 this.onChange = new EventEmitter();
28 this.showPlaceholder = false;
29 this.activeItem = -1;
30 // eslint-disable-next-line @typescript-eslint/no-explicit-any
31 this.onTouched = Function.prototype;
32 // eslint-disable-next-line @typescript-eslint/no-explicit-any
33 this.onChanged = Function.prototype;
34 this._items = [];
35 this.transfer = transfer;
36 this.currentZoneIndex = SortableComponent.globalZoneIndex++;
37 this.transfer
38 .onCaptureItem()
39 .subscribe((item) => this.onDrop(item));
40 }
41 get items() {
42 return this._items;
43 }
44 set items(value) {
45 this._items = value;
46 const out = this.items.map((x) => x.initData);
47 this.onChanged(out);
48 this.onChange.emit(out);
49 }
50 onItemDragstart(event, item, i) {
51 this.initDragstartEvent(event);
52 this.onTouched();
53 this.transfer.dragStart({
54 event,
55 item,
56 i,
57 initialIndex: i,
58 lastZoneIndex: this.currentZoneIndex,
59 overZoneIndex: this.currentZoneIndex
60 });
61 }
62 onItemDragover(event, i) {
63 if (!this.transfer.getItem()) {
64 return;
65 }
66 event.preventDefault();
67 const dragItem = this.transfer.captureItem(this.currentZoneIndex, this.items.length);
68 // eslint-disable-next-line @typescript-eslint/no-explicit-any
69 let newArray = [];
70 if (!dragItem) {
71 return;
72 }
73 if (!this.items.length) {
74 newArray = [dragItem.item];
75 }
76 else if (dragItem.i > i) {
77 newArray = [
78 ...this.items.slice(0, i),
79 dragItem.item,
80 ...this.items.slice(i, dragItem.i),
81 ...this.items.slice(dragItem.i + 1)
82 ];
83 }
84 else {
85 // this.draggedItem.i < i
86 newArray = [
87 ...this.items.slice(0, dragItem.i),
88 ...this.items.slice(dragItem.i + 1, i + 1),
89 dragItem.item,
90 ...this.items.slice(i + 1)
91 ];
92 }
93 this.items = newArray;
94 dragItem.i = i;
95 this.activeItem = i;
96 this.updatePlaceholderState();
97 }
98 cancelEvent(event) {
99 if (!this.transfer.getItem() || !event) {
100 return;
101 }
102 event.preventDefault();
103 }
104 onDrop(item) {
105 if (item &&
106 item.overZoneIndex !== this.currentZoneIndex &&
107 item.lastZoneIndex === this.currentZoneIndex) {
108 this.items = this.items.filter((x, i) => i !== item.i);
109 this.updatePlaceholderState();
110 }
111 this.resetActiveItem();
112 }
113 resetActiveItem(event) {
114 this.cancelEvent(event);
115 this.activeItem = -1;
116 }
117 registerOnChange(callback) {
118 this.onChanged = callback;
119 }
120 registerOnTouched(callback) {
121 this.onTouched = callback;
122 }
123 // eslint-disable-next-line @typescript-eslint/no-explicit-any
124 writeValue(value) {
125 if (value) {
126 // eslint-disable-next-line @typescript-eslint/no-explicit-any
127 this.items = value.map((x, i) => ({
128 id: i,
129 initData: x,
130 value: this.fieldName ? x[this.fieldName] : x
131 }));
132 }
133 else {
134 this.items = [];
135 }
136 this.updatePlaceholderState();
137 }
138 updatePlaceholderState() {
139 this.showPlaceholder = !this._items.length;
140 }
141 getItemStyle(isActive) {
142 return isActive
143 ? Object.assign({}, this.itemStyle, this.itemActiveStyle)
144 : this.itemStyle;
145 }
146 initDragstartEvent(event) {
147 var _a;
148 // it is necessary for mozilla
149 // data type should be 'Text' instead of 'text/plain' to keep compatibility
150 // with IE
151 (_a = event.dataTransfer) === null || _a === void 0 ? void 0 : _a.setData('Text', 'placeholder');
152 }
153}
154SortableComponent.globalZoneIndex = 0;
155SortableComponent.decorators = [
156 { type: Component, args: [{
157 selector: 'bs-sortable',
158 exportAs: 'bs-sortable',
159 template: `
160<div
161 [ngClass]="wrapperClass"
162 [ngStyle]="wrapperStyle"
163 (dragover)="cancelEvent($event)"
164 (dragenter)="cancelEvent($event)"
165 (drop)="resetActiveItem($event)"
166 (mouseleave)="resetActiveItem($event)">
167 <div
168 *ngIf="showPlaceholder"
169 [ngClass]="placeholderClass"
170 [ngStyle]="placeholderStyle"
171 (dragover)="onItemDragover($event, 0)"
172 (dragenter)="cancelEvent($event)"
173 >{{placeholderItem}}</div>
174 <div
175 *ngFor="let item of items; let i=index;"
176 [ngClass]="[ itemClass, i === activeItem ? itemActiveClass : '' ]"
177 [ngStyle]="getItemStyle(i === activeItem)"
178 draggable="true"
179 (dragstart)="onItemDragstart($event, item, i)"
180 (dragend)="resetActiveItem($event)"
181 (dragover)="onItemDragover($event, i)"
182 (dragenter)="cancelEvent($event)"
183 aria-dropeffect="move"
184 [attr.aria-grabbed]="i === activeItem"
185 ><ng-template [ngTemplateOutlet]="itemTemplate || defItemTemplate"
186 [ngTemplateOutletContext]="{item:item, index: i}"></ng-template></div>
187</div>
188
189<ng-template #defItemTemplate let-item="item">{{item.value}}</ng-template>
190`,
191 providers: [
192 {
193 provide: NG_VALUE_ACCESSOR,
194 useExisting: forwardRef(() => SortableComponent),
195 multi: true
196 }
197 ]
198 },] }
199];
200SortableComponent.ctorParameters = () => [
201 { type: DraggableItemService }
202];
203SortableComponent.propDecorators = {
204 fieldName: [{ type: Input }],
205 wrapperClass: [{ type: Input }],
206 wrapperStyle: [{ type: Input }],
207 itemClass: [{ type: Input }],
208 itemStyle: [{ type: Input }],
209 itemActiveClass: [{ type: Input }],
210 itemActiveStyle: [{ type: Input }],
211 placeholderClass: [{ type: Input }],
212 placeholderStyle: [{ type: Input }],
213 placeholderItem: [{ type: Input }],
214 itemTemplate: [{ type: Input }],
215 onChange: [{ type: Output }]
216};
217//# sourceMappingURL=sortable.component.js.map
\No newline at end of file