@gp-grid/angular 0.19.0 → 0.20.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.
@@ -1,7 +1,7 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { input, output, computed, TemplateRef, ChangeDetectionStrategy, Component, ViewChild, signal, effect, HostListener, inject, PLATFORM_ID, InjectionToken, Injectable } from '@angular/core';
3
3
  import { NgTemplateOutlet, isPlatformBrowser } from '@angular/common';
4
- import { getFieldValue, isCellSelected, isCellActive, formatCellValue, isCellInFillPreview, buildCellClasses, isCellEditing, calculateFilterPopupPosition, bindPeekSelectAll, calculateScaledColumnPositions, getTotalWidth, calculateFillHandlePosition, DataSourceOwner, AutoScrollDriver, PendingRowDragController, PendingCellTapController, TouchScrollController, InputEventAdapter, applyBatchInstructions, scrollCellIntoView, GridCore, createMutableClientDataSource } from '@gp-grid/core';
4
+ import { getFieldValue, isCellSelected, isCellActive, formatCellValue, isCellInFillPreview, buildCellClasses, isCellEditing, labelsForSelectedValues, rawValuesForLabels, groupDistinctValues, isBlankCellValue, calculateFilterPopupPosition, bindPeekSelectAll, calculateScaledColumnPositions, getTotalWidth, calculateFillHandlePosition, DataSourceOwner, AutoScrollDriver, PendingRowDragController, PendingCellTapController, TouchScrollController, InputEventAdapter, applyBatchInstructions, scrollCellIntoView, GridCore, createMutableClientDataSource } from '@gp-grid/core';
5
5
  export { GridCore, createClientDataSource, createDataSourceFromArray, createMutableClientDataSource, createServerDataSource } from '@gp-grid/core';
6
6
 
7
7
  const TEMPLATE$2 = `
@@ -540,19 +540,21 @@ const FILTER_POPUP_TEMPLATE = `
540
540
  <button type="button" (click)="deselectAll()">Deselect All</button>
541
541
  </div>
542
542
  <div class="gp-grid-filter-list">
