@progress/kendo-angular-grid 20.1.0-develop.33 → 20.1.0-develop.34

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.
@@ -2,7 +2,7 @@
2
2
  * Copyright © 2025 Progress Software Corporation. All rights reserved.
3
3
  * Licensed under commercial license. See LICENSE.md in the project root for more information
4
4
  *-------------------------------------------------------------------------------------------*/
5
- import { Component, Input, EventEmitter, Output } from '@angular/core';
5
+ import { Component, Input, EventEmitter, Output, ViewChild } from '@angular/core';
6
6
  import { filterBy } from '@progress/kendo-data-query';
7
7
  import { NgFor, NgIf } from '@angular/common';
8
8
  import { CheckBoxComponent, TextBoxComponent, TextBoxPrefixTemplateDirective } from '@progress/kendo-angular-inputs';
@@ -12,10 +12,10 @@ import { ContextService } from '../common/provider.service';
12
12
  import { LocalDataChangesService } from '../editing/local-data-changes.service';
13
13
  import { FormatPipe } from '../rendering/common/format.pipe';
14
14
  import { isPresent, replaceMessagePlaceholder } from '@progress/kendo-angular-common';
15
+ import { FilterInputDirective } from './filter-input.directive';
15
16
  import * as i0 from "@angular/core";
16
17
  import * as i1 from "../common/provider.service";
17
18
  import * as i2 from "../editing/local-data-changes.service";
18
- const DEFAULT_SEARCH_DELAY = 500;
19
19
  /**
20
20
  * @hidden
21
21
  */
