@progress/kendo-angular-grid 18.0.1-develop.3 → 18.1.0-develop.10

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.
Files changed (36) hide show
  1. package/columns/cell-rowspan.d.ts +10 -0
  2. package/columns/column-base.d.ts +8 -2
  3. package/editing-directives/local-row-edit.service.d.ts +1 -1
  4. package/esm2022/aggregates/selection-aggregate.service.mjs +1 -1
  5. package/esm2022/columns/cell-rowspan.mjs +5 -0
  6. package/esm2022/columns/column-base.mjs +13 -2
  7. package/esm2022/dragdrop/draggable-column.directive.mjs +2 -2
  8. package/esm2022/editing-directives/local-row-edit.service.mjs +1 -1
  9. package/esm2022/grid.component.mjs +6 -3
  10. package/esm2022/grouping/group-panel.component.mjs +1 -1
  11. package/esm2022/navigation/logical-cell.directive.mjs +7 -1
  12. package/esm2022/navigation/navigation.service.mjs +27 -35
  13. package/esm2022/package-metadata.mjs +2 -2
  14. package/esm2022/pdf/pdf.component.mjs +1 -1
  15. package/esm2022/rendering/cell.component.mjs +1 -1
  16. package/esm2022/rendering/common/col-group.component.mjs +1 -1
  17. package/esm2022/rendering/footer/footer.component.mjs +1 -1
  18. package/esm2022/rendering/header/header.component.mjs +2 -2
  19. package/esm2022/rendering/list.component.mjs +14 -5
  20. package/esm2022/rendering/rowspan.service.mjs +24 -0
  21. package/esm2022/rendering/table-body.component.mjs +106 -64
  22. package/esm2022/selection/selection-default.mjs +1 -1
  23. package/fesm2022/progress-kendo-angular-grid.mjs +202 -123
  24. package/index.d.ts +1 -0
  25. package/navigation/logical-cell.directive.d.ts +1 -0
  26. package/navigation/navigation.service.d.ts +2 -0
  27. package/package.json +19 -19
  28. package/pdf/pdf.component.d.ts +1 -1
  29. package/rendering/cell.component.d.ts +1 -1
  30. package/rendering/common/col-group.component.d.ts +1 -1
  31. package/rendering/footer/footer.component.d.ts +1 -1
  32. package/rendering/header/header.component.d.ts +1 -1
  33. package/rendering/list.component.d.ts +4 -1
  34. package/rendering/rowspan.service.d.ts +14 -0
  35. package/rendering/table-body.component.d.ts +9 -2
  36. package/schematics/ngAdd/index.js +4 -4
@@ -0,0 +1,10 @@
1
+ /**-----------------------------------------------------------------------------------------
2
+ * Copyright © 2025 Progress Software Corporation. All rights reserved.
3
+ * Licensed under commercial license. See LICENSE.md in the project root for more information
4
+ *-------------------------------------------------------------------------------------------*/
5
+ import { RowArgs } from "../rendering/common/row-args";
6
+ import { ColumnBase } from "./column-base";
7
+ /**
8
+ * Represents the callback that is used to determine the rowspan of each cell in a given column.
9
+ */
10
+ export type CellRowspanFn = (row: RowArgs, column: ColumnBase, data: any[]) => number;
@@ -2,11 +2,12 @@
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 { TemplateRef, QueryList } from '@angular/core';
5
+ import { TemplateRef, QueryList, SimpleChanges } from '@angular/core';
6
6
  import { HeaderTemplateDirective } from '../rendering/header/header-template.directive';
7
7
  import { FooterTemplateDirective } from '../rendering/footer/footer-template.directive';
8
8
  import { ColumnMenuTemplateDirective } from '../column-menu/column-menu-template.directive';
9
9
  import { IdService } from '../common/id.service';
10
+ import { CellRowspanFn } from './cell-rowspan';
10
11
  import * as i0 from "@angular/core";
11
12
  /**
12
13
  * @hidden
@@ -214,6 +215,10 @@ export declare class ColumnBase {
214
215
  footerClass: string | string[] | Set<string> | {
215
216
  [key: string]: any;
216
217
  };
218
+ /**
219
+ * Defines a function that is used to determine the rowspan of each column cell.
220
+ */
221
+ cellRowspan: CellRowspanFn;
217
222
  /**
218
223
  * @hidden
219
224
  */
