@progress/kendo-angular-listbox 11.4.0-develop.1 → 11.4.0-develop.11

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.
@@ -3,16 +3,17 @@
3
3
  * Licensed under commercial license. See LICENSE.md in the project root for more information
4
4
  *-------------------------------------------------------------------------------------------*/
5
5
  import * as i0 from '@angular/core';
6
- import { EventEmitter, Injectable, Directive, Input, HostBinding, HostListener, isDevMode, Component, ContentChild, Output, NgModule } from '@angular/core';
6
+ import { EventEmitter, Injectable, Directive, Input, HostBinding, HostListener, isDevMode, Component, ContentChild, ViewChild, ViewChildren, Output, NgModule } from '@angular/core';
7
7
  import { validatePackage } from '@progress/kendo-licensing';
8
8
  import { Subscription } from 'rxjs';
9
9
  import { getter } from '@progress/kendo-common';
10
10
  import { caretAltUpIcon, caretAltDownIcon, caretAltLeftIcon, caretAltRightIcon, caretDoubleAltRightIcon, caretDoubleAltLeftIcon, xIcon } from '@progress/kendo-svg-icons';
11
- import * as i2 from '@progress/kendo-angular-buttons';
11
+ import { Keys, isChanged } from '@progress/kendo-angular-common';
12
+ import { take } from 'rxjs/operators';
13
+ import * as i3 from '@progress/kendo-angular-buttons';
12
14
  import { ButtonsModule } from '@progress/kendo-angular-buttons';
13
- import * as i3 from '@angular/common';
15
+ import * as i4 from '@angular/common';
14
16
  import { CommonModule } from '@angular/common';
15
- import { isChanged } from '@progress/kendo-angular-common';
16
17
 