@@ -24,64 +24,51 @@ export class MultiCheckboxFilterComponent {
24
24
  dataChangesService;
25
25
  column;
26
26
  filterChange = new EventEmitter();
27
+ filterInput;
27
28
  constructor(ctx, dataChangesService) {
28
29
  this.ctx = ctx;
29
30
  this.dataChangesService = dataChangesService;
30
31
  }
31
- listData;
32
+ listData = [];
32
33
  searchIcon = searchIcon;
33
34
  showSelectAll = true;
34
35
  currentlySelected = new Set();
35
36
  isSearched = false;
36
37
  currentFilter;
37
- typingTimeout;
38
- baseListData;
38
+ filterChangeSub;
39
+ baseListData = [];
39
40
  ngOnInit() {
40
- let mappedData = [];
41
- let filteredMappedData = [];
42
- if (this.column.filter === 'date') {
43
- mappedData = this.getUniqueDateValues(this.gridData);
44
- filteredMappedData = this.getUniqueDateValues(this.filteredGridData);
45
- this.baseListData = mappedData.sort((a, b) => { return a - b; });
46
- this.currentlySelected = new Set(filteredMappedData);
47
- }
48
- else {
49
- mappedData = this.gridData.map(i => i[this.column.field]);
50
- filteredMappedData = this.filteredGridData.map(i => i[this.column.field]);
51
- this.baseListData = [...new Set(mappedData)].sort((a, b) => { return a - b; });
52
- this.currentlySelected = new Set(filteredMappedData);
53
- }
54
- this.listData = this.baseListData;
55
- this.currentFilter = this.ctx.grid?.filter;
41
+ this.initializeData();
42
+ this.currentFilter = this.ctx.grid?.filter ?? this.emptyFilter();
43
+ this.updateSelectionFromFilter();
56
44
  if (this.currentFilter) {
57
45
  this.filterChange.emit(this.currentFilter);
58
46
  }
59
47
  }
48
+ ngAfterViewInit() {
49
+ if (this.filterInput) {
50
+ this.filterChangeSub = this.filterInput.change.subscribe(this.onSearch.bind(this));
51
+ }
52
+ }
60
53
  ngOnDestroy() {
61
- if (this.typingTimeout) {
62
- clearTimeout(this.typingTimeout);
63
- this.typingTimeout = null;
54
+ if (this.filterChangeSub) {
55
+ this.filterChangeSub.unsubscribe();
56
+ this.filterChangeSub = null;
64
57
  }
65
58
  }
66
59
  isItemSelected(item) {
67
60
  return this.currentlySelected.has(item);
68
61
  }
69
62
  onSearch(value) {
70
- clearTimeout(this.typingTimeout);
71
- this.typingTimeout = setTimeout(() => {
72
- this.isSearched = value.length > 0;
73
- this.showSelectAll = !this.isSearched;
74
- const field = this.column?.field;
75
- if (!field) {
76
- this.listData = [];
77
- return;
78
- }
79
- if (!value) {
80
- this.listData = this.baseListData;
81
- return;
82
- }
83
- this.listData = filterBy(this.baseListData, { operator: 'contains', value });
84
- }, DEFAULT_SEARCH_DELAY);
63
+ const searchValue = value;
64
+ this.isSearched = searchValue.length > 0;
65
+ this.showSelectAll = !this.isSearched;
66
+ if (!searchValue) {
67
+ this.listData = [...this.baseListData];
68
+ }
69
+ else {
70
+ this.listData = filterBy(this.baseListData, { operator: 'contains', value: searchValue });
71
+ }
85
72
  }
86
73
  handleCheckBoxChange(checkedState, value, selectAllChecked) {
87
74
  const field = this.column?.field;
@@ -89,88 +76,72 @@ export class MultiCheckboxFilterComponent {
89
76
  return;
90
77
  }
91
78
  if (!this.currentFilter) {
92
- const existing = this.ctx.grid.filter;
93
- this.currentFilter = existing ? { ...existing } : { logic: 'and', filters: [] };
79
+ this.currentFilter = this.emptyFilter();
94
80
  }
95
- const compositeIndex = this.currentFilter.filters.findIndex((f) => f && Array.isArray(f.filters) && f.filters.some((ff) => ff.field === field));
81
+ const newFilter = this.currentFilter;
82
+ const filters = [...newFilter.filters];
83
+ const compositeIndex = this.getCompositeFilterIndex(newFilter);
96
84
  let fieldFilters = [];
97
- if (compositeIndex !== -1) {
98
- fieldFilters = [...this.currentFilter.filters[compositeIndex].filters];
85
+ if (compositeIndex !== -1 && filters[compositeIndex].filters && !selectAllChecked) {
86
+ fieldFilters = [...filters[compositeIndex].filters];
99
87
  }
100
- const valueForItem = (item) => item?.[field];
101
- if (selectAllChecked) {
102
- if (checkedState) {
103
- fieldFilters = [];
104
- this.currentlySelected = new Set(this.baseListData);
105
- }
106
- else {
107
- fieldFilters = this.gridData.map(item => ({
108
- field,
109
- operator: 'neq',
110
- value: valueForItem(item)
111
- }));
112
- this.currentlySelected.clear();
88
+ if (checkedState && selectAllChecked) {
89
+ this.listData.forEach(item => {
90
+ if (!fieldFilters.some(f => f.value === item)) {
91
+ fieldFilters.push({ field, operator: 'eq', value: item });
92
+ }
93
+ });
94
+ }
95
+ else if (!checkedState && selectAllChecked) {
96
+ fieldFilters = [];
97
+ }
98
+ else if (checkedState) {
99
+ if (!fieldFilters.some(f => f.value === value)) {
100
+ fieldFilters.push({ field, operator: 'eq', value });
113
101
  }
114
102
  }
115
103
  else {
116
- const exists = fieldFilters.some(f => f.value === value);
117
- if (checkedState) {
118
- if (!exists) {
119
- fieldFilters.push({ field, operator: 'eq', value });
120
- }
121
- else {
122
- fieldFilters = fieldFilters.filter(f => f.value !== value);
123
- }
124
- this.currentlySelected.add(value);
125
- }
126
- else {
127
- if (exists) {
128
- fieldFilters = fieldFilters.filter(f => f.value !== value);
129
- }
130
- else {
131
- fieldFilters.push({ field, operator: 'neq', value });
132
- }
133
- this.currentlySelected.delete(value);
104
+ const idx = fieldFilters.findIndex(f => f.value === value);
105
+ if (idx > -1) {
106
+ fieldFilters.splice(idx, 1);
134
107
  }
135
108
  }
136
109
  if (fieldFilters.length === 0) {
137
110
  if (compositeIndex !== -1) {
138
- this.currentFilter.filters.splice(compositeIndex, 1);
139
- }
140
- else {
141
- this.currentFilter.filters = fieldFilters;
111
+ filters.splice(compositeIndex, 1);
142
112
  }
143
113
  }
144
114
  else {
145
- const compositeBlock = {
146
- logic: 'and',
147
- filters: fieldFilters
148
- };
115
+ const block = { logic: 'or', filters: fieldFilters };
149
116
  if (compositeIndex !== -1) {
150
- this.currentFilter.filters[compositeIndex] = compositeBlock;
117
+ filters[compositeIndex] = block;
151
118
  }
152
119
  else {
153
- this.currentFilter.filters.push(compositeBlock);
120
+ filters.push(block);
154
121
  }
155
122
  }
123
+ newFilter.logic = 'and';
124
+ newFilter.filters = filters;
125
+ this.currentFilter = newFilter;
126
+ this.updateSelectionFromFilter();
156
127
  this.filterChange.emit(this.currentFilter);
157
128
  }
158
129
  get filteredGridData() {
159
130
  return filterBy(this.gridData, this.ctx.grid?.filter);
160
131
  }
161
132
  get selectAllChecked() {
162
- if (!this.listData) {
133
+ if (!this.listData || this.listData.length === 0) {
163
134
  return false;
164
135
  }
165
- if (this.currentlySelected.size >= this.listData.length && this.currentlySelected.size > 0) {
166
- return true;
167
- }
168
- else if (this.currentlySelected.size === 0) {
136
+ const total = this.listData.length;
137
+ const selectedInView = this.listData.filter(i => this.currentlySelected.has(i)).length;
138
+ if (selectedInView === 0) {
169
139
  return false;
170
140
  }
171
- else {
172
- return 'indeterminate';
141
+ if (selectedInView === total) {
142
+ return true;
173
143
  }
144
+ return 'indeterminate';
174
145
  }
175
146
  get gridData() {
176
147
  let data = [];
@@ -212,12 +183,61 @@ export class MultiCheckboxFilterComponent {
212
183
  return acc;
213
184
  }, []);
214
185
  }
186
+ initializeData() {
187
+ if (!this.column) {
188
+ this.baseListData = [];
189
+ return;
190
+ }
191
+ const isDate = this.column.filter === 'date';
192
+ if (isDate) {
193
+ const dates = this.getUniqueDateValues(this.gridData);
194
+ const sortedDates = [...dates].sort((a, b) => a - b);
195
+ this.baseListData = sortedDates;
196
+ }
197
+ else {
198
+ const field = this.column.field;
199
+ const mapped = this.gridData.map(item => item?.[field]);
200
+ this.baseListData = [...new Set(mapped)].sort((a, b) => {
201
+ if (a > b) {
202
+ return 1;
203
+ }
204
+ if (a < b) {
205
+ return -1;
206
+ }
207
+ return 0;
208
+ });
209
+ }
210
+ this.listData = [...this.baseListData];
211
+ }
212
+ emptyFilter() { return { filters: [], logic: 'and' }; }
213
+ getCompositeFilterIndex(filter) {
214
+ const field = this.column?.field;
215
+ return filter.filters.findIndex((f) => f.filters?.length && f.filters[0].field === field);
216
+ }
217
+ updateSelectionFromFilter() {
218
+ this.currentlySelected.clear();
219
+ if (!this.currentFilter || !this.column?.field) {
220
+ return;
221
+ }
222
+ const idx = this.getCompositeFilterIndex(this.currentFilter);
223
+ if (idx === -1) {
224
+ return;
225
+ }
226
+ const block = this.currentFilter.filters[idx];
227
+ if (block && Array.isArray(block.filters)) {
228
+ block.filters.forEach((f) => {
229
+ if (f.field === this.column.field && f.operator === 'eq') {
230
+ this.currentlySelected.add(f.value);
231
+ }
232
+ });
233
+ }
234
+ }
215
235
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: MultiCheckboxFilterComponent, deps: [{ token: i1.ContextService }, { token: i2.LocalDataChangesService }], target: i0.ɵɵFactoryTarget.Component });
216
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: MultiCheckboxFilterComponent, isStandalone: true, selector: "kendo-grid-multicheckbox-filter", inputs: { column: "column" }, outputs: { filterChange: "filterChange" }, ngImport: i0, template: `
236
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: MultiCheckboxFilterComponent, isStandalone: true, selector: "kendo-grid-multicheckbox-filter", inputs: { column: "column" }, outputs: { filterChange: "filterChange" }, viewQueries: [{ propertyName: "filterInput", first: true, predicate: FilterInputDirective, descendants: true }], ngImport: i0, template: `
217
237
  <kendo-textbox *ngIf="normalizedFilterVariant.search"
238
+ kendoFilterInput
218
239
  class="k-searchbox"
219
- [placeholder]="messageFor('multiCheckboxFilterSearchPlaceholder')"
220
- (valueChange)="onSearch($event)">
240
+ [placeholder]="messageFor('multiCheckboxFilterSearchPlaceholder')">
221
241
  <ng-template kendoTextBoxPrefixTemplate>
222
242
  <kendo-icon-wrapper innerCssClass="k-input-icon" name="search" [svgIcon]="searchIcon"></kendo-icon-wrapper>
223
243
  </ng-template>
@@ -243,7 +263,7 @@ export class MultiCheckboxFilterComponent {
243
263
  </li>
244
264
  </ul>
245
265
  <div class="k-filter-selected-items">{{selectedItemsMessage}}</div>
246
- `, isInline: true, dependencies: [{ kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: CheckBoxComponent, selector: "kendo-checkbox", inputs: ["checkedState", "rounded"], outputs: ["checkedStateChange"], exportAs: ["kendoCheckBox"] }, { kind: "component", type: TextBoxComponent, selector: "kendo-textbox", inputs: ["focusableId", "title", "type", "disabled", "readonly", "tabindex", "value", "selectOnFocus", "showSuccessIcon", "showErrorIcon", "clearButton", "successIcon", "successSvgIcon", "errorIcon", "errorSvgIcon", "clearButtonIcon", "clearButtonSvgIcon", "size", "rounded", "fillMode", "tabIndex", "placeholder", "maxlength", "inputAttributes"], outputs: ["valueChange", "inputFocus", "inputBlur", "focus", "blur"], exportAs: ["kendoTextBox"] }, { kind: "directive", type: TextBoxPrefixTemplateDirective, selector: "[kendoTextBoxPrefixTemplate]", inputs: ["showSeparator"] }, { kind: "component", type: IconWrapperComponent, selector: "kendo-icon-wrapper", inputs: ["name", "svgIcon", "innerCssClass", "customFontClass", "size"], exportAs: ["kendoIconWrapper"] }, { kind: "pipe", type: FormatPipe, name: "format" }] });
266
+ `, isInline: true, dependencies: [{ kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: CheckBoxComponent, selector: "kendo-checkbox", inputs: ["checkedState", "rounded"], outputs: ["checkedStateChange"], exportAs: ["kendoCheckBox"] }, { kind: "component", type: TextBoxComponent, selector: "kendo-textbox", inputs: ["focusableId", "title", "type", "disabled", "readonly", "tabindex", "value", "selectOnFocus", "showSuccessIcon", "showErrorIcon", "clearButton", "successIcon", "successSvgIcon", "errorIcon", "errorSvgIcon", "clearButtonIcon", "clearButtonSvgIcon", "size", "rounded", "fillMode", "tabIndex", "placeholder", "maxlength", "inputAttributes"], outputs: ["valueChange", "inputFocus", "inputBlur", "focus", "blur"], exportAs: ["kendoTextBox"] }, { kind: "directive", type: TextBoxPrefixTemplateDirective, selector: "[kendoTextBoxPrefixTemplate]", inputs: ["showSeparator"] }, { kind: "component", type: IconWrapperComponent, selector: "kendo-icon-wrapper", inputs: ["name", "svgIcon", "innerCssClass", "customFontClass", "size"], exportAs: ["kendoIconWrapper"] }, { kind: "pipe", type: FormatPipe, name: "format" }, { kind: "directive", type: FilterInputDirective, selector: "[kendoFilterInput]", inputs: ["filterDelay", "columnLabel", "value"] }] });
247
267
  }
248
268
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: MultiCheckboxFilterComponent, decorators: [{
249
269
  type: Component,
@@ -251,9 +271,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
251
271
  selector: 'kendo-grid-multicheckbox-filter',
252
272
  template: `
253
273
  <kendo-textbox *ngIf="normalizedFilterVariant.search"
274
+ kendoFilterInput
254
275
  class="k-searchbox"
255
- [placeholder]="messageFor('multiCheckboxFilterSearchPlaceholder')"
256
- (valueChange)="onSearch($event)">
276
+ [placeholder]="messageFor('multiCheckboxFilterSearchPlaceholder')">
257
277
  <ng-template kendoTextBoxPrefixTemplate>
258
278
  <kendo-icon-wrapper innerCssClass="k-input-icon" name="search" [svgIcon]="searchIcon"></kendo-icon-wrapper>
259
279
  </ng-template>
@@ -281,10 +301,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
281
301
  <div class="k-filter-selected-items">{{selectedItemsMessage}}</div>
282
302
  `,
283
303
  standalone: true,
284
- imports: [NgFor, NgIf, CheckBoxComponent, TextBoxComponent, TextBoxPrefixTemplateDirective, IconWrapperComponent, FormatPipe]
304
+ imports: [NgFor, NgIf, CheckBoxComponent, TextBoxComponent, TextBoxPrefixTemplateDirective, IconWrapperComponent, FormatPipe, FilterInputDirective]
285
305
  }]
286
306
  }], ctorParameters: function () { return [{ type: i1.ContextService }, { type: i2.LocalDataChangesService }]; }, propDecorators: { column: [{
287
307
  type: Input
288
308
  }], filterChange: [{
289
309
  type: Output
310
+ }], filterInput: [{
311
+ type: ViewChild,
312
+ args: [FilterInputDirective]
290
313
  }] } });
