@progress/kendo-angular-listbox 21.0.0-develop.20 → 21.0.0-develop.22

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.
@@ -52,6 +52,7 @@ export declare class DataBindingDirective implements OnChanges, OnDestroy {
52
52
  private removeSelectedItems;
53
53
  private transferSelectedItems;
54
54
  private transferAll;
55
+ private updateListBoxIndices;
55
56
  static ɵfac: i0.ɵɵFactoryDeclaration<DataBindingDirective, never>;
56
57
  static ɵdir: i0.ɵɵDirectiveDeclaration<DataBindingDirective, "[kendoListBoxDataBinding]", never, { "connectedWith": { "alias": "connectedWith"; "required": false; }; }, {}, never, never, true, never>;
57
58
  }
@@ -98,11 +98,37 @@ export class DataBindingDirective {
98
98
  }
99
99
  this.selectedBoxSub.add(this.listbox.selectionChange.subscribe(() => {
100
100
  this.selectedBox = this.listbox;
101
+ const connectedNavService = this.connectedWith.keyboardNavigationService;
102
+ const connectedSelService = this.connectedWith.selectionService;
103
+ let lastSelectedIndex = 0;
104
+ if (connectedSelService.selectedIndices.length > 0) {
105
+ lastSelectedIndex = connectedSelService.rangeSelectionTargetIndex ?? connectedSelService.lastSelectedOrUnselectedIndex ?? 0;
106
+ }
101
107
  this.connectedWith.clearSelection();
108
+ if (this.connectedWith.data?.length > 0) {
109
+ const validIndex = Math.min(lastSelectedIndex, this.connectedWith.data.length - 1);
110
+ this.updateListBoxIndices(connectedNavService, connectedSelService, validIndex, false);
111
+ }
112
+ else {
113
+ this.updateListBoxIndices(connectedNavService, connectedSelService, 0, false);
114
+ }
102
115
  }));
103
116
  this.selectedBoxSub.add(this.connectedWith.selectionChange.subscribe(() => {
104
117
  this.selectedBox = this.connectedWith;
118
+ const listboxNavService = this.listbox.keyboardNavigationService;
119
+ const listboxSelService = this.listbox.selectionService;
120
+ let lastSelectedIndex = 0;
121
+ if (listboxSelService.selectedIndices.length > 0) {
122
+ lastSelectedIndex = listboxSelService.rangeSelectionTargetIndex ?? listboxSelService.lastSelectedOrUnselectedIndex ?? 0;
123
+ }
105
124
  this.listbox.clearSelection();
125
+ if (this.listbox.data?.length > 0) {
126
+ const validIndex = Math.min(lastSelectedIndex, this.listbox.data.length - 1);
127
+ this.updateListBoxIndices(listboxNavService, listboxSelService, validIndex, false);
128
+ }
129
+ else {
130
+ this.updateListBoxIndices(listboxNavService, listboxSelService, 0, false);
131
+ }
106
132
  }));
107
133
  }
108
134
  }
@@ -183,20 +209,51 @@ export class DataBindingDirective {
183
209
  if (!target || !source || !isPresent(selectedIndices) || selectedIndices.length === 0) {
184
210
  return;
185
211
  }
212
+ const sourceLastIndex = source.selectionService.rangeSelectionTargetIndex ??
213
+ source.selectionService.lastSelectedOrUnselectedIndex ??
214
+ 0;
186
215
  target.data.push(...selectedIndices.map(index => source.data[index]));
187
216
  source.data = source.data.filter((_, index) => !selectedIndices.includes(index));
188
217
  source.clearSelection();
189
- target.select([target.data.length - 1]);
218
+ const removedBeforeAnchor = selectedIndices.filter(i => i < sourceLastIndex).length;
219
+ const adjustedAnchorIndex = Math.max(0, Math.min(sourceLastIndex - removedBeforeAnchor, source.data.length - 1));
220
+ const sourceNavService = source.keyboardNavigationService;
221
+ const sourceSelService = source.selectionService;
222
+ if (source.data.length > 0) {
223
+ this.updateListBoxIndices(sourceNavService, sourceSelService, adjustedAnchorIndex);
224
+ }
225
+ const targetIndex = target.data.length - 1;
226
+ target.select([targetIndex]);
227
+ const targetNavService = target.keyboardNavigationService;
228
+ const targetSelService = target.selectionService;
229
+ this.updateListBoxIndices(targetNavService, targetSelService, targetIndex);
190
230
  this.selectedBox = target;
191
231
  }
192
232
  transferAll(source, target) {
193
- if (!target || !source) {
233
+ if (!target || !source || source.data?.length === 0) {
194
234
  return;
195
235
  }
196
- target.data.splice(target.data.length, 0, ...source.data.splice(0, source.data.length));
197
- target.select([target.data.length - 1]);
236
+ const itemsToTransfer = source.data.splice(0, source.data.length);
237
+ target.data.push(...itemsToTransfer);
238
+ source.clearSelection();
239
+ const sourceNavService = source.keyboardNavigationService;
240
+ const sourceSelService = source.selectionService;
241
+ this.updateListBoxIndices(sourceNavService, sourceSelService, 0);
242
+ const targetIndex = target.data.length - 1;
243
+ target.select([targetIndex]);
244
+ const targetNavService = target.keyboardNavigationService;
245
+ const targetSelService = target.selectionService;
246
+ this.updateListBoxIndices(targetNavService, targetSelService, targetIndex);
198
247
  this.selectedBox = target;
199
248
  }
249
+ updateListBoxIndices = (keyboardNavService, selectionService, index, setFocusedIndex = true) => {
250
+ if (setFocusedIndex) {
251
+ keyboardNavService.focusedListboxItemIndex = index;
252
+ }
253
+ keyboardNavService.selectedListboxItemIndex = index;
254
+ selectionService.rangeSelectionAnchorIndex = index;
255
+ selectionService.lastSelectedOrUnselectedIndex = index;
256
+ };
200
257
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DataBindingDirective, deps: [{ token: i1.ListBoxComponent }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Directive });
201
258
  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 });
