@visactor/vtable-plugins 1.22.4-alpha.1 → 1.22.4-alpha.3

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.
@@ -10075,6 +10075,7 @@ ${recordsStr}
10075
10075
  engine;
10076
10076
  listeners = [];
10077
10077
  table;
10078
+ filterMenuStates = new Map();
10078
10079
  constructor(table, engine) {
10079
10080
  this.state = {
10080
10081
  filters: new Map()
@@ -10160,6 +10161,55 @@ ${recordsStr}
10160
10161
  ];
10161
10162
  return shouldApplyActions.includes(action.type) || action.payload.enable;
10162
10163
  }
10164
+ initializeFilterMenuState(fieldId, candidateValues, displayToRawMap) {
10165
+ this.filterMenuStates.set(fieldId, {
10166
+ stableCandidateValues: candidateValues,
10167
+ currentSearchKeyword: '',
10168
+ displayToRawMap: displayToRawMap
10169
+ });
10170
+ }
10171
+ getStableCandidateValues(fieldId) {
10172
+ return this.filterMenuStates.get(fieldId)?.stableCandidateValues || [];
10173
+ }
10174
+ updateSearchKeyword(fieldId, keyword) {
10175
+ const menuState = this.filterMenuStates.get(fieldId);
10176
+ if (!menuState) {
10177
+ return;
10178
+ }
10179
+ menuState.currentSearchKeyword = keyword;
10180
+ }
10181
+ getVisibleSelectedValues(fieldId) {
10182
+ const menuState = this.filterMenuStates.get(fieldId);
10183
+ const filter = this.getFilterState(fieldId);
10184
+ if (!menuState || !filter?.values) {
10185
+ return new Set();
10186
+ }
10187
+ const allSelectedValues = new Set(filter.values);
10188
+ const keyword = menuState.currentSearchKeyword;
10189
+ const filterKeywords = keyword
10190
+ .toUpperCase()
10191
+ .split(' ')
10192
+ .filter(s => s);
10193
+ if (filterKeywords.length === 0) {
10194
+ return allSelectedValues;
10195
+ }
10196
+ const visibleSelected = new Set();
10197
+ for (const candidate of menuState.stableCandidateValues) {
10198
+ const displayValue = candidate.value;
10199
+ const txtValue = String(displayValue).toUpperCase();
10200
+ const match = filterKeywords.some(keyword => txtValue.includes(keyword));
10201
+ if (match) {
10202
+ const rawValue = menuState.displayToRawMap ? menuState.displayToRawMap.get(displayValue) : displayValue;
10203
+ if (allSelectedValues.has(rawValue)) {
10204
+ visibleSelected.add(rawValue);
10205
+ }
10206
+ }
10207
+ }
10208
+ return visibleSelected;
10209
+ }
10210
+ getCurrentSearchKeyword(fieldId) {
10211
+ return this.filterMenuStates.get(fieldId)?.currentSearchKeyword || '';
10212
+ }
10163
10213
  }
10164
10214
 
10165
10215
  const filterStyles = {
@@ -10339,9 +10389,9 @@ ${recordsStr}
10339
10389
  this.collectUniqueColumnValues(fieldId);
10340
10390
  }
10341
10391
  collectUniqueColumnValues(fieldId) {
10342
- const isEnable = this.filterStateManager.getFilterState(fieldId)?.enable;
10343
- const displayValueMap = new Map();
10344
- const rawToDisplayMap = new Map();
10392
+ if (this.uniqueKeys.has(fieldId)) {
10393
+ return;
10394
+ }
10345
10395
  const displayToRawMap = new Map();
10346
10396
  let targetCol = -1;
10347
10397
  for (let col = 0; col < this.table.colCount; col++) {
@@ -10358,82 +10408,106 @@ ${recordsStr}
10358
10408
  break;
10359
10409
  }
10360
10410
  }