@@ -10,7 +10,7 @@ export const packageMetadata = {
10
10
  productName: 'Kendo UI for Angular',
11
11
  productCode: 'KENDOUIANGULAR',
12
12
  productCodes: ['KENDOUIANGULAR'],
13
- publishDate: 1760814554,
14
- version: '20.1.0-develop.33',
13
+ publishDate: 1760965348,
14
+ version: '20.1.0-develop.34',
15
15
  licensingDocsUrl: 'https://www.telerik.com/kendo-angular-ui/my-license/'
16
16
  };
@@ -10727,7 +10727,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
10727
10727
  }]
10728
10728
  }], ctorParameters: function () { return [{ type: i1$4.IntlService }]; } });
10729
10729
 
10730
- const DEFAULT_SEARCH_DELAY = 500;
10731
10730
  /**
10732
10731
  * @hidden
10733
10732
  */
@@ -10736,64 +10735,51 @@ class MultiCheckboxFilterComponent {
10736
10735
  dataChangesService;
10737
10736
  column;
10738
10737
  filterChange = new EventEmitter();
10738
+ filterInput;
10739
10739
  constructor(ctx, dataChangesService) {
10740
10740
  this.ctx = ctx;
10741
10741
  this.dataChangesService = dataChangesService;
10742
10742
  }
10743
- listData;
10743
+ listData = [];
10744
10744
  searchIcon = searchIcon;
10745
10745
  showSelectAll = true;
10746
10746
  currentlySelected = new Set();
10747
10747
  isSearched = false;
10748
10748
  currentFilter;
10749
- typingTimeout;
10750
- baseListData;
10749
+ filterChangeSub;
10750
+ baseListData = [];
10751
10751
  ngOnInit() {
10752
- let mappedData = [];
10753
- let filteredMappedData = [];
10754
- if (this.column.filter === 'date') {
10755
- mappedData = this.getUniqueDateValues(this.gridData);
10756
- filteredMappedData = this.getUniqueDateValues(this.filteredGridData);
10757
- this.baseListData = mappedData.sort((a, b) => { return a - b; });
10758
- this.currentlySelected = new Set(filteredMappedData);
10759
- }
10760
- else {
10761
- mappedData = this.gridData.map(i => i[this.column.field]);
10762
- filteredMappedData = this.filteredGridData.map(i => i[this.column.field]);
10763
- this.baseListData = [...new Set(mappedData)].sort((a, b) => { return a - b; });
10764
- this.currentlySelected = new Set(filteredMappedData);
10765
- }
10766
- this.listData = this.baseListData;
10767
- this.currentFilter = this.ctx.grid?.filter;
10752
+ this.initializeData();
10753
+ this.currentFilter = this.ctx.grid?.filter ?? this.emptyFilter();
10754
+ this.updateSelectionFromFilter();
10768
10755
  if (this.currentFilter) {
10769
10756
  this.filterChange.emit(this.currentFilter);
10770
10757
  }
10771
10758
  }
10759
+ ngAfterViewInit() {
10760
+ if (this.filterInput) {
10761
+ this.filterChangeSub = this.filterInput.change.subscribe(this.onSearch.bind(this));
10762
+ }
10763
+ }
10772
10764
  ngOnDestroy() {
10773
- if (this.typingTimeout) {
10774
- clearTimeout(this.typingTimeout);
10775
- this.typingTimeout = null;
10765
+ if (this.filterChangeSub) {
10766
+ this.filterChangeSub.unsubscribe();
10767
+ this.filterChangeSub = null;
10776
10768
  }
10777
10769
  }
10778
10770
  isItemSelected(item) {
10779
10771
  return this.currentlySelected.has(item);
10780
10772
  }
10781
10773
  onSearch(value) {
10782
- clearTimeout(this.typingTimeout);
10783
- this.typingTimeout = setTimeout(() => {
10784
- this.isSearched = value.length > 0;
10785
- this.showSelectAll = !this.isSearched;
10786
- const field = this.column?.field;
10787
- if (!field) {
10788
- this.listData = [];
10789
- return;
10790
- }
10791
- if (!value) {
10792
- this.listData = this.baseListData;
10793
- return;
10794
- }
10795
- this.listData = filterBy(this.baseListData, { operator: 'contains', value });
10796
- }, DEFAULT_SEARCH_DELAY);
10774
+ const searchValue = value;
10775
+ this.isSearched = searchValue.length > 0;
10776
+ this.showSelectAll = !this.isSearched;
10777
+ if (!searchValue) {
10778
+ this.listData = [...this.baseListData];
10779
+ }
10780
+ else {
10781
+ this.listData = filterBy(this.baseListData, { operator: 'contains', value: searchValue });
10782
+ }
10797
10783
  }
10798
10784
  handleCheckBoxChange(checkedState, value, selectAllChecked) {
10799
10785
  const field = this.column?.field;
@@ -10801,88 +10787,72 @@ class MultiCheckboxFilterComponent {
10801
10787
  return;
10802
10788
  }
10803
10789
  if (!this.currentFilter) {
10804
- const existing = this.ctx.grid.filter;
10805
- this.currentFilter = existing ? { ...existing } : { logic: 'and', filters: [] };
10790
+ this.currentFilter = this.emptyFilter();
10806
10791
  }
10807
- const compositeIndex = this.currentFilter.filters.findIndex((f) => f && Array.isArray(f.filters) && f.filters.some((ff) => ff.field === field));
10792
+ const newFilter = this.currentFilter;
10793
+ const filters = [...newFilter.filters];
10794
+ const compositeIndex = this.getCompositeFilterIndex(newFilter);
10808
10795
  let fieldFilters = [];
10809
- if (compositeIndex !== -1) {
10810
- fieldFilters = [...this.currentFilter.filters[compositeIndex].filters];
10796
+ if (compositeIndex !== -1 && filters[compositeIndex].filters && !selectAllChecked) {
10797
+ fieldFilters = [...filters[compositeIndex].filters];
10811
10798
  }
10812
- const valueForItem = (item) => item?.[field];
10813
- if (selectAllChecked) {
10814
- if (checkedState) {
10815
- fieldFilters = [];
10816
- this.currentlySelected = new Set(this.baseListData);
10817
- }
10818
- else {
10819
- fieldFilters = this.gridData.map(item => ({
10820
- field,
10821
- operator: 'neq',
10822
- value: valueForItem(item)
10823
- }));
10824
- this.currentlySelected.clear();
10799
+ if (checkedState && selectAllChecked) {
10800
+ this.listData.forEach(item => {
10801
+ if (!fieldFilters.some(f => f.value === item)) {
10802
+ fieldFilters.push({ field, operator: 'eq', value: item });
10803
+ }
10804
+ });
10805
+ }
10806
+ else if (!checkedState && selectAllChecked) {
10807
+ fieldFilters = [];
10808
+ }
10809
+ else if (checkedState) {
10810
+ if (!fieldFilters.some(f => f.value === value)) {
10811
+ fieldFilters.push({ field, operator: 'eq', value });
10825
10812
  }
10826
10813
  }
10827
10814
  else {
10828
- const exists = fieldFilters.some(f => f.value === value);
10829
- if (checkedState) {
10830
- if (!exists) {
10831
- fieldFilters.push({ field, operator: 'eq', value });
10832
- }
10833
- else {
10834
- fieldFilters = fieldFilters.filter(f => f.value !== value);
10835
- }
10836
- this.currentlySelected.add(value);
10837
- }
10838
- else {
10839
- if (exists) {
10840
- fieldFilters = fieldFilters.filter(f => f.value !== value);
10841
- }
10842
- else {
10843
- fieldFilters.push({ field, operator: 'neq', value });
10844
- }
10845
- this.currentlySelected.delete(value);
10815
+ const idx = fieldFilters.findIndex(f => f.value === value);
10816
+ if (idx > -1) {
10817
+ fieldFilters.splice(idx, 1);
10846
10818
  }
10847
10819
  }
10848
10820
  if (fieldFilters.length === 0) {
10849
10821
  if (compositeIndex !== -1) {
10850
- this.currentFilter.filters.splice(compositeIndex, 1);
10851
- }
10852
- else {
10853
- this.currentFilter.filters = fieldFilters;
10822
+ filters.splice(compositeIndex, 1);
10854
10823
  }
10855
10824
  }
10856
10825
  else {
10857
- const compositeBlock = {
10858
- logic: 'and',
10859
- filters: fieldFilters
10860
- };
10826
+ const block = { logic: 'or', filters: fieldFilters };
10861
10827
  if (compositeIndex !== -1) {
10862
- this.currentFilter.filters[compositeIndex] = compositeBlock;
10828
+ filters[compositeIndex] = block;
10863
10829
  }
10864
10830
  else {
10865
- this.currentFilter.filters.push(compositeBlock);
10831
+ filters.push(block);
10866
10832
  }
10867
10833
  }
10834
+ newFilter.logic = 'and';
10835
+ newFilter.filters = filters;
10836
+ this.currentFilter = newFilter;
10837
+ this.updateSelectionFromFilter();
10868
10838
  this.filterChange.emit(this.currentFilter);
10869
10839
  }
10870
10840
  get filteredGridData() {
10871
10841
  return filterBy(this.gridData, this.ctx.grid?.filter);
10872
10842
  }
10873
10843
  get selectAllChecked() {
10874
- if (!this.listData) {
10844
+ if (!this.listData || this.listData.length === 0) {
10875
10845
  return false;
10876
10846
  }
10877
- if (this.currentlySelected.size >= this.listData.length && this.currentlySelected.size > 0) {
10878
- return true;
10879
- }
10880
- else if (this.currentlySelected.size === 0) {
10847
+ const total = this.listData.length;
10848
+ const selectedInView = this.listData.filter(i => this.currentlySelected.has(i)).length;
10849
+ if (selectedInView === 0) {
10881
10850
  return false;
10882
10851
  }
10883
- else {
10884
- return 'indeterminate';
10852
+ if (selectedInView === total) {
10853
+ return true;
10885
10854
  }
10855
+ return 'indeterminate';
10886
10856
  }
10887
10857
  get gridData() {
10888
10858
  let data = [];
@@ -10924,12 +10894,61 @@ class MultiCheckboxFilterComponent {
10924
10894
  return acc;
10925
10895
  }, []);
10926
10896
  }
10897
+ initializeData() {
10898
+ if (!this.column) {
10899
+ this.baseListData = [];
10900
+ return;
10901
+ }
10902
+ const isDate = this.column.filter === 'date';
10903
+ if (isDate) {
10904
+ const dates = this.getUniqueDateValues(this.gridData);
10905
+ const sortedDates = [...dates].sort((a, b) => a - b);
10906
+ this.baseListData = sortedDates;
10907
+ }
10908
+ else {
10909
+ const field = this.column.field;
10910
+ const mapped = this.gridData.map(item => item?.[field]);
10911
+ this.baseListData = [...new Set(mapped)].sort((a, b) => {
10912
+ if (a > b) {
10913
+ return 1;
10914
+ }
10915
+ if (a < b) {
10916
+ return -1;
10917
+ }
10918
+ return 0;
10919
+ });
10920
+ }
10921
+ this.listData = [...this.baseListData];
10922
+ }
10923
+ emptyFilter() { return { filters: [], logic: 'and' }; }
10924
+ getCompositeFilterIndex(filter) {
10925
+ const field = this.column?.field;
10926
+ return filter.filters.findIndex((f) => f.filters?.length && f.filters[0].field === field);
10927
+ }
10928
+ updateSelectionFromFilter() {
10929
+ this.currentlySelected.clear();
10930
+ if (!this.currentFilter || !this.column?.field) {
10931
+ return;
10932
+ }
10933
+ const idx = this.getCompositeFilterIndex(this.currentFilter);
10934
+ if (idx === -1) {
10935
+ return;
10936
+ }
10937
+ const block = this.currentFilter.filters[idx];
10938
+ if (block && Array.isArray(block.filters)) {
10939
+ block.filters.forEach((f) => {
10940
+ if (f.field === this.column.field && f.operator === 'eq') {
10941
+ this.currentlySelected.add(f.value);
10942
+ }
10943
+ });
10944
+ }
10945
+ }
10927
10946
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: MultiCheckboxFilterComponent, deps: [{ token: ContextService }, { token: LocalDataChangesService }], target: i0.ɵɵFactoryTarget.Component });
10928
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: MultiCheckboxFilterComponent, isStandalone: true, selector: "kendo-grid-multicheckbox-filter", inputs: { column: "column" }, outputs: { filterChange: "filterChange" }, ngImport: i0, template: `
10947
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: MultiCheckboxFilterComponent, isStandalone: true, selector: "kendo-grid-multicheckbox-filter", inputs: { column: "column" }, outputs: { filterChange: "filterChange" }, viewQueries: [{ propertyName: "filterInput", first: true, predicate: FilterInputDirective, descendants: true }], ngImport: i0, template: `
10929
10948
  <kendo-textbox *ngIf="normalizedFilterVariant.search"
10949
+ kendoFilterInput
10930
10950
  class="k-searchbox"
10931
- [placeholder]="messageFor('multiCheckboxFilterSearchPlaceholder')"
10932
- (valueChange)="onSearch($event)">
10951
+ [placeholder]="messageFor('multiCheckboxFilterSearchPlaceholder')">
10933
10952
  <ng-template kendoTextBoxPrefixTemplate>
10934
10953
  <kendo-icon-wrapper innerCssClass="k-input-icon" name="search" [svgIcon]="searchIcon"></kendo-icon-wrapper>
10935
10954
  </ng-template>
@@ -10955,7 +10974,7 @@ class MultiCheckboxFilterComponent {
10955
10974
  </li>
10956
10975
  </ul>
10957
10976
  <div class="k-filter-selected-items">{{selectedItemsMessage}}</div>
10958
- `, isInline: true, dependencies: [{ kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: CheckBoxComponent, selector: "kendo-checkbox", inputs: ["checkedState", "rounded"], outputs: ["checkedStateChange"], exportAs: ["kendoCheckBox"] }, { kind: "component", type: TextBoxComponent, selector: "kendo-textbox", inputs: ["focusableId", "title", "type", "disabled", "readonly", "tabindex", "value", "selectOnFocus", "showSuccessIcon", "showErrorIcon", "clearButton", "successIcon", "successSvgIcon", "errorIcon", "errorSvgIcon", "clearButtonIcon", "clearButtonSvgIcon", "size", "rounded", "fillMode", "tabIndex", "placeholder", "maxlength", "inputAttributes"], outputs: ["valueChange", "inputFocus", "inputBlur", "focus", "blur"], exportAs: ["kendoTextBox"] }, { kind: "directive", type: TextBoxPrefixTemplateDirective, selector: "[kendoTextBoxPrefixTemplate]", inputs: ["showSeparator"] }, { kind: "component", type: IconWrapperComponent, selector: "kendo-icon-wrapper", inputs: ["name", "svgIcon", "innerCssClass", "customFontClass", "size"], exportAs: ["kendoIconWrapper"] }, { kind: "pipe", type: FormatPipe, name: "format" }] });
10977
+ `, isInline: true, dependencies: [{ kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: CheckBoxComponent, selector: "kendo-checkbox", inputs: ["checkedState", "rounded"], outputs: ["checkedStateChange"], exportAs: ["kendoCheckBox"] }, { kind: "component", type: TextBoxComponent, selector: "kendo-textbox", inputs: ["focusableId", "title", "type", "disabled", "readonly", "tabindex", "value", "selectOnFocus", "showSuccessIcon", "showErrorIcon", "clearButton", "successIcon", "successSvgIcon", "errorIcon", "errorSvgIcon", "clearButtonIcon", "clearButtonSvgIcon", "size", "rounded", "fillMode", "tabIndex", "placeholder", "maxlength", "inputAttributes"], outputs: ["valueChange", "inputFocus", "inputBlur", "focus", "blur"], exportAs: ["kendoTextBox"] }, { kind: "directive", type: TextBoxPrefixTemplateDirective, selector: "[kendoTextBoxPrefixTemplate]", inputs: ["showSeparator"] }, { kind: "component", type: IconWrapperComponent, selector: "kendo-icon-wrapper", inputs: ["name", "svgIcon", "innerCssClass", "customFontClass", "size"], exportAs: ["kendoIconWrapper"] }, { kind: "pipe", type: FormatPipe, name: "format" }, { kind: "directive", type: FilterInputDirective, selector: "[kendoFilterInput]", inputs: ["filterDelay", "columnLabel", "value"] }] });
10959
10978
  }
10960
10979
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: MultiCheckboxFilterComponent, decorators: [{
10961
10980
  type: Component,
@@ -10963,9 +10982,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
10963
10982
  selector: 'kendo-grid-multicheckbox-filter',
10964
10983
  template: `
10965
10984
  <kendo-textbox *ngIf="normalizedFilterVariant.search"
10985
+ kendoFilterInput
10966
10986
  class="k-searchbox"
10967
- [placeholder]="messageFor('multiCheckboxFilterSearchPlaceholder')"
10968
- (valueChange)="onSearch($event)">
10987
+ [placeholder]="messageFor('multiCheckboxFilterSearchPlaceholder')">
10969
10988
  <ng-template kendoTextBoxPrefixTemplate>
10970
10989
  <kendo-icon-wrapper innerCssClass="k-input-icon" name="search" [svgIcon]="searchIcon"></kendo-icon-wrapper>
10971
10990
  </ng-template>
@@ -10993,12 +11012,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
10993
11012
  <div class="k-filter-selected-items">{{selectedItemsMessage}}</div>
10994
11013
  `,
10995
11014
  standalone: true,
10996
- imports: [NgFor, NgIf, CheckBoxComponent, TextBoxComponent, TextBoxPrefixTemplateDirective, IconWrapperComponent, FormatPipe]
11015
+ imports: [NgFor, NgIf, CheckBoxComponent, TextBoxComponent, TextBoxPrefixTemplateDirective, IconWrapperComponent, FormatPipe, FilterInputDirective]
10997
11016
  }]
10998
11017
  }], ctorParameters: function () { return [{ type: ContextService }, { type: LocalDataChangesService }]; }, propDecorators: { column: [{
10999
11018
  type: Input
11000
11019
  }], filterChange: [{
11001
11020
  type: Output
11021
+ }], filterInput: [{
11022
+ type: ViewChild,
11023
+ args: [FilterInputDirective]
11002
11024
  }] } });