202
259
  }
@@ -28,7 +28,7 @@ export class KeyboardNavigationService {
28
28
  this.renderer = renderer;
29
29
  this.zone = zone;
30
30
  }
31
- onKeyDown(event, toolsRef, toolbar, childListbox, parentListbox, listboxItems) {
31
+ onKeyDown(event, toolsRef, toolbar, childListbox, parentListbox, listboxItems, currentListbox) {
32
32
  const target = event.target;
33
33
  const keyCode = normalizeNumpadKeys(event);
34
34
  const ctrlOrMetaKey = event.ctrlKey || event.metaKey;
@@ -68,7 +68,7 @@ export class KeyboardNavigationService {
68
68
  isTransferToolVisible = activeToolbar.some(tool => tool.name.startsWith('transfer'));
69
69
  }
70
70
  if ((keyCode === Keys.ArrowRight || keyCode === Keys.ArrowLeft) && ctrlOrMetaKey && isTransferToolVisible) {
71
- this.onArrowLeftOrRight(keyCode, parentListbox, childListbox, event, listboxItems);
71
+ this.onArrowLeftOrRight(keyCode, parentListbox, childListbox, event, listboxItems, currentListbox);
72
72
  }
73
73
  else if ((keyCode === Keys.ArrowUp || keyCode === Keys.ArrowDown)) {
74
74
  this.onArrowUpOrDown(keyCode, ctrlOrMetaKey, event, activeToolbar, listboxItems);
@@ -122,9 +122,7 @@ export class KeyboardNavigationService {
122
122
  ctrlKey,
123
123
  shiftKey
124
124
  });
125
- if (!shiftKey) {
126
- this.selectedListboxItemIndex = this.focusedListboxItemIndex;
127
- }
125
+ this.selectedListboxItemIndex = this.focusedListboxItemIndex;
128
126
  }
129
127
  onArrowUpOrDown(keyCode, ctrlOrMetaKey, event, activeToolbar, listboxItems) {
130
128
  event.preventDefault();
@@ -149,13 +147,16 @@ export class KeyboardNavigationService {
149
147
  this.onSelectionChange.emit({ index: this.selectedListboxItemIndex, prevIndex: this.focusedListboxItemIndex });
150
148
  this.focusedListboxItemIndex = this.selectedListboxItemIndex;
151
149
  }
152
- onArrowLeftOrRight(keyCode, parentListbox, childListbox, event, listboxItems) {
150
+ onArrowLeftOrRight(keyCode, parentListbox, childListbox, event, listboxItems, currentListbox) {
153
151
  event.preventDefault();
154
152
  if (event.shiftKey) {
155
153
  this.transferAllItems(keyCode, childListbox, parentListbox);
156
154
  return;
157
155
  }
158
- if (this.selectedListboxItemIndex >= 0) {
156
+ const isArrowRight = keyCode === Keys.ArrowRight;
157
+ const sourceListbox = isArrowRight ? currentListbox : childListbox || parentListbox?.childListbox;
158
+ const hasSelection = sourceListbox?.selectedIndices && sourceListbox.selectedIndices.length > 0;
159
+ if (hasSelection) {
159
160
  this.transferItem(keyCode, childListbox, parentListbox, listboxItems);
160
161
  }
161
162
  }
@@ -313,19 +314,17 @@ export class KeyboardNavigationService {
313
314
  }
314
315
  }
315
316
  onArrowDown(listboxItems) {
316
- if (this.selectedListboxItemIndex < listboxItems.length - 1) {
317
- const offset = this.selectedListboxItemIndex ? this.selectedListboxItemIndex : this.focusedListboxItemIndex;
318
- this.selectedListboxItemIndex = offset + 1;
319
- const previousItem = listboxItems[this.selectedListboxItemIndex - 1]?.nativeElement;
317
+ if (this.focusedListboxItemIndex < listboxItems.length - 1) {
318
+ this.selectedListboxItemIndex = this.focusedListboxItemIndex + 1;
319
+ const previousItem = listboxItems[this.focusedListboxItemIndex]?.nativeElement;
320
320
  const currentItem = listboxItems[this.selectedListboxItemIndex]?.nativeElement;
321
321
  this.changeTabindex(previousItem, currentItem);
322
322
  }
323
323
  }
324
324
  onArrowUp(listboxItems) {
325
- if (this.selectedListboxItemIndex > 0 || this.focusedListboxItemIndex > 0) {
326
- const offset = this.selectedListboxItemIndex ? this.selectedListboxItemIndex : this.focusedListboxItemIndex;
327
- this.selectedListboxItemIndex = offset - 1;
328
- const previousItem = listboxItems[this.selectedListboxItemIndex + 1]?.nativeElement;
325
+ if (this.focusedListboxItemIndex > 0) {
326
+ this.selectedListboxItemIndex = this.focusedListboxItemIndex - 1;
327
+ const previousItem = listboxItems[this.focusedListboxItemIndex]?.nativeElement;
329
328
  const currentItem = listboxItems[this.selectedListboxItemIndex]?.nativeElement;
330
329
  this.changeTabindex(previousItem, currentItem);
331
330
  }
@@ -4,7 +4,7 @@
4
4
  *-------------------------------------------------------------------------------------------*/
5
5
  /* eslint-disable @typescript-eslint/no-inferrable-types */
6
6
  /* eslint-disable @typescript-eslint/no-explicit-any */
7
- import { Component, ContentChild, ElementRef, EventEmitter, HostBinding, Input, isDevMode, NgZone, Output, QueryList, Renderer2, ViewChild, ViewChildren } from '@angular/core';
7
+ import { ChangeDetectorRef, Component, ContentChild, ElementRef, EventEmitter, HostBinding, Input, isDevMode, NgZone, Output, QueryList, Renderer2, ViewChild, ViewChildren } from '@angular/core';
8
8
  import { validatePackage } from '@progress/kendo-licensing';
9
9
  import { Subscription } from 'rxjs';
10
10
  import { packageMetadata } from './package-metadata';
@@ -62,6 +62,7 @@ export class ListBoxComponent {
62
62
  renderer;
63
63
  zone;
64
64
  localization;
65
+ cdr;
65
66
  /**
66
67
  * @hidden
67
68
  */
@@ -218,13 +219,14 @@ export class ListBoxComponent {
218
219
  subs = new Subscription();
219
220
  shouldFireFocusIn = false;
220
221
  _selectable = 'single';
221
- constructor(keyboardNavigationService, selectionService, hostElement, renderer, zone, localization) {
222
+ constructor(keyboardNavigationService, selectionService, hostElement, renderer, zone, localization, cdr) {
222
223
  this.keyboardNavigationService = keyboardNavigationService;
223
224
  this.selectionService = selectionService;
224
225
  this.hostElement = hostElement;
225
226
  this.renderer = renderer;
226
227
  this.zone = zone;
227
228
  this.localization = localization;
229
+ this.cdr = cdr;
228
230
  validatePackage(packageMetadata);
229
231
  this.setToolbarClass(DEFAULT_TOOLBAR_POSITION);
230
232
  this.setSizingClass(this.size);
@@ -260,6 +262,7 @@ export class ListBoxComponent {
260
262
  const isListboxChild = this.parentListbox && !this.childListbox;
261
263
  const isListboxParentAndChild = !!(this.parentListbox && this.childListbox);
262
264
  const isListboxParent = !!(this.childListbox || (!this.childListbox && !this.parentListbox));
265
+ this.shouldFireFocusIn = false;
263
266
  if (isListboxChild || (isListboxParentAndChild && isActionTransferFrom)) {
264
267
  this.parentListbox.action.next(actionName);
265
268
  }
@@ -276,6 +279,9 @@ export class ListBoxComponent {
276
279
  const currentTool = toolsRef[navService.focusedToolIndex]?.element;
277
280
  navService.changeTabindex(prevTool, currentTool);
278
281
  }
282
+ this.zone.runOutsideAngular(() => setTimeout(() => {
283
+ this.shouldFireFocusIn = true;
284
+ }));
279
285
  }
280
286
  /**
281
287
  * Selects multiple ListBox nodes programmatically.
@@ -356,16 +362,17 @@ export class ListBoxComponent {
356
362
  const newFocusIndex = isPresent(this.selectionService.rangeSelectionTargetIndex)
357
363
  ? this.selectionService.rangeSelectionTargetIndex
358
364
  : this.selectionService.lastSelectedOrUnselectedIndex;
359
- if (newFocusIndex !== null && newFocusIndex !== undefined) {
365
+ if (isPresent(newFocusIndex)) {
360
366
  const listboxItems = this.listboxItems.toArray();
361
367
  const previousItem = listboxItems[navService.focusedListboxItemIndex]?.nativeElement;
362
368
  const currentItem = listboxItems[newFocusIndex]?.nativeElement;
363
- if (previousItem !== currentItem && currentItem) {
369
+ if (previousItem && currentItem) {
364
370
  navService.changeTabindex(previousItem, currentItem, false);
365
371
  navService.focusedListboxItemIndex = newFocusIndex;
366
372
  navService.selectedListboxItemIndex = newFocusIndex;
367
373
  }
368
374
  }
375
+ this.cdr.markForCheck();
369
376
  this.zone.runOutsideAngular(() => setTimeout(() => {
370
377
  this.shouldFireFocusIn = true;
371
378
  }));
@@ -391,6 +398,7 @@ export class ListBoxComponent {
391
398
  deselectedIndices: null
392
399
  });
393
400
  }
401
+ this.cdr.markForCheck();
394
402
  }
395
403
  }));
396
404
  this.subs.add(navService.onSelectToEnd.subscribe(({ direction }) => {
@@ -410,6 +418,7 @@ export class ListBoxComponent {
410
418
  const targetItem = listboxItems[targetIndex]?.nativeElement;
411
419
  navService.changeTabindex(currentItem, targetItem);
412
420
  navService.focusedListboxItemIndex = targetIndex;
421
+ this.cdr.markForCheck();
413
422
  this.zone.runOutsideAngular(() => setTimeout(() => {
414
423
  this.shouldFireFocusIn = true;
415
424
  }));
@@ -418,7 +427,7 @@ export class ListBoxComponent {
418
427
  if (this.listboxElement) {
419
428
  this.subs.add(this.renderer.listen(this.listboxElement.nativeElement, 'focusin', (event) => this.onFocusIn(event)));
420
429
  }
421
- this.subs.add(this.renderer.listen(hostEl, 'keydown', (event) => navService.onKeyDown(event, toolsRef, this.selectedTools, this.childListbox, this.parentListbox, this.listboxItems.toArray())));
430
+ this.subs.add(this.renderer.listen(hostEl, 'keydown', (event) => navService.onKeyDown(event, toolsRef, this.selectedTools, this.childListbox, this.parentListbox, this.listboxItems.toArray(), this)));
422
431
  this.subs.add(navService.onSelectionChange.subscribe((event) => {
423
432
  const { index, ctrlKey, shiftKey } = event;
424
433
  this.selectionService.select(index, ctrlKey, shiftKey);
@@ -510,7 +519,7 @@ export class ListBoxComponent {
510
519
  setSizingClass(size) {
511
520
  this.renderer.addClass(this.hostElement.nativeElement, `k-listbox-${sizeClassMap[size]}`);
512
521
  }
513
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ListBoxComponent, deps: [{ token: i1.KeyboardNavigationService }, { token: i2.ListBoxSelectionService }, { token: i0.ElementRef }, { token: i0.Renderer2 }, { token: i0.NgZone }, { token: i3.LocalizationService }], target: i0.ɵɵFactoryTarget.Component });
522
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ListBoxComponent, deps: [{ token: i1.KeyboardNavigationService }, { token: i2.ListBoxSelectionService }, { token: i0.ElementRef }, { token: i0.Renderer2 }, { token: i0.NgZone }, { token: i3.LocalizationService }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
514
523
  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: [
515
524
  ListBoxSelectionService,
516
525
  KeyboardNavigationService,
@@ -722,7 +731,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
722
731
  standalone: true,
723
732
  imports: [LocalizedMessagesDirective, NgIf, NgFor, ButtonComponent, ItemSelectableDirective, TemplateContextDirective]
724
733
  }]
725
- }], ctorParameters: () => [{ type: i1.KeyboardNavigationService }, { type: i2.ListBoxSelectionService }, { type: i0.ElementRef }, { type: i0.Renderer2 }, { type: i0.NgZone }, { type: i3.LocalizationService }], propDecorators: { listboxClassName: [{
734
+ }], ctorParameters: () => [{ type: i1.KeyboardNavigationService }, { type: i2.ListBoxSelectionService }, { type: i0.ElementRef }, { type: i0.Renderer2 }, { type: i0.NgZone }, { type: i3.LocalizationService }, { type: i0.ChangeDetectorRef }], propDecorators: { listboxClassName: [{
726
735
  type: HostBinding,
727
736
  args: ['class.k-listbox']
728
737
  }], direction: [{
@@ -10,7 +10,7 @@ export const packageMetadata = {
10
10
  productName: 'Kendo UI for Angular',
11
11
  productCode: 'KENDOUIANGULAR',
12
12
  productCodes: ['KENDOUIANGULAR'],
13
- publishDate: 1762335746,
14
- version: '21.0.0-develop.20',
13
+ publishDate: 1762432333,
14
+ version: '21.0.0-develop.22',
15
15
  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'
16
16
  };
@@ -25,8 +25,8 @@ const packageMetadata = {
25
25
  productName: 'Kendo UI for Angular',
26
26
  productCode: 'KENDOUIANGULAR',
27
27
  productCodes: ['KENDOUIANGULAR'],
28
- publishDate: 1762335746,
29
- version: '21.0.0-develop.20',
28
+ publishDate: 1762432333,
29
+ version: '21.0.0-develop.22',
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
 
@@ -335,7 +335,7 @@ class KeyboardNavigationService {
335
335
  this.renderer = renderer;
336
336
  this.zone = zone;
337
337
  }
338
- onKeyDown(event, toolsRef, toolbar, childListbox, parentListbox, listboxItems) {
338
+ onKeyDown(event, toolsRef, toolbar, childListbox, parentListbox, listboxItems, currentListbox) {
339
339
  const target = event.target;
340
340
  const keyCode = normalizeNumpadKeys(event);
341
341
  const ctrlOrMetaKey = event.ctrlKey || event.metaKey;
@@ -375,7 +375,7 @@ class KeyboardNavigationService {
375
375
  isTransferToolVisible = activeToolbar.some(tool => tool.name.startsWith('transfer'));
376
376
  }
377
377
  if ((keyCode === Keys.ArrowRight || keyCode === Keys.ArrowLeft) && ctrlOrMetaKey && isTransferToolVisible) {
378
- this.onArrowLeftOrRight(keyCode, parentListbox, childListbox, event, listboxItems);
378
+ this.onArrowLeftOrRight(keyCode, parentListbox, childListbox, event, listboxItems, currentListbox);
379
379
  }
380
380
  else if ((keyCode === Keys.ArrowUp || keyCode === Keys.ArrowDown)) {
381
381
  this.onArrowUpOrDown(keyCode, ctrlOrMetaKey, event, activeToolbar, listboxItems);
@@ -429,9 +429,7 @@ class KeyboardNavigationService {
429
429
  ctrlKey,
430
430
  shiftKey
431
431
  });
432
- if (!shiftKey) {
433
- this.selectedListboxItemIndex = this.focusedListboxItemIndex;
434
- }
432
+ this.selectedListboxItemIndex = this.focusedListboxItemIndex;
435
433
  }
436
434
  onArrowUpOrDown(keyCode, ctrlOrMetaKey, event, activeToolbar, listboxItems) {
437
435
  event.preventDefault();
@@ -456,13 +454,16 @@ class KeyboardNavigationService {
456
454
  this.onSelectionChange.emit({ index: this.selectedListboxItemIndex, prevIndex: this.focusedListboxItemIndex });
457
455
  this.focusedListboxItemIndex = this.selectedListboxItemIndex;
458
456
  }
459
- onArrowLeftOrRight(keyCode, parentListbox, childListbox, event, listboxItems) {
457
+ onArrowLeftOrRight(keyCode, parentListbox, childListbox, event, listboxItems, currentListbox) {
460
458
  event.preventDefault();
461
459
  if (event.shiftKey) {
462
460
  this.transferAllItems(keyCode, childListbox, parentListbox);
463
461
  return;
464
462
  }
465
- 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) {
466
467
  this.transferItem(keyCode, childListbox, parentListbox, listboxItems);
467
468
  }
468
469
  }
@@ -620,19 +621,17 @@ class KeyboardNavigationService {
620
621
  }
621
622
  }
622
623
  onArrowDown(listboxItems) {
623
- if (this.selectedListboxItemIndex < listboxItems.length - 1) {
624
- const offset = this.selectedListboxItemIndex ? this.selectedListboxItemIndex : this.focusedListboxItemIndex;
625
- this.selectedListboxItemIndex = offset + 1;
626
- 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;
627
627
  const currentItem = listboxItems[this.selectedListboxItemIndex]?.nativeElement;
628
628
  this.changeTabindex(previousItem, currentItem);
629
629
  }
630
630
  }
631
631
  onArrowUp(listboxItems) {
632
- if (this.selectedListboxItemIndex > 0 || this.focusedListboxItemIndex > 0) {
633
- const offset = this.selectedListboxItemIndex ? this.selectedListboxItemIndex : this.focusedListboxItemIndex;
634
- this.selectedListboxItemIndex = offset - 1;
635
- 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;
636
635
  const currentItem = listboxItems[this.selectedListboxItemIndex]?.nativeElement;
637
636
  this.changeTabindex(previousItem, currentItem);
638
637
  }
@@ -813,6 +812,7 @@ class ListBoxComponent {
813
812
  renderer;
814
813
  zone;
815
814
  localization;
815
+ cdr;
816
816
  /**
817
817
  * @hidden
818
818
  */
@@ -969,13 +969,14 @@ class ListBoxComponent {
969
969
  subs = new Subscription();
970
970
  shouldFireFocusIn = false;
971
971
  _selectable = 'single';
972
- constructor(keyboardNavigationService, selectionService, hostElement, renderer, zone, localization) {
972
+ constructor(keyboardNavigationService, selectionService, hostElement, renderer, zone, localization, cdr) {
973
973
  this.keyboardNavigationService = keyboardNavigationService;
974
974
  this.selectionService = selectionService;
975
975
  this.hostElement = hostElement;
976
976
  this.renderer = renderer;
977
977
  this.zone = zone;
978
978
  this.localization = localization;
979
+ this.cdr = cdr;
979
980
  validatePackage(packageMetadata);
980
981
  this.setToolbarClass(DEFAULT_TOOLBAR_POSITION);
981
982
  this.setSizingClass(this.size);
@@ -1011,6 +1012,7 @@ class ListBoxComponent {
1011
1012
  const isListboxChild = this.parentListbox && !this.childListbox;
1012
1013
  const isListboxParentAndChild = !!(this.parentListbox && this.childListbox);
1013
1014
  const isListboxParent = !!(this.childListbox || (!this.childListbox && !this.parentListbox));
1015
+ this.shouldFireFocusIn = false;
1014
1016
  if (isListboxChild || (isListboxParentAndChild && isActionTransferFrom)) {
1015
1017
  this.parentListbox.action.next(actionName);
1016
1018
  }
@@ -1027,6 +1029,9 @@ class ListBoxComponent {
1027
1029
  const currentTool = toolsRef[navService.focusedToolIndex]?.element;
1028
1030
  navService.changeTabindex(prevTool, currentTool);
1029
1031
  }
1032
+ this.zone.runOutsideAngular(() => setTimeout(() => {
1033
+ this.shouldFireFocusIn = true;
1034
+ }));
1030
1035
  }
1031
1036
  /**
1032
1037
  * Selects multiple ListBox nodes programmatically.
@@ -1107,16 +1112,17 @@ class ListBoxComponent {
1107
1112
  const newFocusIndex = isPresent$1(this.selectionService.rangeSelectionTargetIndex)
1108
1113
  ? this.selectionService.rangeSelectionTargetIndex
1109
1114
  : this.selectionService.lastSelectedOrUnselectedIndex;
1110
- if (newFocusIndex !== null && newFocusIndex !== undefined) {
1115
+ if (isPresent$1(newFocusIndex)) {
1111
1116
  const listboxItems = this.listboxItems.toArray();
1112
1117
  const previousItem = listboxItems[navService.focusedListboxItemIndex]?.nativeElement;
1113
1118
  const currentItem = listboxItems[newFocusIndex]?.nativeElement;
1114
- if (previousItem !== currentItem && currentItem) {
1119
+ if (previousItem && currentItem) {
1115
1120
  navService.changeTabindex(previousItem, currentItem, false);
1116
1121
  navService.focusedListboxItemIndex = newFocusIndex;
1117
1122
  navService.selectedListboxItemIndex = newFocusIndex;
1118
1123
  }
1119
1124
  }
1125
+ this.cdr.markForCheck();
1120
1126
  this.zone.runOutsideAngular(() => setTimeout(() => {
1121
1127
  this.shouldFireFocusIn = true;
1122
1128
  }));
@@ -1142,6 +1148,7 @@ class ListBoxComponent {
1142
1148
  deselectedIndices: null
1143
1149
  });
1144
1150
  }
1151
+ this.cdr.markForCheck();
1145
1152
  }
1146
1153
  }));
1147
1154
  this.subs.add(navService.onSelectToEnd.subscribe(({ direction }) => {
@@ -1161,6 +1168,7 @@ class ListBoxComponent {
1161
1168
  const targetItem = listboxItems[targetIndex]?.nativeElement;
1162
1169
  navService.changeTabindex(currentItem, targetItem);
1163
1170
  navService.focusedListboxItemIndex = targetIndex;
1171
+ this.cdr.markForCheck();
1164
1172
  this.zone.runOutsideAngular(() => setTimeout(() => {
1165
1173
  this.shouldFireFocusIn = true;
1166
1174
  }));
@@ -1169,7 +1177,7 @@ class ListBoxComponent {
1169
1177
  if (this.listboxElement) {
1170
1178
  this.subs.add(this.renderer.listen(this.listboxElement.nativeElement, 'focusin', (event) => this.onFocusIn(event)));
1171
1179
  }
1172
- this.subs.add(this.renderer.listen(hostEl, 'keydown', (event) => navService.onKeyDown(event, toolsRef, this.selectedTools, this.childListbox, this.parentListbox, this.listboxItems.toArray())));
1180
+ this.subs.add(this.renderer.listen(hostEl, 'keydown', (event) => navService.onKeyDown(event, toolsRef, this.selectedTools, this.childListbox, this.parentListbox, this.listboxItems.toArray(), this)));
1173
1181
  this.subs.add(navService.onSelectionChange.subscribe((event) => {
1174
1182
  const { index, ctrlKey, shiftKey } = event;
1175
1183
  this.selectionService.select(index, ctrlKey, shiftKey);
@@ -1261,7 +1269,7 @@ class ListBoxComponent {
1261
1269
  setSizingClass(size) {
1262
1270
  this.renderer.addClass(this.hostElement.nativeElement, `k-listbox-${sizeClassMap[size]}`);
1263
1271
  }
1264
- 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 }], target: i0.ɵɵFactoryTarget.Component });
1272
+ 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 });
1265
1273
  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: [
1266
1274
  ListBoxSelectionService,
1267
1275
  KeyboardNavigationService,
@@ -1473,7 +1481,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
1473
1481
  standalone: true,
1474
1482
  imports: [LocalizedMessagesDirective, NgIf, NgFor, ButtonComponent, ItemSelectableDirective, TemplateContextDirective]
1475
1483
  }]
1476
- }], ctorParameters: () => [{ type: KeyboardNavigationService }, { type: ListBoxSelectionService }, { type: i0.ElementRef }, { type: i0.Renderer2 }, { type: i0.NgZone }, { type: i1.LocalizationService }], propDecorators: { listboxClassName: [{
1484
+ }], ctorParameters: () => [{ type: KeyboardNavigationService }, { type: ListBoxSelectionService }, { type: i0.ElementRef }, { type: i0.Renderer2 }, { type: i0.NgZone }, { type: i1.LocalizationService }, { type: i0.ChangeDetectorRef }], propDecorators: { listboxClassName: [{
1477
1485
  type: HostBinding,
1478
1486
  args: ['class.k-listbox']
1479
1487
  }], direction: [{
@@ -1606,11 +1614,37 @@ class DataBindingDirective {
1606
1614
  }
1607
1615
  this.selectedBoxSub.add(this.listbox.selectionChange.subscribe(() => {
1608
1616
  this.selectedBox = this.listbox;
1617
+ const connectedNavService = this.connectedWith.keyboardNavigationService;
1618
+ const connectedSelService = this.connectedWith.selectionService;
1619
+ let lastSelectedIndex = 0;
1620
+ if (connectedSelService.selectedIndices.length > 0) {
1621
+ lastSelectedIndex = connectedSelService.rangeSelectionTargetIndex ?? connectedSelService.lastSelectedOrUnselectedIndex ?? 0;
1622
+ }
1609
1623
  this.connectedWith.clearSelection();
1624
+ if (this.connectedWith.data?.length > 0) {
1625
+ const validIndex = Math.min(lastSelectedIndex, this.connectedWith.data.length - 1);
1626
+ this.updateListBoxIndices(connectedNavService, connectedSelService, validIndex, false);
1627
+ }
1628
+ else {
1629
+ this.updateListBoxIndices(connectedNavService, connectedSelService, 0, false);
1630
+ }
1610
1631
  }));
1611
1632
  this.selectedBoxSub.add(this.connectedWith.selectionChange.subscribe(() => {
1612
1633
  this.selectedBox = this.connectedWith;
1634
+ const listboxNavService = this.listbox.keyboardNavigationService;
1635
+ const listboxSelService = this.listbox.selectionService;
1636
+ let lastSelectedIndex = 0;
1637
+ if (listboxSelService.selectedIndices.length > 0) {
1638
+ lastSelectedIndex = listboxSelService.rangeSelectionTargetIndex ?? listboxSelService.lastSelectedOrUnselectedIndex ?? 0;
1639
+ }
1613
1640
  this.listbox.clearSelection();
1641
+ if (this.listbox.data?.length > 0) {
1642
+ const validIndex = Math.min(lastSelectedIndex, this.listbox.data.length - 1);
1643
+ this.updateListBoxIndices(listboxNavService, listboxSelService, validIndex, false);
1644
+ }
1645
+ else {
1646
+ this.updateListBoxIndices(listboxNavService, listboxSelService, 0, false);
1647
+ }
1614
1648
  }));
1615
1649
  }
1616
1650
  }
@@ -1691,20 +1725,51 @@ class DataBindingDirective {
1691
1725
  if (!target || !source || !isPresent(selectedIndices) || selectedIndices.length === 0) {
1692
1726
  return;
1693
1727
  }
1728
+ const sourceLastIndex = source.selectionService.rangeSelectionTargetIndex ??
1729
+ source.selectionService.lastSelectedOrUnselectedIndex ??
1730
+ 0;
1694
1731
  target.data.push(...selectedIndices.map(index => source.data[index]));
1695
1732
  source.data = source.data.filter((_, index) => !selectedIndices.includes(index));
1696
1733
  source.clearSelection();
1697
- target.select([target.data.length - 1]);
1734
+ const removedBeforeAnchor = selectedIndices.filter(i => i < sourceLastIndex).length;
1735
+ const adjustedAnchorIndex = Math.max(0, Math.min(sourceLastIndex - removedBeforeAnchor, source.data.length - 1));
1736
+ const sourceNavService = source.keyboardNavigationService;
1737
+ const sourceSelService = source.selectionService;
1738
+ if (source.data.length > 0) {
1739
+ this.updateListBoxIndices(sourceNavService, sourceSelService, adjustedAnchorIndex);
1740
+ }
1741
+ const targetIndex = target.data.length - 1;
1742
+ target.select([targetIndex]);
1743
+ const targetNavService = target.keyboardNavigationService;
1744
+ const targetSelService = target.selectionService;
1745
+ this.updateListBoxIndices(targetNavService, targetSelService, targetIndex);
1698
1746
  this.selectedBox = target;
1699
1747
  }
1700
1748
  transferAll(source, target) {
1701
- if (!target || !source) {
1749
+ if (!target || !source || source.data?.length === 0) {
1702
1750
  return;
1703
1751
  }
1704
- target.data.splice(target.data.length, 0, ...source.data.splice(0, source.data.length));
1705
- target.select([target.data.length - 1]);
1752
+ const itemsToTransfer = source.data.splice(0, source.data.length);
1753
+ target.data.push(...itemsToTransfer);
1754
+ source.clearSelection();
1755
+ const sourceNavService = source.keyboardNavigationService;
1756
+ const sourceSelService = source.selectionService;
1757
+ this.updateListBoxIndices(sourceNavService, sourceSelService, 0);
1758
+ const targetIndex = target.data.length - 1;
1759
+ target.select([targetIndex]);
1760
+ const targetNavService = target.keyboardNavigationService;
1761
+ const targetSelService = target.selectionService;
1762
+ this.updateListBoxIndices(targetNavService, targetSelService, targetIndex);
1706
1763
  this.selectedBox = target;
1707
1764
  }
1765
+ updateListBoxIndices = (keyboardNavService, selectionService, index, setFocusedIndex = true) => {
1766
+ if (setFocusedIndex) {
1767
+ keyboardNavService.focusedListboxItemIndex = index;
1768
+ }
1769
+ keyboardNavService.selectedListboxItemIndex = index;
1770
+ selectionService.rangeSelectionAnchorIndex = index;
1771
+ selectionService.lastSelectedOrUnselectedIndex = index;
1772
+ };
1708
1773
  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 });
1709
1774
  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 });
1710
1775
  }
@@ -36,7 +36,7 @@ export declare class KeyboardNavigationService {
36
36
  direction: 'home' | 'end';
37
37
  }>;
38
38
  constructor(renderer: Renderer2, zone: NgZone);
39
- onKeyDown(event: any, toolsRef: Array<Button>, toolbar: Tool[], childListbox: ListBoxComponent, parentListbox: ListBoxComponent, listboxItems: Array<ElementRef>): void;
39
+ onKeyDown(event: any, toolsRef: Array<Button>, toolbar: Tool[], childListbox: ListBoxComponent, parentListbox: ListBoxComponent, listboxItems: Array<ElementRef>, currentListbox: ListBoxComponent): void;
40
40
  changeTabindex(previousItem: HTMLElement, currentItem: HTMLElement, shouldBlur?: boolean): void;
41
41
  private handleToolbarArrows;
42
42
  private onSpaceKey;
@@ -2,7 +2,7 @@
2
2
  * Copyright © 2025 Progress Software Corporation. All rights reserved.
3
3
  * Licensed under commercial license. See LICENSE.md in the project root for more information
4
4
  *-------------------------------------------------------------------------------------------*/
5
- import { AfterViewInit, ElementRef, EventEmitter, NgZone, OnDestroy, OnInit, QueryList, Renderer2 } from '@angular/core';
5
+ import { AfterViewInit, ChangeDetectorRef, ElementRef, EventEmitter, NgZone, OnDestroy, OnInit, QueryList, Renderer2 } from '@angular/core';
6
6
  import { ListBoxSelectionEvent, ListBoxSelectionService } from './selection.service';
7
7
  import { ItemTemplateDirective } from './item-template.directive';
8
8
  import { Direction } from './util';
@@ -49,6 +49,7 @@ export declare class ListBoxComponent implements OnInit, AfterViewInit, OnDestro
49
49
  private renderer;
50
50
  private zone;
51
51
  private localization;
52
+ private cdr;
52
53
  /**
53
54
  * @hidden
54
55
  */
@@ -177,7 +178,7 @@ export declare class ListBoxComponent implements OnInit, AfterViewInit, OnDestro
177
178
  private subs;
178
179
  private shouldFireFocusIn;
179
180
  private _selectable;
180
- constructor(keyboardNavigationService: KeyboardNavigationService, selectionService: ListBoxSelectionService, hostElement: ElementRef, renderer: Renderer2, zone: NgZone, localization: LocalizationService);
181
+ constructor(keyboardNavigationService: KeyboardNavigationService, selectionService: ListBoxSelectionService, hostElement: ElementRef, renderer: Renderer2, zone: NgZone, localization: LocalizationService, cdr: ChangeDetectorRef);
181
182
  ngOnInit(): void;
182
183
  ngAfterViewInit(): void;
183
184
  ngOnDestroy(): void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@progress/kendo-angular-listbox",
3
- "version": "21.0.0-develop.20",
3
+ "version": "21.0.0-develop.22",
4
4
  "description": "Kendo UI for Angular ListBox",
5
5
  "license": "SEE LICENSE IN LICENSE.md",
6
6
  "author": "Progress",
@@ -44,7 +44,7 @@
44
44
  "package": {
45
45
  "productName": "Kendo UI for Angular",
46
46
  "productCode": "KENDOUIANGULAR",
47
- "publishDate": 1762335746,
47
+ "publishDate": 1762432333,
48
48
  "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"
49
49
  }
50
50
  },
@@ -54,14 +54,14 @@
54
54
  "@angular/core": "18 - 20",
55
55
  "@angular/platform-browser": "18 - 20",
56
56
  "@progress/kendo-licensing": "^1.7.0",
57
- "@progress/kendo-angular-buttons": "21.0.0-develop.20",
58
- "@progress/kendo-angular-common": "21.0.0-develop.20",
59
- "@progress/kendo-angular-popup": "21.0.0-develop.20",
57
+ "@progress/kendo-angular-buttons": "21.0.0-develop.22",
58
+ "@progress/kendo-angular-common": "21.0.0-develop.22",
59
+ "@progress/kendo-angular-popup": "21.0.0-develop.22",
60
60
  "rxjs": "^6.5.3 || ^7.0.0"
61
61
  },
62
62
  "dependencies": {
63
63
  "tslib": "^2.3.1",
64
- "@progress/kendo-angular-schematics": "21.0.0-develop.20",
64
+ "@progress/kendo-angular-schematics": "21.0.0-develop.22",
65
65
  "@progress/kendo-common": "^1.0.1",
66
66
  "node-html-parser": "^7.0.1"
67
67
  },
@@ -7,11 +7,11 @@ function default_1(options) {
7
7
  // Additional dependencies to install.
8
8
  // See https://github.com/telerik/kendo-schematics/issues/28
9
9
  peerDependencies: {
10
- '@progress/kendo-angular-buttons': '21.0.0-develop.20',
11
- '@progress/kendo-angular-common': '21.0.0-develop.20',
12
- '@progress/kendo-angular-l10n': '21.0.0-develop.20',
10
+ '@progress/kendo-angular-buttons': '21.0.0-develop.22',
11
+ '@progress/kendo-angular-common': '21.0.0-develop.22',
12
+ '@progress/kendo-angular-l10n': '21.0.0-develop.22',
13
13
  // Peer of kendo-angular-buttons
14
- '@progress/kendo-angular-popup': '21.0.0-develop.20'
14
+ '@progress/kendo-angular-popup': '21.0.0-develop.22'
15
15
  } });
16
16
  return (0, schematics_1.externalSchematic)('@progress/kendo-angular-schematics', 'ng-add', finalOptions);
17
17
  }
@@ -29,7 +29,7 @@ export declare class ListBoxSelectionService {
29
29
  selectionMode: SelectionMode;
30
30
  lastSelectedOrUnselectedIndex: number | null;
31
31
  rangeSelectionTargetIndex: number | null;
32
- private rangeSelectionAnchorIndex;
32
+ rangeSelectionAnchorIndex: number | null;
33
33
  onSelect: EventEmitter<any>;
34
34
  select(index: number, ctrlKey?: boolean, shiftKey?: boolean): void;
35
35
  selectRange(targetIndex: number): void;