@progress/kendo-angular-listbox 21.0.0-develop.9 → 21.0.1-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.
@@ -9,7 +9,7 @@ import { Subscription } from 'rxjs';
9
9
  import { getter } from '@progress/kendo-common';
10
10
  import { caretAltUpIcon, caretAltDownIcon, caretAltRightIcon, caretAltLeftIcon, caretDoubleAltRightIcon, caretDoubleAltLeftIcon, xIcon } from '@progress/kendo-svg-icons';
11
11
  import { ButtonComponent } from '@progress/kendo-angular-buttons';
12
- import { normalizeNumpadKeys, Keys, TemplateContextDirective, isChanged, ResizeBatchService } from '@progress/kendo-angular-common';
12
+ import { normalizeNumpadKeys, Keys, isPresent as isPresent$1, TemplateContextDirective, isChanged, ResizeBatchService } from '@progress/kendo-angular-common';
13
13
  import { take } from 'rxjs/operators';
14
14
  import * as i1 from '@progress/kendo-angular-l10n';
15
15
  import { ComponentMessages, LocalizationService, L10N_PREFIX } from '@progress/kendo-angular-l10n';
@@ -25,8 +25,8 @@ const packageMetadata = {
25
25
  productName: 'Kendo UI for Angular',
26
26
  productCode: 'KENDOUIANGULAR',
27
27
  productCodes: ['KENDOUIANGULAR'],
28
- publishDate: 1761910558,
29
- version: '21.0.0-develop.9',
28
+ publishDate: 1762953613,
29
+ version: '21.0.1-develop.1',
30
30
  licensingDocsUrl: 'https://www.telerik.com/kendo-angular-ui/my-license/?utm_medium=product&utm_source=kendoangular&utm_campaign=kendo-ui-angular-purchase-license-keys-warning'
31
31
  };
32
32
 