10361
- if (isEnable) {
10362
- const records = this.table.internalProps.records;
10363
- const recordsLength = records.length;
10364
- for (let i = 0; i < recordsLength; i++) {
10365
- let rawValue;
10366
- let displayValue;
10367
- if (targetCol !== -1) {
10368
- const row = this.table.columnHeaderLevelCount + i;
10369
- const currentRecord = records[i];
10370
- rawValue = currentRecord[fieldId];
10371
- const bodyInfo = this.table.internalProps.layoutMap.getBody(targetCol, row);
10372
- if (bodyInfo &&
10373
- 'fieldFormat' in bodyInfo &&
10374
- bodyInfo.fieldFormat &&
10375
- typeof bodyInfo.fieldFormat === 'function') {
10376
- displayValue = bodyInfo.fieldFormat({ [fieldId]: rawValue });
10377
- }
10378
- else {
10379
- displayValue = rawValue;
10380
- }
10381
- }
10382
- if (rawValue !== undefined && rawValue !== null) {
10383
- displayValueMap.set(displayValue, (displayValueMap.get(displayValue) || 0) + 1);
10384
- rawToDisplayMap.set(rawValue, displayValue);
10385
- displayToRawMap.set(displayValue, rawValue);
10386
- }
10387
- }
10388
- }
10389
- else {
10390
- const dataSource = this.table.internalProps.dataSource;
10391
- const currentLength = dataSource.length;
10392
- for (let i = 0; i < currentLength; i++) {
10393
- let rawValue;
10394
- let displayValue;
10395
- if (targetCol !== -1) {
10396
- const row = this.table.columnHeaderLevelCount + i;
10397
- if (row < this.table.rowCount) {
10398
- rawValue = this.table.getCellOriginValue(targetCol, row);
10399
- displayValue = this.table.getCellValue(targetCol, row);
10400
- }
10411
+ const records = this.table.internalProps.records;
10412
+ const recordsLength = records.length;
10413
+ for (let i = 0; i < recordsLength; i++) {
10414
+ let rawValue;
10415
+ let displayValue;
10416
+ if (targetCol !== -1) {
10417
+ const row = this.table.columnHeaderLevelCount + i;
10418
+ const currentRecord = records[i];
10419
+ rawValue = currentRecord[fieldId];
10420
+ const bodyInfo = this.table.internalProps.layoutMap.getBody(targetCol, row);
10421
+ if (bodyInfo &&
10422
+ 'fieldFormat' in bodyInfo &&
10423
+ bodyInfo.fieldFormat &&
10424
+ typeof bodyInfo.fieldFormat === 'function') {
10425
+ displayValue = bodyInfo.fieldFormat({ [fieldId]: rawValue });
10401
10426
  }
10402
10427
  else {
10403
- rawValue = this.table.getFieldData(String(fieldId), targetCol !== -1 ? targetCol : 0, this.table.columnHeaderLevelCount + i);
10404
10428
  displayValue = rawValue;
10405
10429
  }
10406
- if (rawValue !== undefined && rawValue !== null) {
10407
- displayValueMap.set(displayValue, (displayValueMap.get(displayValue) || 0) + 1);
10408
- rawToDisplayMap.set(rawValue, displayValue);
10409
- displayToRawMap.set(displayValue, rawValue);
10410
- }
10430
+ }
10431
+ else {
10432
+ rawValue = records[i][fieldId];
10433
+ displayValue = rawValue;
10434
+ }
10435
+ if (rawValue !== undefined && rawValue !== null && !displayToRawMap.has(displayValue)) {
10436
+ displayToRawMap.set(displayValue, rawValue);
10411
10437
  }
10412
10438
  }
10413
10439
  this.displayToRawValueMap.set(fieldId, displayToRawMap);
10414
- const uniqueValues = Array.from(displayValueMap.entries()).map(([displayValue, count]) => ({
10440
+ const uniqueValues = Array.from(displayToRawMap.entries()).map(([displayValue, rawValue]) => ({
10415
10441
  value: displayValue,
10416
- count,
10417
- rawValue: displayToRawMap.get(displayValue)
10442
+ count: 0,
10443
+ rawValue: rawValue
10418
10444
  }));
10419
10445
  this.uniqueKeys.set(fieldId, uniqueValues);
10420
10446
  }
10447
+ updateCandidateCounts(fieldId) {
10448
+ const uniqueValues = this.uniqueKeys.get(fieldId);
10449
+ if (!uniqueValues) {
10450
+ return;
10451
+ }
10452
+ const filter = this.filterStateManager.getFilterState(fieldId);
10453
+ const isFiltered = filter?.enable;
10454
+ const dataSource = isFiltered
10455
+ ? this.table.internalProps.records
10456
+ : this.table.internalProps.dataSource;
10457
+ let targetCol = -1;
10458
+ for (let col = 0; col < this.table.colCount; col++) {
10459
+ for (let row = this.table.columnHeaderLevelCount; row < this.table.rowCount; row++) {
10460
+ if (!this.table.internalProps.layoutMap.isHeader(col, row)) {
10461
+ const bodyInfo = this.table.internalProps.layoutMap.getBody(col, row);
10462
+ if (bodyInfo && bodyInfo.field === fieldId) {
10463
+ targetCol = col;
10464
+ break;
10465
+ }
10466
+ }
10467
+ }
10468
+ if (targetCol !== -1) {
10469
+ break;
10470
+ }
10471
+ }
10472
+ const dataLength = dataSource.length;
10473
+ const countMap = new Map();
10474
+ for (let i = 0; i < dataLength; i++) {
10475
+ let displayValue;
10476
+ if (targetCol !== -1) {
10477
+ const row = this.table.columnHeaderLevelCount + i;
10478
+ if (row < this.table.rowCount) {
10479
+ displayValue = this.table.getCellValue(targetCol, row);
10480
+ }
10481
+ }
10482
+ else {
10483
+ displayValue = this.table.getFieldData(String(fieldId), targetCol !== -1 ? targetCol : 0, this.table.columnHeaderLevelCount + i);
10484
+ }
10485
+ if (displayValue !== undefined && displayValue !== null) {
10486
+ countMap.set(displayValue, (countMap.get(displayValue) || 0) + 1);
10487
+ }
10488
+ }
10489
+ uniqueValues.forEach(item => {
10490
+ item.count = countMap.get(item.value) || 0;
10491
+ });
10492
+ }
10421
10493
  onValueSelect(fieldId, displayValue, selected) {
10422
10494
  const displayToRawMap = this.displayToRawValueMap.get(fieldId);
10423
10495
  const rawValue = displayToRawMap ? displayToRawMap.get(displayValue) : displayValue;
10424
10496
  const filter = this.filterStateManager.getFilterState(fieldId);
10497
+ let updatedValues;
10425
10498
  if (!filter) {
10499
+ updatedValues = selected ? [rawValue] : [];
10426
10500
  this.filterStateManager.dispatch({
10427
10501
  type: exports.FilterActionType.ADD_FILTER,
10428
10502
  payload: {
10429
10503
  field: fieldId,
10430
10504
  type: 'byValue',
10431
- values: [rawValue]
10505
+ values: updatedValues
10432
10506
  }
10433
10507
  });
10434
10508
  }
10435
10509
  else {
10436
- const updatedValues = selected
10510
+ updatedValues = selected
10437
10511
  ? [...(filter.values || []), rawValue]
10438
10512
  : (filter.values || []).filter(v => v !== rawValue);
10439
10513
  this.filterStateManager.dispatch({
@@ -10445,22 +10519,45 @@ ${recordsStr}
10445
10519
  });
10446
10520
  }
10447
10521
  }
10522
+ isValueVisible(displayValue, keyword) {
10523
+ if (!keyword) {
10524
+ return true;
10525
+ }
10526
+ const filterKeywords = keyword
10527
+ .toUpperCase()
10528
+ .split(' ')
10529
+ .filter(s => s);
10530
+ const txtValue = String(displayValue).toUpperCase();
10531
+ return filterKeywords.some(keyword => txtValue.includes(keyword));
10532
+ }
10448
10533
  toggleSelectAll(fieldId, selected) {
10534
+ const currentKeyword = this.filterStateManager.getCurrentSearchKeyword(fieldId);
10535
+ const stableCandidates = this.filterStateManager.getStableCandidateValues(fieldId);
10536
+ const displayToRawMap = this.displayToRawValueMap.get(fieldId);
10537
+ const visibleRawValues = stableCandidates
10538
+ .filter(candidate => this.isValueVisible(candidate.value, currentKeyword))
10539
+ .map(candidate => (displayToRawMap ? displayToRawMap.get(candidate.value) : candidate.value));
10449
10540
  const filter = this.filterStateManager.getFilterState(fieldId);
10450
- const rawValuesToUpdate = selected ? this.uniqueKeys.get(fieldId)?.map(item => item.rawValue) || [] : [];
10541
+ const currentValues = new Set(filter?.values || []);
10542
+ let updatedValues;
10543
+ if (selected) {
10544
+ updatedValues = Array.from(new Set([...currentValues, ...visibleRawValues]));
10545
+ }
10546
+ else {
10547
+ updatedValues = Array.from(currentValues).filter(value => !visibleRawValues.includes(value));
10548
+ }
10451
10549
  if (!filter) {
10452
10550
  this.filterStateManager.dispatch({
10453
10551
  type: exports.FilterActionType.ADD_FILTER,
10454
10552
  payload: {
10455
10553
  field: fieldId,
10456
10554
  type: 'byValue',
10457
- values: rawValuesToUpdate,
10555
+ values: updatedValues,
10458
10556
  enable: true
10459
10557
  }
10460
10558
  });
10461
10559
  }
10462
10560
  else {
10463
- const updatedValues = selected ? rawValuesToUpdate : [];
10464
10561
  this.filterStateManager.dispatch({
10465
10562
  type: exports.FilterActionType.UPDATE_FILTER,
10466
10563
  payload: {
@@ -10471,79 +10568,88 @@ ${recordsStr}
10471
10568
  }
10472
10569
  }
10473
10570
  onSearch(fieldId, value) {
10571
+ this.filterStateManager.updateSearchKeyword(fieldId, value);
10572
+ const items = this.valueFilterOptionList.get(fieldId);
10474
10573
  const filterKeywords = value
10475
10574
  .toUpperCase()
10476
10575
  .split(' ')
10477
10576
  .filter(s => s);
10478
- const items = this.valueFilterOptionList.get(fieldId);
10479
10577
  for (const item of items) {
10480
10578
  const txtValue = item.id.toUpperCase() || '';
10481
10579
  const match = filterKeywords.some(keyword => txtValue.includes(keyword));
10482
- item.itemContainer.style.display = filterKeywords.length === 0 || match ? 'flex' : 'none';
10580
+ const isVisible = filterKeywords.length === 0 || match;
10581
+ item.itemContainer.style.display = isVisible ? 'flex' : 'none';
10483
10582
  }
10484
10583
  }
10485
10584
  initFilterStateFromTableData(fieldId) {
10486
10585
  const filter = this.filterStateManager.getFilterState(fieldId);
10487
- const selectedRawValues = new Set();
10488
- const displayToRawMap = this.displayToRawValueMap.get(fieldId);
10489
- let targetCol = -1;
10490
- for (let col = 0; col < this.table.colCount; col++) {
10491
- for (let row = this.table.columnHeaderLevelCount; row < this.table.rowCount; row++) {
10492
- if (!this.table.internalProps.layoutMap.isHeader(col, row)) {
10493
- const bodyInfo = this.table.internalProps.layoutMap.getBody(col, row);
10494
- if (bodyInfo && bodyInfo.field === fieldId) {
10495
- targetCol = col;
10496
- break;
10586
+ const isEnable = filter?.enable;
10587
+ if (isEnable) {
10588
+ const selectedRawValues = new Set();
10589
+ const displayToRawMap = this.displayToRawValueMap.get(fieldId);
10590
+ let targetCol = -1;
10591
+ for (let col = 0; col < this.table.colCount; col++) {
10592
+ for (let row = this.table.columnHeaderLevelCount; row < this.table.rowCount; row++) {
10593
+ if (!this.table.internalProps.layoutMap.isHeader(col, row)) {
10594
+ const bodyInfo = this.table.internalProps.layoutMap.getBody(col, row);
10595
+ if (bodyInfo && bodyInfo.field === fieldId) {
10596
+ targetCol = col;
10597
+ break;
10598
+ }
10497
10599
  }
10498
10600
  }
10601
+ if (targetCol !== -1) {
10602
+ break;
10603
+ }
10499
10604
  }
10500
- if (targetCol !== -1) {
10501
- break;
10502
- }
10503
- }
10504
- const dataSource = this.table.internalProps.dataSource;
10505
- const currentLength = dataSource.length;
10506
- for (let i = 0; i < currentLength; i++) {
10507
- let displayValue;
10508
- let rawValue;
10509
- if (targetCol !== -1) {
10510
- const row = this.table.columnHeaderLevelCount + i;
10511
- if (row < this.table.rowCount) {
10512
- displayValue = this.table.getCellValue(targetCol, row);
10605
+ const dataSource = this.table.internalProps.dataSource;
10606
+ const currentLength = dataSource.length;
10607
+ for (let i = 0; i < currentLength; i++) {
10608
+ let displayValue;
10609
+ let rawValue;
10610
+ if (targetCol !== -1) {
10611
+ const row = this.table.columnHeaderLevelCount + i;
10612
+ if (row < this.table.rowCount) {
10613
+ displayValue = this.table.getCellValue(targetCol, row);
10614
+ rawValue = displayToRawMap ? displayToRawMap.get(displayValue) : displayValue;
10615
+ }
10616
+ }
10617
+ else {
10618
+ displayValue = this.table.getFieldData(String(fieldId), targetCol !== -1 ? targetCol : 0, this.table.columnHeaderLevelCount + i);
10513
10619
  rawValue = displayToRawMap ? displayToRawMap.get(displayValue) : displayValue;
10514
10620
  }
10621
+ if (rawValue !== undefined && rawValue !== null) {
10622
+ selectedRawValues.add(rawValue);
10623
+ }
10515
10624
  }
10516
- else {
10517
- displayValue = this.table.getFieldData(String(fieldId), targetCol !== -1 ? targetCol : 0, this.table.columnHeaderLevelCount + i);
10518
- rawValue = displayToRawMap ? displayToRawMap.get(displayValue) : displayValue;
10519
- }
10520
- if (rawValue !== undefined && rawValue !== null) {
10521
- selectedRawValues.add(rawValue);
10625
+ const hasChanged = !arrayEqual(filter.values, Array.from(selectedRawValues));
10626
+ if (hasChanged) {
10627
+ this.filterStateManager.dispatch({
10628
+ type: exports.FilterActionType.UPDATE_FILTER,
10629
+ payload: {
10630
+ field: fieldId,
10631
+ values: Array.from(selectedRawValues)
10632
+ }
10633
+ });
10522
10634
  }
10523
10635
  }
10524
- const hasChanged = !filter || !arrayEqual(filter.values, Array.from(selectedRawValues));
10525
- if (!hasChanged) {
10526
- return;
10527
- }
10528
- if (filter) {
10529
- this.filterStateManager.dispatch({
10530
- type: exports.FilterActionType.UPDATE_FILTER,
10531
- payload: {
10532
- field: fieldId,
10533
- type: 'byValue',
10534
- values: Array.from(selectedRawValues)
10535
- }
10536
- });
10537
- }
10538
10636
  else {
10539
- this.filterStateManager.dispatch({
10540
- type: exports.FilterActionType.ADD_FILTER,
10541
- payload: {
10542
- field: fieldId,
10543
- type: 'byValue',
10544
- values: Array.from(selectedRawValues)
10545
- }
10546
- });
10637
+ if (!filter) {
10638
+ const availableRawValues = this.uniqueKeys
10639
+ .get(fieldId)
10640
+ ?.filter(item => item.count > 0)
10641
+ ?.map(item => item.rawValue)
10642
+ .filter(v => v !== undefined && v !== null) || [];
10643
+ this.filterStateManager.dispatch({
10644
+ type: exports.FilterActionType.ADD_FILTER,
10645
+ payload: {
10646
+ field: fieldId,
10647
+ type: 'byValue',
10648
+ values: availableRawValues,
10649
+ enable: false
10650
+ }
10651
+ });
10652
+ }
10547
10653
  }
10548
10654
  }
10549
10655
  syncCheckboxesWithFilterState(filter) {
@@ -10558,8 +10664,7 @@ ${recordsStr}
10558
10664
  const rawValue = displayToRawMap ? displayToRawMap.get(displayValue) : displayValue;
10559
10665
  optionDom.checkbox.checked = selectedRawValues.some(v => v === rawValue);
10560
10666
  const count = this.uniqueKeys.get(filter.field)?.find(key => String(key.value) === optionDom.id)?.count || 0;
10561
- optionDom.countSpan.textContent = String(count);
10562
- optionDom.itemContainer.style.display = count === 0 ? 'none' : 'flex';
10667
+ optionDom.checkbox.disabled = count === 0;
10563
10668
  });
10564
10669
  }
10565
10670
  syncSelectAllWithFilterState(filter) {
@@ -10587,14 +10692,14 @@ ${recordsStr}
10587
10692
  }
10588
10693
  }
10589
10694
  applyFilter(fieldId = this.selectedField) {
10590
- const selectedKeys = this.filterStateManager.getFilterState(fieldId)?.values || [];
10591
- if (selectedKeys.length > 0 && selectedKeys.length < this.uniqueKeys.get(fieldId)?.length) {
10695
+ const visibleSelectedKeys = Array.from(this.filterStateManager.getVisibleSelectedValues(fieldId));
10696
+ if (visibleSelectedKeys.length > 0 && visibleSelectedKeys.length < this.uniqueKeys.get(fieldId)?.length) {
10592
10697
  this.filterStateManager.dispatch({
10593
10698
  type: exports.FilterActionType.APPLY_FILTERS,
10594
10699
  payload: {
10595
10700
  field: fieldId,
10596
10701
  type: 'byValue',
10597
- values: selectedKeys,
10702
+ values: visibleSelectedKeys,
10598
10703
  enable: true
10599
10704
  }
10600
10705
  });
@@ -10655,12 +10760,14 @@ ${recordsStr}
10655
10760
  this.uniqueKeys.get(field)?.forEach(({ value, count, rawValue }) => {
10656
10761
  const itemDiv = document.createElement('div');
10657
10762
  applyStyles(itemDiv, this.styles.optionItem);
10763
+ itemDiv.style.display = 'flex';
10658
10764
  const label = document.createElement('label');
10659
10765
  applyStyles(label, this.styles.optionLabel);
10660
10766
  const checkbox = document.createElement('input');
10661
10767
  checkbox.type = 'checkbox';
10662
10768
  checkbox.value = String(value);
10663
10769
  checkbox.checked = selectedRawValueSet.has(rawValue);
10770
+ checkbox.disabled = count === 0;
10664
10771
  applyStyles(checkbox, this.styles.checkbox);
10665
10772
  const countSpan = document.createElement('span');
10666
10773
  countSpan.textContent = String(count);
@@ -10705,7 +10812,17 @@ ${recordsStr}
10705
10812
  });
10706
10813
  }
10707
10814
  show() {
10815
+ this.collectUniqueColumnValues(this.selectedField);
10816
+ this.updateCandidateCounts(this.selectedField);
10708
10817
  this.initFilterStateFromTableData(this.selectedField);
10818
+ const uniqueValues = this.uniqueKeys.get(this.selectedField);
10819
+ const displayToRawMap = this.displayToRawValueMap.get(this.selectedField);
10820
+ if (uniqueValues && displayToRawMap) {
10821
+ this.filterStateManager.initializeFilterMenuState(this.selectedField, uniqueValues, displayToRawMap);
10822
+ }
10823
+ if (this.filterByValueSearchInput) {
10824
+ this.filterByValueSearchInput.value = '';
10825
+ }
10709
10826
  this.renderFilterOptions(this.selectedField);
10710
10827
  this.filterByValuePanel.style.display = 'block';
10711
10828
  }
@@ -11035,6 +11152,8 @@ ${recordsStr}
11035
11152
  filterModes = [];
11036
11153
  filterMenu;
11037
11154
  filterMenuWidth;
11155
+ currentCol;
11156
+ currentRow;
11038
11157
  filterTabByValue;
11039
11158
  filterTabByCondition;
11040
11159
  clearFilterOptionLink;
@@ -11047,6 +11166,11 @@ ${recordsStr}
11047
11166
  this.valueFilter = new ValueFilter(this.table, this.filterStateManager, this.styles);
11048
11167
  this.conditionFilter = new ConditionFilter(this.table, this.filterStateManager, this.styles);
11049
11168
  this.filterMenuWidth = 300;
11169
+ this.filterStateManager.subscribe(state => {
11170
+ if (this.isVisible && this.selectedField !== null) {
11171
+ this.updateClearFilterButtonState(this.selectedField);
11172
+ }
11173
+ });
11050
11174
  }
11051
11175
  onTabSwitch(tab) {
11052
11176
  this.activeTab = tab;
@@ -11089,6 +11213,14 @@ ${recordsStr}
11089
11213
  }
11090
11214
  this.hide();
11091
11215
  }
11216
+ updateClearFilterButtonState(field) {
11217
+ const currentFilter = this.filterStateManager.getFilterState(field);
11218
+ const hasActiveFilter = currentFilter && currentFilter.enable;
11219
+ this.clearFilterOptionLink.style.display = 'inline';
11220
+ this.clearFilterOptionLink.style.opacity = hasActiveFilter ? '1' : '0.5';
11221
+ this.clearFilterOptionLink.style.pointerEvents = hasActiveFilter ? 'auto' : 'none';
11222
+ this.clearFilterOptionLink.style.cursor = hasActiveFilter ? 'pointer' : 'not-allowed';
11223
+ }
11092
11224
  render(container) {
11093
11225
  this.filterMenu = document.createElement('div');
11094
11226
  applyStyles(this.filterMenu, this.styles.filterMenu);
@@ -11148,20 +11280,24 @@ ${recordsStr}
11148
11280
  e.stopPropagation();
11149
11281
  });
11150
11282
  }
11151
- show(col, row, filterModes) {
11152
- this.filterModes = filterModes;
11153
- if (!this.filterModes.includes('byValue')) {
11154
- this.filterTabByValue.style.display = 'none';
11155
- setTimeout(() => this.onTabSwitch('byCondition'), 0);
11283
+ adjustMenuPosition(col, row, providedLeft, providedTop) {
11284
+ if (typeof providedLeft === 'number' && typeof providedTop === 'number') {
11285
+ this.filterMenu.style.display = this.isVisible ? 'block' : 'none';
11286
+ this.filterMenu.style.left = `${providedLeft}px`;
11287
+ this.filterMenu.style.top = `${providedTop}px`;
11288
+ return;
11156
11289
  }
11157
- else if (!this.filterModes.includes('byCondition')) {
11158
- this.filterTabByCondition.style.display = 'none';
11159
- setTimeout(() => this.onTabSwitch('byValue'), 0);
11290
+ const effectiveCol = typeof col === 'number' ? col : this.currentCol;
11291
+ const effectiveRow = typeof row === 'number' ? row : this.currentRow;
11292
+ if (typeof effectiveCol !== 'number' || typeof effectiveRow !== 'number') {
11293
+ return;
11160
11294
  }
11295
+ this.currentCol = effectiveCol;
11296
+ this.currentRow = effectiveRow;
11161
11297
  let left = 0;
11162
11298
  let top = 0;
11163
11299
  const canvasBounds = this.table.canvas.getBoundingClientRect();
11164
- const cell = this.table.getMergeCellRect(col, row);
11300
+ const cell = this.table.getCellRelativeRect(effectiveCol, effectiveRow);
11165
11301
  if (cell.right < this.filterMenuWidth) {
11166
11302
  left = cell.left + canvasBounds.left;
11167
11303
  top = cell.bottom + canvasBounds.top;
@@ -11170,9 +11306,22 @@ ${recordsStr}
11170
11306
  left = cell.right + canvasBounds.left - this.filterMenuWidth;
11171
11307
  top = cell.bottom + canvasBounds.top;
11172
11308
  }
11173
- this.filterMenu.style.display = 'block';
11309
+ this.filterMenu.style.display = this.isVisible ? 'block' : 'none';
11174
11310
  this.filterMenu.style.left = `${left}px`;
11175
11311
  this.filterMenu.style.top = `${top}px`;
11312
+ }
11313
+ show(col, row, filterModes) {
11314
+ this.filterModes = filterModes;
11315
+ if (!this.filterModes.includes('byValue')) {
11316
+ this.filterTabByValue.style.display = 'none';
11317
+ this.onTabSwitch('byCondition');
11318
+ }
11319
+ else if (!this.filterModes.includes('byCondition')) {
11320
+ this.filterTabByCondition.style.display = 'none';
11321
+ this.onTabSwitch('byValue');
11322
+ }
11323
+ this.adjustMenuPosition(col, row);
11324
+ this.filterMenu.style.display = 'block';
11176
11325
  const field = this.table.internalProps.layoutMap.getHeaderField(col, row);
11177
11326
  this.updateSelectedField(field);
11178
11327
  const currentFilter = this.filterStateManager.getFilterState(field);
@@ -11182,6 +11331,7 @@ ${recordsStr}
11182
11331
  else {
11183
11332
  this.onTabSwitch('byValue');
11184
11333
  }
11334
+ this.updateClearFilterButtonState(field);
11185
11335
  setTimeout(() => {
11186
11336
  this.isVisible = true;
11187
11337
  }, 0);
@@ -20661,9 +20811,15 @@ ${recordsStr}
20661
20811
  class FilterPlugin {
20662
20812
  id = `filter`;
20663
20813
  name = 'Filter';
20664
- runTime = [VTable.TABLE_EVENT_TYPE.BEFORE_INIT, VTable.TABLE_EVENT_TYPE.BEFORE_UPDATE_OPTION, VTable.TABLE_EVENT_TYPE.ICON_CLICK];
20814
+ runTime = [
20815
+ VTable.TABLE_EVENT_TYPE.BEFORE_INIT,
20816
+ VTable.TABLE_EVENT_TYPE.BEFORE_UPDATE_OPTION,
20817
+ VTable.TABLE_EVENT_TYPE.ICON_CLICK,
20818
+ VTable.TABLE_EVENT_TYPE.SCROLL
20819
+ ];
20665
20820
  pluginOptions;
20666
20821
  table;
20822
+ columns;
20667
20823
  filterEngine;
20668
20824
  filterStateManager;
20669
20825
  filterToolbar;
@@ -20702,11 +20858,12 @@ ${recordsStr}
20702
20858
  this.filterEngine = new FilterEngine();
20703
20859
  this.filterStateManager = new FilterStateManager(this.table, this.filterEngine);
20704
20860
  this.filterToolbar = new FilterToolbar(this.table, this.filterStateManager, this.pluginOptions.styles);
20861
+ this.columns = eventArgs.options.columns;
20705
20862
  this.filterToolbar.render(document.body);
20706
- this.updateFilterIcons(eventArgs.options);
20863
+ this.updateFilterIcons(this.columns);
20707
20864
  this.filterStateManager.subscribe(() => {
20708
- this.updateFilterIcons(eventArgs.options);
20709
- this.table.updateColumns(eventArgs.options.columns, {
20865
+ this.updateFilterIcons(this.columns);
20866
+ this.table.updateColumns(this.columns, {
20710
20867
  clearRowHeightCache: false
20711
20868
  });
20712
20869
  });
@@ -20716,6 +20873,7 @@ ${recordsStr}
20716
20873
  ...this.pluginOptions,
20717
20874
  ...eventArgs.options.plugins.find(plugin => plugin.id === this.id).pluginOptions
20718
20875
  };
20876
+ this.columns = eventArgs.options.columns;
20719
20877
  this.handleOptionUpdate(eventArgs.options);
20720
20878
  }
20721
20879
  else if ((runtime === VTable.TABLE_EVENT_TYPE.ICON_CLICK && eventArgs.name === 'filter-icon') ||
@@ -20733,6 +20891,11 @@ ${recordsStr}
20733
20891
  this.filterToolbar.show(col, row, this.pluginOptions.filterModes);
20734
20892
  }
20735
20893
  }
20894
+ else if (runtime === VTable.TABLE_EVENT_TYPE.SCROLL) {
20895
+ if (eventArgs.scrollDirection === 'horizontal') {
20896
+ this.filterToolbar.adjustMenuPosition();
20897
+ }
20898
+ }
20736
20899
  }
20737
20900
  update() {
20738
20901
  if (this.filterStateManager) {
@@ -20744,7 +20907,7 @@ ${recordsStr}
20744
20907
  if (this.filterStateManager && currentActiveFields.length > 0) {
20745
20908
  this.validateFilterStatesAfterUpdate(options, currentActiveFields);
20746
20909
  }
20747
- this.updateFilterIcons(options);
20910
+ this.updateFilterIcons(options.columns);
20748
20911
  }
20749
20912
  reapplyActiveFilters() {
20750
20913
  this.filterStateManager.reapplyCurrentFilters();
@@ -20765,13 +20928,13 @@ ${recordsStr}
20765
20928
  });
20766
20929
  });
20767
20930
  }
20768
- updateFilterIcons(options) {
20931
+ updateFilterIcons(columns = []) {
20769
20932
  const filterIcon = this.pluginOptions.filterIcon;
20770
20933
  const filteringIcon = this.pluginOptions.filteringIcon;
20771
20934
  const isIconEqual = (a, b) => a === b || (a && b && typeof a === 'object' && typeof b === 'object' && a.name === b.name);
20772
20935
  const toIconList = icons => (icons ? (Array.isArray(icons) ? icons : [icons]) : []);
20773
20936
  const compactIcons = list => (list.length === 0 ? undefined : list.length === 1 ? list[0] : list);
20774
- options.columns.forEach(column => {
20937
+ columns.forEach(column => {
20775
20938
  const shouldShow = this.shouldEnableFilterForColumn(column.field, column);
20776
20939
  const isFiltering = !!this.filterStateManager.getFilterState(column.field)?.enable;
20777
20940
  let icons = toIconList(column.headerIcon);
@@ -20832,14 +20995,16 @@ ${recordsStr}
20832
20995
  };
20833
20996
  }
20834
20997
  setFilterState(filterState) {
20835
- if (!this.filterStateManager || !filterState || !filterState.filters) {
20998
+ if (!this.filterStateManager) {
20836
20999
  return;
20837
21000
  }
20838
21001
  this.filterStateManager.dispatch({
20839
21002
  type: exports.FilterActionType.CLEAR_ALL_FILTERS,
20840
21003
  payload: {}
20841
21004
  });
20842
- this.table.columns;
21005
+ if (!filterState || !filterState.filters) {
21006
+ return;
21007
+ }
20843
21008
  Object.entries(filterState.filters).forEach(([, config]) => {
20844
21009
  if (config.enable) {
20845
21010
  this.filterStateManager.dispatch({