@progress/kendo-angular-sortable 21.4.1 → 22.0.0-develop.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.
@@ -1,1204 +0,0 @@
1
- /**-----------------------------------------------------------------------------------------
2
- * Copyright © 2026 Progress Software Corporation. All rights reserved.
3
- * Licensed under commercial license. See LICENSE.md in the project root for more information
4
- *-------------------------------------------------------------------------------------------*/
5
- /* eslint-disable @typescript-eslint/no-explicit-any */
6
- import { Component, Input, Output, QueryList, ContentChildren, ViewChild, ViewChildren, TemplateRef, ElementRef, EventEmitter, HostBinding, NgZone, ChangeDetectorRef, forwardRef, Renderer2 } from '@angular/core';
7
- import { Subject, merge } from 'rxjs';
8
- import { isDocumentAvailable, isChanged, Keys, EventsOutsideAngularDirective, normalizeKeys } from '@progress/kendo-angular-common';
9
- import { getAllFocusableChildren, keepFocusWithinComponent, relativeContextElement } from './util';
10
- import { filter, take } from 'rxjs/operators';
11
- import { LocalizationService, L10N_PREFIX } from '@progress/kendo-angular-l10n';
12
- import { validatePackage } from '@progress/kendo-licensing';
13
- import { packageMetadata } from './package-metadata';
14
- import { SortableService } from './sortable.service';
15
- import { DraggableDirective } from './draggable.directive';
16
- import { SortableContainer } from './sortable-container';
17
- import { ItemTemplateDirective, PlaceholderTemplateDirective } from './item-template.directive';
18
- import { NavigateEvent } from './navigate-event';
19
- import { DraggableEvent } from './draggable-event';
20
- import { DragStartEvent, DragOverEvent, DragEndEvent } from './sortable-events';
21
- import { Draggable } from '@progress/kendo-draggable';
22
- import { NgClass, NgStyle, NgTemplateOutlet } from '@angular/common';
23
- import * as i0 from "@angular/core";
24
- import * as i1 from "@progress/kendo-angular-l10n";
25
- import * as i2 from "./sortable.service";
26
- const KEY_SHORTCUTS = 'Control+ArrowLeft Control+ArrowRight Meta+ArrowLeft Meta+ArrowRight';
27
- /**
28
- * Represents the [Kendo UI Sortable component for Angular]({% slug overview_sortable %}).
29
- *
30
- * @example
31
- * ```html
32
- * <kendo-sortable [data]="['Item 1', 'Item 2', 'Item 3']"></kendo-sortable>
33
- * ```
34
- */
35
- /**
36
- * Represents the Kendo UI Sortable component for Angular.
37
- */
38
- export class SortableComponent {
39
- ngZone;
40
- renderer;
41
- changeDetector;
42
- localization;
43
- cdr;
44
- /**
45
- * Specifies the tab index of the Sortable component.
46
- */
47
- tabIndex = null;
48
- /**
49
- * Configures how the Sortable component tracks changes in its items collection.
50
- */
51
- trackBy = (_, idx) => idx;
52
- /**
53
- * Sets an array of any data that is used as a data source for the Sortable.
54
- */
55
- set data(data) {
56
- this._data = data;
57
- //Cache each _data item instance locally to avoid repaint due to the ngTemplateOutletContext (generated by itemData)
58
- //This prevents destroying the kendoDraggable instance, which otherwise leads to losing the dragEnd event
59
- //due to non-exisitng HTML element
60
- this.cacheData();
61
- }
62
- get data() {
63
- return this._data;
64
- }
65
- /**
66
- * Sets a boolean value that determines whether the Sortable items are navigable using the keyboard. [See example]({% slug keyboard_navigation_sortable %}).
67
- * @default true
68
- */
69
- navigable = true;
70
- /**
71
- * Enables or disables built-in animations.
72
- * @default false
73
- */
74
- animation = false;
75
- /**
76
- * Sets an array of integers that represent the indexes of the disabled items from the data array. [See example](slug:items_sortable#toc-disabling-items).
77
- */
78
- disabledIndexes = [];
79
- /**
80
- * Sets a string that represents the name of the zone to which the Sortable belongs
81
- * ([see example](slug:items_sortable#toc-transferring-of-items)). Items can be transferred
82
- * between Sortables in the same zone.
83
- */
84
- zone = undefined;
85
- /**
86
- * Defines the zones from which items can be transferred onto the current Sortable component
87
- * ([see example](slug:items_sortable#toc-transferring-of-items)). If the `acceptZones` property
88
- * of the target Sortable is set, you can transfer items between Sortables in different zones.
89
- */
90
- acceptZones = undefined;
91
- /**
92
- * Represents the CSS styles applied to each Sortable item.
93
- */
94
- itemStyle = {};
95
- /**
96
- * Defines the CSS styles applied to an empty item ([see example]({% slug templates_sortable %})).
97
- */
98
- emptyItemStyle = undefined;
99
- /**
100
- * Defines the CSS styles which are applied to the currently dragged item ([see example]({% slug templates_sortable %})).
101
- */
102
- activeItemStyle = undefined;
103
- /**
104
- * Defines the CSS styles which are applied to all disabled items.
105
- */
106
- disabledItemStyle = undefined;
107
- /**
108
- * Defines the class which is applied to each Sortable item.
109
- */
110
- itemClass = "";
111
- /**
112
- * Defines the class which is applied to the active Sortable item.
113
- */
114
- activeItemClass = null;
115
- /**
116
- * Defines the class which is applied to the empty item when the Sortable has empty data.
117
- */
118
- emptyItemClass = null;
119
- /**
120
- * Defines the class which is applied to each disabled Sortable item.
121
- */
122
- disabledItemClass = null;
123
- /**
124
- * Sets the text message that will be displayed when the Sortable has no items.
125
- */
126
- emptyText = "Empty";
127
- /**
128
- * @hidden
129
- */
130
- defaultTemplateRef = null;
131
- /**
132
- * Defines the template that will be used for rendering the items.
133
- * @hidden
134
- */
135
- itemTemplateDirectiveRef = null;
136
- /**
137
- * Defines the template that will be used for rendering the placeholder.
138
- * @hidden
139
- */
140
- placeholderTemplateDirectiveRef = null;
141
- itemWrappers = new QueryList();
142
- draggables;
143
- noDataContainer;
144
- hint;
145
- /**
146
- * Fires when the dragging of an item is started.
147
- */
148
- dragStart = new EventEmitter();
149
- /**
150
- * Fires when the dragging of an item is completed.
151
- */
152
- dragEnd = new EventEmitter();
153
- /**
154
- * Fires while the dragging of an item is in progress.
155
- */
156
- dragOver = new EventEmitter();
157
- /**
158
- * Fires when dragging an item outside of the component.
159
- */
160
- dragLeave = new EventEmitter();
161
- /**
162
- * Fires while the moving an item from one position to another.
163
- */
164
- dataMove = new EventEmitter();
165
- /**
166
- * Fires when a new item is added to the Sortable.
167
- */
168
- dataAdd = new EventEmitter();
169
- /**
170
- * Fires when an item is removed from the Sortable.
171
- */
172
- dataRemove = new EventEmitter();
173
- /**
174
- * Fires when navigating using the keyboard.
175
- */
176
- navigate = new EventEmitter();
177
- /**
178
- * The index of the currently focused item.
179
- * If no item is focused, set to `-1`.
180
- */
181
- activeIndex = -1;
182
- get touchAction() {
183
- return "none";
184
- }
185
- get dir() {
186
- return this.direction;
187
- }
188
- hostRole = 'list';
189
- /**
190
- * Flag indicating if the component is currently playing animations.
191
- * @hidden
192
- */
193
- animating = false;
194
- /**
195
- * The index of the currently dragged item.
196
- */
197
- dragIndex = -1;
198
- /**
199
- * The index of the item above which the dragged item is.
200
- */
201
- dragOverIndex = -1;
202
- onDragStartSubject = new Subject();
203
- onDragOverSubject = new Subject();
204
- onDragLeaveSubject = new Subject();
205
- onDragEndSubject = new Subject();
206
- /**
207
- * The SortableComponent's HTMLElement.
208
- */
209
- wrapper;
210
- /**
211
- * The location of the hint indicator when dragging on mobile devices.
212
- */
213
- hintLocation = null;
214
- id;
215
- itemTemplateRef;
216
- placeholderTemplateRef;
217
- _data;
218
- _localData = [];
219
- /**
220
- * @hidden
221
- */
222
- ariaKeyShortcuts = KEY_SHORTCUTS;
223
- localizationChangeSubscription;
224
- dragStartSubscription;
225
- dragOverSubscription;
226
- dragLeaveSubscription;
227
- dragEndSubscription;
228
- childrenTabindexSubscription;
229
- focusableItems = [];
230
- animationDuration = 300;
231
- afterKeyPress = false;
232
- sortableService = null;
233
- _hideActiveItem = false;
234
- prevActiveIndex = 0;
235
- direction;
236
- _animating;
237
- draggable;
238
- offsetParent;
239
- setItemData(data, i) {
240
- this._localData[i].item = data.item;
241
- this._localData[i].index = data.index;
242
- this._localData[i].hidden = data.hidden;
243
- }
244
- /**
245
- * @hidden
246
- */
247
- itemTemplate(index) {
248
- let template = this.itemTemplateRef;
249
- if (index === this.dragOverIndex) {
250
- template = this.placeholderTemplateRef;
251
- }
252
- else if (index === this.dragIndex) {
253
- template = this.itemTemplateRef;
254
- }
255
- return template;
256
- }
257
- constructor(ngZone, renderer, changeDetector, localization, wrapper, sortableService, cdr) {
258
- this.ngZone = ngZone;
259
- this.renderer = renderer;
260
- this.changeDetector = changeDetector;
261
- this.localization = localization;
262
- this.cdr = cdr;
263
- validatePackage(packageMetadata);
264
- this.wrapper = wrapper.nativeElement;
265
- this.direction = localization.rtl ? 'rtl' : 'ltr';
266
- this.sortableService = sortableService;
267
- this.subscribeEvents();
268
- }
269
- ngOnInit() {
270
- if (!this.data) {
271
- this.data = [];
272
- }
273
- this.id = this.sortableService.registerComponent(this);
274
- this.dragIndex = -1;
275
- const display = "display";
276
- if (this.activeItemStyle && !this.activeItemStyle[display]) {
277
- this.activeItemStyle[display] = "";
278
- }
279
- if (!this.itemStyle[display]) {
280
- this.itemStyle[display] = "";
281
- }
282
- if (this.wrapper) {
283
- this.draggable = new Draggable({
284
- press: (e) => this.sortableService.onPress(e),
285
- drag: (e) => this.sortableService.onDrag(e),
286
- release: (e) => this.sortableService.onRelease(e)
287
- });
288
- this.ngZone.runOutsideAngular(() => {
289
- this.draggable.bindTo(this.wrapper);
290
- });
291
- }
292
- }
293
- ngAfterViewInit() {
294
- if (this.navigable) {
295
- this.setInitialItemTabindex();
296
- this.setFocusableChildren();
297
- }
298
- this.childrenTabindexSubscription = this.itemWrappers.changes.subscribe(() => {
299
- if (this.navigable) {
300
- this.setInitialItemTabindex();
301
- this.setFocusableChildren();
302
- }
303
- });
304
- }
305
- ngOnChanges(changes) {
306
- if (this.data && isChanged('disabledIndexes', changes, false)) {
307
- this.cacheData();
308
- }
309
- }
310
- ngOnDestroy() {
311
- this.unsubscribeEvents();
312
- this.sortableService.unregisterComponent(this.id);
313
- if (this.draggable) {
314
- this.draggable.destroy();
315
- }
316
- }
317
- ngAfterContentInit() {
318
- this.itemTemplateRef = this.itemTemplateDirectiveRef.first || this.defaultTemplateRef.first;
319
- this.placeholderTemplateRef = this.placeholderTemplateDirectiveRef.first || this.defaultTemplateRef.first;
320
- }
321
- ngAfterViewChecked() {
322
- if (this.navigable) {
323
- if (this.afterKeyPress) {
324
- const elems = this.itemWrappers.toArray();
325
- if (elems && elems.length > 0 && this.activeIndex > -1) {
326
- const currentItem = elems[this.activeIndex].nativeElement;
327
- const prevItem = elems[this.prevActiveIndex].nativeElement;
328
- this.renderer.setAttribute(prevItem, 'tabindex', '-1');
329
- this.renderer.setAttribute(currentItem, 'tabindex', '0');
330
- currentItem.focus();
331
- }
332
- }
333
- this.afterKeyPress = false;
334
- }
335
- }
336
- /**
337
- * @hidden
338
- */
339
- setFocusableChildren() {
340
- this.itemWrappers.toArray().forEach((item) => {
341
- const itemEl = item.nativeElement;
342
- const focusableChildren = getAllFocusableChildren(itemEl);
343
- if (focusableChildren.length > 0) {
344
- this.focusableItems.push(focusableChildren);
345
- focusableChildren.forEach(focusableChild => {
346
- this.renderer.setAttribute(focusableChild, 'tabindex', '-1');
347
- });
348
- }
349
- });
350
- }
351
- /**
352
- * @hidden
353
- */
354
- updateCacheIndices() {
355
- this._localData.forEach((item, index) => {
356
- item.index = index;
357
- });
358
- }
359
- /**
360
- * @hidden
361
- */
362
- cacheData() {
363
- this._localData = [];
364
- this._data.forEach((item, index) => {
365
- this._localData.push({ item: item, active: false, disabled: !this.itemEnabled(index), index: index, hidden: false });
366
- });
367
- }
368
- /**
369
- * @hidden
370
- */
371
- startDrag(event) {
372
- const startEvent = new DraggableEvent(event);
373
- this.onDragStartSubject.next(startEvent);
374
- const prevented = startEvent.isDefaultPrevented();
375
- if (!prevented) {
376
- this.offsetParent = relativeContextElement(this.wrapper);
377
- }
378
- return prevented;
379
- }
380
- /**
381
- * @hidden
382
- */
383
- setInitialItemTabindex() {
384
- this.itemWrappers.toArray().forEach((item, index) => {
385
- if (this.itemEnabled(index)) {
386
- const isFirstItem = index === 0 ? 0 : -1;
387
- const tabIndexValue = `${this.navigable ? this.tabIndex || isFirstItem : this.tabIndex}`;
388
- const hasItemTabindex = item.nativeElement.getAttribute('tabindex');
389
- if (!hasItemTabindex) {
390
- this.renderer.setAttribute(item.nativeElement, 'tabindex', tabIndexValue);
391
- }
392
- }
393
- });
394
- }
395
- /**
396
- * @hidden
397
- */
398
- drag(event) {
399
- const dragEvent = new DraggableEvent(event);
400
- this.onDragOverSubject.next(dragEvent);
401
- return dragEvent.isDefaultPrevented();
402
- }
403
- /**
404
- * @hidden
405
- */
406
- leave(event) {
407
- const leaveEvent = new DraggableEvent(event);
408
- this.onDragLeaveSubject.next(leaveEvent);
409
- return leaveEvent.isDefaultPrevented();
410
- }
411
- /**
412
- * @hidden
413
- */
414
- endDrag(event) {
415
- const endEvent = new DraggableEvent(event);
416
- this.onDragEndSubject.next(endEvent);
417
- return endEvent.isDefaultPrevented();
418
- }
419
- /**
420
- * @hidden
421
- */
422
- hintVisible() {
423
- return this.dragIndex >= 0 && this.hintLocation && this === this.sortableService.getSource();
424
- }
425
- /**
426
- * @hidden
427
- */
428
- currentItemStyle(index) {
429
- if (index === -1) {
430
- return this.emptyItemStyle ? this.emptyItemStyle : this.itemStyle;
431
- }
432
- if (!this.itemEnabled(index) && this.disabledItemStyle) {
433
- return this.disabledItemStyle;
434
- }
435
- if (index === this.dragIndex || (this.dragIndex === -1 && index === this.activeIndex)) {
436
- if (this.hideActiveItem) {
437
- return { "display": "none" };
438
- }
439
- if (this.activeItemStyle) {
440
- return this.activeItemStyle;
441
- }
442
- }
443
- return this.itemStyle;
444
- }
445
- /**
446
- * @hidden
447
- */
448
- currentItemClass(index) {
449
- if (index === -1) {
450
- return this.emptyItemClass ? this.emptyItemClass : this.itemClass;
451
- }
452
- if (!this.itemEnabled(index) && this.disabledItemClass) {
453
- return this.disabledItemClass;
454
- }
455
- if ((index === this.dragIndex || this.dragIndex === -1 && index === this.activeIndex) && this.activeItemClass) {
456
- return this.activeItemClass;
457
- }
458
- return this.itemClass;
459
- }
460
- /**
461
- * @hidden
462
- */
463
- hintStyle() {
464
- const position = {
465
- "left": this.hintLocation.x + 10 + "px",
466
- "position": "fixed",
467
- "top": this.hintLocation.y + 10 + "px"
468
- };
469
- const style = {};
470
- Object.assign(style, this.currentItemStyle(this.dragIndex), position);
471
- return style;
472
- }
473
- /**
474
- * @hidden
475
- */
476
- itemEnabled(index) {
477
- return this.disabledIndexes.indexOf(index) === -1;
478
- }
479
- /**
480
- * @hidden
481
- */
482
- acceptDragFrom(sortableComponent) {
483
- if (this.acceptZones === undefined) {
484
- return (this.zone === sortableComponent.zone);
485
- }
486
- else if (sortableComponent.zone !== undefined) {
487
- return (this.acceptZones.indexOf(sortableComponent.zone) !== -1);
488
- }
489
- return false;
490
- }
491
- /**
492
- * @hidden
493
- */
494
- ariaDropEffect(index) {
495
- return this.itemEnabled(index) ? "move" : "none";
496
- }
497
- /**
498
- * @hidden
499
- */
500
- focusHandler(index) {
501
- if (this.navigable) {
502
- this.activeIndex = index;
503
- }
504
- }
505
- /**
506
- * @hidden
507
- */
508
- blurHandler() {
509
- if (this.navigable && !this.afterKeyPress) {
510
- this.prevActiveIndex = this.activeIndex;
511
- this.activeIndex = -1;
512
- }
513
- }
514
- /**
515
- * @hidden
516
- */
517
- onArrowHandler(event, keyCode) {
518
- const leftKey = this.direction === 'rtl' ? Keys.ArrowRight : Keys.ArrowLeft;
519
- const dir = keyCode === Keys.ArrowUp || keyCode === leftKey ? -1 : 1;
520
- const limit = this.data.length - 1;
521
- let targetIndex = this.activeIndex + dir;
522
- while (!this.itemEnabled(targetIndex) && targetIndex <= limit) {
523
- targetIndex += dir;
524
- }
525
- targetIndex = Math.min(Math.max(targetIndex, 0), limit);
526
- this.prevActiveIndex = this.activeIndex;
527
- if (!this.itemEnabled(targetIndex)) {
528
- return;
529
- }
530
- const ctrl = event.ctrlKey || event.metaKey;
531
- const navigateEvent = new NavigateEvent({ index: targetIndex, oldIndex: this.activeIndex, ctrlKey: ctrl });
532
- this.navigate.emit(navigateEvent);
533
- if (!navigateEvent.isDefaultPrevented()) {
534
- this.activeIndex = targetIndex;
535
- }
536
- this.dragIndex = -1;
537
- this.dragOverIndex = -1;
538
- event.stopPropagation();
539
- event.preventDefault();
540
- this.afterKeyPress = true;
541
- }
542
- /**
543
- * @hidden
544
- */
545
- onEnterHandler(item) {
546
- const focusableItems = this.focusableItems[this.activeIndex];
547
- focusableItems.forEach(focusableItem => {
548
- this.renderer.setAttribute(focusableItem, 'tabindex', '0');
549
- });
550
- this.renderer.setAttribute(item, 'tabindex', '-1');
551
- focusableItems[0].focus();
552
- }
553
- /**
554
- * @hidden
555
- */
556
- onEscapeHandler(event) {
557
- const focusableItems = this.focusableItems[this.prevActiveIndex];
558
- const item = (event?.target).closest('[data-sortable-item]');
559
- focusableItems.forEach(focusableItem => {
560
- this.renderer.setAttribute(focusableItem, 'tabindex', '-1');
561
- });
562
- this.renderer.setAttribute(item, 'tabindex', '0');
563
- item.focus();
564
- }
565
- /**
566
- * @hidden
567
- */
568
- keydownHandler = (event) => {
569
- if (!this.navigable) {
570
- return;
571
- }
572
- this.cdr.markForCheck();
573
- const targetIsWrapper = this.itemWrappers.toArray().some((item) => item.nativeElement === event.target);
574
- const index = this.activeIndex === -1 ? this.prevActiveIndex : this.activeIndex;
575
- const item = this.itemWrappers.toArray()[index]?.nativeElement;
576
- const isItemFocused = document.activeElement === item;
577
- const hasFocus = this.activeIndex !== -1;
578
- const keyCode = normalizeKeys(event);
579
- if (keyCode === Keys.Tab && !isItemFocused) {
580
- keepFocusWithinComponent(event, item);
581
- return;
582
- }
583
- if (keyCode === Keys.Escape && this.focusableItems.length > 0 && this.activeIndex === -1) {
584
- this.onEscapeHandler(event);
585
- return;
586
- }
587
- if (!targetIsWrapper) {
588
- return;
589
- }
590
- if (this.navigable && hasFocus) {
591
- const isArrowKey = [Keys.ArrowUp, Keys.ArrowDown, Keys.ArrowLeft, Keys.ArrowRight].includes(keyCode);
592
- if (isArrowKey) {
593
- this.ngZone.run(() => this.onArrowHandler(event, keyCode));
594
- }
595
- else if (keyCode === Keys.Enter && isItemFocused && this.focusableItems.length > 0) {
596
- this.onEnterHandler(item);
597
- }
598
- }
599
- };
600
- /**
601
- * Removes the currently active item from the Data collection that the Sortable uses.
602
- */
603
- removeDataItem(index) {
604
- this.dragIndex = -1;
605
- this.dragOverIndex = -1;
606
- this._localData.splice(index, 1);
607
- this.data.splice(index, 1);
608
- this.updateCacheIndices();
609
- }
610
- /**
611
- * Sets a boolean value that indicates whether the item will be hidden or not.
612
- * @hidden
613
- */
614
- hideItem(index, hidden = true) {
615
- this._localData[index].hidden = hidden;
616
- }
617
- /**
618
- * Gets or sets a boolean value that indicates whether the currently dragged item will be hidden.
619
- *
620
- * If the currently dragged item is hidden, returns `true`.
621
- * If the currently dragged item is visible, returns `false`.
622
- */
623
- get hideActiveItem() {
624
- return this._hideActiveItem;
625
- }
626
- set hideActiveItem(value) {
627
- this.activeIndex = -1;
628
- this._hideActiveItem = value;
629
- }
630
- /**
631
- * Clears the active item.
632
- * An active item is the one that is currently focused when the user navigates with the keyboard.
633
- */
634
- clearActiveItem() {
635
- if (this.navigable) {
636
- this.fixFocus();
637
- }
638
- else {
639
- this.activeIndex = -1;
640
- }
641
- this.dragIndex = -1;
642
- }
643
- /**
644
- * Returns the currently active item when the user navigates with the keyboard.
645
- * @return - The data item which is currently active.
646
- */
647
- getActiveItem() {
648
- if (this.data && this.dragIndex >= 0 && this.dragIndex < this.data.length) {
649
- return this.data[this.dragIndex];
650
- }
651
- }
652
- /**
653
- * Inserts a new data item at a particular index in the Sortable component.
654
- * @param dataItem - The data item.
655
- * @param index - The index at which the data item is inserted.
656
- */
657
- addDataItem(dataItem, index) {
658
- const originDraggable = this.sortableService.originDraggable;
659
- if (originDraggable && originDraggable.parent === this) {
660
- const animation = this.animation;
661
- this.hideItem(originDraggable.index, false);
662
- this.animation = false;
663
- this.moveItem(originDraggable.index, index);
664
- this.animation = animation;
665
- }
666
- else {
667
- this.data.splice(index, 0, dataItem);
668
- this._localData.splice(index, 0, { item: dataItem, active: false, disabled: !this.itemEnabled(index), index: index, hidden: false });
669
- this.updateCacheIndices();
670
- }
671
- this.dragIndex = index;
672
- this.dragOverIndex = index;
673
- this.ngZone.onStable.pipe(take(1)).subscribe(() => {
674
- this.sortableService.target = this;
675
- this.sortableService.setSource(this);
676
- this.sortableService.activeDraggable = this.draggables.toArray()[index];
677
- this.sortableService.lastDraggable = null;
678
- });
679
- }
680
- /**
681
- * Moves a data item from one index to another in the Sortable component.
682
- * @param fromIndex - The data item's index.
683
- * @param toIndex - The index which the data item should be moved to. Item currently sitting at that index is pushed back one position.
684
- */
685
- moveItem(fromIndex, toIndex) {
686
- if (toIndex === fromIndex) {
687
- return;
688
- }
689
- let dragIndex = fromIndex;
690
- const d = toIndex > dragIndex ? 1 : -1;
691
- const originalIndexAnimate = dragIndex;
692
- const toAnimate = [];
693
- let prevIndex = dragIndex;
694
- let tmp;
695
- while (dragIndex !== toIndex) {
696
- dragIndex += d;
697
- if (this.itemEnabled(dragIndex) || dragIndex === toIndex) {
698
- if (this.animation) {
699
- toAnimate.push({ next: dragIndex, prev: prevIndex });
700
- }
701
- tmp = this._localData[prevIndex].index;
702
- this._localData[prevIndex].index = this._localData[dragIndex].index;
703
- this._localData[dragIndex].index = tmp;
704
- tmp = this._localData[prevIndex];
705
- this._localData[prevIndex] = this._localData[dragIndex];
706
- this._localData[dragIndex] = tmp;
707
- tmp = this.data[prevIndex];
708
- this.data[prevIndex] = this.data[dragIndex];
709
- this.data[dragIndex] = tmp;
710
- prevIndex = dragIndex;
711
- }
712
- }
713
- this.dragIndex = dragIndex;
714
- this.dragOverIndex = dragIndex;
715
- this.activeIndex = dragIndex;
716
- if (this.focusableItems.length > 0) {
717
- this.swapFocusableChildren(fromIndex, toIndex);
718
- }
719
- if (this.animation) {
720
- setTimeout(() => {
721
- toAnimate.push({ next: originalIndexAnimate, prev: dragIndex });
722
- this.animating = true;
723
- this.animate(toAnimate);
724
- });
725
- }
726
- this.ngZone.onStable.pipe(take(1)).subscribe(() => {
727
- this.sortableService.activeDraggable = this.draggables.toArray()[dragIndex];
728
- this.sortableService.lastDraggable = null;
729
- });
730
- }
731
- /**
732
- * @hidden
733
- */
734
- animate(draggables) {
735
- const itemArray = this.itemWrappers.toArray();
736
- const prevClientRect = [];
737
- const nextClientRect = [];
738
- clearTimeout(this._animating);
739
- for (let i = 0; i < draggables.length; i++) {
740
- prevClientRect.push(itemArray[draggables[i].prev].nativeElement.getBoundingClientRect());
741
- nextClientRect.push(itemArray[draggables[i].next].nativeElement.getBoundingClientRect());
742
- }
743
- for (let i = 0; i < draggables.length; i++) {
744
- const nextIndex = draggables[i].prev;
745
- const targetRect = nextClientRect[i];
746
- const currentRect = prevClientRect[i];
747
- const target = itemArray[nextIndex].nativeElement;
748
- this.applyAnimationStyle(target, 'transition', 'none');
749
- this.applyAnimationStyle(target, 'transform', 'translate3d('
750
- + (targetRect.left - currentRect.left).toString() + 'px,'
751
- + (targetRect.top - currentRect.top).toString() + 'px,0)');
752
- this.reflow(target);
753
- }
754
- for (let i = 0; i < draggables.length; i++) {
755
- const nextIndex = draggables[i].prev;
756
- const target = itemArray[nextIndex].nativeElement;
757
- this.applyAnimationStyle(target, 'transition', 'all ' + this.animationDuration + 'ms');
758
- this.applyAnimationStyle(target, 'transform', 'translate3d(0,0,0)');
759
- clearTimeout(target.animated);
760
- target.animated = setTimeout(() => {
761
- this.applyAnimationStyle(target, 'transition', '');
762
- this.applyAnimationStyle(target, 'transform', '');
763
- target.animated = false;
764
- }, this.animationDuration);
765
- }
766
- this._animating = setTimeout(() => {
767
- this.animating = false;
768
- }, this.animationDuration);
769
- }
770
- /**
771
- * @hidden
772
- */
773
- positionHintFromEvent(event) {
774
- const offset = this.parentOffset();
775
- this.hintLocation = event ? { x: event.clientX - offset.left, y: event.clientY - offset.top } : null;
776
- }
777
- /**
778
- * @hidden
779
- */
780
- parentOffset() {
781
- const offsetParent = this.offsetParent;
782
- if (offsetParent) {
783
- const rect = offsetParent.getBoundingClientRect();
784
- return {
785
- left: rect.left - offsetParent.scrollLeft,
786
- top: rect.top - offsetParent.scrollTop
787
- };
788
- }
789
- return { left: 0, top: 0 };
790
- }
791
- /**
792
- * @hidden
793
- */
794
- markForCheck() {
795
- this.changeDetector.markForCheck();
796
- }
797
- /**
798
- * @hidden
799
- */
800
- reflow(element) {
801
- return element.offsetWidth;
802
- }
803
- /**
804
- * @hidden
805
- */
806
- swapFocusableChildren(firstItemIndex, secondItemIndex) {
807
- [this.focusableItems[firstItemIndex], this.focusableItems[secondItemIndex]] = [this.focusableItems[secondItemIndex], this.focusableItems[firstItemIndex]];
808
- }
809
- /**
810
- * @hidden
811
- */
812
- applyAnimationStyle(el, prop, val) {
813
- const style = el && el.style;
814
- if (style) {
815
- if (!(prop in style)) {
816
- prop = '-webkit-' + prop;
817
- }
818
- style[prop] = val;
819
- }
820
- }
821
- subscribeEvents() {
822
- this.localizationChangeSubscription = this.localization
823
- .changes
824
- .subscribe(({ rtl }) => this.direction = rtl ? 'rtl' : 'ltr');
825
- this.dragStartSubscription = this.onDragStartSubject
826
- .subscribe((event) => {
827
- if (!event.target) {
828
- return;
829
- }
830
- this.sortableService.originDraggable = event.target;
831
- this.sortableService.originIndex = event.target.index;
832
- this.sortableService.activeDraggable = event.target;
833
- this.sortableService.lastDraggable = event.target;
834
- this.sortableService.target = this;
835
- this.sortableService.setSource(this);
836
- const dragStartEvent = new DragStartEvent({ index: event.target.index });
837
- this.dragStart.emit(dragStartEvent);
838
- if (dragStartEvent.isDefaultPrevented()) {
839
- event.preventDefault();
840
- }
841
- else {
842
- if (!event.target.disabled) {
843
- if (this.sortableService.target) {
844
- this.sortableService.target.dragOverIndex = -1;
845
- this.sortableService.target.dragIndex = -1;
846
- }
847
- this.dragOverIndex = event.target.index;
848
- this.dragIndex = event.target.index;
849
- }
850
- }
851
- });
852
- this.dragOverSubscription = this.onDragOverSubject.pipe(filter(event => event.target && event.target.el.nativeElement.style.transition.length === 0), filter(() => {
853
- // Drag started from a disabled item
854
- return this.sortableService.originDraggable && !this.sortableService.originDraggable.disabled;
855
- }), filter(() => {
856
- return this.sortableService && this.acceptDragFrom(this.sortableService.getSource());
857
- }), filter((event) => {
858
- return event.target !== this.sortableService.lastDraggable;
859
- }))
860
- .subscribe((event) => {
861
- this.sortableService.lastDraggable = event.target;
862
- const originDraggable = this.sortableService.originDraggable;
863
- let targetIndex = event.target.index;
864
- if (originDraggable.hidden && originDraggable.parent === this) {
865
- if (originDraggable.index < event.target.index) {
866
- targetIndex = event.target.index - 1;
867
- }
868
- }
869
- this.sortableService.target = this;
870
- const oldIndex = this.sortableService.activeDraggable ? this.sortableService.activeDraggable.index : 0;
871
- const dragOverEvent = new DragOverEvent({ index: targetIndex, oldIndex: oldIndex });
872
- this.dragOver.emit(dragOverEvent);
873
- if (!dragOverEvent.isDefaultPrevented() && event.target && event.target.index >= 0) {
874
- this.dragOverIndex = event.target.index;
875
- this.placeHolderItemData(event.target);
876
- }
877
- });
878
- this.dragEndSubscription = this.onDragEndSubject
879
- .subscribe((event) => {
880
- const source = this.sortableService.getSource();
881
- if (!source) {
882
- return;
883
- }
884
- const target = this.sortableService.target;
885
- const index = event.target ? event.target.index : -1;
886
- const oldIndex = this.sortableService.originDraggable ? this.sortableService.originIndex : -1;
887
- this.hintLocation = null;
888
- const dragEndEvent = new DragEndEvent({ index: index, oldIndex: oldIndex });
889
- this.dragEnd.emit(dragEndEvent);
890
- if (!dragEndEvent.isDefaultPrevented()) {
891
- source.dragIndex = -1;
892
- source.dragOverIndex = -1;
893
- source.activeIndex = -1;
894
- if (target && target !== source) {
895
- target.dragIndex = -1;
896
- target.dragOverIndex = -1;
897
- }
898
- setTimeout(() => {
899
- this.sortableService.activeDraggable = null;
900
- this.sortableService.lastDraggable = null;
901
- this.sortableService.originDraggable = null;
902
- this.sortableService.target = null;
903
- this.sortableService.setSource(null);
904
- });
905
- }
906
- });
907
- this.dragLeaveSubscription = this.onDragLeaveSubject.pipe(filter((e) => {
908
- if (!isDocumentAvailable()) {
909
- return false;
910
- }
911
- return this.wrapper !== document.elementFromPoint(e.originalEvent.pageX, e.originalEvent.pageY);
912
- }), filter((_e) => {
913
- return !this.animating;
914
- }), filter(_ => this.sortableService.target && this.sortableService.target.dragOverIndex > -1))
915
- .subscribe(() => {
916
- this.dragLeave.emit({ index: this.sortableService.originDraggable.index });
917
- this.sortableService.lastDraggable = null;
918
- this.dragOverIndex = -1;
919
- this.sortableService.target = null;
920
- });
921
- }
922
- unsubscribeEvents() {
923
- if (this.localizationChangeSubscription) {
924
- this.localizationChangeSubscription.unsubscribe();
925
- }
926
- if (this.childrenTabindexSubscription) {
927
- this.childrenTabindexSubscription.unsubscribe();
928
- }
929
- this.dragStartSubscription.unsubscribe();
930
- this.dragOverSubscription.unsubscribe();
931
- this.dragEndSubscription.unsubscribe();
932
- this.dragLeaveSubscription.unsubscribe();
933
- }
934
- placeHolderItemData(draggable) {
935
- if (draggable.disabled) {
936
- return;
937
- }
938
- const target = this.sortableService.target;
939
- const source = this.sortableService.getSource();
940
- const originalData = Object.assign({}, this._localData[draggable.index]);
941
- const newData = source._localData[source.dragIndex];
942
- this.setItemData(newData, draggable.index);
943
- const endSub = source.onDragEndSubject.pipe(take(1)).subscribe(() => {
944
- this.setItemData(originalData, draggable.index);
945
- });
946
- const leaveSub = target.onDragLeaveSubject.pipe(take(1)).subscribe(() => {
947
- this.setItemData(originalData, draggable.index);
948
- });
949
- const overSub = merge(this.onDragOverSubject.pipe(filter(() => {
950
- return draggable.index !== this.dragOverIndex;
951
- })), this.onDragLeaveSubject).subscribe(() => {
952
- this.setItemData(originalData, draggable.index);
953
- endSub.unsubscribe();
954
- overSub.unsubscribe();
955
- leaveSub.unsubscribe();
956
- });
957
- }
958
- fixFocus() {
959
- if (this.itemWrappers) {
960
- const itemArray = this.itemWrappers.toArray();
961
- if (this.dragIndex > -1 && itemArray && itemArray.length > 0) {
962
- itemArray[this.dragIndex].nativeElement.focus();
963
- this.activeIndex = this.dragIndex;
964
- }
965
- }
966
- }
967
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SortableComponent, deps: [{ token: i0.NgZone }, { token: i0.Renderer2 }, { token: i0.ChangeDetectorRef }, { token: i1.LocalizationService }, { token: i0.ElementRef }, { token: i2.SortableService }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
968
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: SortableComponent, isStandalone: true, selector: "kendo-sortable", inputs: { tabIndex: "tabIndex", trackBy: "trackBy", data: "data", navigable: "navigable", animation: "animation", disabledIndexes: "disabledIndexes", zone: "zone", acceptZones: "acceptZones", itemStyle: "itemStyle", emptyItemStyle: "emptyItemStyle", activeItemStyle: "activeItemStyle", disabledItemStyle: "disabledItemStyle", itemClass: "itemClass", activeItemClass: "activeItemClass", emptyItemClass: "emptyItemClass", disabledItemClass: "disabledItemClass", emptyText: "emptyText", activeIndex: "activeIndex" }, outputs: { dragStart: "dragStart", dragEnd: "dragEnd", dragOver: "dragOver", dragLeave: "dragLeave", dataMove: "dataMove", dataAdd: "dataAdd", dataRemove: "dataRemove", navigate: "navigate" }, host: { properties: { "style.touch-action": "this.touchAction", "attr.dir": "this.dir", "attr.role": "this.hostRole" } }, providers: [
969
- LocalizationService,
970
- {
971
- provide: L10N_PREFIX,
972
- useValue: 'kendo.sortable'
973
- },
974
- {
975
- provide: SortableContainer,
976
- useExisting: forwardRef(() => SortableComponent)
977
- }
978
- ], queries: [{ propertyName: "defaultTemplateRef", predicate: TemplateRef }, { propertyName: "itemTemplateDirectiveRef", predicate: ItemTemplateDirective, read: TemplateRef }, { propertyName: "placeholderTemplateDirectiveRef", predicate: PlaceholderTemplateDirective, read: TemplateRef }], viewQueries: [{ propertyName: "noDataContainer", first: true, predicate: ["noDataRef"], descendants: true }, { propertyName: "hint", first: true, predicate: ["hint"], descendants: true }, { propertyName: "itemWrappers", predicate: ["itemWrapper"], descendants: true }, { propertyName: "draggables", predicate: DraggableDirective, descendants: true }], exportAs: ["kendoSortable"], usesOnChanges: true, ngImport: i0, template: `
979
- @for (item of _localData; track trackBy(i, item); let i = $index) {
980
- <div #itemWrapper
981
- kendoDraggable
982
- role="listitem"
983
- [attr.aria-grabbed]="i===dragIndex"
984
- [attr.aria-disabled]="!itemEnabled(i)"
985
- [attr.aria-keyshortcuts]="navigable ? ariaKeyShortcuts : ''"
986
- [attr.aria-dropeffect]="ariaDropEffect(i)"
987
- [attr.data-sortable-item] = "true"
988
- [attr.data-sortable-index]="i"
989
- [attr.data-sortable-id]="id"
990
- [index]="i"
991
- [hidden]="item.hidden"
992
- [disabled]="!itemEnabled(i)"
993
- [ngClass]="currentItemClass(i)"
994
- [ngStyle]="currentItemStyle(i)"
995
- (focus)="focusHandler(i)"
996
- (blur)="blurHandler()"
997
- [kendoEventsOutsideAngular]="{
998
- keydown: keydownHandler
999
- }"
1000
- >
1001
- @if (itemTemplateRef) {
1002
- <ng-container
1003
- [ngTemplateOutlet]="itemTemplate(i)"
1004
- [ngTemplateOutletContext]="item">
1005
- </ng-container>
1006
- }
1007
- @if (!itemTemplateRef) {
1008
- {{item.item}}
1009
- }
1010
- </div>
1011
- }
1012
-
1013
- @if (!_data.length || _localData.length === 1 && _localData[0].hidden) {
1014
- <ng-container #noDataRef>
1015
- <div
1016
- kendoDraggable
1017
- [index]="0"
1018
- [disabled]="true"
1019
- [attr.data-sortable-id]="id"
1020
- [attr.data-sortable-index]="0"
1021
- [ngStyle]="currentItemStyle(-1)"
1022
- [ngClass]="currentItemClass(-1)"
1023
- >{{emptyText}}</div>
1024
- </ng-container>
1025
- }
1026
- @if (hintVisible()) {
1027
- <div [ngStyle]="hintStyle()" [ngClass]="currentItemClass(dragIndex)">
1028
- @if (itemTemplateRef) {
1029
- <ng-container
1030
- [ngTemplateOutlet]="itemTemplateRef"
1031
- [ngTemplateOutletContext]="{item: _localData[dragIndex].item}">
1032
- </ng-container>
1033
- }
1034
- @if (!itemTemplateRef) {
1035
- {{_localData[dragIndex].item}}
1036
- }
1037
- </div>
1038
- }
1039
- `, isInline: true, dependencies: [{ kind: "directive", type: DraggableDirective, selector: "[kendoDraggable]", inputs: ["index", "disabled", "hidden"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: EventsOutsideAngularDirective, selector: "[kendoEventsOutsideAngular]", inputs: ["kendoEventsOutsideAngular", "scope"] }] });
1040
- }
1041
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SortableComponent, decorators: [{
1042
- type: Component,
1043
- args: [{
1044
- exportAs: 'kendoSortable',
1045
- providers: [
1046
- LocalizationService,
1047
- {
1048
- provide: L10N_PREFIX,
1049
- useValue: 'kendo.sortable'
1050
- },
1051
- {
1052
- provide: SortableContainer,
1053
- useExisting: forwardRef(() => SortableComponent)
1054
- }
1055
- ],
1056
- selector: 'kendo-sortable',
1057
- template: `
1058
- @for (item of _localData; track trackBy(i, item); let i = $index) {
1059
- <div #itemWrapper
1060
- kendoDraggable
1061
- role="listitem"
1062
- [attr.aria-grabbed]="i===dragIndex"
1063
- [attr.aria-disabled]="!itemEnabled(i)"
1064
- [attr.aria-keyshortcuts]="navigable ? ariaKeyShortcuts : ''"
1065
- [attr.aria-dropeffect]="ariaDropEffect(i)"
1066
- [attr.data-sortable-item] = "true"
1067
- [attr.data-sortable-index]="i"
1068
- [attr.data-sortable-id]="id"
1069
- [index]="i"
1070
- [hidden]="item.hidden"
1071
- [disabled]="!itemEnabled(i)"
1072
- [ngClass]="currentItemClass(i)"
1073
- [ngStyle]="currentItemStyle(i)"
1074
- (focus)="focusHandler(i)"
1075
- (blur)="blurHandler()"
1076
- [kendoEventsOutsideAngular]="{
1077
- keydown: keydownHandler
1078
- }"
1079
- >
1080
- @if (itemTemplateRef) {
1081
- <ng-container
1082
- [ngTemplateOutlet]="itemTemplate(i)"
1083
- [ngTemplateOutletContext]="item">
1084
- </ng-container>
1085
- }
1086
- @if (!itemTemplateRef) {
1087
- {{item.item}}
1088
- }
1089
- </div>
1090
- }
1091
-
1092
- @if (!_data.length || _localData.length === 1 && _localData[0].hidden) {
1093
- <ng-container #noDataRef>
1094
- <div
1095
- kendoDraggable
1096
- [index]="0"
1097
- [disabled]="true"
1098
- [attr.data-sortable-id]="id"
1099
- [attr.data-sortable-index]="0"
1100
- [ngStyle]="currentItemStyle(-1)"
1101
- [ngClass]="currentItemClass(-1)"
1102
- >{{emptyText}}</div>
1103
- </ng-container>
1104
- }
1105
- @if (hintVisible()) {
1106
- <div [ngStyle]="hintStyle()" [ngClass]="currentItemClass(dragIndex)">
1107
- @if (itemTemplateRef) {
1108
- <ng-container
1109
- [ngTemplateOutlet]="itemTemplateRef"
1110
- [ngTemplateOutletContext]="{item: _localData[dragIndex].item}">
1111
- </ng-container>
1112
- }
1113
- @if (!itemTemplateRef) {
1114
- {{_localData[dragIndex].item}}
1115
- }
1116
- </div>
1117
- }
1118
- `,
1119
- standalone: true,
1120
- imports: [DraggableDirective, NgClass, NgStyle, NgTemplateOutlet, EventsOutsideAngularDirective]
1121
- }]
1122
- }], ctorParameters: () => [{ type: i0.NgZone }, { type: i0.Renderer2 }, { type: i0.ChangeDetectorRef }, { type: i1.LocalizationService }, { type: i0.ElementRef }, { type: i2.SortableService }, { type: i0.ChangeDetectorRef }], propDecorators: { tabIndex: [{
1123
- type: Input
1124
- }], trackBy: [{
1125
- type: Input
1126
- }], data: [{
1127
- type: Input
1128
- }], navigable: [{
1129
- type: Input
1130
- }], animation: [{
1131
- type: Input
1132
- }], disabledIndexes: [{
1133
- type: Input
1134
- }], zone: [{
1135
- type: Input
1136
- }], acceptZones: [{
1137
- type: Input
1138
- }], itemStyle: [{
1139
- type: Input
1140
- }], emptyItemStyle: [{
1141
- type: Input
1142
- }], activeItemStyle: [{
1143
- type: Input
1144
- }], disabledItemStyle: [{
1145
- type: Input
1146
- }], itemClass: [{
1147
- type: Input
1148
- }], activeItemClass: [{
1149
- type: Input
1150
- }], emptyItemClass: [{
1151
- type: Input
1152
- }], disabledItemClass: [{
1153
- type: Input
1154
- }], emptyText: [{
1155
- type: Input
1156
- }], defaultTemplateRef: [{
1157
- type: ContentChildren,
1158
- args: [TemplateRef, { descendants: false }]
1159
- }], itemTemplateDirectiveRef: [{
1160
- type: ContentChildren,
1161
- args: [ItemTemplateDirective, { read: TemplateRef, descendants: false }]
1162
- }], placeholderTemplateDirectiveRef: [{
1163
- type: ContentChildren,
1164
- args: [PlaceholderTemplateDirective, { read: TemplateRef, descendants: false }]
1165
- }], itemWrappers: [{
1166
- type: ViewChildren,
1167
- args: ['itemWrapper']
1168
- }], draggables: [{
1169
- type: ViewChildren,
1170
- args: [DraggableDirective]
1171
- }], noDataContainer: [{
1172
- type: ViewChild,
1173
- args: ['noDataRef']
1174
- }], hint: [{
1175
- type: ViewChild,
1176
- args: ['hint']
1177
- }], dragStart: [{
1178
- type: Output
1179
- }], dragEnd: [{
1180
- type: Output
1181
- }], dragOver: [{
1182
- type: Output
1183
- }], dragLeave: [{
1184
- type: Output
1185
- }], dataMove: [{
1186
- type: Output
1187
- }], dataAdd: [{
1188
- type: Output
1189
- }], dataRemove: [{
1190
- type: Output
1191
- }], navigate: [{
1192
- type: Output
1193
- }], activeIndex: [{
1194
- type: Input
1195
- }], touchAction: [{
1196
- type: HostBinding,
1197
- args: ['style.touch-action']
1198
- }], dir: [{
1199
- type: HostBinding,
1200
- args: ['attr.dir']
1201
- }], hostRole: [{
1202
- type: HostBinding,
1203
- args: ['attr.role']
1204
- }] } });