@@ -279,6 +284,7 @@ export declare class ColumnBase {
279
284
  * @hidden
280
285
  */
281
286
  constructor(parent?: ColumnBase, idService?: IdService);
287
+ ngOnChanges(changes: SimpleChanges): void;
282
288
  static ɵfac: i0.ɵɵFactoryDeclaration<ColumnBase, never>;
283
- static ɵcmp: i0.ɵɵComponentDeclaration<ColumnBase, "kendo-grid-column-base", never, { "resizable": { "alias": "resizable"; "required": false; }; "reorderable": { "alias": "reorderable"; "required": false; }; "minResizableWidth": { "alias": "minResizableWidth"; "required": false; }; "maxResizableWidth": { "alias": "maxResizableWidth"; "required": false; }; "title": { "alias": "title"; "required": false; }; "width": { "alias": "width"; "required": false; }; "autoSize": { "alias": "autoSize"; "required": false; }; "locked": { "alias": "locked"; "required": false; }; "sticky": { "alias": "sticky"; "required": false; }; "hidden": { "alias": "hidden"; "required": false; }; "media": { "alias": "media"; "required": false; }; "lockable": { "alias": "lockable"; "required": false; }; "stickable": { "alias": "stickable"; "required": false; }; "columnMenu": { "alias": "columnMenu"; "required": false; }; "includeInChooser": { "alias": "includeInChooser"; "required": false; }; "tableCellsRole": { "alias": "tableCellsRole"; "required": false; }; "style": { "alias": "style"; "required": false; }; "headerStyle": { "alias": "headerStyle"; "required": false; }; "filterStyle": { "alias": "filterStyle"; "required": false; }; "footerStyle": { "alias": "footerStyle"; "required": false; }; "cssClass": { "alias": "class"; "required": false; }; "headerClass": { "alias": "headerClass"; "required": false; }; "filterClass": { "alias": "filterClass"; "required": false; }; "footerClass": { "alias": "footerClass"; "required": false; }; }, {}, ["footerTemplate", "headerTemplates", "columnMenuTemplates"], never, false, never>;
289
+ static ɵcmp: i0.ɵɵComponentDeclaration<ColumnBase, "kendo-grid-column-base", never, { "resizable": { "alias": "resizable"; "required": false; }; "reorderable": { "alias": "reorderable"; "required": false; }; "minResizableWidth": { "alias": "minResizableWidth"; "required": false; }; "maxResizableWidth": { "alias": "maxResizableWidth"; "required": false; }; "title": { "alias": "title"; "required": false; }; "width": { "alias": "width"; "required": false; }; "autoSize": { "alias": "autoSize"; "required": false; }; "locked": { "alias": "locked"; "required": false; }; "sticky": { "alias": "sticky"; "required": false; }; "hidden": { "alias": "hidden"; "required": false; }; "media": { "alias": "media"; "required": false; }; "lockable": { "alias": "lockable"; "required": false; }; "stickable": { "alias": "stickable"; "required": false; }; "columnMenu": { "alias": "columnMenu"; "required": false; }; "includeInChooser": { "alias": "includeInChooser"; "required": false; }; "tableCellsRole": { "alias": "tableCellsRole"; "required": false; }; "style": { "alias": "style"; "required": false; }; "headerStyle": { "alias": "headerStyle"; "required": false; }; "filterStyle": { "alias": "filterStyle"; "required": false; }; "footerStyle": { "alias": "footerStyle"; "required": false; }; "cssClass": { "alias": "class"; "required": false; }; "headerClass": { "alias": "headerClass"; "required": false; }; "filterClass": { "alias": "filterClass"; "required": false; }; "footerClass": { "alias": "footerClass"; "required": false; }; "cellRowspan": { "alias": "cellRowspan"; "required": false; }; }, {}, ["footerTemplate", "headerTemplates", "columnMenuTemplates"], never, false, never>;
284
290
  }
@@ -7,5 +7,5 @@ import { LocalEditService } from './local-edit.service';
7
7
  * @hidden
8
8
  */
9
9
  export declare class LocalRowEditService extends LocalEditService {
10
- update(_item: any): void;
10
+ update(): void;
11
11
  }
@@ -49,7 +49,7 @@ export class CellSelectionAggregateService {
49
49
  return true;
50
50
  }
