@progress/kendo-angular-grid 19.2.0-develop.9 → 19.2.0

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.
@@ -14139,7 +14139,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
14139
14139
  }] } });
14140
14140
 
14141
14141
  /**
14142
- * Represents a directive that manages keyboard navigation for a column menu item. [See example](slug:columnmenu_grid#customizing-the-content).
14142
+ * Represents a directive that manages keyboard navigation for a column menu item ([see example](slug:columnmenu_grid#customizing-the-content)).
14143
14143
  *
14144
14144
  * @example
14145
14145
  * ```html
@@ -21214,8 +21214,8 @@ const packageMetadata = {
21214
21214
  productName: 'Kendo UI for Angular',
21215
21215
  productCode: 'KENDOUIANGULAR',
21216
21216
  productCodes: ['KENDOUIANGULAR'],
21217
- publishDate: 1751035948,
21218
- version: '19.2.0-develop.9',
21217
+ publishDate: 1751463227,
21218
+ version: '19.2.0',
21219
21219
  licensingDocsUrl: 'https://www.telerik.com/kendo-angular-ui/my-license/'
21220
21220
  };
21221
21221
 
@@ -23200,12 +23200,10 @@ class TableDirective {
23200
23200
  if (isConstrainedMode && !this.service.autoFitResize) {
23201
23201
  this.renderer.setStyle(this.element.nativeElement, 'width', this.service.originalWidth + 'px');
23202
23202
  }
23203
- else {
23204
- if (!this.virtualColumns || this.locked) {
23205
- const delta = deltas.reduce((sum, item) => sum + item, 0);
23206
- const width = this.service.originalWidth + delta;
23207
- this.renderer.setStyle(this.element.nativeElement, 'width', width + 'px');
23208
- }
23203
+ else if (!this.virtualColumns || this.locked) {
23204
+ const delta = deltas.reduce((sum, item) => sum + item, 0);
23205
+ const width = this.service.originalWidth + delta;
23206
+ this.renderer.setStyle(this.element.nativeElement, 'width', width + 'px');
23209
23207
  }
23210
23208
  this.cdr.detectChanges();
23211
23209
  }
@@ -23600,7 +23598,7 @@ class ListComponent {
23600
23598
  if (shouldCalculatePageSize) {
23601
23599
  const calculatedPageSize = this.calcVirtualPageSize();
23602
23600
  if (calculatedPageSize > 0) {
23603
- this.ngZone.onMicrotaskEmpty.pipe(take(1)).subscribe(() => {
23601
+ this.ngZone.onStable.pipe(take(1)).subscribe(() => {
23604
23602
  this.ctx.grid.pageSize = calculatedPageSize;
23605
23603
  this.ngZone.run(() => {
23606
23604
  this.pageChange.emit({
@@ -27645,723 +27643,191 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
27645
27643
  args: [GroupToolbarToolComponent]
27646
27644
  }] } });
27647
27645
 
27646
+ const createControl = (source) => (acc, key) => {
27647
+ acc[key] = new FormControl(source[key]);
27648
+ return acc;
27649
+ };
27650
+ const validateColumnsField = (columns) => expandColumns(columns.toArray())
27651
+ .filter(isColumnComponent)
27652
+ .filter(({ field }) => !isValidFieldName(field))
27653
+ .forEach(({ field }) => console.warn(ColumnConfigurationErrorMessages.fieldName(field)));
27654
+ const handleExpandCollapseGroupsService = (service, expandEmitter, collapseEmitter, map) => (service.changes.pipe(filter(({ group, emit }) => emit && isPresent(group)))
27655
+ .subscribe((x) => x.expand ? expandEmitter.emit(map(x)) : collapseEmitter.emit(map(x))));
27656
+ const handleExpandCollapseDetailsService = (service, expandEmitter, collapseEmitter, map) => (service.changes.pipe(filter(({ dataItem }) => isPresent(dataItem)))
27657
+ .subscribe((x) => x.expand ? expandEmitter.emit(map(x)) : collapseEmitter.emit(map(x))));
27658
+ const isInEditedCell = (element, gridElement) => closest(element, matchesClasses('k-grid-edit-cell')) &&
27659
+ closest(element, matchesNodeName('kendo-grid')) === gridElement;
27660
+ const NOTIFY_DELAY = 500;
27648
27661
  /**
27649
- * A directive that handles in-memory data operations like [paging]({% slug paging_grid %}),
27650
- * [sorting]({% slug sorting_grid %}), and [grouping]({% slug grouping_grid %}).
27662
+ * Represents the Kendo UI for Angular Data Grid component.
27651
27663
  *
27652
- * Use this directive with local data and enable the Grid data operations with minimal configuration.
27653
- * ([More information and examples]({% slug local_data_grid %}#toc-using-the-data-binding-directive)).
27664
+ * Use the `kendo-grid` component to display and manage tabular data.
27654
27665
  *
27655
27666
  * @example
27656
27667
  * ```html
27657
- * <kendo-grid [kendoGridBinding]="gridData"></kendo-grid>
27668
+ * <kendo-grid [data]="gridData"></kendo-grid>
27658
27669
  * ```
27670
+ *
27659
27671
  * @remarks
27660
- * Applied to: {@link GridComponent}.
27672
+ * Supported children components are:
27673
+ * {@link CheckboxColumnComponent},
27674
+ * {@link ColumnChooserComponent},
27675
+ * {@link ColumnComponent},
27676
+ * {@link ColumnGroupComponent},
27677
+ * {@link ColumnMenuAutoSizeAllColumnsComponent},
27678
+ * {@link ColumnMenuAutoSizeColumnComponent},
27679
+ * {@link ColumnMenuChooserComponent},
27680
+ * {@link ColumnMenuComponent},
27681
+ * {@link ColumnMenuFilterComponent},
27682
+ * {@link ColumnMenuItemComponent},
27683
+ * {@link ColumnMenuLockComponent},
27684
+ * {@link ColumnMenuPositionComponent},
27685
+ * {@link ColumnMenuSortComponent},
27686
+ * {@link ColumnMenuStickComponent},
27687
+ * {@link CommandColumnComponent},
27688
+ * {@link CustomMessagesComponent},
27689
+ * {@link ExcelComponent},
27690
+ * {@link GridSpacerComponent},
27691
+ * {@link PDFComponent},
27692
+ * {@link RowReorderColumnComponent},
27693
+ * {@link SpanColumnComponent},
27694
+ * {@link ToolBarComponent}.
27661
27695
  */