17
18
  /**
18
19
  * @hidden
@@ -21,8 +22,8 @@ const packageMetadata = {
21
22
  name: '@progress/kendo-angular-listbox',
22
23
  productName: 'Kendo UI for Angular',
23
24
  productCodes: ['KENDOUIANGULAR', 'KENDOUICOMPLETE'],
24
- publishDate: 1678092873,
25
- version: '11.4.0-develop.1',
25
+ publishDate: 1678814499,
26
+ version: '11.4.0-develop.11',
26
27
  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'
27
28
  };
28
29
 
@@ -34,9 +35,9 @@ class ListBoxSelectionService {
34
35
  this.onSelect = new EventEmitter();
35
36
  this.selectedIndex = null;
36
37
  }
37
- select(index) {
38
+ select(index, dir) {
39
+ this.onSelect.next({ index: index, prevIndex: this.selectedIndex, dir });
38
40
  this.selectedIndex = index;
39
- this.onSelect.next({ index: this.selectedIndex });
40
41
  }
41
42
  isSelected(index) {
42
43
  return index === this.selectedIndex;
@@ -106,13 +107,13 @@ const allTools = [
106
107
  },
107
108
  {
108
109
  name: 'transferTo',
109
- label: 'Transfer From',
110
+ label: 'Transfer To',
110
111
  icon: 'caret-alt-left',
111
112
  svgIcon: caretAltLeftIcon
112
113
  },
113
114
  {
114
115
  name: 'transferFrom',
115
- label: 'Transfer To',
116
+ label: 'Transfer From',
116
117
  icon: 'caret-alt-right',
117
118
  svgIcon: caretAltRightIcon
118
119
  },
@@ -186,6 +187,260 @@ const getTools = (names) => {
186
187
  return names.map(tool => allTools.find(meta => meta.name === tool));
187
188
  };
188
189
 
190
+ /* eslint-disable @typescript-eslint/no-inferrable-types */
191
+ /**
192
+ * @hidden
193
+ */
194
+ class KeyboardNavigationService {
195
+ constructor(renderer, zone) {
196
+ this.renderer = renderer;
197
+ this.zone = zone;
198
+ this.selectedListboxItemIndex = 0;
199
+ this.focusedListboxItemIndex = 0;
200
+ this.focusedToolIndex = 0;
201
+ this.onDeleteEvent = new EventEmitter();
202
+ this.onSelectionChange = new EventEmitter();
203
+ this.onMoveSelectedItem = new EventEmitter();
204
+ this.onTransferAllEvent = new EventEmitter();
205
+ this.onShiftSelectedItem = new EventEmitter();
206
+ }
207
+ onKeyDown(event, toolsRef, toolbar, childListbox, parentListbox) {
208
+ const target = event.target;
209
+ const keyCode = event.keyCode;
210
+ const ctrlOrMetaKey = event.ctrlKey || event.metaKey;
211
+ const parentListboxToolbar = parentListbox === null || parentListbox === void 0 ? void 0 : parentListbox.selectedTools;
212
+ const tool = toolsRef.find(elem => elem.element === target);
213
+ const activeToolbar = toolbar.length > 0 ? toolbar : parentListboxToolbar;
214
+ if (toolsRef.length > 0 || (parentListbox === null || parentListbox === void 0 ? void 0 : parentListbox.tools.toArray().length) > 0) {
215
+ const focusNextTool = (keyCode === Keys.ArrowDown || keyCode === Keys.ArrowRight);
216
+ const focusPrevTool = (keyCode === Keys.ArrowUp || keyCode === Keys.ArrowLeft);
217
+ if ((focusNextTool || focusPrevTool) && tool) {
218
+ const dir = focusPrevTool ? 'up' : 'down';
219
+ this.handleToolbarArrows(toolsRef, dir);
220
+ }
221
+ else if (keyCode === Keys.F10) {
222
+ event.preventDefault();
223
+ this.onF10Key(toolsRef);
224
+ }
225
+ else if (keyCode === Keys.Delete && activeToolbar.some(tool => tool.name === 'remove')) {
226
+ this.onDeleteEvent.emit(this.selectedListboxItemIndex);
227
+ }
228
+ }
229
+ const isTargetListboxItem = this.listboxItems.find(elem => elem === target);
230
+ if (isTargetListboxItem) {
231
+ let isTransferToolVisible;
232
+ if (activeToolbar) {
233
+ isTransferToolVisible = activeToolbar.some(tool => tool.name.startsWith('transfer'));
234
+ }
235
+ if ((keyCode === Keys.ArrowRight || keyCode === Keys.ArrowLeft) && ctrlOrMetaKey && isTransferToolVisible) {
236
+ this.onArrowLeftOrRight(keyCode, parentListbox, childListbox, event);
237
+ }
238
+ else if ((keyCode === Keys.ArrowUp || keyCode === Keys.ArrowDown)) {
239
+ this.onArrowUpOrDown(keyCode, ctrlOrMetaKey, event, activeToolbar);
240
+ }
241
+ else if ((event.metaKey && keyCode === Keys.Enter) || (event.ctrlKey && keyCode === Keys.Space)) {
242
+ event.stopImmediatePropagation();
243
+ event.preventDefault();
244
+ this.onSelectChange();
245
+ }
246
+ else if (keyCode === Keys.Space) {
247
+ if (this.selectedListboxItemIndex !== this.focusedListboxItemIndex) {
248
+ const items = this.listboxItems;
249
+ this.changeTabindex(items[this.selectedListboxItemIndex], items[this.focusedListboxItemIndex]);
250
+ this.selectedListboxItemIndex = this.focusedListboxItemIndex;
251
+ this.onSelectionChange.emit(this.selectedListboxItemIndex);
252
+ }
253
+ }
254
+ }
255
+ }
256
+ changeTabindex(previousItem, currentItem) {
257
+ if (previousItem) {
258
+ this.renderer.setAttribute(previousItem, 'tabindex', '-1');
259
+ previousItem.blur();
260
+ }
261
+ ;
262
+ if (currentItem) {
263
+ this.renderer.setAttribute(currentItem, 'tabindex', '0');
264
+ currentItem.focus();
265
+ }
266
+ }
267
+ onFocusIn() {
268
+ this.onSelectionChange.emit(this.selectedListboxItemIndex);
269
+ }
270
+ handleToolbarArrows(toolsRef, dir) {
271
+ const topReached = dir === 'up' && this.focusedToolIndex <= 0;
272
+ const bottomReached = dir === 'down' && this.focusedToolIndex >= toolsRef.length - 1;
273
+ if (topReached || bottomReached) {
274
+ return;
275
+ }
276
+ const offset = dir === 'up' ? -1 : 1;
277
+ this.focusedToolIndex += offset;
278
+ const prevItem = toolsRef[this.focusedToolIndex + (offset * -1)].element;
279
+ const currentItem = toolsRef[this.focusedToolIndex].element;
280
+ this.changeTabindex(prevItem, currentItem);
281
+ }
282
+ onArrowUpOrDown(keyCode, ctrlOrMetaKey, event, activeToolbar) {
283
+ event.preventDefault();
284
+ const dir = keyCode === Keys.ArrowUp ? 'moveUp' : 'moveDown';
285
+ if (ctrlOrMetaKey) {
286
+ let isMoveToolVisible;
287
+ if (activeToolbar) {
288
+ isMoveToolVisible = activeToolbar.some(tool => tool.name.startsWith('move'));
289
+ }
290
+ if (event.shiftKey && isMoveToolVisible) {
291
+ this.onMoveSelectedItem.emit(dir);
292
+ return;
293
+ }
294
+ this.changeFocusedItem(dir);
295
+ return;
296
+ }
297
+ dir === 'moveUp' ? this.onArrowUp() : this.onArrowDown();
298
+ this.onSelectionChange.emit(this.selectedListboxItemIndex);
299
+ this.focusedListboxItemIndex = this.selectedListboxItemIndex;
300
+ }
301
+ onArrowLeftOrRight(keyCode, parentListbox, childListbox, event) {
302
+ event.preventDefault();
303
+ if (event.shiftKey) {
304
+ this.transferAllItems(keyCode, childListbox, parentListbox);
305
+ return;
306
+ }
307
+ if (this.selectedListboxItemIndex >= 0) {
308
+ this.transferItem(keyCode, childListbox, parentListbox);
309
+ }
310
+ }
311
+ onSelectChange() {
312
+ const canDeslect = (this.selectedListboxItemIndex || this.selectedListboxItemIndex === 0) && this.selectedListboxItemIndex === this.focusedListboxItemIndex;
313
+ if (canDeslect) {
314
+ this.selectedListboxItemIndex = null;
315
+ }
316
+ else {
317
+ const items = this.listboxItems;
318
+ this.changeTabindex(items[this.selectedListboxItemIndex], items[this.focusedListboxItemIndex]);
319
+ this.selectedListboxItemIndex = this.focusedListboxItemIndex;
320
+ }
321
+ this.onSelectionChange.emit(this.selectedListboxItemIndex);
322
+ }
323
+ onF10Key(tools) {
324
+ if (this.focusedToolIndex && this.focusedToolIndex > -1) {
325
+ if (this.focusedToolIndex >= tools.length) {
326
+ tools[tools.length - 1].element.focus();
327
+ }
328
+ else {
329
+ tools[this.focusedToolIndex].element.focus();
330
+ }
331
+ }
332
+ else {
333
+ tools[0].element.focus();
334
+ }
335
+ }
336
+ transferAllItems(keyCode, childListbox, parentListbox) {
337
+ const isArrowRight = keyCode === Keys.ArrowRight;
338
+ const actionToPerform = isArrowRight ? 'transferAllTo' : 'transferAllFrom';
339
+ this.onTransferAllEvent.emit(actionToPerform);
340
+ this.zone.onStable.pipe(take(1)).subscribe(() => {
341
+ const childListboxNav = (childListbox === null || childListbox === void 0 ? void 0 : childListbox.keyboardNavigationService) || (parentListbox === null || parentListbox === void 0 ? void 0 : parentListbox.childListbox.keyboardNavigationService);
342
+ let currentItem;
343
+ if (isArrowRight) {
344
+ if (childListbox) {
345
+ const childListboxItemsLength = childListboxNav.listboxItems.length - 1;
346
+ currentItem = childListboxNav.listboxItems[childListboxItemsLength];
347
+ childListboxNav.focusedListboxItemIndex = childListboxItemsLength;
348
+ childListboxNav.selectedListboxItemIndex = childListboxItemsLength;
349
+ this.focusedListboxItemIndex = 0;
350
+ this.selectedListboxItemIndex = 0;
351
+ }
352
+ }
353
+ else {
354
+ if (parentListbox) {
355
+ const parentListboxNav = parentListbox.keyboardNavigationService;
356
+ const parentListboxItemsLength = parentListboxNav.listboxItems.length - 1;
357
+ currentItem = parentListboxNav.listboxItems[parentListboxItemsLength];
358
+ parentListboxNav.focusedListboxItemIndex = parentListboxItemsLength;
359
+ parentListboxNav.selectedListboxItemIndex = parentListboxItemsLength;
360
+ childListboxNav.focusedListboxItemIndex = 0;
361
+ childListboxNav.selectedListboxItemIndex = 0;
362
+ }
363
+ }
364
+ this.changeTabindex(null, currentItem);
365
+ });
366
+ }
367
+ transferItem(keyCode, childListbox, parentListbox) {
368
+ const isArrowRight = keyCode === Keys.ArrowRight;
369
+ const actionToPerform = isArrowRight ? 'transferFrom' : 'transferTo';
370
+ this.onShiftSelectedItem.emit(actionToPerform);
371
+ const adjustTabindex = (items, firstItem, currentItem) => {
372
+ items.listboxItems.forEach(item => {
373
+ if (item.getAttribute('tabindex') === '0') {
374
+ this.changeTabindex(item, firstItem);
375
+ }
376
+ });
377
+ this.changeTabindex(null, currentItem);
378
+ };
379
+ this.zone.onStable.pipe(take(1)).subscribe(() => {
380
+ if (isArrowRight) {
381
+ if (childListbox) {
382
+ const childListboxNav = childListbox.keyboardNavigationService;
383
+ const childListboxItemsLength = childListboxNav.listboxItems.length - 1;
384
+ const currentItem = childListboxNav.listboxItems[childListboxItemsLength];
385
+ const parentListboxFirstItem = this.listboxItems[0];
386
+ childListboxNav.focusedListboxItemIndex = childListboxItemsLength;
387
+ childListboxNav.selectedListboxItemIndex = childListboxItemsLength;
388
+ this.focusedListboxItemIndex = 0;
389
+ this.selectedListboxItemIndex = 0;
390
+ adjustTabindex(childListboxNav, parentListboxFirstItem, currentItem);
391
+ }
392
+ }
393
+ else {
394
+ if (parentListbox) {
395
+ const childListboxNav = parentListbox.childListbox.keyboardNavigationService;
396
+ const parentListboxNav = parentListbox.keyboardNavigationService;
397
+ const parentListboxItemsLength = parentListboxNav.listboxItems.length - 1;
398
+ const currentItem = parentListboxNav.listboxItems[parentListboxItemsLength];
399
+ const childListboxFirstItem = childListboxNav.listboxItems[0];
400
+ parentListboxNav.focusedListboxItemIndex = parentListboxItemsLength;
401
+ parentListboxNav.selectedListboxItemIndex = parentListboxItemsLength;
402
+ childListboxNav.focusedListboxItemIndex = 0;
403
+ childListboxNav.selectedListboxItemIndex = 0;
404
+ adjustTabindex(parentListboxNav, childListboxFirstItem, currentItem);
405
+ }
406
+ }
407
+ });
408
+ }
409
+ changeFocusedItem(dir) {
410
+ this.listboxItems[this.focusedListboxItemIndex].blur();
411
+ if (this.focusedListboxItemIndex > 0 && dir === 'moveUp') {
412
+ this.focusedListboxItemIndex -= 1;
413
+ }
414
+ else if (this.focusedListboxItemIndex < this.listboxItems.length - 1 && dir === 'moveDown') {
415
+ this.focusedListboxItemIndex += 1;
416
+ }
417
+ this.listboxItems[this.focusedListboxItemIndex].focus();
418
+ }
419
+ onArrowDown() {
420
+ if (this.selectedListboxItemIndex < this.listboxItems.length - 1) {
421
+ const offset = this.selectedListboxItemIndex ? this.selectedListboxItemIndex : this.focusedListboxItemIndex;
422
+ this.selectedListboxItemIndex = offset + 1;
423
+ const previousItem = this.listboxItems[this.selectedListboxItemIndex - 1];
424
+ const currentItem = this.listboxItems[this.selectedListboxItemIndex];
425
+ this.changeTabindex(previousItem, currentItem);
426
+ }
427
+ }
428
+ onArrowUp() {
429
+ if (this.selectedListboxItemIndex > 0 || this.focusedListboxItemIndex > 0) {
430
+ const offset = this.selectedListboxItemIndex ? this.selectedListboxItemIndex : this.focusedListboxItemIndex;
431
+ this.selectedListboxItemIndex = offset - 1;
432
+ const previousItem = this.listboxItems[this.selectedListboxItemIndex + 1];
433
+ const currentItem = this.listboxItems[this.selectedListboxItemIndex];
434
+ this.changeTabindex(previousItem, currentItem);
435
+ }
436
+ }
437
+ }
438
+ KeyboardNavigationService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.12", ngImport: i0, type: KeyboardNavigationService, deps: [{ token: i0.Renderer2 }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Injectable });
439
+ KeyboardNavigationService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.3.12", ngImport: i0, type: KeyboardNavigationService });
440
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.12", ngImport: i0, type: KeyboardNavigationService, decorators: [{
441
+ type: Injectable
442
+ }], ctorParameters: function () { return [{ type: i0.Renderer2 }, { type: i0.NgZone }]; } });
443
+
189
444
  /**
190
445
  * @hidden
191
446
  */