543
- <label class="gp-grid-filter-option">
544
- <input
545
- type="checkbox"
546
- [checked]="includeBlanks"
547
- (change)="includeBlanks = $any($event.target).checked" />
548
- <span class="gp-grid-filter-blank">(Blanks)</span>
549
- </label>
550
- @for (entry of filteredUniqueEntries(); track entry.key) {
543
+ @if (hasBlanks()) {
551
544
  <label class="gp-grid-filter-option">
552
545
  <input
553
546
  type="checkbox"
554
- [checked]="selectedValues.has(entry.key)"
555
- (change)="toggleValue(entry.key, $any($event.target).checked)" />
547
+ [checked]="includeBlanks"
548
+ (change)="includeBlanks = $any($event.target).checked" />
549
+ <span class="gp-grid-filter-blank">(Blanks)</span>
550
+ </label>
551
+ }
552
+ @for (entry of filteredUniqueEntries(); track entry.label) {
553
+ <label class="gp-grid-filter-option">
554
+ <input
555
+ type="checkbox"
556
+ [checked]="selectedLabels.has(entry.label)"
557
+ (change)="toggleValue(entry.label, $any($event.target).checked)" />
556
558
  <span>{{ entry.label }}</span>
557
559
  </label>
558
560
  }
@@ -658,40 +660,24 @@ const defaultNumberCondition = () => ({
658
660
  valueTo: '',
659
661
  nextOperator: 'and',
660
662
  });
661
- const computeUniqueValues = (distinctValues, formatter) => {
662
- const seen = new Set();
663
- const result = [];
664
- for (const val of distinctValues) {
665
- if (val === null || val === undefined || val === '')
666
- continue;
667
- // Key by formatter output when available so filter-time comparison
668
- // (which also goes through the formatter) matches what the user selects.
669
- const key = formatter ? formatter(val) : String(val);
670
- if (!seen.has(key)) {
671
- seen.add(key);
672
- result.push({ key, label: key });
673
- }
674
- }
675
- return result.sort((a, b) => a.label.localeCompare(b.label, undefined, { numeric: true, sensitivity: 'base' }));
676
- };
677
- const initTextState = (filter, uniqueValues) => {
663
+ const initTextState = (filter, entries) => {
678
664
  if (!filter)
679
- return defaultTextState(uniqueValues);
665
+ return defaultTextState(entries);
680
666
  const textConds = filter.conditions.filter((c) => c.type === 'text');
681
667
  if (textConds.length === 0)
682
- return defaultTextState(uniqueValues);
668
+ return defaultTextState(entries);
683
669
  const firstCond = textConds[0];
684
670
  if (firstCond?.selectedValues !== undefined) {
685
671
  return {
686
672
  filterMode: 'values',
687
- selectedValues: new Set(firstCond.selectedValues),
673
+ selectedLabels: labelsForSelectedValues(entries, firstCond.selectedValues),
688
674
  includeBlanks: firstCond.includeBlank ?? true,
689
675
  textConditions: [defaultTextCondition()],
690
676
  };
691
677
  }
692
678
  return {
693
679
  filterMode: 'condition',
694
- selectedValues: new Set(uniqueValues),
680
+ selectedLabels: new Set(entries.map(e => e.label)),
695
681
  includeBlanks: true,
696
682
  textConditions: textConds.map((c, i) => ({
697
683
  operator: c.operator,
@@ -700,9 +686,9 @@ const initTextState = (filter, uniqueValues) => {
700
686
  })),
701
687
  };
702
688
  };
703
- const defaultTextState = (uniqueValues) => ({
689
+ const defaultTextState = (entries) => ({
704
690
  filterMode: 'values',
705
- selectedValues: new Set(uniqueValues),
691
+ selectedLabels: new Set(entries.map(e => e.label)),
706
692
  includeBlanks: true,
707
693
  textConditions: [defaultTextCondition()],
708
694
  });
@@ -726,14 +712,14 @@ const buildTextFilter = (input) => {
726
712
  return buildConditionTextFilter(input.textConditions);
727
713
  };
728
714
  const buildValuesFilter = (input) => {
729
- const allSelected = input.uniqueValues.every(v => input.selectedValues.has(v));
715
+ const allSelected = input.entries.every(e => input.selectedLabels.has(e.label));
730
716
  if (allSelected && input.includeBlanks)
731
717
  return null;
732
718
  return {
733
719
  conditions: [{
734
720
  type: 'text',
735
- operator: 'contains',
736
- selectedValues: new Set(input.selectedValues),
721
+ operator: 'equals',
722
+ selectedValues: rawValuesForLabels(input.entries, input.selectedLabels),
737
723
  includeBlank: input.includeBlanks,
738
724
  }],
739
725
  combination: 'or',
@@ -810,7 +796,8 @@ class FilterPopupComponent {
810
796
  positioned = signal(false, ...(ngDevMode ? [{ debugName: "positioned" }] : /* istanbul ignore next */ []));
811
797
  filterMode = 'values';
812
798
  searchText = '';
813
- selectedValues = new Set();
799
+ // Ticked display labels; raw values are resolved on apply (buildTextFilter).
800
+ selectedLabels = new Set();
814
801
  includeBlanks = true;
815
802
  textConditions = [defaultTextCondition()];
816
803
  numberConditions = [defaultNumberCondition()];
@@ -859,10 +846,12 @@ class FilterPopupComponent {
859
846
  return this.uniqueEntries().length <= MAX_CHECKBOX_VALUES;
860
847
  }
861
848
  uniqueEntries() {
862
- return computeUniqueValues(this.distinctValues(), this.column().valueFormatter);
849
+ return groupDistinctValues(this.distinctValues(), this.column().valueFormatter);
863
850
  }
864
- uniqueValues() {
865
- return this.uniqueEntries().map(e => e.key);
851
+ // Empty arrays count too (tags column with no tags), so the "(Blanks)"
852
+ // opt-out renders whenever blank rows exist — and only then.
853
+ hasBlanks() {
854
+ return this.distinctValues().some(isBlankCellValue);
866
855
  }
867
856
  filteredUniqueEntries() {
868
857
  const search = this.searchText.toLowerCase();
@@ -870,22 +859,22 @@ class FilterPopupComponent {
870
859
  return this.uniqueEntries();
871
860
  return this.uniqueEntries().filter(e => e.label.toLowerCase().includes(search));
872
861
  }
873
- toggleValue(val, checked) {
862
+ toggleValue(label, checked) {
874
863
  if (checked) {
875
- this.selectedValues.add(val);
864
+ this.selectedLabels.add(label);
876
865
  }
877
866
  else {
878
- this.selectedValues.delete(val);
867
+ this.selectedLabels.delete(label);
879
868
  }
880
869
  }
881
870
  selectAll() {
882
871
  this.includeBlanks = true;
883
- for (const v of this.uniqueValues())
884
- this.selectedValues.add(v);
872
+ for (const entry of this.uniqueEntries())
873
+ this.selectedLabels.add(entry.label);
885
874
  }
886
875
  deselectAll() {
887
876
  this.includeBlanks = false;
888
- this.selectedValues.clear();
877
+ this.selectedLabels.clear();
889
878
  }
890
879
  onTextOperatorChange(index, value) {
891
880
  setField(this.textConditions, index, 'operator', value);
@@ -925,8 +914,8 @@ class FilterPopupComponent {
925
914
  return buildNumberFilter(this.numberConditions);
926
915
  return buildTextFilter({
927
916
  filterMode: this.filterMode,
928
- uniqueValues: this.uniqueValues(),
929
- selectedValues: this.selectedValues,
917
+ entries: this.uniqueEntries(),
918
+ selectedLabels: this.selectedLabels,
930
919
  includeBlanks: this.includeBlanks,
931
920
  textConditions: this.textConditions,
932
921
  });
@@ -937,9 +926,9 @@ class FilterPopupComponent {
937
926
  this.numberConditions = initNumberConditions(filter);
938
927
  return;
939
928
  }
940
- const state = initTextState(filter, this.uniqueValues());
929
+ const state = initTextState(filter, this.uniqueEntries());
941
930
  this.filterMode = state.filterMode;
942
- this.selectedValues = state.selectedValues;
931
+ this.selectedLabels = state.selectedLabels;
943
932
  this.includeBlanks = state.includeBlanks;
944
933
  this.textConditions = state.textConditions;
945
934
  }
@@ -970,7 +959,7 @@ class FilterPopupComponent {
970
959
  });
971
960
  };
972
961
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: FilterPopupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
973
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.7", type: FilterPopupComponent, isStandalone: true, selector: "gp-grid-filter-popup", inputs: { column: { classPropertyName: "column", publicName: "column", isSignal: true, isRequired: true, transformFunction: null }, colIndex: { classPropertyName: "colIndex", publicName: "colIndex", isSignal: true, isRequired: true, transformFunction: null }, anchorEl: { classPropertyName: "anchorEl", publicName: "anchorEl", isSignal: true, isRequired: true, transformFunction: null }, distinctValues: { classPropertyName: "distinctValues", publicName: "distinctValues", isSignal: true, isRequired: true, transformFunction: null }, currentFilter: { classPropertyName: "currentFilter", publicName: "currentFilter", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { apply: "apply", close: "close" }, host: { listeners: { "keydown.escape": "onEscape()" } }, viewQueries: [{ propertyName: "popupEl", first: true, predicate: ["popupEl"], descendants: true }], ngImport: i0, template: "\n<div\n #popupEl\n class=\"gp-grid-filter-popup\"\n [style.position]=\"'fixed'\"\n [style.zIndex]=\"10000\"\n [style.top.px]=\"popupTop()\"\n [style.left.px]=\"popupLeft()\"\n [style.minWidth.px]=\"popupMinWidth()\"\n [style.visibility]=\"positioned() ? 'visible' : 'hidden'\"\n (keydown.escape)=\"close.emit()\"\n (click)=\"$event.stopPropagation()\">\n\n <div class=\"gp-grid-filter-header\">\n Filter: {{ column().headerName ?? column().field }}\n </div>\n\n <div [class]=\"'gp-grid-filter-content ' + (isNumberColumn() ? 'gp-grid-filter-number' : 'gp-grid-filter-text')\">\n @if (isNumberColumn()) {\n @for (cond of numberConditions; track $index; let i = $index) {\n <div class=\"gp-grid-filter-condition\">\n @if (i > 0) {\n <div class=\"gp-grid-filter-combination\">\n <button\n type=\"button\"\n [class.active]=\"numberConditions[i - 1]?.nextOperator === 'and'\"\n (click)=\"setNumberNextOp(i - 1, 'and')\">\n AND\n </button>\n <button\n type=\"button\"\n [class.active]=\"numberConditions[i - 1]?.nextOperator === 'or'\"\n (click)=\"setNumberNextOp(i - 1, 'or')\">\n OR\n </button>\n </div>\n }\n <div class=\"gp-grid-filter-row\">\n <select\n [value]=\"cond.operator\"\n (change)=\"onNumberOperatorChange(i, $any($event.target).value)\">\n @for (op of numberOperators; track op.value) {\n <option [value]=\"op.value\">{{ op.label }}</option>\n }\n </select>\n @if (!isValueLessNumberOp(cond.operator)) {\n <input\n type=\"number\"\n [value]=\"cond.value\"\n (input)=\"cond.value = $any($event.target).value\"\n placeholder=\"Value\" />\n @if (cond.operator === 'between') {\n <span class=\"gp-grid-filter-to\">to</span>\n <input\n type=\"number\"\n [value]=\"cond.valueTo\"\n (input)=\"cond.valueTo = $any($event.target).value\"\n placeholder=\"Value\" />\n }\n }\n @if (numberConditions.length > 1) {\n <button\n type=\"button\"\n class=\"gp-grid-filter-remove\"\n (click)=\"removeNumberCondition(i)\">\u00D7</button>\n }\n </div>\n </div>\n }\n <button type=\"button\" class=\"gp-grid-filter-add\" (click)=\"addNumberCondition()\">\n + Add condition\n </button>\n } @else {\n @if (showValuesMode()) {\n <div class=\"gp-grid-filter-mode-toggle\">\n <button\n type=\"button\"\n [class.active]=\"filterMode === 'values'\"\n (click)=\"filterMode = 'values'\">\n Values\n </button>\n <button\n type=\"button\"\n [class.active]=\"filterMode === 'condition'\"\n (click)=\"filterMode = 'condition'\">\n Condition\n </button>\n </div>\n }\n\n @if (filterMode === 'values' && showValuesMode()) {\n <input\n class=\"gp-grid-filter-search\"\n type=\"text\"\n [value]=\"searchText\"\n (input)=\"searchText = $any($event.target).value\"\n placeholder=\"Search...\" />\n <div class=\"gp-grid-filter-actions\">\n <button type=\"button\" (click)=\"selectAll()\">Select All</button>\n <button type=\"button\" (click)=\"deselectAll()\">Deselect All</button>\n </div>\n <div class=\"gp-grid-filter-list\">\n <label class=\"gp-grid-filter-option\">\n <input\n type=\"checkbox\"\n [checked]=\"includeBlanks\"\n (change)=\"includeBlanks = $any($event.target).checked\" />\n <span class=\"gp-grid-filter-blank\">(Blanks)</span>\n </label>\n @for (entry of filteredUniqueEntries(); track entry.key) {\n <label class=\"gp-grid-filter-option\">\n <input\n type=\"checkbox\"\n [checked]=\"selectedValues.has(entry.key)\"\n (change)=\"toggleValue(entry.key, $any($event.target).checked)\" />\n <span>{{ entry.label }}</span>\n </label>\n }\n </div>\n }\n\n @if (filterMode === 'condition') {\n @for (cond of textConditions; track $index; let i = $index) {\n <div class=\"gp-grid-filter-condition\">\n @if (i > 0) {\n <div class=\"gp-grid-filter-combination\">\n <button\n type=\"button\"\n [class.active]=\"textConditions[i - 1]?.nextOperator === 'and'\"\n (click)=\"setTextNextOp(i - 1, 'and')\">\n AND\n </button>\n <button\n type=\"button\"\n [class.active]=\"textConditions[i - 1]?.nextOperator === 'or'\"\n (click)=\"setTextNextOp(i - 1, 'or')\">\n OR\n </button>\n </div>\n }\n <div class=\"gp-grid-filter-row\">\n <select\n [value]=\"cond.operator\"\n (change)=\"onTextOperatorChange(i, $any($event.target).value)\">\n @for (op of textOperators; track op.value) {\n <option [value]=\"op.value\">{{ op.label }}</option>\n }\n </select>\n @if (!isValueLessTextOp(cond.operator)) {\n <input\n class=\"gp-grid-filter-text-input\"\n type=\"text\"\n [value]=\"cond.value\"\n (input)=\"cond.value = $any($event.target).value\"\n placeholder=\"Value\" />\n }\n @if (textConditions.length > 1) {\n <button\n type=\"button\"\n class=\"gp-grid-filter-remove\"\n (click)=\"removeTextCondition(i)\">\u00D7</button>\n }\n </div>\n </div>\n }\n <button type=\"button\" class=\"gp-grid-filter-add\" (click)=\"addTextCondition()\">\n + Add condition\n </button>\n }\n }\n\n <div class=\"gp-grid-filter-buttons\">\n <button type=\"button\" class=\"gp-grid-filter-btn-clear\" (click)=\"handleClear()\">\n Clear\n </button>\n <button type=\"button\" class=\"gp-grid-filter-btn-apply\" (click)=\"handleApply()\">\n Apply\n </button>\n </div>\n </div>\n</div>\n", isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
962
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.7", type: FilterPopupComponent, isStandalone: true, selector: "gp-grid-filter-popup", inputs: { column: { classPropertyName: "column", publicName: "column", isSignal: true, isRequired: true, transformFunction: null }, colIndex: { classPropertyName: "colIndex", publicName: "colIndex", isSignal: true, isRequired: true, transformFunction: null }, anchorEl: { classPropertyName: "anchorEl", publicName: "anchorEl", isSignal: true, isRequired: true, transformFunction: null }, distinctValues: { classPropertyName: "distinctValues", publicName: "distinctValues", isSignal: true, isRequired: true, transformFunction: null }, currentFilter: { classPropertyName: "currentFilter", publicName: "currentFilter", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { apply: "apply", close: "close" }, host: { listeners: { "keydown.escape": "onEscape()" } }, viewQueries: [{ propertyName: "popupEl", first: true, predicate: ["popupEl"], descendants: true }], ngImport: i0, template: "\n<div\n #popupEl\n class=\"gp-grid-filter-popup\"\n [style.position]=\"'fixed'\"\n [style.zIndex]=\"10000\"\n [style.top.px]=\"popupTop()\"\n [style.left.px]=\"popupLeft()\"\n [style.minWidth.px]=\"popupMinWidth()\"\n [style.visibility]=\"positioned() ? 'visible' : 'hidden'\"\n (keydown.escape)=\"close.emit()\"\n (click)=\"$event.stopPropagation()\">\n\n <div class=\"gp-grid-filter-header\">\n Filter: {{ column().headerName ?? column().field }}\n </div>\n\n <div [class]=\"'gp-grid-filter-content ' + (isNumberColumn() ? 'gp-grid-filter-number' : 'gp-grid-filter-text')\">\n @if (isNumberColumn()) {\n @for (cond of numberConditions; track $index; let i = $index) {\n <div class=\"gp-grid-filter-condition\">\n @if (i > 0) {\n <div class=\"gp-grid-filter-combination\">\n <button\n type=\"button\"\n [class.active]=\"numberConditions[i - 1]?.nextOperator === 'and'\"\n (click)=\"setNumberNextOp(i - 1, 'and')\">\n AND\n </button>\n <button\n type=\"button\"\n [class.active]=\"numberConditions[i - 1]?.nextOperator === 'or'\"\n (click)=\"setNumberNextOp(i - 1, 'or')\">\n OR\n </button>\n </div>\n }\n <div class=\"gp-grid-filter-row\">\n <select\n [value]=\"cond.operator\"\n (change)=\"onNumberOperatorChange(i, $any($event.target).value)\">\n @for (op of numberOperators; track op.value) {\n <option [value]=\"op.value\">{{ op.label }}</option>\n }\n </select>\n @if (!isValueLessNumberOp(cond.operator)) {\n <input\n type=\"number\"\n [value]=\"cond.value\"\n (input)=\"cond.value = $any($event.target).value\"\n placeholder=\"Value\" />\n @if (cond.operator === 'between') {\n <span class=\"gp-grid-filter-to\">to</span>\n <input\n type=\"number\"\n [value]=\"cond.valueTo\"\n (input)=\"cond.valueTo = $any($event.target).value\"\n placeholder=\"Value\" />\n }\n }\n @if (numberConditions.length > 1) {\n <button\n type=\"button\"\n class=\"gp-grid-filter-remove\"\n (click)=\"removeNumberCondition(i)\">\u00D7</button>\n }\n </div>\n </div>\n }\n <button type=\"button\" class=\"gp-grid-filter-add\" (click)=\"addNumberCondition()\">\n + Add condition\n </button>\n } @else {\n @if (showValuesMode()) {\n <div class=\"gp-grid-filter-mode-toggle\">\n <button\n type=\"button\"\n [class.active]=\"filterMode === 'values'\"\n (click)=\"filterMode = 'values'\">\n Values\n </button>\n <button\n type=\"button\"\n [class.active]=\"filterMode === 'condition'\"\n (click)=\"filterMode = 'condition'\">\n Condition\n </button>\n </div>\n }\n\n @if (filterMode === 'values' && showValuesMode()) {\n <input\n class=\"gp-grid-filter-search\"\n type=\"text\"\n [value]=\"searchText\"\n (input)=\"searchText = $any($event.target).value\"\n placeholder=\"Search...\" />\n <div class=\"gp-grid-filter-actions\">\n <button type=\"button\" (click)=\"selectAll()\">Select All</button>\n <button type=\"button\" (click)=\"deselectAll()\">Deselect All</button>\n </div>\n <div class=\"gp-grid-filter-list\">\n @if (hasBlanks()) {\n <label class=\"gp-grid-filter-option\">\n <input\n type=\"checkbox\"\n [checked]=\"includeBlanks\"\n (change)=\"includeBlanks = $any($event.target).checked\" />\n <span class=\"gp-grid-filter-blank\">(Blanks)</span>\n </label>\n }\n @for (entry of filteredUniqueEntries(); track entry.label) {\n <label class=\"gp-grid-filter-option\">\n <input\n type=\"checkbox\"\n [checked]=\"selectedLabels.has(entry.label)\"\n (change)=\"toggleValue(entry.label, $any($event.target).checked)\" />\n <span>{{ entry.label }}</span>\n </label>\n }\n </div>\n }\n\n @if (filterMode === 'condition') {\n @for (cond of textConditions; track $index; let i = $index) {\n <div class=\"gp-grid-filter-condition\">\n @if (i > 0) {\n <div class=\"gp-grid-filter-combination\">\n <button\n type=\"button\"\n [class.active]=\"textConditions[i - 1]?.nextOperator === 'and'\"\n (click)=\"setTextNextOp(i - 1, 'and')\">\n AND\n </button>\n <button\n type=\"button\"\n [class.active]=\"textConditions[i - 1]?.nextOperator === 'or'\"\n (click)=\"setTextNextOp(i - 1, 'or')\">\n OR\n </button>\n </div>\n }\n <div class=\"gp-grid-filter-row\">\n <select\n [value]=\"cond.operator\"\n (change)=\"onTextOperatorChange(i, $any($event.target).value)\">\n @for (op of textOperators; track op.value) {\n <option [value]=\"op.value\">{{ op.label }}</option>\n }\n </select>\n @if (!isValueLessTextOp(cond.operator)) {\n <input\n class=\"gp-grid-filter-text-input\"\n type=\"text\"\n [value]=\"cond.value\"\n (input)=\"cond.value = $any($event.target).value\"\n placeholder=\"Value\" />\n }\n @if (textConditions.length > 1) {\n <button\n type=\"button\"\n class=\"gp-grid-filter-remove\"\n (click)=\"removeTextCondition(i)\">\u00D7</button>\n }\n </div>\n </div>\n }\n <button type=\"button\" class=\"gp-grid-filter-add\" (click)=\"addTextCondition()\">\n + Add condition\n </button>\n }\n }\n\n <div class=\"gp-grid-filter-buttons\">\n <button type=\"button\" class=\"gp-grid-filter-btn-clear\" (click)=\"handleClear()\">\n Clear\n </button>\n <button type=\"button\" class=\"gp-grid-filter-btn-apply\" (click)=\"handleApply()\">\n Apply\n </button>\n </div>\n </div>\n</div>\n", isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
974
963
  }
975
964
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: FilterPopupComponent, decorators: [{
976
965
  type: Component,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@gp-grid/angular",
3
3
  "description": "A high-performance Angular data grid component with virtual scrolling, cell selection, sorting, filtering, and Excel-like editing",
4
- "version": "0.19.0",
4
+ "version": "0.20.0",
5
5
  "license": "Apache-2.0",
6
6
  "module": "fesm2022/gp-grid-angular.mjs",
7
7
  "typings": "types/gp-grid-angular.d.ts",
@@ -51,7 +51,7 @@
51
51
  "@angular/core": ">=18.0.0"
52
52
  },
53
53
  "dependencies": {
54
- "@gp-grid/core": "0.19.0",
54
+ "@gp-grid/core": "0.20.0",
55
55
  "tslib": "^2.8.1"
56
56
  },
57
57
  "sideEffects": false,
@@ -1,5 +1,5 @@
1
1
  import * as _gp_grid_core from '@gp-grid/core';
2
- import { HeaderRendererParams, SortDirection, VisibleColumnInfo, HeaderData, ColumnDefinition, SlotData, CellPosition, CellRange, CellValue, CellRendererParams, EditRendererParams, FillHandlePosition, DragState, ColumnFilterModel, BatchChangeSetters, DataSource, RowId, HighlightingOptions, RowLoadingOptions, CellValueChangedEvent, ParallelSortOptions, MutableDataSource } from '@gp-grid/core';
2
+ import { HeaderRendererParams, SortDirection, VisibleColumnInfo, HeaderData, ColumnDefinition, SlotData, CellPosition, CellRange, CellValue, CellRendererParams, EditRendererParams, FillHandlePosition, DragState, ColumnFilterModel, DistinctValueEntry, BatchChangeSetters, DataSource, RowId, HighlightingOptions, RowLoadingOptions, CellValueChangedEvent, ParallelSortOptions, MutableDataSource } from '@gp-grid/core';
3
3
  export { CellDataType, CellPosition, CellRange, CellRendererParams, CellValue, CellValueChangedEvent, ColumnDefinition, ColumnFilterModel, DataSource, DataSourceLoadMode, DataSourceRange, DataSourceRequest, DataSourceResponse, EditRendererParams, FilterCondition, FilterModel, GridCore, GridInstruction, HeaderRendererParams, HighlightContext, HighlightingOptions, MutableDataSource, RowCacheEviction, RowCacheOptions, RowId, RowLoadingMode, RowLoadingOptions, SortDirection, SortModel, createClientDataSource, createDataSourceFromArray, createMutableClientDataSource, createServerDataSource } from '@gp-grid/core';
4
4
  import * as _angular_core from '@angular/core';
5
5
  import { TemplateRef, ElementRef, AfterViewInit, OnDestroy, Signal, OnInit, InjectionToken, Provider } from '@angular/core';
@@ -156,10 +156,6 @@ interface NumberConditionState {
156
156
  nextOperator: 'and' | 'or';
157
157
  }
158
158
  type FilterMode = 'values' | 'condition';
159
- interface FilterEntry {
160
- key: string;
161
- label: string;
162
- }
163
159
 
164
160
  declare class FilterPopupComponent implements AfterViewInit, OnDestroy {
165
161
  popupEl: ElementRef<HTMLDivElement>;
@@ -179,7 +175,7 @@ declare class FilterPopupComponent implements AfterViewInit, OnDestroy {
179
175
  positioned: _angular_core.WritableSignal<boolean>;
180
176
  filterMode: FilterMode;
181
177
  searchText: string;
182
- selectedValues: Set<string>;
178
+ selectedLabels: Set<string>;
183
179
  includeBlanks: boolean;
184
180
  textConditions: TextConditionState[];
185
181
  numberConditions: NumberConditionState[];
@@ -199,10 +195,10 @@ declare class FilterPopupComponent implements AfterViewInit, OnDestroy {
199
195
  onEscape(): void;
200
196
  isNumberColumn(): boolean;
201
197
  showValuesMode(): boolean;
202
- uniqueEntries(): FilterEntry[];
203
- uniqueValues(): string[];
204
- filteredUniqueEntries(): FilterEntry[];
205
- toggleValue(val: string, checked: boolean): void;
198
+ uniqueEntries(): DistinctValueEntry[];
199
+ hasBlanks(): boolean;
200
+ filteredUniqueEntries(): DistinctValueEntry[];
201
+ toggleValue(label: string, checked: boolean): void;
206
202
  selectAll(): void;
207
203
  deselectAll(): void;
208
204
  onTextOperatorChange(index: number, value: string): void;