27662
- class DataBindingDirective {
27663
- grid;
27664
- changeDetector;
27665
- localDataChangesService;
27696
+ class GridComponent {
27697
+ supportService;
27698
+ selectionService;
27699
+ cellSelectionService;
27700
+ wrapper;
27701
+ groupInfoService;
27702
+ groupsService;
27703
+ changeNotification;
27704
+ detailsService;
27705
+ editService;
27706
+ filterService;
27707
+ pdfService;
27708
+ responsiveService;
27709
+ renderer;
27710
+ excelService;
27711
+ ngZone;
27712
+ scrollSyncService;
27713
+ domEvents;
27714
+ columnResizingService;
27715
+ changeDetectorRef;
27716
+ columnReorderService;
27717
+ columnInfoService;
27718
+ navigationService;
27719
+ sortService;
27720
+ scrollRequestService;
27721
+ localization;
27722
+ ctx;
27723
+ sizingService;
27724
+ adaptiveGridService;
27666
27725
  rowReorderService;
27726
+ dataMappingService;
27667
27727
  /**
27668
- * Sets the number of records to skip in the Grid.
27669
- *
27670
- * @default 0
27728
+ * Sets the data of the Grid. If you provide an array, the Grid gets the total count automatically.
27729
+ * ([more information and example]({% slug binding_grid %})).
27671
27730
  */
27672
- set skip(value) {
27673
- if (!isPresent(value)) {
27674
- value = 0;
27731
+ set data(value) {
27732
+ this._data = value;
27733
+ if (this.selectable && this.selectableSettings?.enabled && this.isVirtual) {
27734
+ this.blockArrowSelection = false;
27675
27735
  }
27676
- this.grid.skip = this.state.skip = value;
27677
- if (this.rowReorderService) {
27678
- this.rowReorderService.skip = value;
27736
+ if (this.notifyTimeout) {
27737
+ clearTimeout(this.notifyTimeout);
27738
+ this.notifyTimeout = null;
27739
+ }
27740
+ if (this.rowReorderable) {
27741
+ this.ngZone.runOutsideAngular(() => {
27742
+ this.notifyTimeout = setTimeout(() => {
27743
+ this.notifyReorderContainers();
27744
+ }, NOTIFY_DELAY);
27745
+ });
27679
27746
  }
27680
27747
  }
27748
+ get data() {
27749
+ return this._data;
27750
+ }
27751
+ get hintText() {
27752
+ return this.rowReorderService.getDefaultHintText(this.columnList, this.flatData);
27753
+ }
27681
27754
  /**
27682
- * Sets the sort descriptors for the Grid data.
27683
- *
27755
+ * @hidden
27684
27756
  */
27685
- set sort(value) {
27686
- this.grid.sort = this.state.sort = value;
27757
+ get customHintTemplate() {
27758
+ if (this.rowReorderable) {
27759
+ const allColumns = this.columnList.toArray();
27760
+ const rowReorderColumn = allColumns.find(column => column.isRowReorderColumn);
27761
+ return rowReorderColumn.rowDragHintTemplateRef;
27762
+ }
27687
27763
  }
27688
27764
  /**
27689
- * Sets the filter descriptor for the Grid data.
27690
- *
27765
+ * @hidden
27691
27766
  */
27692
- set filter(value) {
27693
- this.grid.filter = this.state.filter = value;
27767
+ get hintContext() {
27768
+ if (this.customHintTemplate) {
27769
+ const draggedRow = this.rowReorderService?.getDraggedRow(this.flatData);
27770
+ return {
27771
+ $implicit: draggedRow?.dataItem,
27772
+ rowIndex: draggedRow?.rowIndex
27773
+ };
27774
+ }
27694
27775
  }
27695
27776
  /**
27696
- * Sets the page size for the Grid pager.
27697
- *
27777
+ * Defines the page size used by the Grid pager.
27778
+ * Required for the [`paging`]({% slug paging_grid %}) feature.
27698
27779
  */
27699
- set pageSize(value) {
27700
- this.grid.pageSize = this.state.take = value;
27701
- }
27780
+ pageSize;
27702
27781
  /**
27703
- * Sets the group descriptors for the Grid data.
27704
- *
27782
+ * Defines the height in pixels for the Grid when `scrollable` is set.
27783
+ * You can also set the height using `style.height` with units like `px`, `%`, `em`, or `rem`.
27705
27784
  */
27706
- set group(value) {
27707
- this.grid.group = this.state.group = value;
27708
- }
27785
+ height;
27709
27786
  /**
27710
- * Sets the data array for the Grid.
27711
- *
27787
+ * Represents the row height in pixels for each Grid row (`tr`) element.
27788
+ * Required for [virtual scrolling]({% slug scrollmmodes_grid %}).
27789
+ * Set `rowHeight` to match the actual DOM row height. The property does not change the height of the rows in the UI.
27712
27790
  */
27713
- set data(value) {
27714
- this.originalData = value || [];
27715
- if (this.localDataChangesService) {
27716
- this.localDataChangesService.data = value;
27717
- }
27718
- this.dataChanged = true;
27719
- }
27720
- state = {
27721
- skip: 0
27722
- };
27723
- originalData = [];
27724
- dataChanged;
27725
- stateChangeSubscription;
27726
- dataChangedSubscription;
27727
- rowReorderSubscription;
27728
- constructor(grid, changeDetector, localDataChangesService, rowReorderService, ctx) {
27729
- this.grid = grid;
27730
- this.changeDetector = changeDetector;
27731
- this.localDataChangesService = localDataChangesService;
27732
- this.rowReorderService = rowReorderService;
27733
- if (localDataChangesService) {
27734
- this.dataChangedSubscription = this.localDataChangesService.changes.subscribe(this.rebind.bind(this));
27735
- }
27736
- ctx && (ctx.dataBindingDirective = this);
27737
- }
27791
+ rowHeight;
27738
27792
  /**
27739
- * @hidden
27793
+ * Enables or disables adaptive mode. Adaptive rendering is off by default.
27794
+ *
27795
+ * @default 'none'
27740
27796
  */
27741
- ngOnInit() {
27742
- this.applyState(this.state);
27743
- this.stateChangeSubscription = this.grid
27744
- .dataStateChange
27745
- .subscribe(this.onStateChange.bind(this));
27746
- if (this.rowReorderService) {
27747
- this.rowReorderSubscription = this.grid
27748
- .rowReorder
27749
- .subscribe(this.onRowReorder.bind(this));
27750
- }
27751
- }
27797
+ adaptiveMode = 'none';
27752
27798
  /**
27753
- * @hidden
27799
+ * Represents the height in pixels for each Grid detail row (`tr`) element.
27800
+ * Required for [virtual scrolling]({% slug scrollmmodes_grid %}).
27801
+ * Set `detailRowHeight` to match the actual DOM detail row height. The property does not change the height of the rows in the UI.
27754
27802
  */
27755
- ngOnDestroy() {
27756
- if (this.stateChangeSubscription) {
27757
- this.stateChangeSubscription.unsubscribe();
27758
- }
27759
- if (this.dataChangedSubscription) {
27760
- this.dataChangedSubscription.unsubscribe();
27761
- }
27762
- if (this.rowReorderSubscription) {
27763
- this.rowReorderSubscription.unsubscribe();
27764
- }
27765
- }
27803
+ detailRowHeight;
27766
27804
  /**
27767
- * @hidden
27805
+ * Defines the number of records to skip in the pager.
27806
+ * Required for the [`paging`]({% slug paging_grid %}) feature.
27768
27807
  */
27769
- ngOnChanges(changes) {
27770
- if (anyChanged(["pageSize", "skip", "sort", "group", "filter"], changes)) {
27771
- this.rebind();
27772
- }
27808
+ get skip() {
27809
+ return this._skip;
27773
27810
  }
27774
- ngDoCheck() {
27775
- if (this.dataChanged) {
27776
- this.updateGridData();
27811
+ set skip(value) {
27812
+ if (typeof value === 'number' && value >= 0) {
27813
+ this._skip = this.rowReorderService.skip = value;
27777
27814
  }
27778
27815
  }
27779
27816
  /**
27780
- * @hidden
27817
+ * Defines the scroll mode for the Grid.
27818
+ *
27819
+ * @default 'scrollable'
27781
27820
  */
27782
- onStateChange(state) {
27783
- this.applyState(state);
27784
- this.rebind();
27785
- }
27821
+ scrollable = 'scrollable';
27786
27822
  /**
27787
- * @hidden
27823
+ * Enables the selection feature of the Grid. The `selectable` property can be set to `true`, `false`, or an object with additional settings.
27824
+ * [See example](slug:grid_row_selection).
27825
+ *
27826
+ * @default false
27788
27827
  */
27789
- onRowReorder(ev) {
27790
- this.rowReorderService.reorderRows(ev, this.originalData);
27791
- this.rebind();
27792
- }
27828
+ selectable = false;
27793
27829
  /**
27794
- * @hidden
27795
- */
27796
- rebind() {
27797
- this.data = this.originalData;
27798
- this.updateGridData();
27799
- this.notifyDataChange();
27800
- }
27801
- /**
27802
- * Notifies the Grid that its data has changed.
27803
- */
27804
- notifyDataChange() {
27805
- this.grid.onDataChange();
27806
- if (this.changeDetector) {
27807
- this.changeDetector.markForCheck();
27808
- }
27809
- }
27810
- process(state) {
27811
- if (this.grid.isVirtual && (!isPresent(state.take) || state.take === 0)) {
27812
- return {
27813
- data: [],
27814
- total: this.originalData?.length || 0
27815
- };
27816
- }
27817
- return process(this.originalData, state);
27818
- }
27819
- applyState({ skip, take, sort, group, filter }) {
27820
- this.skip = skip;
27821
- this.pageSize = take;
27822
- this.sort = sort;
27823
- this.group = group;
27824
- this.filter = filter;
27825
- }
27826
- updateGridData() {
27827
- this.grid.data = this.process(this.state);
27828
- this.grid.updateNavigationMetadata();
27829
- this.dataChanged = false;
27830
- }
27831
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: DataBindingDirective, deps: [{ token: GridComponent }, { token: i0.ChangeDetectorRef }, { token: LocalDataChangesService }, { token: RowReorderService }, { token: ContextService }], target: i0.ɵɵFactoryTarget.Directive });
27832
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: DataBindingDirective, isStandalone: true, selector: "[kendoGridBinding]", inputs: { skip: "skip", sort: "sort", filter: "filter", pageSize: "pageSize", group: "group", data: ["kendoGridBinding", "data"] }, exportAs: ["kendoGridBinding"], usesOnChanges: true, ngImport: i0 });
27833
- }
27834
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: DataBindingDirective, decorators: [{
27835
- type: Directive,
27836
- args: [{
27837
- selector: '[kendoGridBinding]',
27838
- exportAs: 'kendoGridBinding',
27839
- standalone: true
27840
- }]
27841
- }], ctorParameters: function () { return [{ type: GridComponent }, { type: i0.ChangeDetectorRef }, { type: LocalDataChangesService }, { type: RowReorderService }, { type: ContextService }]; }, propDecorators: { skip: [{
27842
- type: Input
27843
- }], sort: [{
27844
- type: Input
27845
- }], filter: [{
27846
- type: Input
27847
- }], pageSize: [{
27848
- type: Input
27849
- }], group: [{
27850
- type: Input
27851
- }], data: [{
27852
- type: Input,
27853
- args: ["kendoGridBinding"]
27854
- }] } });
27855
-
27856
- const hasGroups = (items) => items && items.length && items[0].field && items[0].items;
27857
- const groupDescriptorsPresent = (descriptors) => isPresent(descriptors) && descriptors.length > 0;
27858
- const processGroups = (data, state) => process(data, state).data;
27859
- const removeParentDescriptors = (parents, owner) => g => g.field !== owner.field && !parents.some(y => y.field === g.field);
27860
- const findGroup = (groupIndex, groups) => {
27861
- const parents = [];
27862
- return {
27863
- group: groupIndex.split("_").reduce((acc, x) => {
27864
- const idx = parseInt(x, 10);
27865
- if (acc.items) {
27866
- parents.push(acc);
27867
- return acc.items[idx];
27868
- }
27869
- return isArray(acc) ? acc[idx] : acc;
27870
- }, groups),
27871
- parents
27872
- };
27873
- };
27874
- const findChildren = (data, parents) => {
27875
- const filters = parents.map(p => ({ field: p.field, operator: "eq", value: p.value }));
27876
- return filterBy(data, {
27877
- filters: filters,
27878
- logic: "and"
27879
- });
27880
- };
27881
- /**
27882
- * @hidden
27883
- */
27884
- const count = (groups, includeFooters = false) => (groups.reduce((acc, group) => {
27885
- if (!group.skipHeader) {
27886
- acc++;
27887
- }
27888
- if (group.items) {
27889
- const children = count(group.items, includeFooters);
27890
- if (includeFooters && children && !group.hideFooter) {
27891
- acc++;
27892
- }
27893
- acc += children;
27894
- }
27895
- return acc;
27896
- }, 0));
27897
- /**
27898
- * @hidden
27899
- */
27900
- const noDescriptors = (descriptors) => !isPresent(descriptors) || !descriptors.length;
27901
- /**
27902
- * @hidden
27903
- */
27904
- const slice = (groups, skip, take, includeFooters = false) => {
27905
- if (!isPresent(take)) {
27906
- return groups;
27907
- }
27908
- const result = [];
27909
- for (let idx = 0, length = groups.length; idx < length; idx++) {
27910
- if (take <= 0) {
27911
- break;
27912
- }
27913
- const group = groups[idx];
27914
- const groupItems = group.items;
27915
- let itemCount = count(groupItems, includeFooters);
27916
- if (includeFooters && groupItems.length) {
27917
- itemCount++;
27918
- }
27919
- const skipHeader = skip > 0;
27920
- if (skip) {
27921
- skip--;
27922
- if (itemCount && skip >= itemCount) {
27923
- skip -= itemCount;
27924
- continue;
27925
- }
27926
- }
27927
- if (!skipHeader || itemCount) {
27928
- const items = [];
27929
- let hideFooter = true;
27930
- if (!skipHeader) {
27931
- take--;
27932
- }
27933
- if (take) {
27934
- if (hasGroups(groupItems)) {
27935
- const children = slice(groupItems, skip, take, includeFooters);
27936
- items.push(...children);
27937
- take -= count(children, includeFooters);
27938
- }
27939
- else {
27940
- items.push(...groupItems.slice(skip, Math.min(skip + take, groupItems.length)));
27941
- take -= items.length;
27942
- }
27943
- if (take && includeFooters) {
27944
- hideFooter = false;
27945
- take--;
27946
- }
27947
- skip = 0;
27948
- }
27949
- result.push({
27950
- aggregates: group.aggregates,
27951
- field: group.field,
27952
- hideFooter,
27953
- items,
27954
- offset: idx,
27955
- skipHeader,
27956
- value: group.value
27957
- });
27958
- }
27959
- }
27960
- return result;
27961
- };
27962
- const skippedHeaders = (groupItem) => {
27963
- let total = 0;
27964
- while (groupItem) {
27965
- if (groupItem.skipHeader) {
27966
- total++;
27967
- }
27968
- groupItem = groupItem.items && groupItem.items[0] || null;
27969
- }
27970
- return total;
27971
- };
27972
- /**
27973
- * A directive which encapsulates the in-memory handling of grouping with virtual scrolling.
27974
- * @remarks
27975
- * Applied to: {@link GridComponent}.
27976
- */
27977
- class GroupBindingDirective extends DataBindingDirective {
27978
- groupsService;
27979
- /**
27980
- * The array of data which will be used to populate the Grid.
27981
- */
27982
- set kendoGridGroupBinding(value) {
27983
- this.groups = null;
27984
- this.grid.resetGroupsState();
27985
- this.data = value;
27986
- }
27987
- /**
27988
- * @hidden
27989
- */
27990
- set data(value) {
27991
- this.originalData = value || [];
27992
- this.dataChanged = true;
27993
- }
27994
- /**
27995
- * Defines the descriptors by which the data will be sorted.
27996
- */
27997
- set sort(value) {
27998
- const noCurrentDescriptors = noDescriptors(this.state.sort);
27999
- const noIncomingDescriptors = noDescriptors(value);
28000
- const clear = this.state.sort !== value && !(noCurrentDescriptors && noIncomingDescriptors);
28001
- this.grid.sort = this.state.sort = value;
28002
- if (clear) {
28003
- this.groups = null;
28004
- this.grid.resetGroupsState();
28005
- }
28006
- }
28007
- /**
28008
- * Defines the descriptor by which the data will be filtered.
28009
- */
28010
- set filter(value) {
28011
- const clear = diffFilters(this.state.filter, value);
28012
- if (clear) {
28013
- this.state.filter = value;
28014
- this.grid.filter = cloneFilters(value);
28015
- this.groups = null;
28016
- this.grid.resetGroupsState();
28017
- }
28018
- }
28019
- /**
28020
- * Defines the descriptors by which the data will be grouped.
28021
- */
28022
- set group(value) {
28023
- // don't clear if no groups are present in previous and current value
28024
- const groupsPresent = groupDescriptorsPresent(this.state.group) || groupDescriptorsPresent(value);
28025
- const clear = this.state.group !== value && groupsPresent;
28026
- this.grid.group = this.state.group = value;
28027
- if (clear) {
28028
- this.groups = null;
28029
- this.grid.resetGroupsState();
28030
- this.skip = 0;
28031
- }
28032
- }
28033
- groups;
28034
- gridSubs = new Subscription();
28035
- constructor(changeDetector, localDataChangesService, ctxService, groupsService) {
28036
- super(ctxService.grid, changeDetector, localDataChangesService, null, ctxService);
28037
- this.groupsService = groupsService;
28038
- ctxService.dataBindingDirective = this;
28039
- }
28040
- ngOnInit() {
28041
- super.ngOnInit();
28042
- this.gridSubs.add(this.grid.groupExpand.subscribe(this.groupExpand.bind(this)));
28043
- this.gridSubs.add(this.grid.groupCollapse.subscribe(this.groupCollapse.bind(this)));
28044
- }
28045
- ngAfterContentInit() {
28046
- if (isDevMode() && this.grid.isGroupExpanded) {
28047
- throw new Error(GridConfigurationErrorMessages.groupBindingDirectives);
28048
- }
28049
- }
28050
- ngOnDestroy() {
28051
- this.gridSubs.unsubscribe();
28052
- }
28053
- /**
28054
- * @hidden
28055
- */
28056
- toggleAll(expand) {
28057
- this.skip = 0;
28058
- this.grid.scrollTo({ row: 0, column: 0 });
28059
- this.groups.forEach((gr, idx) => {
28060
- const expanded = this.groupsService.isExpanded({
28061
- group: gr,
28062
- groupIndex: idx.toString(),
28063
- parentGroup: undefined
28064
- });
28065
- const performToggle = (expand && !expanded) || (!expand && expanded);
28066
- if (performToggle) {
28067
- this.grid.groupsService.toggleRow({
28068
- type: 'group',
28069
- data: gr,
28070
- index: idx.toString(),
28071
- level: 0,
28072
- parentGroup: undefined
28073
- });
28074
- this[expand ? 'groupExpand' : 'groupCollapse']({ groupIndex: idx.toString() });
28075
- }
28076
- });
28077
- }
28078
- /**
28079
- * Collapses all expanded root level groups.
28080
- */
28081
- collapseAll() {
28082
- this.toggleAll(false);
28083
- }
28084
- /**
28085
- * Expands all expanded root level groups.
28086
- */
28087
- expandAll() {
28088
- this.toggleAll(true);
28089
- }
28090
- /**
28091
- * @hidden
28092
- */
28093
- groupExpand({ groupIndex }) {
28094
- const { group, parents } = findGroup(groupIndex, this.groups);
28095
- if (!group) {
28096
- return;
28097
- }
28098
- this.groupsService.expandChildren(groupIndex);
28099
- if (!group.items.length) {
28100
- const descriptors = this.state.group.filter(removeParentDescriptors(parents, group));
28101
- const children = findChildren(this.originalData, parents.concat(group));
28102
- group.items = processGroups(children, {
28103
- filter: this.state.filter,
28104
- group: descriptors,
28105
- sort: this.state.sort
28106
- });
28107
- }
28108
- this.grid.data = this.dataResult(this.state.skip, this.state.take);
28109
- }
28110
- /**
28111
- * @hidden
28112
- */
28113
- groupCollapse({ groupIndex }) {
28114
- const { group } = findGroup(groupIndex, this.groups);
28115
- if (group) {
28116
- group.items = [];
28117
- }
28118
- else {
28119
- return;
28120
- }
28121
- this.grid.data = this.dataResult(this.state.skip, this.state.take);
28122
- }
28123
- process(state) {
28124
- if (state.group && state.group.length) {
28125
- const groups = this.processGroups(state);
28126
- this.grid.skip -= skippedHeaders(groups.data[0]);
28127
- return groups;
28128
- }
28129
- else {
28130
- this.groups = null;
28131
- }
28132
- return super.process(state);
28133
- }
28134
- processGroups(state) {
28135
- if (!this.groups || !this.groups.length) {
28136
- this.groups = processGroups(this.originalData, {
28137
- filter: state.filter,
28138
- group: state.group,
28139
- sort: state.sort
28140
- });
28141
- }
28142
- return this.dataResult(state.skip, state.take);
28143
- }
28144
- dataResult(skip, take) {
28145
- const includeFooters = this.grid.showGroupFooters;
28146
- return {
28147
- data: slice(this.groups, skip, take, includeFooters),
28148
- total: count(this.groups, includeFooters)
28149
- };
28150
- }
28151
- applyState({ skip, take, sort, group, filter }) {
28152
- this.skip = skip;
28153
- this.state.take = take;
28154
- // this.pageSize = take; // do need to update take as the process with slice correctly
28155
- this.sort = sort;
28156
- this.group = group;
28157
- this.filter = filter;
28158
- }
28159
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: GroupBindingDirective, deps: [{ token: i0.ChangeDetectorRef }, { token: LocalDataChangesService }, { token: ContextService }, { token: GroupsService }], target: i0.ɵɵFactoryTarget.Directive });
28160
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: GroupBindingDirective, isStandalone: true, selector: "[kendoGridGroupBinding]", inputs: { kendoGridGroupBinding: "kendoGridGroupBinding", sort: "sort", filter: "filter", group: "group" }, exportAs: ["kendoGridGroupBinding"], usesInheritance: true, ngImport: i0 });
28161
- }
28162
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: GroupBindingDirective, decorators: [{
28163
- type: Directive,
28164
- args: [{
28165
- selector: '[kendoGridGroupBinding]',
28166
- exportAs: 'kendoGridGroupBinding',
28167
- standalone: true
28168
- }]
28169
- }], ctorParameters: function () { return [{ type: i0.ChangeDetectorRef }, { type: LocalDataChangesService }, { type: ContextService }, { type: GroupsService }]; }, propDecorators: { kendoGridGroupBinding: [{
28170
- type: Input,
28171
- args: ["kendoGridGroupBinding"]
28172
- }], sort: [{
28173
- type: Input
28174
- }], filter: [{
28175
- type: Input
28176
- }], group: [{
28177
- type: Input
28178
- }] } });
28179
-
28180
- const createControl = (source) => (acc, key) => {
28181
- acc[key] = new FormControl(source[key]);
28182
- return acc;
28183
- };
28184
- const validateColumnsField = (columns) => expandColumns(columns.toArray())
28185
- .filter(isColumnComponent)
28186
- .filter(({ field }) => !isValidFieldName(field))
28187
- .forEach(({ field }) => console.warn(ColumnConfigurationErrorMessages.fieldName(field)));
28188
- const handleExpandCollapseGroupsService = (service, expandEmitter, collapseEmitter, map) => (service.changes.pipe(filter(({ group, emit }) => emit && isPresent(group)))
28189
- .subscribe((x) => x.expand ? expandEmitter.emit(map(x)) : collapseEmitter.emit(map(x))));
28190
- const handleExpandCollapseDetailsService = (service, expandEmitter, collapseEmitter, map) => (service.changes.pipe(filter(({ dataItem }) => isPresent(dataItem)))
28191
- .subscribe((x) => x.expand ? expandEmitter.emit(map(x)) : collapseEmitter.emit(map(x))));
28192
- const isInEditedCell = (element, gridElement) => closest(element, matchesClasses('k-grid-edit-cell')) &&
28193
- closest(element, matchesNodeName('kendo-grid')) === gridElement;
28194
- const NOTIFY_DELAY = 500;
28195
- /**
28196
- * Represents the Kendo UI for Angular Data Grid component.
28197
- *
28198
- * Use the `kendo-grid` component to display and manage tabular data.
28199
- *
28200
- * @example
28201
- * ```html
28202
- * <kendo-grid [data]="gridData"></kendo-grid>
28203
- * ```
28204
- *
28205
- * @remarks
28206
- * Supported children components are:
28207
- * {@link CheckboxColumnComponent},
28208
- * {@link ColumnChooserComponent},
28209
- * {@link ColumnComponent},
28210
- * {@link ColumnGroupComponent},
28211
- * {@link ColumnMenuAutoSizeAllColumnsComponent},
28212
- * {@link ColumnMenuAutoSizeColumnComponent},
28213
- * {@link ColumnMenuChooserComponent},
28214
- * {@link ColumnMenuComponent},
28215
- * {@link ColumnMenuFilterComponent},
28216
- * {@link ColumnMenuItemComponent},
28217
- * {@link ColumnMenuLockComponent},
28218
- * {@link ColumnMenuPositionComponent},
28219
- * {@link ColumnMenuSortComponent},
28220
- * {@link ColumnMenuStickComponent},
28221
- * {@link CommandColumnComponent},
28222
- * {@link CustomMessagesComponent},
28223
- * {@link ExcelComponent},
28224
- * {@link GridSpacerComponent},
28225
- * {@link PDFComponent},
28226
- * {@link RowReorderColumnComponent},
28227
- * {@link SpanColumnComponent},
28228
- * {@link ToolBarComponent}.
28229
- */
28230
- class GridComponent {
28231
- supportService;
28232
- selectionService;
28233
- cellSelectionService;
28234
- wrapper;
28235
- groupInfoService;
28236
- groupsService;
28237
- changeNotification;
28238
- detailsService;
28239
- editService;
28240
- filterService;
28241
- pdfService;
28242
- responsiveService;
28243
- renderer;
28244
- excelService;
28245
- ngZone;
28246
- scrollSyncService;
28247
- domEvents;
28248
- columnResizingService;
28249
- changeDetectorRef;
28250
- columnReorderService;
28251
- columnInfoService;
28252
- navigationService;
28253
- sortService;
28254
- scrollRequestService;
28255
- localization;
28256
- ctx;
28257
- sizingService;
28258
- adaptiveGridService;
28259
- rowReorderService;
28260
- dataMappingService;
28261
- /**
28262
- * Sets the data of the Grid. If you provide an array, the Grid gets the total count automatically.
28263
- * ([more information and example]({% slug binding_grid %})).
28264
- */
28265
- set data(value) {
28266
- this._data = value;
28267
- if (this.selectable && this.selectableSettings?.enabled && this.isVirtual) {
28268
- this.blockArrowSelection = false;
28269
- }
28270
- if (this.notifyTimeout) {
28271
- clearTimeout(this.notifyTimeout);
28272
- this.notifyTimeout = null;
28273
- }
28274
- if (this.rowReorderable) {
28275
- this.ngZone.runOutsideAngular(() => {
28276
- this.notifyTimeout = setTimeout(() => {
28277
- this.notifyReorderContainers();
28278
- }, NOTIFY_DELAY);
28279
- });
28280
- }
28281
- }
28282
- get data() {
28283
- return this._data;
28284
- }
28285
- get hintText() {
28286
- return this.rowReorderService.getDefaultHintText(this.columnList, this.flatData);
28287
- }
28288
- /**
28289
- * @hidden
28290
- */
28291
- get customHintTemplate() {
28292
- if (this.rowReorderable) {
28293
- const allColumns = this.columnList.toArray();
28294
- const rowReorderColumn = allColumns.find(column => column.isRowReorderColumn);
28295
- return rowReorderColumn.rowDragHintTemplateRef;
28296
- }
28297
- }
28298
- /**
28299
- * @hidden
28300
- */
28301
- get hintContext() {
28302
- if (this.customHintTemplate) {
28303
- const draggedRow = this.rowReorderService?.getDraggedRow(this.flatData);
28304
- return {
28305
- $implicit: draggedRow?.dataItem,
28306
- rowIndex: draggedRow?.rowIndex
28307
- };
28308
- }
28309
- }
28310
- /**
28311
- * Defines the page size used by the Grid pager.
28312
- * Required for the [`paging`]({% slug paging_grid %}) feature.
28313
- */
28314
- pageSize;
28315
- /**
28316
- * Defines the height in pixels for the Grid when `scrollable` is set.
28317
- * You can also set the height using `style.height` with units like `px`, `%`, `em`, or `rem`.
28318
- */
28319
- height;
28320
- /**
28321
- * Represents the row height in pixels for each Grid row (`tr`) element.
28322
- * Required for [virtual scrolling]({% slug scrollmmodes_grid %}).
28323
- * Set `rowHeight` to match the actual DOM row height. The property does not change the height of the rows in the UI.
28324
- */
28325
- rowHeight;
28326
- /**
28327
- * Enables or disables adaptive mode. Adaptive rendering is off by default.
28328
- *
28329
- * @default 'none'
28330
- */
28331
- adaptiveMode = 'none';
28332
- /**
28333
- * Represents the height in pixels for each Grid detail row (`tr`) element.
28334
- * Required for [virtual scrolling]({% slug scrollmmodes_grid %}).
28335
- * Set `detailRowHeight` to match the actual DOM detail row height. The property does not change the height of the rows in the UI.
28336
- */
28337
- detailRowHeight;
28338
- /**
28339
- * Defines the number of records to skip in the pager.
28340
- * Required for the [`paging`]({% slug paging_grid %}) feature.
28341
- */
28342
- get skip() {
28343
- return this._skip;
28344
- }
28345
- set skip(value) {
28346
- if (typeof value === 'number' && value >= 0) {
28347
- this._skip = this.rowReorderService.skip = value;
28348
- }
28349
- }
28350
- /**
28351
- * Defines the scroll mode for the Grid.
28352
- *
28353
- * @default 'scrollable'
28354
- */
28355
- scrollable = 'scrollable';
28356
- /**
28357
- * Enables the selection feature of the Grid. The `selectable` property can be set to `true`, `false`, or an object with additional settings.
28358
- * [See example](slug:grid_row_selection).
28359
- *
28360
- * @default false
28361
- */
28362
- selectable = false;
28363
- /**
28364
- * Sets the descriptors for sorting the data ([see example]({% slug sorting_grid %})).
27830
+ * Sets the descriptors for sorting the data ([see example]({% slug sorting_grid %})).
28365
27831
  */
28366
27832
  set sort(value) {
28367
27833
  if (isArray(value)) {
@@ -30353,7 +29819,7 @@ class GridComponent {
30353
29819
  }
30354
29820
  if (this.groupsService.isExpanded({ groupIndex: index }) !== expand) {
30355
29821
  this.groupsService.toggleRow({ index }, false);
30356
- if (this.ctx.dataBindingDirective && this.ctx.dataBindingDirective instanceof GroupBindingDirective) {
29822
+ if (this.ctx.dataBindingDirective && isPresent(this.ctx.dataBindingDirective.groupExpand)) {
30357
29823
  this.ctx.dataBindingDirective[`group${expand ? 'Expand' : 'Collapse'}`]({ groupIndex: index });
30358
29824
  }
30359
29825
  }
@@ -32295,6 +31761,214 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
32295
31761
  }]
32296
31762
  }], ctorParameters: function () { return [{ type: i1$2.LocalizationService }]; } });