@@ -218,15 +473,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.12", ngImpo
218
473
  args: ['click', ['$event']]
219
474
  }] } });
220
475
 
476
+ /* eslint-disable @typescript-eslint/no-inferrable-types */
221
477
  const DEFAULT_SIZE = 'medium';
478
+ let idx = 0;
222
479
  /**
223
480
  * Represents the [Kendo UI ListBox component for Angular]({% slug overview_listbox %}).
224
481
  */
225
482
  class ListBoxComponent {
226
- constructor(selectionService, renderer, hostElement) {
483
+ constructor(keyboardNavigationService, selectionService, hostElement, renderer, zone) {
484
+ this.keyboardNavigationService = keyboardNavigationService;
227
485
  this.selectionService = selectionService;
228
- this.renderer = renderer;
229
486
  this.hostElement = hostElement;
487
+ this.renderer = renderer;
488
+ this.zone = zone;
230
489
  /**
231
490
  * @hidden
232
491
  */
@@ -235,6 +494,14 @@ class ListBoxComponent {
235
494
  * The data which will be displayed by the ListBox.
236
495
  */
237
496
  this.data = [];
497
+ /**
498
+ * The value of the aria-label attribute of the Listbox element.
499
+ */
500
+ this.listboxLabel = 'Listbox';
501
+ /**
502
+ * The value of the aria-label attribute of the Listbox toolbar element.
503
+ */
504
+ this.listboxToolbarLabel = 'Toolbar';
238
505
  /**
239
506
  * A function which determines if a specific item is disabled.
240
507
  */
@@ -247,18 +514,19 @@ class ListBoxComponent {
247
514
  * Fires when the user clicks a ListBox item.
248
515
  */
249
516
  this.actionClick = new EventEmitter();
517
+ /**
518
+ * @hidden
519
+ */
520
+ this.getChildListbox = new EventEmitter();
250
521
  /**
251
522
  * @hidden
252
523
  */
253
524
  this.selectedTools = allTools;
254
525
  this._size = DEFAULT_SIZE;
255
- this.sub = new Subscription();
526
+ this.subs = new Subscription();
256
527
  validatePackage(packageMetadata);
257
528
  this.setToolbarClass(DEFAULT_TOOLBAR_POSITION);
258
529
  this.setSizingClass(this.size);
259
- this.sub.add(this.selectionService.onSelect.subscribe((e) => {
260
- this.selectionChange.next(e);
261
- }));
262
530
  }
263
531
  /**
264
532
  * Sets the size of the component.
@@ -299,17 +567,53 @@ class ListBoxComponent {
299
567
  get listClasses() {
300
568
  return `k-list k-list-${sizeClassMap[this.size]}`;
301
569
  }
302
- /**
303
- * @hidden
304
- */
570
+ ngOnInit() {
571
+ // This event emitter gives us the connectedWith value from the DataBinding directive
572
+ this.getChildListbox.emit();
573
+ if (this.childListbox) {
574
+ // This allows us to know to which parent Listbox the child Listbox is connected to
575
+ this.childListbox.parentListbox = this;
576
+ }
577
+ if (this.selectedIndex) {
578
+ this.keyboardNavigationService.focusedToolIndex = this.selectedIndex;
579
+ }
580
+ }
581
+ ngAfterViewInit() {
582
+ const toolsRef = this.tools.toArray();
583
+ const hostEl = this.hostElement.nativeElement;
584
+ const navService = this.keyboardNavigationService;
585
+ navService.listboxItems = Array.from(this.listboxElement.nativeElement.querySelectorAll('li.k-list-item'));
586
+ this.setIds();
587
+ this.initSubscriptions(navService, hostEl, toolsRef);
588
+ }
305
589
  ngOnDestroy() {
306
- this.sub.unsubscribe();
590
+ this.subs.unsubscribe();
307
591
  }
308
592
  /**
309
593
  * @hidden
310
594
  */
311
595
  performAction(actionName) {
312
- this.actionClick.next(actionName);
596
+ var _a, _b;
597
+ const isActionTransferTo = actionName === 'transferTo' || actionName === 'transferAllFrom';
598
+ const isListboxChild = this.parentListbox && !this.childListbox;
599
+ const isListboxParentAndChild = !!(this.parentListbox && this.childListbox);
600
+ const isListboxParent = !!(this.childListbox || (!this.childListbox && !this.parentListbox));
601
+ if (isListboxChild || (isListboxParentAndChild && isActionTransferTo)) {
602
+ this.parentListbox.actionClick.next(actionName);
603
+ }
604
+ else if (isListboxParent || (isListboxParentAndChild && !isActionTransferTo)) {
605
+ this.actionClick.next(actionName);
606
+ }
607
+ const toolsRef = this.tools.toArray() || this.parentListbox.tools.toArray();
608
+ const focusedToolIndex = toolsRef.findIndex(elem => elem.nativeElement === document.activeElement);
609
+ if ((this.selectedTools.length > 0 || this.parentListbox.selectedTools.length > 0) && focusedToolIndex > -1) {
610
+ const navService = this.keyboardNavigationService || this.parentListbox.keyboardNavigationService;
611
+ const selectedTools = this.selectedTools || this.parentListbox.selectedTools;
612
+ const prevTool = (_a = toolsRef[navService.focusedToolIndex]) === null || _a === void 0 ? void 0 : _a.element;
613
+ navService.focusedToolIndex = selectedTools.findIndex(tool => tool.name === actionName);
614
+ const currentTool = (_b = toolsRef[navService.focusedToolIndex]) === null || _b === void 0 ? void 0 : _b.element;
615
+ navService.changeTabindex(prevTool, currentTool);
616
+ }
313
617
  }
314
618
  /**
315
619
  * Programmatically selects a ListBox node.
@@ -329,6 +633,15 @@ class ListBoxComponent {
329
633
  get selectedIndex() {
330
634
  return this.selectionService.selectedIndex;
331
635
  }
636
+ /**
637
+ * @hidden
638
+ */
639
+ get getListboxId() {
640
+ const id = ++idx;
641
+ const listboxId = `k-listbox-${id}`;
642
+ return listboxId;
643
+ }
644
+ ;
332
645
  /**
333
646
  * @hidden
334
647
  */
@@ -338,6 +651,102 @@ class ListBoxComponent {
338
651
  }
339
652
  return fieldAccessor(dataItem, this.textField);
340
653
  }
654
+ /**
655
+ * @hidden
656
+ */
657
+ updateListboxItems() {
658
+ this.zone.onStable.pipe(take(1)).subscribe(() => {
659
+ this.keyboardNavigationService.listboxItems = Array.from(this.listboxElement.nativeElement.querySelectorAll('li.k-list-item'));
660
+ });
661
+ }
662
+ /**
663
+ * @hidden
664
+ */
665
+ swapItems(firstItemIndex, secondItemIndex) {
666
+ const listboxItems = this.keyboardNavigationService.listboxItems;
667
+ [listboxItems[firstItemIndex], listboxItems[secondItemIndex]] = [listboxItems[secondItemIndex], listboxItems[firstItemIndex]];
668
+ }
669
+ onClickEvent(listboxItems, prevIndex, currentIndex, dir) {
670
+ this.selectionChange.next(currentIndex);
671
+ this.keyboardNavigationService.selectedListboxItemIndex = currentIndex;
672
+ this.keyboardNavigationService.focusedListboxItemIndex = currentIndex;
673
+ this.zone.onStable.pipe(take(1)).subscribe(() => {
674
+ if (dir)
675
+ this.swapItems(prevIndex, currentIndex);
676
+ const previousItem = prevIndex ? listboxItems[prevIndex] : listboxItems[0];
677
+ const currentItem = listboxItems[currentIndex];
678
+ this.keyboardNavigationService.changeTabindex(previousItem, currentItem);
679
+ });
680
+ }
681
+ initSubscriptions(navService, hostEl, toolsRef) {
682
+ this.subs.add(navService.onDeleteEvent.subscribe((index) => this.onDeleteEvent(index, navService)));
683
+ this.subs.add(this.renderer.listen(hostEl, 'keydown', event => navService.onKeyDown(event, toolsRef, this.selectedTools, this.childListbox, this.parentListbox)));
684
+ this.subs.add(this.renderer.listen(this.listboxElement.nativeElement, 'focusin', () => navService.onFocusIn()));
685
+ this.subs.add(navService.onShiftSelectedItem.subscribe((actionToPerform) => this.onShiftItems(actionToPerform)));
686
+ this.subs.add(navService.onTransferAllEvent.subscribe((actionToPerform) => this.onShiftItems(actionToPerform)));
687
+ this.subs.add(navService.onMoveSelectedItem.subscribe((dir) => this.performAction(dir)));
688
+ this.subs.add(this.selectionService.onSelect.subscribe((e) => this.onClickEvent(navService.listboxItems, e.prevIndex, e.index, e.dir)));
689
+ this.subs.add(navService.onSelectionChange.subscribe((index) => {
690
+ this.selectionService.selectedIndex = index;
691
+ this.selectionChange.next({ index, prevIndex: null });
692
+ }));
693
+ }
694
+ onShiftItems(actionToPerform) {
695
+ var _a, _b;
696
+ this.performAction(actionToPerform);
697
+ if (actionToPerform === 'transferFrom' || actionToPerform === 'transferAllTo') {
698
+ this.updateListboxItems();
699
+ (_a = this.childListbox) === null || _a === void 0 ? void 0 : _a.updateListboxItems();
700
+ }
701
+ else {
702
+ this.updateListboxItems();
703
+ (_b = this.parentListbox) === null || _b === void 0 ? void 0 : _b.updateListboxItems();
704
+ }
705
+ }
706
+ setIds() {
707
+ var _a, _b, _c;
708
+ const listbox = this.listboxElement.nativeElement;
709
+ this.listboxId = this.getListboxId;
710
+ this.renderer.setAttribute(listbox, 'id', this.listboxId);
711
+ if (this.selectedTools.length > 0 || ((_a = this.parentListbox) === null || _a === void 0 ? void 0 : _a.selectedTools.length) > 0) {
712
+ const toolbar = (_b = this.toolbarElement) === null || _b === void 0 ? void 0 : _b.nativeElement;
713
+ const parentToolbar = (_c = this.parentListbox) === null || _c === void 0 ? void 0 : _c.toolbarElement.nativeElement;
714
+ if (this.parentListbox && this.childListbox) {
715
+ this.zone.onStable.pipe(take(1)).subscribe(() => {
716
+ this.toolbarId = `${this.parentListbox.listboxId} ${this.listboxId} ${this.childListbox.listboxId}`;
717
+ this.renderer.setAttribute(toolbar, 'aria-controls', this.toolbarId);
718
+ });
719
+ }
720
+ else if (this.childListbox && !this.parentListbox) {
721
+ this.zone.onStable.pipe(take(1)).subscribe(() => {
722
+ this.toolbarId = this.toolbarId = `${this.listboxId} ${this.childListbox.listboxId}`;
723
+ this.renderer.setAttribute(toolbar, 'aria-controls', this.toolbarId);
724
+ });
725
+ }
726
+ else if (this.parentListbox && this.selectedTools.length > 0) {
727
+ this.toolbarId = `${this.parentListbox.listboxId} ${this.listboxId}`;
728
+ this.parentListbox.toolbarId = this.toolbarId = `${this.parentListbox.listboxId} ${this.listboxId}`;
729
+ this.renderer.setAttribute(toolbar, 'aria-controls', this.toolbarId);
730
+ this.renderer.setAttribute(parentToolbar, 'aria-controls', this.parentListbox.toolbarId);
731
+ }
732
+ else if (!this.parentListbox && !this.childListbox) {
733
+ this.toolbarId = this.listboxId;
734
+ this.renderer.setAttribute(toolbar, 'aria-controls', this.toolbarId);
735
+ }
736
+ }
737
+ }
738
+ onDeleteEvent(index, navService) {
739
+ this.selectionService.selectedIndex = index;
740
+ this.performAction('remove');
741
+ this.updateListboxItems();
742
+ const setIndex = index + 1 === navService.listboxItems.length ?
743
+ { index: index - 1, tabindex: index - 1 } : { index, tabindex: index + 1 };
744
+ navService.changeTabindex(null, navService.listboxItems[setIndex['tabindex']]);
745
+ this.selectionChange.next({ index: setIndex['index'], prevIndex: null });
746
+ navService.selectedListboxItemIndex = setIndex['index'];
747
+ navService.focusedListboxItem = setIndex['index'];
748
+ this.selectionService.selectedIndex = setIndex['index'];
749
+ }
341
750
  setToolbarClass(pos) {
342
751
  Object.keys(actionsClasses).forEach((className) => {
343
752
  if (pos === className) {
@@ -352,95 +761,140 @@ class ListBoxComponent {
352
761
  this.renderer.addClass(this.hostElement.nativeElement, `k-listbox-${sizeClassMap[size]}`);
353
762
  }
354
763
  }
355
- ListBoxComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.12", ngImport: i0, type: ListBoxComponent, deps: [{ token: ListBoxSelectionService }, { token: i0.Renderer2 }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
356
- ListBoxComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.3.12", type: ListBoxComponent, selector: "kendo-listbox", inputs: { textField: "textField", data: "data", size: "size", toolbar: "toolbar", itemDisabled: "itemDisabled" }, outputs: { selectionChange: "selectionChange", actionClick: "actionClick" }, host: { properties: { "class.k-listbox": "this.listboxClassName" } }, providers: [ListBoxSelectionService], queries: [{ propertyName: "itemTemplate", first: true, predicate: ItemTemplateDirective, descendants: true }], ngImport: i0, template: `
357
- <div class="k-listbox-actions" *ngIf="selectedTools.length > 0">
358
- <button
359
- *ngFor="let tool of selectedTools"
360
- kendoButton
361
- [size]="this.size"
362
- [icon]="tool.icon"
363
- [svgIcon]="tool.svgIcon"
364
- (click)="performAction(tool.name)"
365
- role="button"
366
- ></button>
367
- </div>
368
- <div class="k-list-scroller k-selectable">
369
- <div class="{{ listClasses }}">
370
- <div class="k-list-content">
371
- <ul class="k-list-ul">
372
- <li
373
- class="k-list-item"
374
- *ngFor="let item of data; let i = index;"
375
- kendoListBoxItemSelectable
376
- [index]="i"
377
- [class.k-disabled]="itemDisabled(item)"
378
- >
379
- <ng-template *ngIf="itemTemplate; else defaultItemTemplate"
380
- [templateContext]="{
381
- templateRef: itemTemplate.templateRef,
382
- $implicit: item
383
- }">
384
- </ng-template>
385
- <ng-template #defaultItemTemplate>
386
- <span class="k-list-item-text">{{ getText(item) }}</span>
387
- </ng-template>
388
- </li>
389
- </ul>
390
- </div>
764
+ ListBoxComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.12", ngImport: i0, type: ListBoxComponent, deps: [{ token: KeyboardNavigationService }, { token: ListBoxSelectionService }, { token: i0.ElementRef }, { token: i0.Renderer2 }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component });
765
+ ListBoxComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.3.12", type: ListBoxComponent, 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" } }, providers: [ListBoxSelectionService, KeyboardNavigationService], queries: [{ propertyName: "itemTemplate", first: true, predicate: ItemTemplateDirective, descendants: true }], viewQueries: [{ propertyName: "listboxElement", first: true, predicate: ["listbox"], descendants: true }, { propertyName: "toolbarElement", first: true, predicate: ["toolbar"], descendants: true }, { propertyName: "tools", predicate: ["tools"], descendants: true }], ngImport: i0, template: `
766
+ <div
767
+ #toolbar
768
+ class="k-listbox-actions"
769
+ *ngIf="selectedTools.length > 0"
770
+ role="toolbar"
771
+ [attr.aria-label]="listboxToolbarLabel"
772
+ >
773
+ <button
774
+ #tools
775
+ *ngFor="let tool of selectedTools; let i = index;"
776
+ kendoButton
777
+ [attr.tabindex]="i === 0 ? '0' : '-1'"
778
+ [size]="this.size"
779
+ [icon]="tool.icon"
780
+ [svgIcon]="tool.svgIcon"
781
+ [attr.title]="tool.label"
782
+ (click)="performAction(tool.name)"
783
+ role="button"
784
+ ></button>
785
+ </div>
786
+ <div class="k-list-scroller k-selectable">
787
+ <div class="{{ listClasses }}">
788
+ <div class="k-list-content">
789
+ <ul
790
+ #listbox
791
+ class="k-list-ul"
792
+ role="listbox"
793
+ [attr.aria-label]="listboxLabel"
794
+ [attr.aria-multiselectable]="false"
795
+ >
796
+ <li
797
+ *ngFor="let item of data; let i = index;"
798
+ kendoListBoxItemSelectable
799
+ class="k-list-item"
800
+ [attr.tabindex]="i === 0 ? '0' : '-1'"
801
+ role="option"
802
+ [attr.aria-selected]="selectedIndex === i"
803
+ [index]="i"
804
+ [class.k-disabled]="itemDisabled(item)"
805
+ >
806
+ <ng-template *ngIf="itemTemplate; else defaultItemTemplate"
807
+ [templateContext]="{
808
+ templateRef: itemTemplate.templateRef,
809
+ $implicit: item
810
+ }">
811
+ </ng-template>
812
+ <ng-template #defaultItemTemplate>
813
+ <span class="k-list-item-text">{{ getText(item) }}</span>
814
+ </ng-template>
815
+ </li>
816
+ </ul>
391
817
  </div>
392
818
  </div>
393
- `, isInline: true, components: [{ type: i2.Button, selector: "button[kendoButton], span[kendoButton], kendo-button", inputs: ["toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon", "role", "primary", "look"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }], directives: [{ type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i3.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: ItemSelectableDirective, selector: "[kendoListBoxItemSelectable]", inputs: ["index"] }, { type: i2.TemplateContextDirective, selector: "[templateContext]", inputs: ["templateContext"] }] });
819
+ </div>
820
+ `, isInline: true, components: [{ type: i3.Button, selector: "button[kendoButton], span[kendoButton], kendo-button", inputs: ["toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon", "role", "primary", "look"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }], directives: [{ type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i4.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: ItemSelectableDirective, selector: "[kendoListBoxItemSelectable]", inputs: ["index"] }, { type: i3.TemplateContextDirective, selector: "[templateContext]", inputs: ["templateContext"] }] });
394
821
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.12", ngImport: i0, type: ListBoxComponent, decorators: [{
395
822
  type: Component,
396
823
  args: [{
397
824
  selector: 'kendo-listbox',
398
- providers: [ListBoxSelectionService],
825
+ providers: [ListBoxSelectionService, KeyboardNavigationService],
399
826
  template: `
400
- <div class="k-listbox-actions" *ngIf="selectedTools.length > 0">
401
- <button
402
- *ngFor="let tool of selectedTools"
403
- kendoButton
404
- [size]="this.size"
405
- [icon]="tool.icon"
406
- [svgIcon]="tool.svgIcon"
407
- (click)="performAction(tool.name)"
408
- role="button"
409
- ></button>
410
- </div>
411
- <div class="k-list-scroller k-selectable">
412
- <div class="{{ listClasses }}">
413
- <div class="k-list-content">
414
- <ul class="k-list-ul">
415
- <li
416
- class="k-list-item"
417
- *ngFor="let item of data; let i = index;"
418
- kendoListBoxItemSelectable
419
- [index]="i"
420
- [class.k-disabled]="itemDisabled(item)"
421
- >
422
- <ng-template *ngIf="itemTemplate; else defaultItemTemplate"
423
- [templateContext]="{
424
- templateRef: itemTemplate.templateRef,
425
- $implicit: item
426
- }">
427
- </ng-template>
428
- <ng-template #defaultItemTemplate>
429
- <span class="k-list-item-text">{{ getText(item) }}</span>
430
- </ng-template>
431
- </li>
432
- </ul>
433
- </div>
827
+ <div
828
+ #toolbar
829
+ class="k-listbox-actions"
830
+ *ngIf="selectedTools.length > 0"
831
+ role="toolbar"
832
+ [attr.aria-label]="listboxToolbarLabel"
833
+ >
834
+ <button
835
+ #tools
836
+ *ngFor="let tool of selectedTools; let i = index;"
837
+ kendoButton
838
+ [attr.tabindex]="i === 0 ? '0' : '-1'"
839
+ [size]="this.size"
840
+ [icon]="tool.icon"
841
+ [svgIcon]="tool.svgIcon"
842
+ [attr.title]="tool.label"
843
+ (click)="performAction(tool.name)"
844
+ role="button"
845
+ ></button>
846
+ </div>
847
+ <div class="k-list-scroller k-selectable">
848
+ <div class="{{ listClasses }}">
849
+ <div class="k-list-content">
850
+ <ul
851
+ #listbox
852
+ class="k-list-ul"
853
+ role="listbox"
854
+ [attr.aria-label]="listboxLabel"
855
+ [attr.aria-multiselectable]="false"
856
+ >
857
+ <li
858
+ *ngFor="let item of data; let i = index;"
859
+ kendoListBoxItemSelectable
860
+ class="k-list-item"
861
+ [attr.tabindex]="i === 0 ? '0' : '-1'"
862
+ role="option"
863
+ [attr.aria-selected]="selectedIndex === i"
864
+ [index]="i"
865
+ [class.k-disabled]="itemDisabled(item)"
866
+ >
867
+ <ng-template *ngIf="itemTemplate; else defaultItemTemplate"
868
+ [templateContext]="{
869
+ templateRef: itemTemplate.templateRef,
870
+ $implicit: item
871
+ }">
872
+ </ng-template>
873
+ <ng-template #defaultItemTemplate>
874
+ <span class="k-list-item-text">{{ getText(item) }}</span>
875
+ </ng-template>
876
+ </li>
877
+ </ul>
434
878
  </div>