@@ -34,17 +34,138 @@ const packageMetadata = {
34
34
  * @hidden
35
35
  */
36
36
  class ListBoxSelectionService {
37
+ selectedIndices = [];
38
+ selectionMode = 'single';
39
+ lastSelectedOrUnselectedIndex = null;
40
+ rangeSelectionTargetIndex = null;
41
+ rangeSelectionAnchorIndex = null;
37
42
  onSelect = new EventEmitter();
38
- selectedIndex = null;
39
- select(index) {
40
- this.onSelect.next({ index: index, prevIndex: this.selectedIndex });
41
- this.selectedIndex = index;
43
+ select(index, ctrlKey = false, shiftKey = false) {
44
+ const previousSelection = [...this.selectedIndices];
45
+ let selectedIndices = [];
46
+ let deselectedIndices = null;
47
+ const previousTargetIndex = this.rangeSelectionTargetIndex;
48
+ if (this.selectionMode === 'single') {
49
+ if (ctrlKey) {
50
+ const isSelected = this.isSelected(index);
51
+ if (isSelected) {
52
+ this.selectedIndices = [];
53
+ deselectedIndices = [index];
54
+ }
55
+ else {
56
+ this.selectedIndices = [index];
57
+ selectedIndices = [index];
58
+ if (previousSelection.length > 0) {
59
+ deselectedIndices = previousSelection;
60
+ }
61
+ }
62
+ this.lastSelectedOrUnselectedIndex = index;
63
+ }
64
+ else {
65
+ this.selectedIndices = [index];
66
+ selectedIndices = [index];
67
+ this.lastSelectedOrUnselectedIndex = index;
68
+ this.rangeSelectionAnchorIndex = index;
69
+ if (previousSelection.length > 0 && previousSelection[0] !== index) {
70
+ deselectedIndices = previousSelection;
71
+ }
72
+ }
73
+ }
74
+ else if (this.selectionMode === 'multiple') {
75
+ if (shiftKey) {
76
+ let anchorIndex = this.rangeSelectionAnchorIndex ?? this.lastSelectedOrUnselectedIndex ?? 0;
77
+ if (index === anchorIndex) {
78
+ this.selectedIndices = [anchorIndex];
79
+ this.rangeSelectionTargetIndex = index;
80
+ selectedIndices = this.selectedIndices.filter(i => !previousSelection.includes(i));
81
+ const nowDeselected = previousSelection.filter(i => !this.selectedIndices.includes(i));
82
+ deselectedIndices = nowDeselected.length > 0 ? nowDeselected : null;
83
+ }
84
+ else {
85
+ if (previousTargetIndex !== null && previousTargetIndex !== anchorIndex) {
86
+ const previousDirection = previousTargetIndex > anchorIndex ? 'down' : 'up';
87
+ const currentDirection = index > anchorIndex ? 'down' : 'up';
88
+ if (previousDirection !== currentDirection) {
89
+ this.rangeSelectionAnchorIndex = previousTargetIndex;
90
+ anchorIndex = previousTargetIndex;
91
+ }
92
+ }
93
+ const startIndex = Math.min(anchorIndex, index);
94
+ const endIndex = Math.max(anchorIndex, index);
95
+ this.selectedIndices = [];
96
+ for (let i = startIndex; i <= endIndex; i++) {
97
+ this.selectedIndices.push(i);
98
+ }
99
+ this.rangeSelectionTargetIndex = index;
100
+ selectedIndices = this.selectedIndices.filter(i => !previousSelection.includes(i));
101
+ const nowDeselected = previousSelection.filter(i => !this.selectedIndices.includes(i));
102
+ deselectedIndices = nowDeselected.length > 0 ? nowDeselected : null;
103
+ }
104
+ }
105
+ else if (ctrlKey) {
106
+ const indexInSelection = this.selectedIndices.indexOf(index);
107
+ if (indexInSelection === -1) {
108
+ this.selectedIndices.push(index);
109
+ selectedIndices = [index];
110
+ }
111
+ else {
112
+ this.selectedIndices.splice(indexInSelection, 1);
113
+ deselectedIndices = [index];
114
+ }
115
+ this.lastSelectedOrUnselectedIndex = index;
116
+ this.rangeSelectionAnchorIndex = index;
117
+ this.rangeSelectionTargetIndex = index;
118
+ }
119
+ else {
120
+ this.selectedIndices = [index];
121
+ selectedIndices = [index];
122
+ this.lastSelectedOrUnselectedIndex = index;
123
+ this.rangeSelectionAnchorIndex = index;
124
+ this.rangeSelectionTargetIndex = index;
125
+ const nowDeselected = previousSelection.filter(i => i !== index);
126
+ deselectedIndices = nowDeselected.length > 0 ? nowDeselected : null;
127
+ }
128
+ }
129
+ this.onSelect.next({
130
+ selectedIndices: selectedIndices.length > 0 ? selectedIndices : null,
131
+ deselectedIndices
132
+ });
133
+ }
134
+ selectRange(targetIndex) {
135
+ const anchorIndex = this.lastSelectedOrUnselectedIndex ?? 0;
136
+ const startIndex = Math.min(anchorIndex, targetIndex);
137
+ const endIndex = Math.max(anchorIndex, targetIndex);
138
+ this.selectedIndices = [];
139
+ for (let i = startIndex; i <= endIndex; i++) {
140
+ this.selectedIndices.push(i);
141
+ }
142
+ }
143
+ setSelectedIndices(indices) {
144
+ this.selectedIndices = [...indices];
145
+ }
146
+ addToSelectedIndices(index) {
147
+ if (this.selectionMode === 'single') {
148
+ this.selectedIndices = [index];
149
+ }
150
+ else if (this.selectedIndices.indexOf(index) === -1) {
151
+ this.selectedIndices = [...this.selectedIndices, index];
152
+ }
153
+ }
154
+ selectAll(totalItems) {
155
+ if (this.selectionMode === 'multiple') {
156
+ this.selectedIndices = Array.from({ length: totalItems }, (_, i) => i);
157
+ }
158
+ }
159
+ areAllSelected(totalItems) {
160
+ return this.selectedIndices.length === totalItems && totalItems > 0;
42
161
  }
43
162
  isSelected(index) {
44
- return index === this.selectedIndex;
163
+ return this.selectedIndices.indexOf(index) !== -1;
45
164
  }
46
165
  clearSelection() {
47
- this.selectedIndex = null;
166
+ this.selectedIndices = [];
167
+ this.lastSelectedOrUnselectedIndex = null;
168
+ this.rangeSelectionAnchorIndex = null;
48
169
  }
49
170
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ListBoxSelectionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
50
171
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ListBoxSelectionService });
@@ -208,11 +329,13 @@ class KeyboardNavigationService {
208
329
  onTransferAllEvent = new EventEmitter();
209
330
  onShiftSelectedItem = new EventEmitter();
210
331
  onSelectionChange = new EventEmitter();
332
+ onSelectAll = new EventEmitter();
333
+ onSelectToEnd = new EventEmitter();
211
334
  constructor(renderer, zone) {
212
335
  this.renderer = renderer;
213
336
  this.zone = zone;
214
337
  }
215
- onKeyDown(event, toolsRef, toolbar, childListbox, parentListbox, listboxItems) {
338
+ onKeyDown(event, toolsRef, toolbar, childListbox, parentListbox, listboxItems, currentListbox) {
216
339
  const target = event.target;
217
340
  const keyCode = normalizeNumpadKeys(event);
218
341
  const ctrlOrMetaKey = event.ctrlKey || event.metaKey;
@@ -234,6 +357,17 @@ class KeyboardNavigationService {
234
357
  this.onDeleteEvent.emit(this.selectedListboxItemIndex);
235
358
  }
236
359
  }
360
+ if (ctrlOrMetaKey && (event.key === 'a' || event.key === 'A')) {
361
+ event.preventDefault();
362
+ this.onSelectAll.emit();
363
+ return;
364
+ }
365
+ if (ctrlOrMetaKey && event.shiftKey && (keyCode === Keys.Home || keyCode === Keys.End)) {
366
+ event.preventDefault();
367
+ const direction = keyCode === Keys.Home ? 'home' : 'end';
368
+ this.onSelectToEnd.emit({ direction });
369
+ return;
370
+ }
237
371
  const isTargetListboxItem = listboxItems.find(elem => elem.nativeElement === target);
238
372
  if (isTargetListboxItem) {
239
373
  let isTransferToolVisible;
@@ -241,7 +375,7 @@ class KeyboardNavigationService {
241
375
  isTransferToolVisible = activeToolbar.some(tool => tool.name.startsWith('transfer'));
242
376
  }
243
377
  if ((keyCode === Keys.ArrowRight || keyCode === Keys.ArrowLeft) && ctrlOrMetaKey && isTransferToolVisible) {
244
- this.onArrowLeftOrRight(keyCode, parentListbox, childListbox, event, listboxItems);
378
+ this.onArrowLeftOrRight(keyCode, parentListbox, childListbox, event, listboxItems, currentListbox);
245
379
  }
246
380
  else if ((keyCode === Keys.ArrowUp || keyCode === Keys.ArrowDown)) {
247
381
  this.onArrowUpOrDown(keyCode, ctrlOrMetaKey, event, activeToolbar, listboxItems);
@@ -250,9 +384,7 @@ class KeyboardNavigationService {
250
384
  this.onSelectChange(event, listboxItems);
251
385
  }
252
386
  else if (keyCode === Keys.Space) {
253
- if (this.selectedListboxItemIndex !== this.focusedListboxItemIndex) {
254
- this.onSpaceKey(event, listboxItems);
255
- }
387
+ this.onSpaceKey(event, listboxItems);
256
388
  }
257
389
  }
258
390
  }
@@ -276,17 +408,27 @@ class KeyboardNavigationService {
276
408
  }
277
409
  const offset = dir === 'up' ? -1 : 1;
278
410
  this.focusedToolIndex += offset;
279
- const prevItem = toolsRef[this.focusedToolIndex + (offset * -1)].element;
280
- const currentItem = toolsRef[this.focusedToolIndex].element;
411
+ const prevItem = toolsRef[this.focusedToolIndex + (offset * -1)]?.element;
412
+ const currentItem = toolsRef[this.focusedToolIndex]?.element;
281
413
  this.changeTabindex(prevItem, currentItem);
282
414
  }
283
415
  onSpaceKey(event, listboxItems) {
284
416
  event.stopImmediatePropagation();
285
417
  event.preventDefault();
286
- const previousItem = listboxItems[this.selectedListboxItemIndex]?.nativeElement;
287
- const currentItem = listboxItems[this.focusedListboxItemIndex]?.nativeElement;
288
- this.changeTabindex(previousItem, currentItem);
289
- this.onSelectionChange.emit({ index: this.focusedListboxItemIndex, prevIndex: this.selectedListboxItemIndex });
418
+ event.stopPropagation();
419
+ const ctrlKey = event.ctrlKey || event.metaKey;
420
+ const shiftKey = event.shiftKey;
421
+ if (this.selectedListboxItemIndex !== this.focusedListboxItemIndex) {
422
+ const previousItem = listboxItems[this.selectedListboxItemIndex]?.nativeElement;
423
+ const currentItem = listboxItems[this.focusedListboxItemIndex]?.nativeElement;
424
+ this.changeTabindex(previousItem, currentItem);
425
+ }
426
+ this.onSelectionChange.emit({
427
+ index: this.focusedListboxItemIndex,
428
+ prevIndex: this.selectedListboxItemIndex,
429
+ ctrlKey,
430
+ shiftKey
431
+ });
290
432
  this.selectedListboxItemIndex = this.focusedListboxItemIndex;
291
433
  }
292
434
  onArrowUpOrDown(keyCode, ctrlOrMetaKey, event, activeToolbar, listboxItems) {
@@ -304,17 +446,24 @@ class KeyboardNavigationService {
304
446
  this.changeFocusedItem(dir, listboxItems);
305
447
  return;
306
448
  }
449
+ if (event.shiftKey) {
450
+ this.onShiftArrow(dir, listboxItems);
451
+ return;
452
+ }
307
453
  dir === 'moveUp' ? this.onArrowUp(listboxItems) : this.onArrowDown(listboxItems);
308
454
  this.onSelectionChange.emit({ index: this.selectedListboxItemIndex, prevIndex: this.focusedListboxItemIndex });
309
455
  this.focusedListboxItemIndex = this.selectedListboxItemIndex;
310
456
  }
311
- onArrowLeftOrRight(keyCode, parentListbox, childListbox, event, listboxItems) {
457
+ onArrowLeftOrRight(keyCode, parentListbox, childListbox, event, listboxItems, currentListbox) {
312
458
  event.preventDefault();
313
459
  if (event.shiftKey) {
314
460
  this.transferAllItems(keyCode, childListbox, parentListbox);
315
461
  return;
316
462
  }
317
- if (this.selectedListboxItemIndex >= 0) {
463
+ const isArrowRight = keyCode === Keys.ArrowRight;
464
+ const sourceListbox = isArrowRight ? currentListbox : childListbox || parentListbox?.childListbox;
465
+ const hasSelection = sourceListbox?.selectedIndices && sourceListbox.selectedIndices.length > 0;
466
+ if (hasSelection) {
318
467
  this.transferItem(keyCode, childListbox, parentListbox, listboxItems);
319
468
  }
320
469
  }
@@ -337,7 +486,8 @@ class KeyboardNavigationService {
337
486
  this.selectedListboxItemIndex = this.focusedListboxItemIndex;
338
487
  }
339
488
  this.changeTabindex(previousItem, currentItem, !!currentItem);
340
- this.onSelectionChange.emit({ index: this.selectedListboxItemIndex, prevIndex });
489
+ const ctrlKey = event.ctrlKey || event.metaKey;
490
+ this.onSelectionChange.emit({ index: this.selectedListboxItemIndex, prevIndex, ctrlKey });
341
491
  }
342
492
  onF10Key(tools) {
343
493
  if (this.focusedToolIndex && this.focusedToolIndex > -1) {
@@ -439,29 +589,49 @@ class KeyboardNavigationService {
439
589
  });
440
590
  }
441
591
  changeFocusedItem(dir, listboxItems) {
442
- listboxItems[this.focusedListboxItemIndex].nativeElement.blur();
592
+ const previousIndex = this.focusedListboxItemIndex;
593
+ const previousItem = listboxItems[previousIndex].nativeElement;
443
594
  if (this.focusedListboxItemIndex > 0 && dir === 'moveUp') {
444
595
  this.focusedListboxItemIndex -= 1;
445
596
  }
446
597
  else if (this.focusedListboxItemIndex < listboxItems.length - 1 && dir === 'moveDown') {
447
598
  this.focusedListboxItemIndex += 1;
448
599
  }
449
- listboxItems[this.focusedListboxItemIndex].nativeElement.focus();
600
+ const currentItem = listboxItems[this.focusedListboxItemIndex].nativeElement;
601
+ this.changeTabindex(previousItem, currentItem);
602
+ }
603
+ onShiftArrow(dir, listboxItems) {
604
+ const previousFocusIndex = this.focusedListboxItemIndex;
605
+ if (dir === 'moveUp' && this.focusedListboxItemIndex > 0) {
606
+ this.focusedListboxItemIndex -= 1;
607
+ }
608
+ else if (dir === 'moveDown' && this.focusedListboxItemIndex < listboxItems.length - 1) {
609
+ this.focusedListboxItemIndex += 1;
610
+ }
611
+ if (previousFocusIndex !== this.focusedListboxItemIndex) {
612
+ const previousItem = listboxItems[previousFocusIndex]?.nativeElement;
613
+ const currentItem = listboxItems[this.focusedListboxItemIndex]?.nativeElement;
614
+ this.changeTabindex(previousItem, currentItem);
615
+ this.onSelectionChange.emit({
616
+ index: this.focusedListboxItemIndex,
617
+ prevIndex: this.selectedListboxItemIndex,
618
+ shiftKey: true
619
+ });
620
+ this.selectedListboxItemIndex = this.focusedListboxItemIndex;
621
+ }
450
622
  }
451
623
  onArrowDown(listboxItems) {
452
- if (this.selectedListboxItemIndex < listboxItems.length - 1) {
453
- const offset = this.selectedListboxItemIndex ? this.selectedListboxItemIndex : this.focusedListboxItemIndex;
454
- this.selectedListboxItemIndex = offset + 1;
455
- const previousItem = listboxItems[this.selectedListboxItemIndex - 1]?.nativeElement;
624
+ if (this.focusedListboxItemIndex < listboxItems.length - 1) {
625
+ this.selectedListboxItemIndex = this.focusedListboxItemIndex + 1;
626
+ const previousItem = listboxItems[this.focusedListboxItemIndex]?.nativeElement;
456
627
  const currentItem = listboxItems[this.selectedListboxItemIndex]?.nativeElement;
457
628
  this.changeTabindex(previousItem, currentItem);
458
629
  }
459
630
  }
460
631
  onArrowUp(listboxItems) {
461
- if (this.selectedListboxItemIndex > 0 || this.focusedListboxItemIndex > 0) {
462
- const offset = this.selectedListboxItemIndex ? this.selectedListboxItemIndex : this.focusedListboxItemIndex;
463
- this.selectedListboxItemIndex = offset - 1;
464
- const previousItem = listboxItems[this.selectedListboxItemIndex + 1]?.nativeElement;
632
+ if (this.focusedListboxItemIndex > 0) {
633
+ this.selectedListboxItemIndex = this.focusedListboxItemIndex - 1;
634
+ const previousItem = listboxItems[this.focusedListboxItemIndex]?.nativeElement;
465
635
  const currentItem = listboxItems[this.selectedListboxItemIndex]?.nativeElement;
466
636
  this.changeTabindex(previousItem, currentItem);
467
637
  }
@@ -487,7 +657,12 @@ class ItemSelectableDirective {
487
657
  }
488
658
  onClick(event) {
489
659
  event.stopPropagation();
490
- this.selectionService.select(this.index);
660
+ const ctrlKey = event.ctrlKey || event.metaKey;
661
+ const shiftKey = event.shiftKey;
662
+ if (shiftKey) {
663
+ event.preventDefault();
664
+ }
665
+ this.selectionService.select(this.index, ctrlKey, shiftKey);
491
666
  }
492
667
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ItemSelectableDirective, deps: [{ token: ListBoxSelectionService }], target: i0.ɵɵFactoryTarget.Directive });
493
668
  static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.14", type: ItemSelectableDirective, isStandalone: true, selector: "[kendoListBoxItemSelectable]", inputs: { index: "index" }, host: { listeners: { "mousedown": "onClick($event)" }, properties: { "class.k-selected": "this.selectedClassName" } }, ngImport: i0 });
@@ -637,7 +812,7 @@ class ListBoxComponent {
637
812
  renderer;
638
813
  zone;
639
814
  localization;
640
- changeDetector;
815
+ cdr;
641
816
  /**
642
817
  * @hidden
643
818
  */
@@ -670,6 +845,18 @@ class ListBoxComponent {
670
845
  * Specifies the field of the data item that provides the text content of the nodes.
671
846
  */
672
847
  textField;
848
+ /**
849
+ * Sets the selection mode of the ListBox.
850
+ *
851
+ * @default 'single'
852
+ */
853
+ set selectable(mode) {
854
+ this._selectable = mode;
855
+ this.selectionService.selectionMode = mode;
856
+ }
857
+ get selectable() {
858
+ return this._selectable;
859
+ }
673
860
  /**
674
861
  * Specifies the data that the ListBox displays.
675
862
  *
@@ -692,6 +879,8 @@ class ListBoxComponent {
692
879
  /**
693
880
  * Configures the toolbar of the ListBox.
694
881
  * Specifies whether to display a toolbar and which tools and position to use.
882
+ *
883
+ * @default false
695
884
  */
696
885
  set toolbar(config) {
697
886
  let position = DEFAULT_TOOLBAR_POSITION;
@@ -730,7 +919,7 @@ class ListBoxComponent {
730
919
  /**
731
920
  * Fires when you click a ListBox item.
732
921
  */
733
- actionClick = new EventEmitter();
922
+ action = new EventEmitter();
734
923
  /**
735
924
  * @hidden
736
925
  */
@@ -750,7 +939,7 @@ class ListBoxComponent {
750
939
  /**
751
940
  * @hidden
752
941
  */
753
- selectedTools = allTools;
942
+ selectedTools = [];
754
943
  /**
755
944
  * @hidden
756
945
  */
@@ -778,15 +967,16 @@ class ListBoxComponent {
778
967
  localizationSubscription;
779
968
  _size = DEFAULT_SIZE;
780
969
  subs = new Subscription();
781
- shouldFireFocusIn = true;
782
- constructor(keyboardNavigationService, selectionService, hostElement, renderer, zone, localization, changeDetector) {
970
+ shouldFireFocusIn = false;
971
+ _selectable = 'single';
972
+ constructor(keyboardNavigationService, selectionService, hostElement, renderer, zone, localization, cdr) {
783
973
  this.keyboardNavigationService = keyboardNavigationService;
784
974
  this.selectionService = selectionService;
785
975
  this.hostElement = hostElement;
786
976
  this.renderer = renderer;
787
977
  this.zone = zone;
788
978
  this.localization = localization;
789
- this.changeDetector = changeDetector;
979
+ this.cdr = cdr;
790
980
  validatePackage(packageMetadata);
791
981
  this.setToolbarClass(DEFAULT_TOOLBAR_POSITION);
792
982
  this.setSizingClass(this.size);
@@ -799,9 +989,6 @@ class ListBoxComponent {
799
989
  // This allows us to know to which parent Listbox the child Listbox is connected to
800
990
  this.childListbox.parentListbox = this;
801
991
  }
802
- if (this.selectedIndex) {
803
- this.keyboardNavigationService.focusedToolIndex = this.selectedIndex;
804
- }
805
992
  this.localizationSubscription = this.localization.changes.subscribe(({ rtl }) => {
806
993
  this.direction = rtl ? 'rtl' : 'ltr';
807
994
  });
@@ -825,11 +1012,12 @@ class ListBoxComponent {
825
1012
  const isListboxChild = this.parentListbox && !this.childListbox;
826
1013
  const isListboxParentAndChild = !!(this.parentListbox && this.childListbox);
827
1014
  const isListboxParent = !!(this.childListbox || (!this.childListbox && !this.parentListbox));
1015
+ this.shouldFireFocusIn = false;
828
1016
  if (isListboxChild || (isListboxParentAndChild && isActionTransferFrom)) {
829
- this.parentListbox.actionClick.next(actionName);
1017
+ this.parentListbox.action.next(actionName);
830
1018
  }
831
1019
  else if (isListboxParent || (isListboxParentAndChild && !isActionTransferFrom)) {
832
- this.actionClick.next(actionName);
1020
+ this.action.next(actionName);
833
1021
  }
834
1022
  const toolsRef = this.tools.toArray() || this.parentListbox.tools.toArray();
835
1023
  const focusedToolIndex = toolsRef.findIndex(elem => elem.nativeElement === document.activeElement);
@@ -841,12 +1029,27 @@ class ListBoxComponent {
841
1029
  const currentTool = toolsRef[navService.focusedToolIndex]?.element;
842
1030
  navService.changeTabindex(prevTool, currentTool);
843
1031
  }
1032
+ this.cdr.markForCheck();
1033
+ this.zone.runOutsideAngular(() => setTimeout(() => {
1034
+ this.shouldFireFocusIn = true;
1035
+ }));
1036
+ }
1037
+ /**
1038
+ * Selects multiple ListBox nodes programmatically.
1039
+ */
1040
+ select(indices) {
1041
+ const validIndices = indices.filter(index => index >= 0 && index < this.data.length);
1042
+ this.selectionService.setSelectedIndices(validIndices);
844
1043
  }
845
1044
  /**
846
1045
  * Selects a ListBox node programmatically.
1046
+ *
1047
+ * @hidden
847
1048
  */
848
1049
  selectItem(index) {
849
- this.selectionService.selectedIndex = index;
1050
+ if (index >= 0 && index < this.data.length) {
1051
+ this.select([index]);
1052
+ }
850
1053
  }
851
1054
  /**
852
1055
  * Clears the ListBox selection programmatically.
@@ -855,10 +1058,10 @@ class ListBoxComponent {
855
1058
  this.selectionService.clearSelection();
856
1059
  }
857
1060
  /**
858
- * Gets the index of the currently selected item in the ListBox.
1061
+ * Gets the indexes of the currently selected items in the ListBox.
859
1062
  */
860
- get selectedIndex() {
861
- return this.selectionService.selectedIndex;
1063
+ get selectedIndices() {
1064
+ return this.selectionService.selectedIndices;
862
1065
  }
863
1066
  /**
864
1067
  * @hidden
@@ -901,36 +1104,84 @@ class ListBoxComponent {
901
1104
  this.caretAltLeftIcon :
902
1105
  icon;
903
1106
  }
904
- onClickEvent(prevIndex, index) {
905
- this.shouldFireFocusIn = false;
906
- this.selectionChange.next({ index, prevIndex: this.keyboardNavigationService.selectedListboxItemIndex });
907
- this.keyboardNavigationService.selectedListboxItemIndex = index;
908
- this.keyboardNavigationService.focusedListboxItemIndex = index;
909
- this.zone.onStable.pipe(take(1)).subscribe(() => {
910
- const listboxItems = this.listboxItems.toArray();
911
- const previousItem = prevIndex ? listboxItems[prevIndex].nativeElement : listboxItems[0].nativeElement;
912
- const currentItem = listboxItems[index].nativeElement;
913
- this.keyboardNavigationService.changeTabindex(previousItem, currentItem);
914
- });
915
- this.zone.onStable.pipe(take(1)).subscribe(() => {
916
- this.shouldFireFocusIn = true;
917
- });
918
- }
919
1107
  initSubscriptions(navService, hostEl, toolsRef) {
920
1108
  this.subs.add(navService.onShiftSelectedItem.subscribe((actionToPerform) => this.performAction(actionToPerform)));
921
1109
  this.subs.add(navService.onTransferAllEvent.subscribe((actionToPerform) => this.performAction(actionToPerform)));
922
- this.subs.add(this.selectionService.onSelect.subscribe((e) => this.onClickEvent(e.prevIndex, e.index)));
1110
+ this.subs.add(this.selectionService.onSelect.subscribe((event) => {
1111
+ this.shouldFireFocusIn = false;
1112
+ this.selectionChange.next(event);
1113
+ const newFocusIndex = isPresent$1(this.selectionService.rangeSelectionTargetIndex)
1114
+ ? this.selectionService.rangeSelectionTargetIndex
1115
+ : this.selectionService.lastSelectedOrUnselectedIndex;
1116
+ if (isPresent$1(newFocusIndex)) {
1117
+ const listboxItems = this.listboxItems.toArray();
1118
+ const previousItem = listboxItems[navService.focusedListboxItemIndex]?.nativeElement;
1119
+ const currentItem = listboxItems[newFocusIndex]?.nativeElement;
1120
+ if (previousItem && currentItem) {
1121
+ navService.changeTabindex(previousItem, currentItem, false);
1122
+ navService.focusedListboxItemIndex = newFocusIndex;
1123
+ navService.selectedListboxItemIndex = newFocusIndex;
1124
+ }
1125
+ }
1126
+ this.cdr.markForCheck();
1127
+ this.zone.runOutsideAngular(() => setTimeout(() => {
1128
+ this.shouldFireFocusIn = true;
1129
+ }));
1130
+ }));
923
1131
  this.subs.add(navService.onDeleteEvent.subscribe((index) => this.onDeleteEvent(index, navService)));
924
1132
  this.subs.add(navService.onMoveSelectedItem.subscribe((dir) => this.performAction(dir)));
1133
+ this.subs.add(navService.onSelectAll.subscribe(() => {
1134
+ if (this.selectable === 'multiple') {
1135
+ const previousSelection = [...this.selectionService.selectedIndices];
1136
+ const allSelected = this.selectionService.areAllSelected(this.data.length);
1137
+ if (allSelected) {
1138
+ this.selectionService.clearSelection();
1139
+ this.selectionChange.next({
1140
+ selectedIndices: null,
1141
+ deselectedIndices: previousSelection.length > 0 ? previousSelection : null
1142
+ });
1143
+ }
1144
+ else {
1145
+ this.selectionService.selectAll(this.data.length);
1146
+ const selectedIndices = this.selectionService.selectedIndices.filter(i => !previousSelection.includes(i));
1147
+ this.selectionChange.next({
1148
+ selectedIndices: selectedIndices.length > 0 ? selectedIndices : null,
1149
+ deselectedIndices: null
1150
+ });
1151
+ }
1152
+ this.cdr.markForCheck();
1153
+ }
1154
+ }));
1155
+ this.subs.add(navService.onSelectToEnd.subscribe(({ direction }) => {
1156
+ if (this.selectable === 'multiple') {
1157
+ this.shouldFireFocusIn = false;
1158
+ const previousSelection = [...this.selectionService.selectedIndices];
1159
+ const targetIndex = direction === 'home' ? 0 : this.data.length - 1;
1160
+ this.selectionService.selectRange(targetIndex);
1161
+ const selectedIndices = this.selectionService.selectedIndices.filter(i => !previousSelection.includes(i));
1162
+ const deselectedIndices = previousSelection.filter(i => !this.selectionService.selectedIndices.includes(i));
1163
+ this.selectionChange.next({
1164
+ selectedIndices: selectedIndices.length > 0 ? selectedIndices : null,
1165
+ deselectedIndices: deselectedIndices.length > 0 ? deselectedIndices : null
1166
+ });
1167
+ const listboxItems = this.listboxItems.toArray();
1168
+ const currentItem = listboxItems[navService.focusedListboxItemIndex]?.nativeElement;
1169
+ const targetItem = listboxItems[targetIndex]?.nativeElement;
1170
+ navService.changeTabindex(currentItem, targetItem);
1171
+ navService.focusedListboxItemIndex = targetIndex;
1172
+ this.cdr.markForCheck();
1173
+ this.zone.runOutsideAngular(() => setTimeout(() => {
1174
+ this.shouldFireFocusIn = true;
1175
+ }));
1176
+ }
1177
+ }));
925
1178
  if (this.listboxElement) {
926
1179
  this.subs.add(this.renderer.listen(this.listboxElement.nativeElement, 'focusin', (event) => this.onFocusIn(event)));
927
1180
  }
928
- this.subs.add(this.renderer.listen(hostEl, 'keydown', (event) => navService.onKeyDown(event, toolsRef, this.selectedTools, this.childListbox, this.parentListbox, this.listboxItems.toArray())));
929
- this.subs.add(navService.onSelectionChange.subscribe((indexes) => {
930
- const { prevIndex, index } = indexes;
931
- this.selectionService.selectedIndex = index;
932
- this.selectionChange.next({ index, prevIndex });
933
- this.changeDetector.markForCheck();
1181
+ this.subs.add(this.renderer.listen(hostEl, 'keydown', (event) => navService.onKeyDown(event, toolsRef, this.selectedTools, this.childListbox, this.parentListbox, this.listboxItems.toArray(), this)));
1182
+ this.subs.add(navService.onSelectionChange.subscribe((event) => {
1183
+ const { index, ctrlKey, shiftKey } = event;
1184
+ this.selectionService.select(index, ctrlKey, shiftKey);
934
1185
  }));
935
1186
  }
936
1187
  onFocusIn(event) {
@@ -941,8 +1192,15 @@ class ListBoxComponent {
941
1192
  if (index === -1) {
942
1193
  return;
943
1194
  }
944
- this.selectionService.selectedIndex = index;
945
- this.selectionChange.next({ index, prevIndex: null });
1195
+ const previousSelection = [...this.selectionService.selectedIndices];
1196
+ this.selectionService.addToSelectedIndices(index);
1197
+ navService.focusedListboxItemIndex = index;
1198
+ navService.selectedListboxItemIndex = index;
1199
+ const deselected = previousSelection.filter(i => i !== index);
1200
+ this.selectionChange.next({
1201
+ selectedIndices: [index],
1202
+ deselectedIndices: deselected.length > 0 ? deselected : null
1203
+ });
946
1204
  const previousItem = items[navService.selectedListboxItemIndex]?.nativeElement;
947
1205
  const currentItem = items[index]?.nativeElement;
948
1206
  this.renderer.setAttribute(previousItem, 'tabindex', '-1');
@@ -984,17 +1242,20 @@ class ListBoxComponent {
984
1242
  }
985
1243
  }
986
1244
  onDeleteEvent(index, navService) {
987
- this.selectionService.selectedIndex = index;
1245
+ this.selectionService.addToSelectedIndices(index);
988
1246
  this.performAction('remove');
989
1247
  const listboxItems = this.listboxItems.toArray();
990
1248
  const setIndex = index + 1 === listboxItems.length ?
991
1249
  { index: index - 1, tabindex: index - 1 } : { index, tabindex: index + 1 };
992
1250
  navService.changeTabindex(null, listboxItems[setIndex['tabindex']]?.nativeElement);
993
- this.selectionChange.next({ index: setIndex['index'], prevIndex: null });
1251
+ this.selectionChange.next({
1252
+ selectedIndices: [setIndex['index']],
1253
+ deselectedIndices: null
1254
+ });
994
1255
  navService.selectedListboxItemIndex = setIndex['index'];
995
1256
  navService.focusedListboxItemIndex = setIndex['index'];
996
1257
  navService.focusedListboxItem = setIndex['index'];
997
- this.selectionService.selectedIndex = setIndex['index'];
1258
+ this.selectionService.selectedIndices = [setIndex['index']];
998
1259
  }
999
1260
  setToolbarClass(pos) {
1000
1261
  Object.keys(actionsClasses).forEach((className) => {
@@ -1010,7 +1271,7 @@ class ListBoxComponent {
1010
1271
  this.renderer.addClass(this.hostElement.nativeElement, `k-listbox-${sizeClassMap[size]}`);
1011
1272
  }
1012
1273
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ListBoxComponent, deps: [{ token: KeyboardNavigationService }, { token: ListBoxSelectionService }, { token: i0.ElementRef }, { token: i0.Renderer2 }, { token: i0.NgZone }, { token: i1.LocalizationService }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
1013
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: ListBoxComponent, isStandalone: true, selector: "kendo-listbox", inputs: { textField: "textField", data: "data", size: "size", toolbar: "toolbar", listboxLabel: "listboxLabel", listboxToolbarLabel: "listboxToolbarLabel", itemDisabled: "itemDisabled" }, outputs: { selectionChange: "selectionChange", actionClick: "actionClick", getChildListbox: "getChildListbox" }, host: { properties: { "class.k-listbox": "this.listboxClassName", "attr.dir": "this.direction" } }, providers: [
1274
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: ListBoxComponent, isStandalone: true, selector: "kendo-listbox", inputs: { textField: "textField", selectable: "selectable", data: "data", size: "size", toolbar: "toolbar", listboxLabel: "listboxLabel", listboxToolbarLabel: "listboxToolbarLabel", itemDisabled: "itemDisabled" }, outputs: { selectionChange: "selectionChange", action: "action", getChildListbox: "getChildListbox" }, host: { properties: { "class.k-listbox": "this.listboxClassName", "attr.dir": "this.direction" } }, providers: [
1014
1275
  ListBoxSelectionService,
1015
1276
  KeyboardNavigationService,
1016
1277
  LocalizationService,
@@ -1077,16 +1338,16 @@ class ListBoxComponent {
1077
1338
  class="k-list-ul"
1078
1339
  role="listbox"
1079
1340
  [attr.aria-label]="listboxLabel"
1080
- [attr.aria-multiselectable]="false"
1341
+ [attr.aria-multiselectable]="selectable === 'multiple'"
1081
1342
  >
1082
1343
  <li
1083
1344
  #listboxItems
1084
1345
  *ngFor="let item of data; let i = index"
1085
1346
  kendoListBoxItemSelectable
1086
1347
  class="k-list-item"
1087
- [attr.tabindex]="i === 0 ? '0' : '-1'"
1348
+ [attr.tabindex]="i === keyboardNavigationService.focusedListboxItemIndex ? '0' : '-1'"
1088
1349
  role="option"
1089
- [attr.aria-selected]="selectedIndex === i"
1350
+ [attr.aria-selected]="selectedIndices.indexOf(i) >= 0"
1090
1351
  [index]="i"
1091
1352
  [class.k-disabled]="itemDisabled(item)"
1092
1353
  >
@@ -1184,16 +1445,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
1184
1445
  class="k-list-ul"
1185
1446
  role="listbox"
1186
1447
  [attr.aria-label]="listboxLabel"
1187
- [attr.aria-multiselectable]="false"
1448
+ [attr.aria-multiselectable]="selectable === 'multiple'"
1188
1449
  >
1189
1450
  <li
1190
1451
  #listboxItems
1191
1452
  *ngFor="let item of data; let i = index"
1192
1453
  kendoListBoxItemSelectable
1193
1454
  class="k-list-item"
1194
- [attr.tabindex]="i === 0 ? '0' : '-1'"
1455
+ [attr.tabindex]="i === keyboardNavigationService.focusedListboxItemIndex ? '0' : '-1'"
1195
1456
  role="option"
1196
- [attr.aria-selected]="selectedIndex === i"
1457
+ [attr.aria-selected]="selectedIndices.indexOf(i) >= 0"
1197
1458
  [index]="i"
1198
1459
  [class.k-disabled]="itemDisabled(item)"
1199
1460
  >
@@ -1244,6 +1505,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
1244
1505
  args: ['tools']
1245
1506
  }], textField: [{
1246
1507
  type: Input
1508
+ }], selectable: [{
1509
+ type: Input
1247
1510
  }], data: [{
1248
1511
  type: Input
1249
1512
  }], size: [{
@@ -1258,7 +1521,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
1258
1521
  type: Input
1259
1522
  }], selectionChange: [{
1260
1523
  type: Output
1261
- }], actionClick: [{
1524
+ }], action: [{
1262
1525
  type: Output
1263
1526
  }], getChildListbox: [{
1264
1527
  type: Output
@@ -1288,22 +1551,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
1288
1551
  */
1289
1552
  class DataBindingDirective {
1290
1553
  listbox;
1554
+ zone;
1291
1555
  /**
1292
1556
  * Specifies the `ListBoxComponent` instance with which the current ListBox connects.
1293
1557
  * When you link two listboxes through this input, you can transfer items between them.
1294
1558
  */
1295
1559
  connectedWith;
1296
- actionClickSub = new Subscription();
1560
+ actionSub = new Subscription();
1297
1561
  selectedBoxSub = new Subscription();
1298
1562
  connectedWithSub = new Subscription();
1299
1563
  selectedBox;
1300
- constructor(listbox) {
1564
+ constructor(listbox, zone) {
1301
1565
  this.listbox = listbox;
1566
+ this.zone = zone;
1302
1567
  this.selectedBox = this.listbox;
1303
1568
  this.connectedWithSub.add(this.listbox.getChildListbox.subscribe(() => {
1304
1569
  this.listbox.childListbox = this.connectedWith;
1305
1570
  }));
1306
- this.actionClickSub.add(this.listbox.actionClick.subscribe((actionName) => {
1571
+ this.actionSub.add(this.listbox.action.subscribe((actionName) => {
1307
1572
  switch (actionName) {
1308
1573
  case 'moveUp': {
1309
1574
  this.moveVertically('up');
@@ -1314,11 +1579,11 @@ class DataBindingDirective {
1314
1579
  break;
1315
1580
  }
1316
1581
  case 'transferFrom': {
1317
- this.transferItem(this.connectedWith, this.listbox);
1582
+ this.transferSelectedItems(this.connectedWith, this.listbox);
1318
1583
  break;
1319
1584
  }
1320
1585
  case 'transferTo': {
1321
- this.transferItem(this.listbox, this.connectedWith);
1586
+ this.transferSelectedItems(this.listbox, this.connectedWith);
1322
1587
  break;
1323
1588
  }
1324
1589
  case 'transferAllTo': {
@@ -1330,7 +1595,7 @@ class DataBindingDirective {
1330
1595
  break;
1331
1596
  }
1332
1597
  case 'remove': {
1333
- this.removeItem();
1598
+ this.removeSelectedItems();
1334
1599
  break;
1335
1600
  }
1336
1601
  default: {
@@ -1350,11 +1615,37 @@ class DataBindingDirective {
1350
1615
  }
1351
1616
  this.selectedBoxSub.add(this.listbox.selectionChange.subscribe(() => {
1352
1617
  this.selectedBox = this.listbox;
1618
+ const connectedNavService = this.connectedWith.keyboardNavigationService;
1619
+ const connectedSelService = this.connectedWith.selectionService;
1620
+ let lastSelectedIndex = 0;
1621
+ if (connectedSelService.selectedIndices.length > 0) {
1622
+ lastSelectedIndex = connectedSelService.rangeSelectionTargetIndex ?? connectedSelService.lastSelectedOrUnselectedIndex ?? 0;
1623
+ }
1353
1624
  this.connectedWith.clearSelection();
1625
+ if (this.connectedWith.data?.length > 0) {
1626
+ const validIndex = Math.min(lastSelectedIndex, this.connectedWith.data.length - 1);
1627
+ this.updateListBoxIndices(connectedNavService, connectedSelService, validIndex, false);
1628
+ }
1629
+ else {
1630
+ this.updateListBoxIndices(connectedNavService, connectedSelService, 0, false);
1631
+ }
1354
1632
  }));
1355
1633
  this.selectedBoxSub.add(this.connectedWith.selectionChange.subscribe(() => {
1356
1634
  this.selectedBox = this.connectedWith;
1635
+ const listboxNavService = this.listbox.keyboardNavigationService;
1636
+ const listboxSelService = this.listbox.selectionService;
1637
+ let lastSelectedIndex = 0;
1638
+ if (listboxSelService.selectedIndices.length > 0) {
1639
+ lastSelectedIndex = listboxSelService.rangeSelectionTargetIndex ?? listboxSelService.lastSelectedOrUnselectedIndex ?? 0;
1640
+ }
1357
1641
  this.listbox.clearSelection();
1642
+ if (this.listbox.data?.length > 0) {
1643
+ const validIndex = Math.min(lastSelectedIndex, this.listbox.data.length - 1);
1644
+ this.updateListBoxIndices(listboxNavService, listboxSelService, validIndex, false);
1645
+ }
1646
+ else {
1647
+ this.updateListBoxIndices(listboxNavService, listboxSelService, 0, false);
1648
+ }
1358
1649
  }));
1359
1650
  }
1360
1651
  }
@@ -1362,9 +1653,9 @@ class DataBindingDirective {
1362
1653
  * @hidden
1363
1654
  */
1364
1655
  ngOnDestroy() {
1365
- if (this.actionClickSub) {
1366
- this.actionClickSub.unsubscribe();
1367
- this.actionClickSub = null;
1656
+ if (this.actionSub) {
1657
+ this.actionSub.unsubscribe();
1658
+ this.actionSub = null;
1368
1659
  }
1369
1660
  if (this.selectedBoxSub) {
1370
1661
  this.selectedBoxSub.unsubscribe();
@@ -1372,49 +1663,115 @@ class DataBindingDirective {
1372
1663
  }
1373
1664
  }
1374
1665
  moveVertically(dir) {
1375
- const index = this.selectedBox.selectedIndex;
1376
- if (!isPresent(index)) {
1666
+ const selectedIndices = this.selectedBox.selectedIndices;
1667
+ if (!isPresent(selectedIndices) || selectedIndices.length === 0) {
1377
1668
  return;
1378
1669
  }
1379
- const topReached = dir === 'up' && index <= 0;
1380
- const bottomReached = dir === 'down' && index >= this.selectedBox.data.length - 1;
1670
+ const sortedIndices = [...selectedIndices].sort((a, b) => a - b);
1671
+ const topIndex = sortedIndices[0];
1672
+ const bottomIndex = sortedIndices[sortedIndices.length - 1];
1673
+ const topReached = dir === 'up' && topIndex <= 0;
1674
+ const bottomReached = dir === 'down' && bottomIndex >= this.selectedBox.data.length - 1;
1381
1675
  if (topReached || bottomReached) {
1382
1676
  return;
1383
1677
  }
1384
- const newIndex = dir === 'up' ? index - 1 : index + 1;
1678
+ const data = this.selectedBox.data;
1679
+ const newSelectedIndices = [];
1680
+ if (dir === 'up') {
1681
+ for (const index of sortedIndices) {
1682
+ const newIndex = index - 1;
1683
+ [data[newIndex], data[index]] = [data[index], data[newIndex]];
1684
+ newSelectedIndices.push(newIndex);
1685
+ }
1686
+ }
1687
+ else {
1688
+ for (let i = sortedIndices.length - 1; i >= 0; i--) {
1689
+ const index = sortedIndices[i];
1690
+ const newIndex = index + 1;
1691
+ [data[newIndex], data[index]] = [data[index], data[newIndex]];
1692
+ newSelectedIndices.push(newIndex);
1693
+ }
1694
+ }
1695
+ newSelectedIndices.sort((a, b) => a - b);
1696
+ this.selectedBox.selectionService.setSelectedIndices(newSelectedIndices);
1385
1697
  const navigation = this.selectedBox.keyboardNavigationService;
1386
- navigation.focusedListboxItemIndex = navigation.selectedListboxItemIndex = newIndex;
1387
- [this.selectedBox.data[newIndex], this.selectedBox.data[index]] = [this.selectedBox.data[index], this.selectedBox.data[newIndex]];
1388
- this.selectedBox.selectionService.select(newIndex);
1698
+ const currentFocusedIndex = navigation.focusedListboxItemIndex;
1699
+ const focusedItemIndexInSelection = sortedIndices.indexOf(currentFocusedIndex);
1700
+ let newFocusIndex;
1701
+ if (focusedItemIndexInSelection !== -1) {
1702
+ newFocusIndex = newSelectedIndices[focusedItemIndexInSelection];
1703
+ }
1704
+ else {
1705
+ newFocusIndex = dir === 'up' ? topIndex - 1 : bottomIndex + 1;
1706
+ }
1707
+ this.zone.onStable.pipe(take(1)).subscribe(() => {
1708
+ const listboxItems = this.selectedBox.listboxItems.toArray();
1709
+ const previousItem = listboxItems[currentFocusedIndex]?.nativeElement;
1710
+ const currentItem = listboxItems[newFocusIndex]?.nativeElement;
1711
+ navigation.changeTabindex(previousItem, currentItem);
1712
+ navigation.focusedListboxItemIndex = newFocusIndex;
1713
+ navigation.selectedListboxItemIndex = newFocusIndex;
1714
+ });
1389
1715
  }
1390
- removeItem() {
1391
- const index = this.selectedBox.selectedIndex;
1392
- if (!isPresent(index)) {
1716
+ removeSelectedItems() {
1717
+ const itemIndices = this.selectedBox.selectedIndices;
1718
+ if (!isPresent(itemIndices) || itemIndices.length === 0) {
1393
1719
  return;
1394
1720
  }
1395
- this.selectedBox.data.splice(index, 1);
1721
+ this.selectedBox.data = this.selectedBox.data.filter((_, index) => !itemIndices.includes(index));
1396
1722
  this.selectedBox.selectionService.clearSelection();
1397
1723
  }
1398
- transferItem(source, target) {
1399
- const item = source && source.data[source.selectedIndex];
1400
- if (!item || !target || !source) {
1724
+ transferSelectedItems(source, target) {
1725
+ const selectedIndices = source?.data && source?.selectedIndices;
1726
+ if (!target || !source || !isPresent(selectedIndices) || selectedIndices.length === 0) {
1401
1727
  return;
1402
1728
  }
1403
- target.data.push(item);
1404
- source.data.splice(source.selectedIndex, 1);
1729
+ const sourceLastIndex = source.selectionService.rangeSelectionTargetIndex ??
1730
+ source.selectionService.lastSelectedOrUnselectedIndex ??
1731
+ 0;
1732
+ target.data.push(...selectedIndices.map(index => source.data[index]));
1733
+ source.data = source.data.filter((_, index) => !selectedIndices.includes(index));
1405
1734
  source.clearSelection();
1406
- target.selectItem(target.data.length - 1);
1735
+ const removedBeforeAnchor = selectedIndices.filter(i => i < sourceLastIndex).length;
1736
+ const adjustedAnchorIndex = Math.max(0, Math.min(sourceLastIndex - removedBeforeAnchor, source.data.length - 1));
1737
+ const sourceNavService = source.keyboardNavigationService;
1738
+ const sourceSelService = source.selectionService;
1739
+ if (source.data.length > 0) {
1740
+ this.updateListBoxIndices(sourceNavService, sourceSelService, adjustedAnchorIndex);
1741
+ }
1742
+ const targetIndex = target.data.length - 1;
1743
+ target.select([targetIndex]);
1744
+ const targetNavService = target.keyboardNavigationService;
1745
+ const targetSelService = target.selectionService;
1746
+ this.updateListBoxIndices(targetNavService, targetSelService, targetIndex);
1407
1747
  this.selectedBox = target;
1408
1748
  }
1409
1749
  transferAll(source, target) {
1410
- if (!target || !source) {
1750
+ if (!target || !source || source.data?.length === 0) {
1411
1751
  return;
1412
1752
  }
1413
- target.data.splice(target.data.length, 0, ...source.data.splice(0, source.data.length));
1414
- target.selectItem(target.data.length - 1);
1753
+ const itemsToTransfer = source.data.splice(0, source.data.length);
1754
+ target.data.push(...itemsToTransfer);
1755
+ source.clearSelection();
1756
+ const sourceNavService = source.keyboardNavigationService;
1757
+ const sourceSelService = source.selectionService;
1758
+ this.updateListBoxIndices(sourceNavService, sourceSelService, 0);
1759
+ const targetIndex = target.data.length - 1;
1760
+ target.select([targetIndex]);
1761
+ const targetNavService = target.keyboardNavigationService;
1762
+ const targetSelService = target.selectionService;
1763
+ this.updateListBoxIndices(targetNavService, targetSelService, targetIndex);
1415
1764
  this.selectedBox = target;
1416
1765
  }
1417
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DataBindingDirective, deps: [{ token: ListBoxComponent }], target: i0.ɵɵFactoryTarget.Directive });
1766
+ updateListBoxIndices = (keyboardNavService, selectionService, index, setFocusedIndex = true) => {
1767
+ if (setFocusedIndex) {
1768
+ keyboardNavService.focusedListboxItemIndex = index;
1769
+ }
1770
+ keyboardNavService.selectedListboxItemIndex = index;
1771
+ selectionService.rangeSelectionAnchorIndex = index;
1772
+ selectionService.lastSelectedOrUnselectedIndex = index;
1773
+ };
1774
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DataBindingDirective, deps: [{ token: ListBoxComponent }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Directive });
1418
1775
  static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.14", type: DataBindingDirective, isStandalone: true, selector: "[kendoListBoxDataBinding]", inputs: { connectedWith: "connectedWith" }, usesOnChanges: true, ngImport: i0 });
1419
1776
  }
1420
1777
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DataBindingDirective, decorators: [{
@@ -1423,7 +1780,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
1423
1780
  selector: '[kendoListBoxDataBinding]',
1424
1781
  standalone: true
1425
1782
  }]
1426
- }], ctorParameters: () => [{ type: ListBoxComponent }], propDecorators: { connectedWith: [{
1783
+ }], ctorParameters: () => [{ type: ListBoxComponent }, { type: i0.NgZone }], propDecorators: { connectedWith: [{
1427
1784
  type: Input
1428
1785
  }] } });
1429
1786