32297
31763
 
31764
+ /**
31765
+ * A directive that handles in-memory data operations like [paging]({% slug paging_grid %}),
31766
+ * [sorting]({% slug sorting_grid %}), and [grouping]({% slug grouping_grid %}).
31767
+ *
31768
+ * Use this directive with local data and enable the Grid data operations with minimal configuration.
31769
+ * ([More information and examples]({% slug local_data_grid %}#toc-using-the-data-binding-directive)).
31770
+ *
31771
+ * @example
31772
+ * ```html
31773
+ * <kendo-grid [kendoGridBinding]="gridData"></kendo-grid>
31774
+ * ```
31775
+ * @remarks
31776
+ * Applied to: {@link GridComponent}.
31777
+ */
31778
+ class DataBindingDirective {
31779
+ grid;
31780
+ changeDetector;
31781
+ localDataChangesService;
31782
+ rowReorderService;
31783
+ /**
31784
+ * Sets the number of records to skip in the Grid.
31785
+ *
31786
+ * @default 0
31787
+ */
31788
+ set skip(value) {
31789
+ if (!isPresent(value)) {
31790
+ value = 0;
31791
+ }
31792
+ this.grid.skip = this.state.skip = value;
31793
+ if (this.rowReorderService) {
31794
+ this.rowReorderService.skip = value;
31795
+ }
31796
+ }
31797
+ /**
31798
+ * Sets the sort descriptors for the Grid data.
31799
+ *
31800
+ */
31801
+ set sort(value) {
31802
+ this.grid.sort = this.state.sort = value;
31803
+ }
31804
+ /**
31805
+ * Sets the filter descriptor for the Grid data.
31806
+ *
31807
+ */
31808
+ set filter(value) {
31809
+ this.grid.filter = this.state.filter = value;
31810
+ }
31811
+ /**
31812
+ * Sets the page size for the Grid pager.
31813
+ *
31814
+ */
31815
+ set pageSize(value) {
31816
+ this.grid.pageSize = this.state.take = value;
31817
+ }
31818
+ /**
31819
+ * Sets the group descriptors for the Grid data.
31820
+ *
31821
+ */
31822
+ set group(value) {
31823
+ this.grid.group = this.state.group = value;
31824
+ }
31825
+ /**
31826
+ * Sets the data array for the Grid.
31827
+ *
31828
+ */
31829
+ set data(value) {
31830
+ this.originalData = value || [];
31831
+ if (this.localDataChangesService) {
31832
+ this.localDataChangesService.data = value;
31833
+ }
31834
+ this.dataChanged = true;
31835
+ }
31836
+ state = {
31837
+ skip: 0
31838
+ };
31839
+ originalData = [];
31840
+ dataChanged;
31841
+ stateChangeSubscription;
31842
+ dataChangedSubscription;
31843
+ rowReorderSubscription;
31844
+ constructor(grid, changeDetector, localDataChangesService, rowReorderService, ctx) {
31845
+ this.grid = grid;
31846
+ this.changeDetector = changeDetector;
31847
+ this.localDataChangesService = localDataChangesService;
31848
+ this.rowReorderService = rowReorderService;
31849
+ if (localDataChangesService) {
31850
+ this.dataChangedSubscription = this.localDataChangesService.changes.subscribe(this.rebind.bind(this));
31851
+ }
31852
+ ctx && (ctx.dataBindingDirective = this);
31853
+ }
31854
+ /**
31855
+ * @hidden
31856
+ */
31857
+ ngOnInit() {
31858
+ this.applyState(this.state);
31859
+ this.stateChangeSubscription = this.grid
31860
+ .dataStateChange
31861
+ .subscribe(this.onStateChange.bind(this));
31862
+ if (this.rowReorderService) {
31863
+ this.rowReorderSubscription = this.grid
31864
+ .rowReorder
31865
+ .subscribe(this.onRowReorder.bind(this));
31866
+ }
31867
+ }
31868
+ /**
31869
+ * @hidden
31870
+ */
31871
+ ngOnDestroy() {
31872
+ if (this.stateChangeSubscription) {
31873
+ this.stateChangeSubscription.unsubscribe();
31874
+ }
31875
+ if (this.dataChangedSubscription) {
31876
+ this.dataChangedSubscription.unsubscribe();
31877
+ }
31878
+ if (this.rowReorderSubscription) {
31879
+ this.rowReorderSubscription.unsubscribe();
31880
+ }
31881
+ }
31882
+ /**
31883
+ * @hidden
31884
+ */
31885
+ ngOnChanges(changes) {
31886
+ if (anyChanged(["pageSize", "skip", "sort", "group", "filter"], changes)) {
31887
+ this.rebind();
31888
+ }
31889
+ }
31890
+ ngDoCheck() {
31891
+ if (this.dataChanged) {
31892
+ this.updateGridData();
31893
+ }
31894
+ }
31895
+ /**
31896
+ * @hidden
31897
+ */
31898
+ onStateChange(state) {
31899
+ this.applyState(state);
31900
+ this.rebind();
31901
+ }
31902
+ /**
31903
+ * @hidden
31904
+ */
31905
+ onRowReorder(ev) {
31906
+ this.rowReorderService.reorderRows(ev, this.originalData);
31907
+ this.rebind();
31908
+ }
31909
+ /**
31910
+ * @hidden
31911
+ */
31912
+ rebind() {
31913
+ this.data = this.originalData;
31914
+ this.updateGridData();
31915
+ this.notifyDataChange();
31916
+ }
31917
+ /**
31918
+ * Notifies the Grid that its data has changed.
31919
+ */
31920
+ notifyDataChange() {
31921
+ this.grid.onDataChange();
31922
+ if (this.changeDetector) {
31923
+ this.changeDetector.markForCheck();
31924
+ }
31925
+ }
31926
+ process(state) {
31927
+ if (this.grid.isVirtual && (!isPresent(state.take) || state.take === 0)) {
31928
+ return {
31929
+ data: [],
31930
+ total: this.originalData?.length || 0
31931
+ };
31932
+ }
31933
+ return process(this.originalData, state);
31934
+ }
31935
+ applyState({ skip, take, sort, group, filter }) {
31936
+ this.skip = skip;
31937
+ this.pageSize = take;
31938
+ this.sort = sort;
31939
+ this.group = group;
31940
+ this.filter = filter;
31941
+ }
31942
+ updateGridData() {
31943
+ this.grid.data = this.process(this.state);
31944
+ this.grid.updateNavigationMetadata();
31945
+ this.dataChanged = false;
31946
+ }
31947
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: DataBindingDirective, deps: [{ token: GridComponent }, { token: i0.ChangeDetectorRef }, { token: LocalDataChangesService }, { token: RowReorderService }, { token: ContextService }], target: i0.ɵɵFactoryTarget.Directive });
31948
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: DataBindingDirective, isStandalone: true, selector: "[kendoGridBinding]", inputs: { skip: "skip", sort: "sort", filter: "filter", pageSize: "pageSize", group: "group", data: ["kendoGridBinding", "data"] }, exportAs: ["kendoGridBinding"], usesOnChanges: true, ngImport: i0 });
31949
+ }
31950
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: DataBindingDirective, decorators: [{
31951
+ type: Directive,
31952
+ args: [{
31953
+ selector: '[kendoGridBinding]',
31954
+ exportAs: 'kendoGridBinding',
31955
+ standalone: true
31956
+ }]
31957
+ }], ctorParameters: function () { return [{ type: GridComponent }, { type: i0.ChangeDetectorRef }, { type: LocalDataChangesService }, { type: RowReorderService }, { type: ContextService }]; }, propDecorators: { skip: [{
31958
+ type: Input
31959
+ }], sort: [{
31960
+ type: Input
31961
+ }], filter: [{
31962
+ type: Input
31963
+ }], pageSize: [{
31964
+ type: Input
31965
+ }], group: [{
31966
+ type: Input
31967
+ }], data: [{
31968
+ type: Input,
31969
+ args: ["kendoGridBinding"]
31970
+ }] } });
31971
+
32298
31972
  /**
32299
31973
  * Stores the row selection state of the Grid in memory
32300
31974
  * ([see example]({% slug selection_grid %}#toc-toggling-the-selection-functionality)).
@@ -33088,112 +32762,442 @@ class ExpandGroupDirective {
33088
32762
  /**
33089
32763
  * Fires when the `expandedGroupKeys` collection changes.
33090
32764
  */