435
879
  </div>
880
+ </div>
436
881
  `
437
882
  }]
438
- }], ctorParameters: function () { return [{ type: ListBoxSelectionService }, { type: i0.Renderer2 }, { type: i0.ElementRef }]; }, propDecorators: { listboxClassName: [{
883
+ }], ctorParameters: function () { return [{ type: KeyboardNavigationService }, { type: ListBoxSelectionService }, { type: i0.ElementRef }, { type: i0.Renderer2 }, { type: i0.NgZone }]; }, propDecorators: { listboxClassName: [{
439
884
  type: HostBinding,
440
885
  args: ['class.k-listbox']
441
886
  }], itemTemplate: [{
442
887
  type: ContentChild,
443
- args: [ItemTemplateDirective, { static: false }]
888
+ args: [ItemTemplateDirective]
889
+ }], listboxElement: [{
890
+ type: ViewChild,
891
+ args: ['listbox']
892
+ }], toolbarElement: [{
893
+ type: ViewChild,
894
+ args: ['toolbar']
895
+ }], tools: [{
896
+ type: ViewChildren,
897
+ args: ['tools']
444
898
  }], textField: [{
445
899
  type: Input
446
900
  }], data: [{
@@ -449,12 +903,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.12", ngImpo
449
903
  type: Input
450
904
  }], toolbar: [{
451
905
  type: Input
906
+ }], listboxLabel: [{
907
+ type: Input
908
+ }], listboxToolbarLabel: [{
909
+ type: Input
452
910
  }], itemDisabled: [{
453
911
  type: Input
454
912
  }], selectionChange: [{
455
913
  type: Output
456
914
  }], actionClick: [{
457
915
  type: Output
916
+ }], getChildListbox: [{
917
+ type: Output
458
918
  }] } });
459
919
 
460
920
  /**
@@ -465,7 +925,11 @@ class DataBindingDirective {
465
925
  this.listbox = listbox;
466
926
  this.actionClickSub = new Subscription();
467
927
  this.selectedBoxSub = new Subscription();
928
+ this.connectedWithSub = new Subscription();
468
929
  this.selectedBox = this.listbox;
930
+ this.connectedWithSub.add(this.listbox.getChildListbox.subscribe(() => {
931
+ this.listbox.childListbox = this.connectedWith;
932
+ }));
469
933
  this.actionClickSub.add(this.listbox.actionClick.subscribe((actionName) => {
470
934
  switch (actionName) {
471
935
  case 'moveUp': {
@@ -547,7 +1011,7 @@ class DataBindingDirective {
547
1011
  const newIndex = dir === 'up' ? index - 1 : index + 1;
548
1012
  // ES6 Destructuring swap
549
1013
  [this.selectedBox.data[newIndex], this.selectedBox.data[index]] = [this.selectedBox.data[index], this.selectedBox.data[newIndex]];
550
- this.selectedBox.selectionService.select(newIndex);
1014
+ this.selectedBox.selectionService.select(newIndex, dir);
551
1015
  }
552
1016
  removeItem() {
553
1017
  const index = this.selectedBox.selectedIndex;