11003
11025
 
11004
11026
  const isNoValueOperator = operator => (operator === "isnull"
@@ -22819,8 +22841,8 @@ const packageMetadata = {
22819
22841
  productName: 'Kendo UI for Angular',
22820
22842
  productCode: 'KENDOUIANGULAR',
22821
22843
  productCodes: ['KENDOUIANGULAR'],
22822
- publishDate: 1760814554,
22823
- version: '20.1.0-develop.33',
22844
+ publishDate: 1760965348,
22845
+ version: '20.1.0-develop.34',
22824
22846
  licensingDocsUrl: 'https://www.telerik.com/kendo-angular-ui/my-license/'
22825
22847
  };
22826
22848
 
@@ -2,21 +2,23 @@
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 { EventEmitter, OnInit, OnDestroy } from '@angular/core';
5
+ import { EventEmitter, OnInit, OnDestroy, AfterViewInit } from '@angular/core';
6
6
  import { CompositeFilterDescriptor } from '@progress/kendo-data-query';
7
7
  import { SVGIcon } from '@progress/kendo-svg-icons';
8
8
  import { ContextService } from '../common/provider.service';
9
9
  import { LocalDataChangesService } from '../editing/local-data-changes.service';
10
10
  import { FilterVariantSettings } from './filterable';
11
+ import { FilterInputDirective } from './filter-input.directive';
11
12
  import * as i0 from "@angular/core";
12
13
  /**
13
14
  * @hidden
14
15
  */
15
- export declare class MultiCheckboxFilterComponent implements OnInit, OnDestroy {
16
+ export declare class MultiCheckboxFilterComponent implements OnInit, AfterViewInit, OnDestroy {
16
17
  private ctx;
17
18
  private dataChangesService;
18
19
  column: any;
19
20
  filterChange: EventEmitter<CompositeFilterDescriptor>;
21
+ filterInput: FilterInputDirective;
20
22
  constructor(ctx: ContextService, dataChangesService: LocalDataChangesService);
21
23
  listData: any[];
22
24
  searchIcon: SVGIcon;
@@ -24,12 +26,13 @@ export declare class MultiCheckboxFilterComponent implements OnInit, OnDestroy {
24
26
  currentlySelected: Set<any>;
25
27
  private isSearched;
26
28
  private currentFilter;
27
- private typingTimeout;
29
+ private filterChangeSub;
28
30
  private baseListData;
29
31
  ngOnInit(): void;
32
+ ngAfterViewInit(): void;
30
33
  ngOnDestroy(): void;
31
34
  isItemSelected(item: any): boolean;
32
- onSearch(value: string): void;
35
+ onSearch(value: any): void;
33
36
  handleCheckBoxChange(checkedState: any, value: any, selectAllChecked?: boolean): void;
34
37
  get filteredGridData(): any[];
35
38
  get selectAllChecked(): any;
@@ -38,6 +41,10 @@ export declare class MultiCheckboxFilterComponent implements OnInit, OnDestroy {
38
41
  get selectedItemsMessage(): string;
39
42
  messageFor(key: string): string;
40
43
  private getUniqueDateValues;
44
+ private initializeData;
45
+ private emptyFilter;
46
+ private getCompositeFilterIndex;
47
+ private updateSelectionFromFilter;
41
48
  static ɵfac: i0.ɵɵFactoryDeclaration<MultiCheckboxFilterComponent, never>;
42
49
  static ɵcmp: i0.ɵɵComponentDeclaration<MultiCheckboxFilterComponent, "kendo-grid-multicheckbox-filter", never, { "column": { "alias": "column"; "required": false; }; }, { "filterChange": "filterChange"; }, never, never, true, never>;
43
50
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@progress/kendo-angular-grid",
3
- "version": "20.1.0-develop.33",
3
+ "version": "20.1.0-develop.34",
4
4
  "description": "Kendo UI Grid for Angular - high performance data grid with paging, filtering, virtualization, CRUD, and more.",
5
5
  "license": "SEE LICENSE IN LICENSE.md",
6
6
  "author": "Progress",
@@ -41,7 +41,7 @@
41
41
  "package": {
42
42
  "productName": "Kendo UI for Angular",
43
43
  "productCode": "KENDOUIANGULAR",
44
- "publishDate": 1760814554,
44
+ "publishDate": 1760965348,
45
45
  "licensingDocsUrl": "https://www.telerik.com/kendo-angular-ui/my-license/"
46
46
  }
47
47
  },
@@ -54,29 +54,29 @@
54
54
  "@progress/kendo-data-query": "^1.0.0",
55
55
  "@progress/kendo-drawing": "^1.21.0",
56
56
  "@progress/kendo-licensing": "^1.7.0",
57
- "@progress/kendo-angular-buttons": "20.1.0-develop.33",
58
- "@progress/kendo-angular-common": "20.1.0-develop.33",
59
- "@progress/kendo-angular-dateinputs": "20.1.0-develop.33",
60
- "@progress/kendo-angular-layout": "20.1.0-develop.33",
61
- "@progress/kendo-angular-navigation": "20.1.0-develop.33",
62
- "@progress/kendo-angular-dropdowns": "20.1.0-develop.33",
63
- "@progress/kendo-angular-excel-export": "20.1.0-develop.33",
64
- "@progress/kendo-angular-icons": "20.1.0-develop.33",
65
- "@progress/kendo-angular-inputs": "20.1.0-develop.33",
66
- "@progress/kendo-angular-conversational-ui": "20.1.0-develop.33",
67
- "@progress/kendo-angular-intl": "20.1.0-develop.33",
68
- "@progress/kendo-angular-l10n": "20.1.0-develop.33",
69
- "@progress/kendo-angular-label": "20.1.0-develop.33",
70
- "@progress/kendo-angular-pager": "20.1.0-develop.33",
71
- "@progress/kendo-angular-pdf-export": "20.1.0-develop.33",
72
- "@progress/kendo-angular-popup": "20.1.0-develop.33",
73
- "@progress/kendo-angular-toolbar": "20.1.0-develop.33",
74
- "@progress/kendo-angular-utils": "20.1.0-develop.33",
57
+ "@progress/kendo-angular-buttons": "20.1.0-develop.34",
58
+ "@progress/kendo-angular-common": "20.1.0-develop.34",
59
+ "@progress/kendo-angular-dateinputs": "20.1.0-develop.34",
60
+ "@progress/kendo-angular-layout": "20.1.0-develop.34",
61
+ "@progress/kendo-angular-navigation": "20.1.0-develop.34",
62
+ "@progress/kendo-angular-dropdowns": "20.1.0-develop.34",
63
+ "@progress/kendo-angular-excel-export": "20.1.0-develop.34",
64
+ "@progress/kendo-angular-icons": "20.1.0-develop.34",
65
+ "@progress/kendo-angular-inputs": "20.1.0-develop.34",
66
+ "@progress/kendo-angular-conversational-ui": "20.1.0-develop.34",
67
+ "@progress/kendo-angular-intl": "20.1.0-develop.34",
68
+ "@progress/kendo-angular-l10n": "20.1.0-develop.34",
69
+ "@progress/kendo-angular-label": "20.1.0-develop.34",
70
+ "@progress/kendo-angular-pager": "20.1.0-develop.34",
71
+ "@progress/kendo-angular-pdf-export": "20.1.0-develop.34",
72
+ "@progress/kendo-angular-popup": "20.1.0-develop.34",
73
+ "@progress/kendo-angular-toolbar": "20.1.0-develop.34",
74
+ "@progress/kendo-angular-utils": "20.1.0-develop.34",
75
75
  "rxjs": "^6.5.3 || ^7.0.0"
76
76
  },
77
77
  "dependencies": {
78
78
  "tslib": "^2.3.1",
79
- "@progress/kendo-angular-schematics": "20.1.0-develop.33",
79
+ "@progress/kendo-angular-schematics": "20.1.0-develop.34",
80
80
  "@progress/kendo-common": "^1.0.1",
81
81
  "@progress/kendo-file-saver": "^1.0.0"
82
82
  },
@@ -4,14 +4,14 @@ const schematics_1 = require("@angular-devkit/schematics");
4
4
  function default_1(options) {
5
5
  const finalOptions = Object.assign(Object.assign({}, options), { mainNgModule: 'GridModule', package: 'grid', peerDependencies: {
6
6
  // peer deps of the dropdowns
7
- '@progress/kendo-angular-treeview': '20.1.0-develop.33',
8
- '@progress/kendo-angular-navigation': '20.1.0-develop.33',
7
+ '@progress/kendo-angular-treeview': '20.1.0-develop.34',
8
+ '@progress/kendo-angular-navigation': '20.1.0-develop.34',
9
9
  // peer dependency of kendo-angular-inputs
10
- '@progress/kendo-angular-dialog': '20.1.0-develop.33',
10
+ '@progress/kendo-angular-dialog': '20.1.0-develop.34',
11
11
  // peer dependency of kendo-angular-icons
12
12
  '@progress/kendo-svg-icons': '^4.0.0',
13
13
  // peer dependency of kendo-angular-layout
14
- '@progress/kendo-angular-progressbar': '20.1.0-develop.33'
14
+ '@progress/kendo-angular-progressbar': '20.1.0-develop.34'
15
15
  } });
16
16
  return (0, schematics_1.externalSchematic)('@progress/kendo-angular-schematics', 'ng-add', finalOptions);
17
17
  }