33091
- expandedGroupKeysChange = new EventEmitter();
32765
+ expandedGroupKeysChange = new EventEmitter();
32766
+ /**
32767
+ * Sets the item format stored in the `expandedGroupKeys` collection.
32768
+ * Accepts a property name or a function that returns a unique key for each group
32769
+ * ([see example]({% slug groups_expanded_state_grid %}#toc-custom-group-key-format)).
32770
+ */
32771
+ get expandGroupBy() {
32772
+ return this._expandGroupBy;
32773
+ }
32774
+ set expandGroupBy(key) {
32775
+ if (typeof key === 'function') {
32776
+ this._expandGroupBy = key;
32777
+ }
32778
+ }
32779
+ /**
32780
+ * Holds the collection of expanded group keys.
32781
+ * Set this property to control which group rows are expanded.
32782
+ */
32783
+ get expandedGroupKeys() {
32784
+ return this._expandedGroupKeys;
32785
+ }
32786
+ set expandedGroupKeys(expandedGroups) {
32787
+ this._expandedGroupKeys = (expandedGroups || []).slice();
32788
+ }
32789
+ /**
32790
+ * Specifies if group items are expanded by default.
32791
+ * @default false
32792
+ */
32793
+ groupsInitiallyExpanded = false;
32794
+ _expandGroupBy;
32795
+ _expandedGroupKeys;
32796
+ subscriptions = new Subscription();
32797
+ constructor(grid) {
32798
+ this.grid = grid;
32799
+ this.grid.isGroupExpanded = this.isExpanded.bind(this);
32800
+ this.subscriptions.add(merge(this.grid.groupExpand.pipe(map(e => ({ expand: true, ...e }))), this.grid.groupCollapse.pipe(map(e => ({ expand: false, ...e })))).subscribe(this.toggleState.bind(this)));
32801
+ }
32802
+ ngOnDestroy() {
32803
+ this.subscriptions.unsubscribe();
32804
+ }
32805
+ get keyGetter() {
32806
+ return this.expandGroupBy || DEFAULT_KEY_GETTER;
32807
+ }
32808
+ /**
32809
+ * @hidden
32810
+ */
32811
+ isExpanded(groupArgs) {
32812
+ const itemIndex = this.getItemIndex(groupArgs);
32813
+ return itemIndex > -1 ? !this.groupsInitiallyExpanded : this.groupsInitiallyExpanded;
32814
+ }
32815
+ getItemIndex(groupArgs) {
32816
+ if (this.expandGroupBy) {
32817
+ return this.expandedGroupKeys.indexOf(this.keyGetter(groupArgs));
32818
+ }
32819
+ return this.expandedGroupKeys.findIndex(item => {
32820
+ let index = 0;
32821
+ let parentGroup = groupArgs.parentGroup;
32822
+ while (isPresent(parentGroup)) {
32823
+ if (!isPresent(item.parentGroupKeys) || !isPresent(item.parentGroupKeys[index]) ||
32824
+ parentGroup.group.value !== item.parentGroupKeys[index].value ||
32825
+ parentGroup.group.field !== item.parentGroupKeys[index].field) {
32826
+ return false;
32827
+ }
32828
+ parentGroup = parentGroup.parentGroup;
32829
+ index++;
32830
+ }
32831
+ return item.value === groupArgs.group.value && item.field === groupArgs.group.field;
32832
+ });
32833
+ }
32834
+ toggleState(groupArgs) {
32835
+ const key = this.keyGetter(groupArgs);
32836
+ if (Boolean(this.groupsInitiallyExpanded) !== groupArgs.expand) {
32837
+ this.expandedGroupKeys.push(key);
32838
+ }
32839
+ else {
32840
+ const index = this.expandedGroupKeys.findIndex(group => {
32841
+ if (this.expandGroupBy) {
32842
+ return group === key;
32843
+ }
32844
+ else if (key.parentGroupKeys?.length === 0) {
32845
+ return group.value === key.value;
32846
+ }
32847
+ return JSON.stringify(group) === JSON.stringify(key);
32848
+ });
32849
+ this.expandedGroupKeys.splice(index, 1);
32850
+ }
32851
+ this.expandedGroupKeysChange.emit(this.expandedGroupKeys.slice());
32852
+ }
32853
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ExpandGroupDirective, deps: [{ token: GridComponent }], target: i0.ɵɵFactoryTarget.Directive });
32854
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: ExpandGroupDirective, isStandalone: true, selector: "[kendoGridExpandGroupBy]", inputs: { expandGroupBy: ["kendoGridExpandGroupBy", "expandGroupBy"], expandedGroupKeys: "expandedGroupKeys", groupsInitiallyExpanded: "groupsInitiallyExpanded" }, outputs: { expandedGroupKeysChange: "expandedGroupKeysChange" }, exportAs: ["kendoGridExpandGroupBy"], ngImport: i0 });
32855
+ }
32856
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ExpandGroupDirective, decorators: [{
32857
+ type: Directive,
32858
+ args: [{
32859
+ selector: '[kendoGridExpandGroupBy]',
32860
+ exportAs: 'kendoGridExpandGroupBy',
32861
+ standalone: true
32862
+ }]
32863
+ }], ctorParameters: function () { return [{ type: GridComponent }]; }, propDecorators: { expandedGroupKeysChange: [{
32864
+ type: Output
32865
+ }], expandGroupBy: [{
32866
+ type: Input,
32867
+ args: ['kendoGridExpandGroupBy']
32868
+ }], expandedGroupKeys: [{
32869
+ type: Input
32870
+ }], groupsInitiallyExpanded: [{
32871
+ type: Input
32872
+ }] } });
32873
+
32874
+ const hasGroups = (items) => items && items.length && items[0].field && items[0].items;
32875
+ const groupDescriptorsPresent = (descriptors) => isPresent(descriptors) && descriptors.length > 0;
32876
+ const processGroups = (data, state) => process(data, state).data;
32877
+ const removeParentDescriptors = (parents, owner) => g => g.field !== owner.field && !parents.some(y => y.field === g.field);
32878
+ const findGroup = (groupIndex, groups) => {
32879
+ const parents = [];
32880
+ return {
32881
+ group: groupIndex.split("_").reduce((acc, x) => {
32882
+ const idx = parseInt(x, 10);
32883
+ if (acc.items) {
32884
+ parents.push(acc);
32885
+ return acc.items[idx];
32886
+ }
32887
+ return isArray(acc) ? acc[idx] : acc;
32888
+ }, groups),
32889
+ parents
32890
+ };
32891
+ };
32892
+ const findChildren = (data, parents) => {
32893
+ const filters = parents.map(p => ({ field: p.field, operator: "eq", value: p.value }));
32894
+ return filterBy(data, {
32895
+ filters: filters,
32896
+ logic: "and"
32897
+ });
32898
+ };
32899
+ /**
32900
+ * @hidden
32901
+ */
32902
+ const count = (groups, includeFooters = false) => (groups.reduce((acc, group) => {
32903
+ if (!group.skipHeader) {
32904
+ acc++;
32905
+ }
32906
+ if (group.items) {
32907
+ const children = count(group.items, includeFooters);
32908
+ if (includeFooters && children && !group.hideFooter) {
32909
+ acc++;
32910
+ }
32911
+ acc += children;
32912
+ }
32913
+ return acc;
32914
+ }, 0));
32915
+ /**
32916
+ * @hidden
32917
+ */
32918
+ const noDescriptors = (descriptors) => !isPresent(descriptors) || !descriptors.length;
32919
+ /**
32920
+ * @hidden
32921
+ */
32922
+ const slice = (groups, skip, take, includeFooters = false) => {
32923
+ if (!isPresent(take)) {
32924
+ return groups;
32925
+ }
32926
+ const result = [];
32927
+ for (let idx = 0, length = groups.length; idx < length; idx++) {
32928
+ if (take <= 0) {
32929
+ break;
32930
+ }
32931
+ const group = groups[idx];
32932
+ const groupItems = group.items;
32933
+ let itemCount = count(groupItems, includeFooters);
32934
+ if (includeFooters && groupItems.length) {
32935
+ itemCount++;
32936
+ }
32937
+ const skipHeader = skip > 0;
32938
+ if (skip) {
32939
+ skip--;
32940
+ if (itemCount && skip >= itemCount) {
32941
+ skip -= itemCount;
32942
+ continue;
32943
+ }
32944
+ }
32945
+ if (!skipHeader || itemCount) {
32946
+ const items = [];
32947
+ let hideFooter = true;
32948
+ if (!skipHeader) {
32949
+ take--;
32950
+ }
32951
+ if (take) {
32952
+ if (hasGroups(groupItems)) {
32953
+ const children = slice(groupItems, skip, take, includeFooters);
32954
+ items.push(...children);
32955
+ take -= count(children, includeFooters);
32956
+ }
32957
+ else {
32958
+ items.push(...groupItems.slice(skip, Math.min(skip + take, groupItems.length)));
32959
+ take -= items.length;
32960
+ }
32961
+ if (take && includeFooters) {
32962
+ hideFooter = false;
32963
+ take--;
32964
+ }
32965
+ skip = 0;
32966
+ }
32967
+ result.push({
32968
+ aggregates: group.aggregates,
32969
+ field: group.field,
32970
+ hideFooter,
32971
+ items,
32972
+ offset: idx,
32973
+ skipHeader,
32974
+ value: group.value
32975
+ });
32976
+ }
32977
+ }
32978
+ return result;
32979
+ };
32980
+ const skippedHeaders = (groupItem) => {
32981
+ let total = 0;
32982
+ while (groupItem) {
32983
+ if (groupItem.skipHeader) {
32984
+ total++;
32985
+ }
32986
+ groupItem = groupItem.items && groupItem.items[0] || null;
32987
+ }
32988
+ return total;
32989
+ };
32990
+ /**
32991
+ * A directive which encapsulates the in-memory handling of grouping with virtual scrolling.
32992
+ * @remarks
32993
+ * Applied to: {@link GridComponent}.
32994
+ */
32995
+ class GroupBindingDirective extends DataBindingDirective {
32996
+ groupsService;
32997
+ /**
32998
+ * The array of data which will be used to populate the Grid.
32999
+ */
33000
+ set kendoGridGroupBinding(value) {
33001
+ this.groups = null;
33002
+ this.grid.resetGroupsState();
33003
+ this.data = value;
33004
+ }
33005
+ /**
33006
+ * @hidden
33007
+ */
33008
+ set data(value) {
33009
+ this.originalData = value || [];
33010
+ this.dataChanged = true;
33011
+ }
33092
33012
  /**
33093
- * Sets the item format stored in the `expandedGroupKeys` collection.
33094
- * Accepts a property name or a function that returns a unique key for each group
33095
- * ([see example]({% slug groups_expanded_state_grid %}#toc-custom-group-key-format)).
33013
+ * Defines the descriptors by which the data will be sorted.
33096
33014
  */
33097
- get expandGroupBy() {
33098
- return this._expandGroupBy;
33015
+ set sort(value) {
33016
+ const noCurrentDescriptors = noDescriptors(this.state.sort);
33017
+ const noIncomingDescriptors = noDescriptors(value);
33018
+ const clear = this.state.sort !== value && !(noCurrentDescriptors && noIncomingDescriptors);
33019
+ this.grid.sort = this.state.sort = value;
33020
+ if (clear) {
33021
+ this.groups = null;
33022
+ this.grid.resetGroupsState();
33023
+ }
33099
33024
  }
33100
- set expandGroupBy(key) {
33101
- if (typeof key === 'function') {
33102
- this._expandGroupBy = key;
33025
+ /**
33026
+ * Defines the descriptor by which the data will be filtered.
33027
+ */
33028
+ set filter(value) {
33029
+ const clear = diffFilters(this.state.filter, value);
33030
+ if (clear) {
33031
+ this.state.filter = value;
33032
+ this.grid.filter = cloneFilters(value);
33033
+ this.groups = null;
33034
+ this.grid.resetGroupsState();
33103
33035
  }
33104
33036
  }
33105
33037
  /**
33106
- * Holds the collection of expanded group keys.
33107
- * Set this property to control which group rows are expanded.
33038
+ * Defines the descriptors by which the data will be grouped.
33108
33039
  */
33109
- get expandedGroupKeys() {
33110
- return this._expandedGroupKeys;
33040
+ set group(value) {
33041
+ // don't clear if no groups are present in previous and current value
33042
+ const groupsPresent = groupDescriptorsPresent(this.state.group) || groupDescriptorsPresent(value);
33043
+ const clear = this.state.group !== value && groupsPresent;
33044
+ this.grid.group = this.state.group = value;
33045
+ if (clear) {
33046
+ this.groups = null;
33047
+ this.grid.resetGroupsState();
33048
+ this.skip = 0;
33049
+ }
33111
33050
  }
33112
- set expandedGroupKeys(expandedGroups) {
33113
- this._expandedGroupKeys = (expandedGroups || []).slice();
33051
+ groups;
33052
+ gridSubs = new Subscription();
33053
+ constructor(changeDetector, localDataChangesService, ctxService, groupsService) {
33054
+ super(ctxService.grid, changeDetector, localDataChangesService, null, ctxService);
33055
+ this.groupsService = groupsService;
33056
+ ctxService.dataBindingDirective = this;
33057
+ }
33058
+ ngOnInit() {
33059
+ super.ngOnInit();
33060
+ this.gridSubs.add(this.grid.groupExpand.subscribe(this.groupExpand.bind(this)));
33061
+ this.gridSubs.add(this.grid.groupCollapse.subscribe(this.groupCollapse.bind(this)));
33062
+ }
33063
+ ngAfterContentInit() {
33064
+ if (isDevMode() && this.grid.isGroupExpanded) {
33065
+ throw new Error(GridConfigurationErrorMessages.groupBindingDirectives);
33066
+ }
33067
+ }
33068
+ ngOnDestroy() {
33069
+ this.gridSubs.unsubscribe();
33114
33070
  }
33115
33071
  /**
33116
- * Specifies if group items are expanded by default.
33117
- * @default false
33072
+ * @hidden
33118
33073
  */
33119
- groupsInitiallyExpanded = false;
33120
- _expandGroupBy;
33121
- _expandedGroupKeys;
33122
- subscriptions = new Subscription();
33123
- constructor(grid) {
33124
- this.grid = grid;
33125
- this.grid.isGroupExpanded = this.isExpanded.bind(this);
33126
- this.subscriptions.add(merge(this.grid.groupExpand.pipe(map(e => ({ expand: true, ...e }))), this.grid.groupCollapse.pipe(map(e => ({ expand: false, ...e })))).subscribe(this.toggleState.bind(this)));
33074
+ toggleAll(expand) {
33075
+ this.skip = 0;
33076
+ this.grid.scrollTo({ row: 0, column: 0 });
33077
+ this.groups.forEach((gr, idx) => {
33078
+ const expanded = this.groupsService.isExpanded({
33079
+ group: gr,
33080
+ groupIndex: idx.toString(),
33081
+ parentGroup: undefined
33082
+ });
33083
+ const performToggle = (expand && !expanded) || (!expand && expanded);
33084
+ if (performToggle) {
33085
+ this.grid.groupsService.toggleRow({
33086
+ type: 'group',
33087
+ data: gr,
33088
+ index: idx.toString(),
33089
+ level: 0,
33090
+ parentGroup: undefined
33091
+ });
33092
+ this[expand ? 'groupExpand' : 'groupCollapse']({ groupIndex: idx.toString() });
33093
+ }
33094
+ });
33127
33095
  }
33128
- ngOnDestroy() {
33129
- this.subscriptions.unsubscribe();
33096
+ /**
33097
+ * Collapses all expanded root level groups.
33098
+ */
33099
+ collapseAll() {
33100
+ this.toggleAll(false);
33130
33101
  }
33131
- get keyGetter() {
33132
- return this.expandGroupBy || DEFAULT_KEY_GETTER;
33102
+ /**
33103
+ * Expands all expanded root level groups.
33104
+ */
33105
+ expandAll() {
33106
+ this.toggleAll(true);
33133
33107
  }
33134
33108
  /**
33135
33109
  * @hidden
33136
33110
  */
33137
- isExpanded(groupArgs) {
33138
- const itemIndex = this.getItemIndex(groupArgs);
33139
- return itemIndex > -1 ? !this.groupsInitiallyExpanded : this.groupsInitiallyExpanded;
33111
+ groupExpand({ groupIndex }) {
33112
+ const { group, parents } = findGroup(groupIndex, this.groups);
33113
+ if (!group) {
33114
+ return;
33115
+ }
33116
+ this.groupsService.expandChildren(groupIndex);
33117
+ if (!group.items.length) {
33118
+ const descriptors = this.state.group.filter(removeParentDescriptors(parents, group));
33119
+ const children = findChildren(this.originalData, parents.concat(group));
33120
+ group.items = processGroups(children, {
33121
+ filter: this.state.filter,
33122
+ group: descriptors,
33123
+ sort: this.state.sort
33124
+ });
33125
+ }
33126
+ this.grid.data = this.dataResult(this.state.skip, this.state.take);
33140
33127
  }
33141
- getItemIndex(groupArgs) {
33142
- if (this.expandGroupBy) {
33143
- return this.expandedGroupKeys.indexOf(this.keyGetter(groupArgs));
33128
+ /**
33129
+ * @hidden
33130
+ */
33131
+ groupCollapse({ groupIndex }) {
33132
+ const { group } = findGroup(groupIndex, this.groups);
33133
+ if (group) {
33134
+ group.items = [];
33144
33135
  }
33145
- return this.expandedGroupKeys.findIndex(item => {
33146
- let index = 0;
33147
- let parentGroup = groupArgs.parentGroup;
33148
- while (isPresent(parentGroup)) {
33149
- if (!isPresent(item.parentGroupKeys) || !isPresent(item.parentGroupKeys[index]) ||
33150
- parentGroup.group.value !== item.parentGroupKeys[index].value ||
33151
- parentGroup.group.field !== item.parentGroupKeys[index].field) {
33152
- return false;
33153
- }
33154
- parentGroup = parentGroup.parentGroup;
33155
- index++;
33156
- }
33157
- return item.value === groupArgs.group.value && item.field === groupArgs.group.field;
33158
- });
33136
+ else {
33137
+ return;
33138
+ }
33139
+ this.grid.data = this.dataResult(this.state.skip, this.state.take);
33159
33140
  }
33160
- toggleState(groupArgs) {
33161
- const key = this.keyGetter(groupArgs);
33162
- if (Boolean(this.groupsInitiallyExpanded) !== groupArgs.expand) {
33163
- this.expandedGroupKeys.push(key);
33141
+ process(state) {
33142
+ if (this.grid.isVirtual && (!isPresent(state.take) || state.take === 0)) {
33143
+ return {
33144
+ data: [],
33145
+ total: this.originalData?.length || 0
33146
+ };
33147
+ }
33148
+ if (state.group && state.group.length) {
33149
+ const groups = this.processGroups(state);
33150
+ this.grid.skip -= skippedHeaders(groups.data[0]);
33151
+ return groups;
33164
33152
  }
33165
33153
  else {
33166
- const index = this.expandedGroupKeys.findIndex(group => {
33167
- if (this.expandGroupBy) {
33168
- return group === key;
33169
- }
33170
- else if (key.parentGroupKeys?.length === 0) {
33171
- return group.value === key.value;
33172
- }
33173
- return JSON.stringify(group) === JSON.stringify(key);
33154
+ this.groups = null;
33155
+ }
33156
+ return super.process(state);
33157
+ }
33158
+ processGroups(state) {
33159
+ if (!this.groups || !this.groups.length) {
33160
+ this.groups = processGroups(this.originalData, {
33161
+ filter: state.filter,
33162
+ group: state.group,
33163
+ sort: state.sort
33174
33164
  });
33175
- this.expandedGroupKeys.splice(index, 1);
33176
33165
  }
33177
- this.expandedGroupKeysChange.emit(this.expandedGroupKeys.slice());
33166
+ return this.dataResult(state.skip, state.take);
33178
33167
  }
33179
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ExpandGroupDirective, deps: [{ token: GridComponent }], target: i0.ɵɵFactoryTarget.Directive });
33180
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: ExpandGroupDirective, isStandalone: true, selector: "[kendoGridExpandGroupBy]", inputs: { expandGroupBy: ["kendoGridExpandGroupBy", "expandGroupBy"], expandedGroupKeys: "expandedGroupKeys", groupsInitiallyExpanded: "groupsInitiallyExpanded" }, outputs: { expandedGroupKeysChange: "expandedGroupKeysChange" }, exportAs: ["kendoGridExpandGroupBy"], ngImport: i0 });
33168
+ dataResult(skip, take) {
33169
+ const includeFooters = this.grid.showGroupFooters;
33170
+ return {
33171
+ data: slice(this.groups, skip, take, includeFooters),
33172
+ total: count(this.groups, includeFooters)
33173
+ };
33174
+ }
33175
+ applyState({ skip, take, sort, group, filter }) {
33176
+ this.skip = skip;
33177
+ this.state.take = take;
33178
+ // this.pageSize = take; // do need to update take as the process with slice correctly
33179
+ this.sort = sort;
33180
+ this.group = group;
33181
+ this.filter = filter;
33182
+ }
33183
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: GroupBindingDirective, deps: [{ token: i0.ChangeDetectorRef }, { token: LocalDataChangesService }, { token: ContextService }, { token: GroupsService }], target: i0.ɵɵFactoryTarget.Directive });
33184
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: GroupBindingDirective, isStandalone: true, selector: "[kendoGridGroupBinding]", inputs: { kendoGridGroupBinding: "kendoGridGroupBinding", sort: "sort", filter: "filter", group: "group" }, exportAs: ["kendoGridGroupBinding"], usesInheritance: true, ngImport: i0 });
33181
33185
  }
33182
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ExpandGroupDirective, decorators: [{
33186
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: GroupBindingDirective, decorators: [{
33183
33187
  type: Directive,
33184
33188
  args: [{
33185
- selector: '[kendoGridExpandGroupBy]',
33186
- exportAs: 'kendoGridExpandGroupBy',
33189
+ selector: '[kendoGridGroupBinding]',
33190
+ exportAs: 'kendoGridGroupBinding',
33187
33191
  standalone: true
33188
33192
  }]
33189
- }], ctorParameters: function () { return [{ type: GridComponent }]; }, propDecorators: { expandedGroupKeysChange: [{
33190
- type: Output
33191
- }], expandGroupBy: [{
33193
+ }], ctorParameters: function () { return [{ type: i0.ChangeDetectorRef }, { type: LocalDataChangesService }, { type: ContextService }, { type: GroupsService }]; }, propDecorators: { kendoGridGroupBinding: [{
33192
33194
  type: Input,
33193
- args: ['kendoGridExpandGroupBy']
33194
- }], expandedGroupKeys: [{
33195
+ args: ["kendoGridGroupBinding"]
33196
+ }], sort: [{
33195
33197
  type: Input
33196
- }], groupsInitiallyExpanded: [{
33198
+ }], filter: [{
33199
+ type: Input
33200
+ }], group: [{
33197
33201
  type: Input
33198
33202
  }] } });
33199
33203
 
@@ -34039,15 +34043,6 @@ class UndoRedoDirective {
34039
34043
  }
34040
34044
  ngOnInit() {
34041
34045
  this.stack = new UndoRedoStack(this.maxStoredStates);
34042
- this.stack.add({
34043
- originalEvent: {
34044
- skip: this.host.skip,
34045
- take: this.host.pageSize,
34046
- sort: this.host.sort,
34047
- filter: this.host.filter,
34048
- group: this.host.group
34049
- }, gridState: this.host.currentState
34050
- });
34051
34046
  this.subs = this.host.gridStateChange.subscribe((state) => {
34052
34047
  if (this.addToState) {
34053
34048
  this.stack.add({
@@ -34118,7 +34113,7 @@ class UndoRedoDirective {
34118
34113
  if (isSaveOrRemove) {
34119
34114
  if (originalAction === 'save') {
34120
34115
  const stateItem = this.getGridDataItems(this.stack.current.gridState.currentData).find(item => item[this.itemIdKey] === event.originalEvent.dataItem[this.itemIdKey]);
34121
- Object.assign(event.originalEvent.originalDataItem, stateItem);
34116
+ this.localDataChangesService?.data.splice(event.originalEvent.rowIndex, 1, stateItem);
34122
34117
  }
34123
34118
  else if (action === 'Undo') {
34124
34119
  this.localDataChangesService?.data.splice(event.originalEvent.rowIndex, 0, event.originalEvent.dataItem);
@@ -34131,13 +34126,24 @@ class UndoRedoDirective {
34131
34126
  else {
34132
34127
  this.host.loadState({ ...this.stack.current.gridState, currentData: null });
34133
34128
  if (this.isDataStateChangeEvent(event.originalEvent)) {
34134
- const { skip, take, sort, filter, group } = this.stack.current.gridState;
34129
+ const { skip, take, sort, filter, group } = event.gridState;
34135
34130
  this.host.dataStateChange.emit({ skip, take, sort, filter, group });
34136
34131
  }
34137
34132
  }
34138
34133
  }));
34139
34134
  });
34140
34135
  }
34136
+ ngAfterViewInit() {
34137
+ this.stack.add({
34138
+ originalEvent: {
34139
+ skip: this.host.skip,
34140
+ take: this.host.pageSize,
34141
+ sort: this.host.sort,
34142
+ filter: this.host.filter,
34143
+ group: this.host.group
34144
+ }, gridState: this.host.currentState
34145
+ });
34146
+ }
34141
34147
  ngOnDestroy() {
34142
34148
  this.stack.clear();
34143
34149
  this.stack = null;