51
51
  init() {
52
- this.sub.add(this.ctx.grid.dataStateChange.subscribe(_ => {
52
+ this.sub.add(this.ctx.grid.dataStateChange.subscribe(() => {
53
53
  // nullifies aggregates and sets default count to avoid mismatching state -
54
54
  // https://github.com/telerik/kendo-angular-private/issues/2964
55
55
  this.nullifyAggregates();
@@ -0,0 +1,5 @@
1
+ /**-----------------------------------------------------------------------------------------
2
+ * Copyright © 2025 Progress Software Corporation. All rights reserved.
3
+ * Licensed under commercial license. See LICENSE.md in the project root for more information
4
+ *-------------------------------------------------------------------------------------------*/
5
+ export {};
@@ -7,7 +7,7 @@ import { HeaderTemplateDirective } from '../rendering/header/header-template.dir
7
7
  import { FooterTemplateDirective } from '../rendering/footer/footer-template.directive';
8
8
  import { ColumnMenuTemplateDirective } from '../column-menu/column-menu-template.directive';
9
9
  import { IdService } from '../common/id.service';
10
- import { ColumnConfigurationErrorMessages } from '../common/error-messages';
10
+ import { ColumnConfigurationErrorMessages, GridConfigurationErrorMessages } from '../common/error-messages';
11
11
  import * as i0 from "@angular/core";
12
12
  import * as i1 from "../common/id.service";
13
13
  /**
@@ -219,6 +219,10 @@ export class ColumnBase {
219
219
  *
220
220
  */
221
221
  footerClass;
222
+ /**
223
+ * Defines a function that is used to determine the rowspan of each column cell.
224
+ */
225
+ cellRowspan;
222
226
  /**
223
227
  * @hidden
224
228
  */
@@ -313,8 +317,13 @@ export class ColumnBase {
313
317
  throw new Error(ColumnConfigurationErrorMessages.columnNested);
314
318
  }
315
319
  }
320
+ ngOnChanges(changes) {
321
+ if (isDevMode() && changes['cellRowspan'] && typeof changes['cellRowspan'].currentValue !== 'function') {
322
+ throw new Error(GridConfigurationErrorMessages.functionType('cellRowspan', changes['cellRowspan'].currentValue));
323
+ }
324
+ }
316
325
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ColumnBase, deps: [{ token: ColumnBase }, { token: i1.IdService }], target: i0.ɵɵFactoryTarget.Component });
317
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: ColumnBase, selector: "kendo-grid-column-base", inputs: { resizable: "resizable", reorderable: "reorderable", minResizableWidth: "minResizableWidth", maxResizableWidth: "maxResizableWidth", title: "title", width: "width", autoSize: "autoSize", locked: "locked", sticky: "sticky", hidden: "hidden", media: "media", lockable: "lockable", stickable: "stickable", columnMenu: "columnMenu", includeInChooser: "includeInChooser", tableCellsRole: "tableCellsRole", style: "style", headerStyle: "headerStyle", filterStyle: "filterStyle", footerStyle: "footerStyle", cssClass: ["class", "cssClass"], headerClass: "headerClass", filterClass: "filterClass", footerClass: "footerClass" }, queries: [{ propertyName: "footerTemplate", first: true, predicate: FooterTemplateDirective, descendants: true }, { propertyName: "headerTemplates", predicate: HeaderTemplateDirective }, { propertyName: "columnMenuTemplates", predicate: ColumnMenuTemplateDirective }], ngImport: i0, template: ``, isInline: true });
326
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: ColumnBase, selector: "kendo-grid-column-base", inputs: { resizable: "resizable", reorderable: "reorderable", minResizableWidth: "minResizableWidth", maxResizableWidth: "maxResizableWidth", title: "title", width: "width", autoSize: "autoSize", locked: "locked", sticky: "sticky", hidden: "hidden", media: "media", lockable: "lockable", stickable: "stickable", columnMenu: "columnMenu", includeInChooser: "includeInChooser", tableCellsRole: "tableCellsRole", style: "style", headerStyle: "headerStyle", filterStyle: "filterStyle", footerStyle: "footerStyle", cssClass: ["class", "cssClass"], headerClass: "headerClass", filterClass: "filterClass", footerClass: "footerClass", cellRowspan: "cellRowspan" }, queries: [{ propertyName: "footerTemplate", first: true, predicate: FooterTemplateDirective, descendants: true }, { propertyName: "headerTemplates", predicate: HeaderTemplateDirective }, { propertyName: "columnMenuTemplates", predicate: ColumnMenuTemplateDirective }], usesOnChanges: true, ngImport: i0, template: ``, isInline: true });
318
327
  }
319
328
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ColumnBase, decorators: [{
320
329
  type: Component,
@@ -371,6 +380,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
371
380
  type: Input
372
381
  }], footerClass: [{
373
382
  type: Input
383
+ }], cellRowspan: [{
384
+ type: Input
374
385
  }], headerTemplates: [{
375
386
  type: ContentChildren,
376
387
  args: [HeaderTemplateDirective, { descendants: false }]
@@ -64,13 +64,13 @@ export class DraggableColumnDirective {
64
64
  this.renderer = renderer;
65
65
  }
66
66
  ngOnInit() {
67
- this.subscriptions.add(this.zone.runOutsideAngular(() => this.draggable.kendoPress.pipe(filter(_ => this.enabled), filter(({ originalEvent: { target } }) => target === this.element.nativeElement || skipButtons(target)), tap((e) => {
67
+ this.subscriptions.add(this.zone.runOutsideAngular(() => this.draggable.kendoPress.pipe(filter(() => this.enabled), filter(({ originalEvent: { target } }) => target === this.element.nativeElement || skipButtons(target)), tap((e) => {
68
68
  const originalEvent = e.originalEvent;
69
69
  if (!e.isTouch) {
70
70
  originalEvent.preventDefault();
71
71
  }
72
72
  this.nav.navigateTo(originalEvent.target);
73
- }), switchMap(preventOnDblClick(this.draggable.kendoRelease)), tap((_) => {
73
+ }), switchMap(preventOnDblClick(this.draggable.kendoRelease)), tap(() => {
74
74
  this.hint.create(this.context.hint);
75
75
  this.cue.create();
76
76
  }), switchMap(down => this.draggable.kendoDrag.pipe(tap((e) => {
@@ -7,7 +7,7 @@ import { LocalEditService } from './local-edit.service';
7
7
  * @hidden
8
8
  */
9
9
  export class LocalRowEditService extends LocalEditService {
10
- update(_item) {
10
+ update() {
11
11
  this.dataChanged();
12
12
  }
13
13
  }
@@ -95,6 +95,7 @@ import { ToolbarComponent as GridToolbarComponent } from './rendering/toolbar/to
95
95
  import { LocalizedMessagesDirective } from './localization/localized-messages.directive';
96
96
  import { IconWrapperComponent } from '@progress/kendo-angular-icons';
97
97
  import { PagerTemplateDirective, PagerContextService, PagerNavigationService, KENDO_PAGER } from '@progress/kendo-angular-pager';
98
+ import { RowspanService } from './rendering/rowspan.service';
98
99
  import * as i0 from "@angular/core";
99
100
  import * as i1 from "./layout/browser-support.service";
100
101
  import * as i2 from "./selection/selection.service";
@@ -2069,7 +2070,7 @@ export class GridComponent {
2069
2070
  applyAutoSize() {
2070
2071
  const cols = this.columns.filter((c) => this.autoSize ? c.autoSize !== false : c.autoSize);
2071
2072
  if (cols.length > 0) {
2072
- this.ngZone.onStable.pipe(take(1)).subscribe(_ => this.autoFitColumns(cols));
2073
+ this.ngZone.onStable.pipe(take(1)).subscribe(() => this.autoFitColumns(cols));
2073
2074
  }
2074
2075
  }
2075
2076
  onColumnRangeChange(range) {
@@ -2213,7 +2214,8 @@ export class GridComponent {
2213
2214
  ContextService,
2214
2215
  SizingOptionsService,
2215
2216
  RowReorderService,
2216
- ClipboardService
2217
+ ClipboardService,
2218
+ RowspanService
2217
2219
  ], queries: [{ propertyName: "columns", predicate: ColumnBase }, { propertyName: "detailTemplateChildren", predicate: DetailTemplateDirective }, { propertyName: "cellLoadingTemplateChildren", predicate: CellLoadingTemplateDirective }, { propertyName: "loadingTemplateChildren", predicate: LoadingTemplateDirective }, { propertyName: "statusBarTemplateChildren", predicate: StatusBarTemplateDirective }, { propertyName: "noRecordsTemplateChildren", predicate: NoRecordsTemplateDirective }, { propertyName: "pagerTemplateChildren", predicate: PagerTemplateDirective }, { propertyName: "toolbarTemplateChildren", predicate: ToolbarTemplateDirective }, { propertyName: "columnMenuTemplates", predicate: ColumnMenuTemplateDirective }], viewQueries: [{ propertyName: "lockedHeader", first: true, predicate: ["lockedHeader"], descendants: true }, { propertyName: "header", first: true, predicate: ["header"], descendants: true }, { propertyName: "ariaRoot", first: true, predicate: ["ariaRoot"], descendants: true, static: true }, { propertyName: "dragTargetContainer", first: true, predicate: DragTargetContainerDirective, descendants: true }, { propertyName: "dropTargetContainer", first: true, predicate: DropTargetContainerDirective, descendants: true }, { propertyName: "footer", predicate: ["footer"], descendants: true }], exportAs: ["kendoGrid"], usesOnChanges: true, ngImport: i0, template: `
2218
2220
  <ng-container kendoGridLocalizedMessages
2219
2221
  i18n-groupPanelEmpty="kendo.grid.groupPanelEmpty|The label visible in the Grid group panel when it is empty"
@@ -2920,7 +2922,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
2920
2922
  ContextService,
2921
2923
  SizingOptionsService,
2922
2924
  RowReorderService,
2923
- ClipboardService
2925
+ ClipboardService,
2926
+ RowspanService
2924
2927
  ],
2925
2928
  selector: 'kendo-grid',
2926
2929
  template: `
@@ -227,7 +227,7 @@ export class GroupPanelComponent {
227
227
  .reduce((acc, target) => merge(acc, target.leave), from([]));
228
228
  const dropStream = this.dropTargets
229
229
  .reduce((acc, target) => merge(acc, target.drop), from([]));
230
- this.targetSubscription.add(enterStream.pipe(tap(_ => {
230
+ this.targetSubscription.add(enterStream.pipe(tap(() => {
231
231
  this.hint.removeLock();
232
232
  this.destroyMenu();
233
233
  }), filter(({ draggable, target }) => this.canDrop(draggable.context, target.context)), tap(this.enter.bind(this)), switchMapTo(dropStream.pipe(takeUntil(leaveStream.pipe(tap(this.leave.bind(this))))))).subscribe(this.drop.bind(this)));
@@ -47,6 +47,9 @@ export class LogicalCellDirective {
47
47
  return this.idService.cellId(this.logicalRowIndex, this.logicalColIndex);
48
48
  }
49
49
  }
50
+ get cellRowspan() {
51
+ return String(this.rowSpan);
52
+ }
50
53
  get ariaColIndex() {
51
54
  if (this.logicalSlaveCell || this.logicalColIndex === -1) {
52
55
  return undefined;
@@ -153,7 +156,7 @@ export class LogicalCellDirective {
153
156
  return this.navigationService.isCellFocused(this);
154
157
  }
155
158
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: LogicalCellDirective, deps: [{ token: i1.FocusGroup }, { token: i0.ElementRef }, { token: i2.ColumnInfoService }, { token: i3.IdService }, { token: i4.NavigationService }, { token: i0.Renderer2 }, { token: i0.NgZone }, { token: CELL_CONTEXT, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
156
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: LogicalCellDirective, isStandalone: true, selector: "[kendoGridLogicalCell]", inputs: { logicalColIndex: "logicalColIndex", logicalRowIndex: "logicalRowIndex", logicalSlaveCell: "logicalSlaveCell", colIndex: "colIndex", colSpan: "colSpan", rowSpan: "rowSpan", groupItem: "groupItem", dataRowIndex: "dataRowIndex", dataItem: "dataItem", detailExpandCell: "detailExpandCell", headerLabelText: "headerLabelText" }, host: { properties: { "attr.id": "this.id", "attr.aria-colindex": "this.ariaColIndex" } }, providers: [{
159
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: LogicalCellDirective, isStandalone: true, selector: "[kendoGridLogicalCell]", inputs: { logicalColIndex: "logicalColIndex", logicalRowIndex: "logicalRowIndex", logicalSlaveCell: "logicalSlaveCell", colIndex: "colIndex", colSpan: "colSpan", rowSpan: "rowSpan", groupItem: "groupItem", dataRowIndex: "dataRowIndex", dataItem: "dataItem", detailExpandCell: "detailExpandCell", headerLabelText: "headerLabelText" }, host: { properties: { "attr.id": "this.id", "attr.rowspan": "this.cellRowspan", "attr.aria-colindex": "this.ariaColIndex" } }, providers: [{
157
160
  provide: FocusGroup,
158
161
  deps: [FocusRoot],
159
162
  useClass: FocusGroup
@@ -200,6 +203,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
200
203
  }], id: [{
201
204
  type: HostBinding,
202
205
  args: ['attr.id']
206
+ }], cellRowspan: [{
207
+ type: HostBinding,
208
+ args: ['attr.rowspan']
203
209
  }], ariaColIndex: [{
204
210
  type: HostBinding,
205
211
  args: ['attr.aria-colindex']
@@ -152,6 +152,7 @@ export class NavigationService {
152
152
  pagerIsNavigable = false;
153
153
  tableIsNavigable = false;
154
154
  toolbarIsNavigable = false;
155
+ lastCellRowIndex;
155
156
  get activeDataRow() {
156
157
  return Math.max(0, this.activeRowIndex - this.meta.headerRows);
157
158
  }
@@ -420,78 +421,62 @@ export class NavigationService {
420
421
  .forEach(cell => cell.focusGroup && cell.focusGroup.activate());
421
422
  }
422
423
  moveCursorFwd() {
424
+ this.lastCellRowIndex = this.activeCell.rowIndex;
423
425
  return this.ctx.localization.rtl ? this.cursor.moveLeft() : this.cursor.moveRight();
424
426
  }
425
427
  moveCursorBwd() {
428
+ this.lastCellRowIndex = this.activeCell.rowIndex;
426
429
  return this.ctx.localization.rtl ? this.cursor.moveRight() : this.cursor.moveLeft();
427
430
  }
428
431
  onCursorKeydown(args) {
429
432
  let preventDefault = false;
430
433
  const modifier = args.ctrlKey || args.metaKey;
431
- const step = modifier ? 5 : 1;
434
+ let step = modifier ? 5 : 1;
435
+ const rowspan = +args.target?.getAttribute('rowspan');
436
+ let rowspanOffset = 0;
432
437
  if (!this.onCellKeydown(args)) {
433
438
  return;
434
439
  }
435
440
  const row = this.cursor.row;
441
+ const dir = args.keyCode === Keys.ArrowDown ? 'Down' : 'Up';
442
+ const right = args.keyCode === Keys.ArrowRight;
436
443
  switch (args.keyCode) {
437
444
  case Keys.ArrowDown:
438
- if (args.shiftKey) {
439
- if (this.ctx.grid.blockArrowSelection) {
440
- return;
441
- }
442
- preventDefault = this.cursor.moveDown(step);
443
- if (this.activeRow?.dataItem) {
444
- this.handleVerticalArrowSelection(step);
445
- }
446
- }
447
- else {
448
- preventDefault = this.cursor.moveDown(step);
449
- }
450
- break;
451
445
  case Keys.ArrowUp:
446
+ if (rowspan > 1) {
447
+ rowspanOffset = this.calculateRowspanOffset(dir, rowspan);
448
+ step += rowspanOffset;
449
+ }
452
450
  if (args.shiftKey) {
453
451
  if (this.ctx.grid.blockArrowSelection) {
454
452
  return;
455
453
  }
456
- preventDefault = this.cursor.moveUp(step);
454
+ preventDefault = this.cursor[`move${dir}`](step);
457
455
  if (this.activeRow?.dataItem) {
458
- this.handleVerticalArrowSelection(-step);
456
+ const sign = dir === 'Down' ? 1 : -1;
457
+ this.handleVerticalArrowSelection(sign * step);
459
458
  }
460
459
  }
461
460
  else {
462
- preventDefault = this.cursor.moveUp(step);
461
+ preventDefault = this.cursor[`move${dir}`](step);
463
462
  }
463
+ this.lastCellRowIndex = this.activeRowIndex;
464
464
  break;
465
465
  case Keys.ArrowRight:
466
- if (args.altKey && this.ctx.grid.resizable && this.isColumnResizable) {
467
- this.columnResize(true);
468
- break;
469
- }
470
- if (args.shiftKey) {
471
- if (this.ctx.grid.blockArrowSelection) {
472
- return;
473
- }
474
- preventDefault = this.moveCursorFwd();
475
- this.handleHorizontalArrowSelection(args);
476
- }
477
- else {
478
- preventDefault = this.moveCursorFwd();
479
- }
480
- break;
481
466
  case Keys.ArrowLeft:
482
467
  if (args.altKey && this.ctx.grid.resizable && this.isColumnResizable) {
483
- this.columnResize(false);
468
+ this.columnResize(right);
484
469
  break;
485
470
  }
486
471
  if (args.shiftKey) {
487
472
  if (this.ctx.grid.blockArrowSelection) {
488
473
  return;
489
474
  }
490
- preventDefault = this.moveCursorBwd();
475
+ preventDefault = this[`moveCursor${right ? 'Fwd' : 'Bwd'}`]();
491
476
  this.handleHorizontalArrowSelection(args);
492
477
  }
493
478
  else {
494
- preventDefault = this.moveCursorBwd();
479
+ preventDefault = this[`moveCursor${right ? 'Fwd' : 'Bwd'}`]();
495
480
  }
496
481
  break;
497
482
  case Keys.PageDown:
@@ -744,6 +729,13 @@ export class NavigationService {
744
729
  const ev = rowSelectionService.selectRange(startRowIndex, endRowIndex);
745
730
  rowSelectionService.changes.emit(ev);
746
731
  }
732
+ calculateRowspanOffset(direction, cellRowspan) {
733
+ if (!isPresent(this.lastCellRowIndex)) {
734
+ return 0;
735
+ }
736
+ const offset = direction === 'Up' ? Math.abs(this.lastCellRowIndex - this.activeRowIndex) : (this.activeRowIndex + cellRowspan - this.lastCellRowIndex - 1);
737
+ return offset;
738
+ }
747
739
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: NavigationService, deps: [{ token: i0.NgZone }, { token: i1.DomEventsService }, { token: i2.PagerContextService }, { token: i3.ScrollRequestService }, { token: i4.GroupsService }, { token: i5.DetailsService }, { token: i6.FocusRoot }, { token: i7.EditService }, { token: i0.ChangeDetectorRef }, { token: i8.ContextService }, { token: i9.ColumnResizingService }, { token: i10.FocusableDirective, optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
748
740
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: NavigationService });
749
741
  }
@@ -10,7 +10,7 @@ export const packageMetadata = {
10
10
  productName: 'Kendo UI for Angular',
11
11
  productCode: 'KENDOUIANGULAR',
12
12
  productCodes: ['KENDOUIANGULAR'],
13
- publishDate: 1738076727,
14
- version: '18.0.1-develop.3',
13
+ publishDate: 1738357372,
14
+ version: '18.1.0-develop.10',
15
15
  licensingDocsUrl: 'https://www.telerik.com/kendo-angular-ui/my-license/'
16
16
  };
@@ -88,7 +88,7 @@ export class PDFComponent extends PDFExportComponent {
88
88
  /**
89
89
  * @hidden
90
90
  */
91
- saveAs(_) {
91
+ saveAs() {
92
92
  throw new Error(GridConfigurationErrorMessages.unsupportedMethod('saveAs', 'GridComponent.saveAsPDF'));
93
93
  }
94
94
  /**
@@ -138,7 +138,7 @@ export class CellComponent {
138
138
  ngDoCheck() {
139
139
  this.updateCellContext();
140
140
  }
141
- ngOnChanges(_changes) {
141
+ ngOnChanges() {
142
142
  this.updateTemplateContext();
143
143
  }
144
144
  ngAfterContentChecked() {
@@ -19,7 +19,7 @@ export class ColGroupComponent {
19
19
  get columnsToRender() {
20
20
  return columnsToRender(this.columns);
21
21
  }
22
- trackBy(index, _item) {
22
+ trackBy(index) {
23
23
  return index;
24
24
  }
25
25
  isSorted(column) {
@@ -38,7 +38,7 @@ export class FooterComponent {
38
38
  get columnsToRender() {
39
39
  return columnsToRender(this.columns || []);
40
40
  }
41
- trackByIndex(index, _item) {
41
+ trackByIndex(index) {
42
42
  return index;
43
43
  }
44
44
  logicalColumnIndex(column) {
@@ -343,7 +343,7 @@ export class HeaderComponent {
343
343
  }
344
344
  canDrop(draggable, target) {
345
345
  isDocumentAvailable() && this.zone.runOutsideAngular(() => {
346
- document.addEventListener('pointerup', e => {
346
+ document.addEventListener('pointerup', () => {
347
347
  this.stopSorting = true;
348
348
  setTimeout(() => this.stopSorting = false);
349
349
  }, {
@@ -368,7 +368,7 @@ export class HeaderComponent {
368
368
  isCheckboxColumn(column) {
369
369
  return isCheckboxColumn(column) && !column.templateRef;
370
370
  }
371
- trackByIndex(index, _item) {
371
+ trackByIndex(index) {
372
372
  return index;
373
373
  }
374
374
  addStickyStyles(column) {
@@ -40,6 +40,7 @@ import { ColGroupComponent } from './common/col-group.component';
40
40
  import { GridTableDirective } from './grid-table.directive';
41
41
  import { TableDirective } from '../column-resizing/table.directive';
42
42
  import { NgIf } from '@angular/common';
43
+ import { RowspanService } from './rowspan.service';
43
44
  import * as i0 from "@angular/core";
44
45
  import * as i1 from "./details/details.service";
45
46
  import * as i2 from "../data/change-notification.service";
@@ -55,6 +56,7 @@ import * as i11 from "../common/provider.service";
55
56
  import * as i12 from "../column-resizing/column-resizing.service";
56
57
  import * as i13 from "../pdf/pdf.service";
57
58
  import * as i14 from "../common/column-info.service";
59
+ import * as i15 from "./rowspan.service";
58
60
  const elementAt = (index, elements, elementOffset) => {
59
61
  for (let idx = 0, elementIdx = 0; idx < elements.length; idx++) {
60
62
  const offset = elementOffset(elements[idx]);
@@ -119,6 +121,7 @@ export class ListComponent {
119
121
  changeDetector;
120
122
  pdfService;
121
123
  columnInfo;
124
+ rowspanService;
122
125
  hostClass = true;
123
126
  hostRole = 'presentation';
124
127
  data;
@@ -175,6 +178,7 @@ export class ListComponent {
175
178
  columnsEndIdx;
176
179
  viewportColumnsWidth;
177
180
  scrollLeft = 0;
181
+ observer;
178
182
  get lockedLeafColumns() {
179
183
  return this.columns.lockedLeafColumns;
180
184
  }
@@ -206,7 +210,7 @@ export class ListComponent {
206
210
  rtl = false;
207
211
  columnUpdateFrame;
208
212
  hasLockedContainer;
209
- constructor(scrollerFactory, detailsService, changeNotification, suspendService, groupsService, ngZone, renderer, scrollSyncService, resizeService, editService, supportService, navigationService, scrollRequestService, ctx, columnResizingService, changeDetector, pdfService, columnInfo) {
213
+ constructor(scrollerFactory, detailsService, changeNotification, suspendService, groupsService, ngZone, renderer, scrollSyncService, resizeService, editService, supportService, navigationService, scrollRequestService, ctx, columnResizingService, changeDetector, pdfService, columnInfo, rowspanService) {
210
214
  this.changeNotification = changeNotification;
211
215
  this.suspendService = suspendService;
212
216
  this.groupsService = groupsService;
@@ -222,6 +226,7 @@ export class ListComponent {
222
226
  this.changeDetector = changeDetector;
223
227
  this.pdfService = pdfService;
224
228
  this.columnInfo = columnInfo;
229
+ this.rowspanService = rowspanService;
225
230
  this.scroller = scrollerFactory(this.dispatcher);
226
231
  this.subscriptions = detailsService.changes.subscribe(x => this.detailExpand(x));
227
232
  this.subscriptions.add(scrollRequestService.requests.subscribe(req => isPresent(req.adjustIndex) ? this.scrollTo(req.request, req.adjustIndex) : this.scrollToItem(req.request)));
@@ -293,6 +298,7 @@ export class ListComponent {
293
298
  if (this.resizeService) {
294
299
  this.resizeService.destroy();
295
300
  }
301
+ this.observer?.disconnect();
296
302
  this.cleanupScroller();
297
303
  }
298
304
  init() {
@@ -398,7 +404,7 @@ export class ListComponent {
398
404
  const shouldScroll = () => this.isVirtual && this.skip > 0 && this.total > 0;
399
405
  const sub = this.changeNotification.changes
400
406
  .pipe(filter(shouldScroll))
401
- .subscribe(_ => {
407
+ .subscribe(() => {
402
408
  this.scrollTo({ row: this.skip });
403
409
  sub.unsubscribe();
404
410
  });
@@ -409,7 +415,10 @@ export class ListComponent {
409
415
  return merge(this.changeNotification.changes, this.groupsService.changes
410
416
  .pipe(filter(isLocked), switchMapTo(onStable())), this.editService.changed, this.resizeService.changes, this.columnResizingService.changes
411
417
  .pipe(filter(change => change.type === 'end')), this.supportService.changes)
412
- .pipe(tap(() => this.resetNavigationViewport()), filter(isLocked))
418
+ .pipe(tap(() => {
419
+ this.ngZone.run(() => this.rowspanService.reset());
420
+ this.resetNavigationViewport();
421
+ }), filter(isLocked))
413
422
  .subscribe(() => {
414
423
  const scrollTop = this.container.nativeElement.scrollTop;
415
424
  const scrollLeft = this.container.nativeElement.scrollLeft;
@@ -652,7 +661,7 @@ export class ListComponent {
652
661
  }
653
662
  return element.offsetLeft;
654
663
  }
655
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ListComponent, deps: [{ token: SCROLLER_FACTORY_TOKEN }, { token: i1.DetailsService }, { token: i2.ChangeNotificationService }, { token: i3.SuspendService }, { token: i4.GroupsService }, { token: i0.NgZone }, { token: i0.Renderer2 }, { token: i5.ScrollSyncService }, { token: i6.ResizeService }, { token: i7.EditService }, { token: i8.BrowserSupportService }, { token: i9.NavigationService }, { token: i10.ScrollRequestService }, { token: i11.ContextService }, { token: i12.ColumnResizingService }, { token: i0.ChangeDetectorRef }, { token: i13.PDFService }, { token: i14.ColumnInfoService }], target: i0.ɵɵFactoryTarget.Component });
664
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ListComponent, deps: [{ token: SCROLLER_FACTORY_TOKEN }, { token: i1.DetailsService }, { token: i2.ChangeNotificationService }, { token: i3.SuspendService }, { token: i4.GroupsService }, { token: i0.NgZone }, { token: i0.Renderer2 }, { token: i5.ScrollSyncService }, { token: i6.ResizeService }, { token: i7.EditService }, { token: i8.BrowserSupportService }, { token: i9.NavigationService }, { token: i10.ScrollRequestService }, { token: i11.ContextService }, { token: i12.ColumnResizingService }, { token: i0.ChangeDetectorRef }, { token: i13.PDFService }, { token: i14.ColumnInfoService }, { token: i15.RowspanService }], target: i0.ɵɵFactoryTarget.Component });
656
665
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: ListComponent, isStandalone: true, selector: "kendo-grid-list", inputs: { data: "data", groups: "groups", total: "total", rowHeight: "rowHeight", stickyRowHeight: "stickyRowHeight", detailRowHeight: "detailRowHeight", take: "take", skip: "skip", columns: "columns", detailTemplate: "detailTemplate", noRecordsTemplate: "noRecordsTemplate", selectable: "selectable", groupable: "groupable", filterable: "filterable", rowClass: "rowClass", rowSticky: "rowSticky", loading: "loading", trackBy: "trackBy", virtualColumns: "virtualColumns", isVirtual: "isVirtual", cellLoadingTemplate: "cellLoadingTemplate", loadingTemplate: "loadingTemplate", sort: "sort", size: "size" }, outputs: { contentScroll: "contentScroll", pageChange: "pageChange", scrollBottom: "scrollBottom" }, host: { properties: { "class.k-grid-container": "this.hostClass", "attr.role": "this.hostRole" } }, providers: [
657
666
  {
658
667
  provide: SCROLLER_FACTORY_TOKEN,
@@ -899,7 +908,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
899
908
  }], ctorParameters: function () { return [{ type: undefined, decorators: [{
900
909
  type: Inject,
901
910
  args: [SCROLLER_FACTORY_TOKEN]
902
- }] }, { type: i1.DetailsService }, { type: i2.ChangeNotificationService }, { type: i3.SuspendService }, { type: i4.GroupsService }, { type: i0.NgZone }, { type: i0.Renderer2 }, { type: i5.ScrollSyncService }, { type: i6.ResizeService }, { type: i7.EditService }, { type: i8.BrowserSupportService }, { type: i9.NavigationService }, { type: i10.ScrollRequestService }, { type: i11.ContextService }, { type: i12.ColumnResizingService }, { type: i0.ChangeDetectorRef }, { type: i13.PDFService }, { type: i14.ColumnInfoService }]; }, propDecorators: { hostClass: [{
911
+ }] }, { type: i1.DetailsService }, { type: i2.ChangeNotificationService }, { type: i3.SuspendService }, { type: i4.GroupsService }, { type: i0.NgZone }, { type: i0.Renderer2 }, { type: i5.ScrollSyncService }, { type: i6.ResizeService }, { type: i7.EditService }, { type: i8.BrowserSupportService }, { type: i9.NavigationService }, { type: i10.ScrollRequestService }, { type: i11.ContextService }, { type: i12.ColumnResizingService }, { type: i0.ChangeDetectorRef }, { type: i13.PDFService }, { type: i14.ColumnInfoService }, { type: i15.RowspanService }]; }, propDecorators: { hostClass: [{
903
912
  type: HostBinding,
904
913
  args: ['class.k-grid-container']
905
914
  }], hostRole: [{
@@ -0,0 +1,24 @@
1
+ /**-----------------------------------------------------------------------------------------
2
+ * Copyright © 2025 Progress Software Corporation. All rights reserved.
3
+ * Licensed under commercial license. See LICENSE.md in the project root for more information
4
+ *-------------------------------------------------------------------------------------------*/
5
+ /**
6
+ * @hidden
7
+ */
8
+ export class RowspanService {
9
+ skipCells = [];
10
+ addCells(rowIndex, colIndex, rowspan) {
11
+ for (let i = 1; i < rowspan; i++) {
12
+ if (!this.skipCells.some(this.cellExists(rowIndex + i, colIndex))) {
13
+ this.skipCells.push({ rowIndex: rowIndex + i, colIndex });
14
+ }
15
+ }
16
+ }
17
+ reset() {
18
+ this.skipCells = [];
19
+ }
20
+ shouldSkip(rowIndex, colIndex) {
21
+ return !!this.skipCells.find(this.cellExists(rowIndex, colIndex));
22
+ }
23
+ cellExists = (rowIndex, colIndex) => cell => cell.rowIndex === rowIndex && cell.colIndex === colIndex;
24
+ }