ag-grid-url-sync 0.2.1 → 0.2.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.
package/LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ Copyright 2025 NearForm
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md CHANGED
@@ -2,11 +2,13 @@
2
2
 
3
3
  # AG Grid URL Sync
4
4
 
5
- A lightweight TypeScript library for synchronizing AG Grid text filters with URL parameters, enabling shareable filter states through clean, human-readable URLs.
5
+ A lightweight TypeScript library for synchronizing AG Grid text and number filters with URL parameters, enabling shareable filter states through clean, human-readable URLs.
6
6
 
7
7
  ## Features
8
8
 
9
9
  - 🔍 **Complete text filter support** - All 8 AG Grid text operations (contains, equals, not contains, not equal, starts with, ends with, blank, not blank)
10
+ - 🔢 **Complete number filter support** - All 9 AG Grid number operations (equals, not equal, greater than, greater than or equal, less than, less than or equal, in range, blank, not blank)
11
+ - 🔢 **Complete date filter support** - All 9 AG Grid date operations (equals, not equal, before, before or equal, after, after or equal, in range, blank, not blank) with strict ISO date validation (YYYY-MM-DD)
10
12
  - 🔗 Manual URL generation for sharing filter states
11
13
  - ↔️ Bidirectional sync between grid and URL
12
14
  - 🛠️ Framework agnostic - works with any AG Grid setup
@@ -21,7 +23,9 @@ A lightweight TypeScript library for synchronizing AG Grid text filters with URL
21
23
 
22
24
  ## Supported Filter Types
23
25
 
24
- Supports **all AG Grid text filter operations** (8 total) with clean, human-readable URL parameters:
26
+ Supports **all AG Grid text, number, and date filter operations** (26 total) with clean, human-readable URL parameters:
27
+
28
+ ### Text Filters (8 operations)
25
29
 
26
30
  | Filter Type | Operation | URL Format | Example | Description |
27
31
  | ----------- | ------------ | ---------------------------- | -------------------------- | --------------------------- |
@@ -34,41 +38,96 @@ Supports **all AG Grid text filter operations** (8 total) with clean, human-read
34
38
  | **Text** | Blank | `f_column_blank=true` | `f_optional_blank=true` | Field is empty/null |
35
39
  | **Text** | Not Blank | `f_column_notBlank=true` | `f_required_notBlank=true` | Field has any value |
36
40
 
37
- > **💡 Naming Convention Note**: URL parameters use short, clean names for better readability (`eq` for equals, `neq` for not equal). These are automatically converted to the proper AG Grid operation names (`equals`, `notEqual`) internally. This design keeps URLs concise while maintaining full compatibility with AG Grid's filter API.
41
+ ### Number Filters (9 operations)
42
+
43
+ | Filter Type | Operation | URL Format | Example | Description |
44
+ | ----------- | --------------------- | ------------------------ | ------------------------ | ----------------------------- |
45
+ | **Number** | Equals | `f_column_eq=value` | `f_age_eq=30` | Number equals value exactly |
46
+ | **Number** | Not Equal | `f_column_neq=value` | `f_salary_neq=50000` | Number does not equal value |
47
+ | **Number** | Greater Than | `f_column_gt=value` | `f_experience_gt=5` | Number is greater than value |
48
+ | **Number** | Greater Than or Equal | `f_column_gte=value` | `f_score_gte=85` | Number is ≥ value |
49
+ | **Number** | Less Than | `f_column_lt=value` | `f_budget_lt=10000` | Number is less than value |
50
+ | **Number** | Less Than or Equal | `f_column_lte=value` | `f_hours_lte=40` | Number is ≤ value |
51
+ | **Number** | In Range | `f_column_range=min,max` | `f_age_range=25,45` | Number is between min and max |
52
+ | **Number** | Blank | `f_column_blank=true` | `f_bonus_blank=true` | Field is empty/null |
53
+ | **Number** | Not Blank | `f_column_notBlank=true` | `f_salary_notBlank=true` | Field has any numeric value |
54
+
55
+ ### Date Filters (9 operations)
56
+
57
+ | Filter Type | Operation | URL Format | Example | Description |
58
+ | ----------- | --------------- | ------------------------------ | ------------------------------------------ | ----------------------------------------- |
59
+ | **Date** | Equals | `f_column_eq=YYYY-MM-DD` | `f_created_eq=2024-01-15` | Date equals value exactly |
60
+ | **Date** | Not Equal | `f_column_neq=YYYY-MM-DD` | `f_deadline_neq=2024-12-31` | Date does not equal value |
61
+ | **Date** | Before | `f_column_before=YYYY-MM-DD` | `f_deadline_before=2024-12-31` | Date is before value |
62
+ | **Date** | Before or Equal | `f_column_beforeEq=YYYY-MM-DD` | `f_archived_beforeEq=2024-12-31` | Date is before or equal to value |
63
+ | **Date** | After | `f_column_after=YYYY-MM-DD` | `f_created_after=2024-01-01` | Date is after value |
64
+ | **Date** | After or Equal | `f_column_afterEq=YYYY-MM-DD` | `f_updated_afterEq=2024-06-01` | Date is after or equal to value |
65
+ | **Date** | In Range | `f_column_daterange=start,end` | `f_period_daterange=2024-01-01,2024-12-31` | Date is between start and end (inclusive) |
66
+ | **Date** | Blank | `f_column_blank=true` | `f_deadline_blank=true` | Date field is empty/null |
67
+ | **Date** | Not Blank | `f_column_notBlank=true` | `f_created_notBlank=true` | Date field has any value |
68
+
69
+ > **🗓️ Date Format Note**: Date filters require strict ISO format (`YYYY-MM-DD`). All date values are validated for real calendar dates, including leap years and month boundaries. Invalid or non-ISO dates will be rejected.
38
70
 
39
71
  ### Filter Detection
40
72
 
41
73
  The library automatically works with:
42
74
 
43
- - Columns configured with `filter: 'agTextColumnFilter'`
75
+ - **Text columns** configured with `filter: 'agTextColumnFilter'`
76
+ - **Number columns** configured with `filter: 'agNumberColumnFilter'`
77
+ - **Auto-detection** based on `cellDataType: 'number'` configuration
44
78
  - Columns with `filter: true` (AG Grid's default text filter)
45
- - Any column where users apply text-based filters
46
- - All AG Grid text filter operations through the filter menu
79
+ - Any column where users apply text, number, or date-based filters
80
+ - All AG Grid text, number, and date filter operations through the filter menu
81
+ - **Mixed filter types** - combine text, number, and date filters in the same URL
47
82
 
48
83
  ### URL Examples
49
84
 
50
- **Simple filter:**
85
+ **Simple text filter:**
51
86
 
52
87
  ```
53
88
  https://app.com/data?f_name_contains=john
54
89
  ```
55
90
 
56
- **Multiple operations:**
91
+ **Simple number filter:**
92
+
93
+ ```
94
+ https://app.com/data?f_salary_gt=75000
95
+ ```
96
+
97
+ **Simple date filter:**
98
+
99
+ ```
100
+ https://app.com/data?f_created_eq=2024-01-15
101
+ ```
102
+
103
+ **Date range filter:**
57
104
 
58
105
  ```
59
- https://app.com/data?f_name_contains=john&f_department_eq=Engineering&f_email_startsWith=admin&f_status_neq=inactive
106
+ https://app.com/data?f_period_daterange=2024-01-01,2024-12-31
60
107
  ```
61
108
 
62
- **Complex filtering with all operations:**
109
+ **Date before/after filter:**
63
110
 
64
111
  ```
65
- https://app.com/data?f_name_contains=john&f_email_startsWith=admin&f_status_neq=inactive&f_description_endsWith=test&f_optional_blank=true&f_required_notBlank=true&f_tags_notContains=spam&f_title_eq=manager
112
+ https://app.com/data?f_deadline_before=2024-12-31&f_created_after=2024-01-01
66
113
  ```
67
114
 
68
- **Blank operations:**
115
+ **Mixed text, number, and date filters:**
69
116
 
70
117
  ```
71
- https://app.com/data?f_comments_blank=true&f_name_notBlank=true
118
+ https://app.com/data?f_name_contains=john&f_department_eq=Engineering&f_salary_gte=80000&f_age_range=25,45&f_created_after=2024-01-01
119
+ ```
120
+
121
+ **All date operations:**
122
+
123
+ ```
124
+ https://app.com/data?f_created_eq=2024-01-15&f_deadline_neq=2024-12-31&f_start_after=2024-01-01&f_end_before=2024-12-31&f_updated_afterEq=2024-06-01&f_archived_beforeEq=2024-12-31&f_period_daterange=2024-01-01,2024-12-31&f_optional_blank=true&f_required_notBlank=true
125
+ ```
126
+
127
+ **Blank operations (text, number, and date):**
128
+
129
+ ```
130
+ https://app.com/data?f_comments_blank=true&f_bonus_blank=true&f_name_notBlank=true&f_salary_notBlank=true&f_deadline_blank=true&f_created_notBlank=true
72
131
  ```
73
132
 
74
133
  ## Installation
@@ -177,13 +236,23 @@ import { createUrlSync } from 'ag-grid-url-sync'
177
236
  // Initialize with AG Grid API
178
237
  const urlSync = createUrlSync(gridApi)
179
238
 
180
- // Generate shareable URL with current filters
239
+ // Generate shareable URL with current filters (text and number)
181
240
  const shareableUrl = urlSync.generateUrl()
241
+ // Result: https://app.com/data?f_name_contains=john&f_salary_gte=80000&f_age_range=25,45
182
242
 
183
- // Apply filters from URL
184
- urlSync.applyFromUrl()
243
+ // Apply filters from URL (automatically detects text vs number columns)
244
+ urlSync.applyFromUrl(
245
+ 'https://app.com/data?f_name_contains=manager&f_salary_gt=75000'
246
+ )
185
247
 
186
- // Clear all text filters
248
+ // Apply specific filter programmatically
249
+ urlSync.applyFilters({
250
+ name: { filterType: 'text', type: 'contains', filter: 'john' },
251
+ salary: { filterType: 'number', type: 'greaterThan', filter: 75000 },
252
+ age: { filterType: 'number', type: 'inRange', filter: 25, filterTo: 45 }
253
+ })
254
+
255
+ // Clear all text and number filters
187
256
  urlSync.clearFilters()
188
257
  ```
189
258
 
@@ -241,10 +310,32 @@ function GridComponent() {
241
310
  <AgGridReact
242
311
  onGridReady={params => setGridApi(params.api)}
243
312
  rowData={rowData}
244
- columnDefs={columnDefs}
313
+ columnDefs={[
314
+ {
315
+ field: 'name',
316
+ filter: 'agTextColumnFilter',
317
+ floatingFilter: true
318
+ },
319
+ {
320
+ field: 'email',
321
+ filter: 'agTextColumnFilter',
322
+ floatingFilter: true
323
+ },
324
+ {
325
+ field: 'salary',
326
+ filter: 'agNumberColumnFilter',
327
+ floatingFilter: true
328
+ },
329
+ {
330
+ field: 'age',
331
+ filter: 'agNumberColumnFilter',
332
+ floatingFilter: true
333
+ }
334
+ ]}
245
335
  defaultColDef={{
246
- filter: 'agTextColumnFilter',
247
- floatingFilter: true
336
+ flex: 1,
337
+ sortable: true,
338
+ resizable: true
248
339
  }}
249
340
  />
250
341
  </div>
@@ -253,65 +344,6 @@ function GridComponent() {
253
344
  }
254
345
  ```
255
346
 
256
- ### React Router Integration
257
-
258
- For single-page applications using React Router, you can sync filters with the browser URL:
259
-
260
- ```tsx
261
- import React, { useState } from 'react'
262
- import { useNavigate, useLocation } from 'react-router-dom'
263
- import { useAGGridUrlSync } from 'ag-grid-url-sync/react'
264
-
265
- function RouterGridComponent() {
266
- const [gridApi, setGridApi] = useState(null)
267
- const navigate = useNavigate()
268
- const location = useLocation()
269
-
270
- const { shareUrl, clearFilters, hasFilters, isReady } = useAGGridUrlSync(
271
- gridApi,
272
- {
273
- autoApplyOnMount: true, // Automatically apply filters from URL on mount
274
- paramPrefix: 'f_'
275
- }
276
- )
277
-
278
- // Update browser URL with current filters
279
- const updateUrlWithFilters = () => {
280
- const url = shareUrl()
281
- const urlParams = new URL(url).searchParams
282
- const currentParams = new URLSearchParams(location.search)
283
-
284
- // Clear existing filter params
285
- for (const [key] of currentParams) {
286
- if (key.startsWith('f_')) {
287
- currentParams.delete(key)
288
- }
289
- }
290
-
291
- // Add current filter params
292
- for (const [key, value] of urlParams) {
293
- currentParams.set(key, value)
294
- }
295
-
296
- navigate(`${location.pathname}?${currentParams.toString()}`, {
297
- replace: true
298
- })
299
- }
300
-
301
- return (
302
- <div>
303
- <button onClick={updateUrlWithFilters} disabled={!isReady}>
304
- Update URL with Filters
305
- </button>
306
- <button onClick={clearFilters} disabled={!hasFilters}>
307
- Clear All Filters
308
- </button>
309
- {/* Grid component */}
310
- </div>
311
- )
312
- }
313
- ```
314
-
315
347
  ### React Hook API
316
348
 
317
349
  #### `useAGGridUrlSync(gridApi, options?)`
@@ -449,16 +481,21 @@ The library generates clean, human-readable URLs:
449
481
 
450
482
  ```
451
483
  Base URL: https://app.com/page
452
- With filters: https://app.com/page?f_name_contains=john&f_status_eq=active
484
+ With text filters: https://app.com/page?f_name_contains=john&f_status_eq=active
485
+ With number filters: https://app.com/page?f_salary_gte=75000&f_age_range=25,45
486
+ Mixed filters: https://app.com/page?f_name_contains=john&f_salary_gte=75000&f_age_lt=50
453
487
  ```
454
488
 
455
489
  Parameter structure:
456
490
 
457
491
  - Prefix: `f_` (configurable - useful for multi-grid scenarios)
458
492
  - Format: `f_{columnName}_{operation}={value}`
459
- - Operations: `contains`, `eq` (equals)
493
+ - **Text Operations**: `contains`, `eq`, `neq`, `startsWith`, `endsWith`, `notContains`, `blank`, `notBlank`
494
+ - **Number Operations**: `eq`, `neq`, `gt`, `gte`, `lt`, `lte`, `range`, `blank`, `notBlank`
495
+ - **Date Operations**: `eq`, `neq`, `before`, `beforeEq`, `after`, `afterEq`, `daterange`, `blank`, `notBlank`
496
+ - **Range Format**: `f_column_range=min,max` (comma-separated values)
460
497
  - Standard URL encoding for special characters
461
- - Supports column names with underscores (e.g., `user_id`, `created_date`)
498
+ - Supports column names with underscores (e.g., `user_id`, `salary_base`, `created_date`)
462
499
 
463
500
  ## API Reference
464
501
 
@@ -568,32 +605,37 @@ Check out the [examples](./examples) directory for comprehensive working demos:
568
605
  Comprehensive demonstration showcasing all features:
569
606
 
570
607
  - **Complete text filter support** - All 8 AG Grid text operations (contains, equals, not contains, not equal, starts with, ends with, blank, not blank)
571
- - **Filter scenarios** - Pre-configured examples (Sales team, Engineering, Executive view, etc.)
572
- - **New operation demos** - Dedicated buttons for startsWith, endsWith, and notContains filters
608
+ - **Complete number filter support** - All 9 AG Grid number operations (equals, not equal, greater than, greater than or equal, less than, less than or equal, in range, blank, not blank)
609
+ - **Complete date filter support** - All 9 AG Grid date operations (equals, not equal, before, before or equal, after, after or equal, in range, blank, not blank)
610
+ - **Mixed filter scenarios** - Text, number, and date filters working together (Sales team + salary filters, Engineering + experience, Executive view with high salaries)
611
+ - **Number operation demos** - Dedicated buttons for salary ranges, age filtering, experience thresholds, and blank number fields
573
612
  - **URL sharing workflow** - Copy to clipboard, email, and Slack integration
574
- - **Performance monitoring** - Real-time benchmarks and stress testing
575
- - **Error handling** - Malformed URL and invalid filter testing
576
- - **Complete API showcase** - Generate URLs, apply filters, clear filters
613
+ - **Performance monitoring** - Real-time benchmarks and stress testing with mixed filter types
614
+ - **Error handling** - Malformed URL and invalid filter testing for both text, number, and date
615
+ - **Complete API showcase** - Generate URLs, apply filters, clear filters with comprehensive examples
577
616
 
578
617
  ### ⚛️ [React Integration](./examples/react-basic/basic-grid.tsx)
579
618
 
580
619
  Clean React component showing the `useAGGridUrlSync` hook:
581
620
 
582
- - React hook integration with AG Grid
583
- - Automatic filter state management
621
+ - React hook integration with AG Grid for text, number, and date filters
622
+ - Automatic filter state management for mixed filter types
584
623
  - Share button with clipboard functionality
585
624
  - Filter status indicators and controls
586
- - Auto-apply filters on component mount
587
- - Full TypeScript support
625
+ - Auto-apply filters on component mount with all filter types
626
+ - Enhanced employee data with salary, age, and experience columns
627
+ - Full TypeScript support with comprehensive type coverage
588
628
 
589
629
  ### 📊 [Shared Data](./examples/shared-data.js)
590
630
 
591
631
  Common data and column definitions used across examples:
592
632
 
593
- - Employee data with realistic company information
594
- - Project data with status and priority fields
595
- - Column definitions with complete text filter support
596
- - All 8 text filter operations enabled by default
633
+ - Enhanced employee data with salary, age, and experience number fields
634
+ - Project data with budget and numeric fields for comprehensive testing
635
+ - Column definitions with complete text, number, and date filter support
636
+ - All 8 text filter operations, 9 number filter operations, and 9 date filter operations enabled
637
+ - Realistic data ranges for demonstrating number and date filters (salaries $58K-$95K, ages 26-42, experience 3-15 years)
638
+ - Includes null values for demonstrating blank/notBlank operations
597
639
 
598
640
  All examples work out-of-the-box and demonstrate the complete functionality. The HTML demo can be opened directly in your browser, while the React example shows clean integration patterns.
599
641
 
@@ -607,6 +649,6 @@ All examples work out-of-the-box and demonstrate the complete functionality. The
607
649
 
608
650
  ## License
609
651
 
610
- This project is licensed under the ISC License - see the [LICENSE](LICENSE) file for details.
652
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
611
653
 
612
654
  [![banner](https://raw.githubusercontent.com/nearform/.github/refs/heads/master/assets/os-banner-green.svg)](https://www.nearform.com/contact/?utm_source=open-source&utm_medium=banner&utm_campaign=os-project-pages)
@@ -1,4 +1,9 @@
1
1
  import type { FilterState, InternalConfig } from './types.js';
2
+ import type { GridApi } from 'ag-grid-community';
3
+ /**
4
+ * Detects the expected filter type for a column based on AG Grid configuration
5
+ */
6
+ export declare function detectColumnFilterType(gridApi: GridApi, columnId: string): 'text' | 'number' | 'date';
2
7
  /**
3
8
  * Gets the current filter model from AG Grid
4
9
  */
@@ -1,4 +1,63 @@
1
- import { AG_GRID_OPERATION_NAMES, REVERSE_AG_GRID_OPERATION_NAMES } from './types.js';
1
+ import { AG_GRID_OPERATION_NAMES, REVERSE_AG_GRID_OPERATION_NAMES, TEXT_FILTER_OPERATIONS, NUMBER_FILTER_OPERATIONS, DATE_FILTER_OPERATIONS } from './types.js';
2
+ /**
3
+ * Converts a date string to ISO format (YYYY-MM-DD)
4
+ * Handles both date strings and undefined values
5
+ * Returns an empty string if the input is undefined or invalid
6
+ */
7
+ function toIsoDateString(val) {
8
+ if (!val)
9
+ return '';
10
+ const match = val.match(/^(\d{4}-\d{2}-\d{2})/);
11
+ return match?.[1] ? match[1] : val || '';
12
+ }
13
+ /**
14
+ * Type predicate to check if an operation is a valid text filter operation
15
+ */
16
+ function isTextFilterOperation(operation) {
17
+ return TEXT_FILTER_OPERATIONS.includes(operation);
18
+ }
19
+ /**
20
+ * Type predicate to check if an operation is a valid number filter operation
21
+ */
22
+ function isNumberFilterOperation(operation) {
23
+ return NUMBER_FILTER_OPERATIONS.includes(operation);
24
+ }
25
+ /**
26
+ * Type predicate to check if an operation is a valid date filter operation
27
+ */
28
+ function isDateFilterOperation(operation) {
29
+ return DATE_FILTER_OPERATIONS.includes(operation);
30
+ }
31
+ /**
32
+ * Detects the expected filter type for a column based on AG Grid configuration
33
+ */
34
+ export function detectColumnFilterType(gridApi, columnId) {
35
+ try {
36
+ const column = gridApi.getColumn(columnId);
37
+ if (!column)
38
+ return 'text'; // Default to text for unknown columns
39
+ const colDef = column.getColDef();
40
+ // 1. Explicit filter configuration takes priority
41
+ if (colDef.filter === 'agDateColumnFilter')
42
+ return 'date';
43
+ if (colDef.filter === 'agNumberColumnFilter')
44
+ return 'number';
45
+ if (colDef.filter === 'agTextColumnFilter')
46
+ return 'text';
47
+ // 2. Cell data type configuration
48
+ if (colDef.cellDataType === 'date')
49
+ return 'date';
50
+ if (colDef.cellDataType === 'number')
51
+ return 'number';
52
+ if (colDef.cellDataType === 'text')
53
+ return 'text';
54
+ // 3. Default to text for backward compatibility
55
+ return 'text';
56
+ }
57
+ catch {
58
+ return 'text'; // Safe fallback
59
+ }
60
+ }
2
61
  /**
3
62
  * Gets the current filter model from AG Grid
4
63
  */
@@ -12,17 +71,71 @@ export function getFilterModel(config) {
12
71
  for (const [column, filter] of Object.entries(model)) {
13
72
  if (!filter || typeof filter !== 'object')
14
73
  continue;
15
- const { filterType, type, filter: value } = filter;
16
- if (filterType !== 'text')
17
- continue;
18
- // Map AG Grid operations back to our internal operations using centralized mapping
19
- const internalOperation = REVERSE_AG_GRID_OPERATION_NAMES[type];
20
- if (internalOperation) {
21
- filterState[column] = {
22
- filterType: 'text',
23
- type: internalOperation,
24
- filter: value || ''
74
+ const { filterType, type, filter: value, filterTo, dateFrom, dateTo } = filter;
75
+ // Handle text filters
76
+ if (filterType === 'text') {
77
+ // Map AG Grid operations back to our internal operations using centralized mapping
78
+ const internalOperation = REVERSE_AG_GRID_OPERATION_NAMES[type];
79
+ // Use type predicate to validate and narrow the type
80
+ if (internalOperation && isTextFilterOperation(internalOperation)) {
81
+ filterState[column] = {
82
+ filterType: 'text',
83
+ type: internalOperation, // Now properly typed as TextFilterOperation
84
+ filter: String(value || '')
85
+ };
86
+ }
87
+ }
88
+ // Handle number filters
89
+ else if (filterType === 'number') {
90
+ // Map AG Grid number operations back to our internal operations
91
+ const internalOperation = REVERSE_AG_GRID_OPERATION_NAMES[type];
92
+ // Use type predicate to validate and narrow the type
93
+ if (internalOperation && isNumberFilterOperation(internalOperation)) {
94
+ const numberFilter = {
95
+ filterType: 'number',
96
+ type: internalOperation, // Now properly typed as NumberFilterOperation
97
+ filter: typeof value === 'number' ? value : 0
98
+ };
99
+ // Handle range operations
100
+ if (internalOperation === 'inRange' && typeof filterTo === 'number') {
101
+ ;
102
+ numberFilter.filterTo = filterTo;
103
+ }
104
+ filterState[column] = numberFilter;
105
+ }
106
+ }
107
+ // Handle date filters
108
+ else if (filterType === 'date') {
109
+ // Map AG Grid date operations back to our internal operations
110
+ const internalOperation = REVERSE_AG_GRID_OPERATION_NAMES[type];
111
+ // For date operations, we need to handle the special case where AG Grid uses
112
+ // generic operation names but we have date-specific internal names
113
+ let mappedOperation = internalOperation;
114
+ // Map AG Grid generic date operations to our date-specific operations
115
+ const AG_GRID_TO_DATE_OPERATION_MAP = {
116
+ lessThan: 'dateBefore',
117
+ lessThanOrEqual: 'dateBeforeOrEqual',
118
+ greaterThan: 'dateAfter',
119
+ greaterThanOrEqual: 'dateAfterOrEqual',
120
+ inRange: 'dateRange'
25
121
  };
122
+ mappedOperation = type
123
+ ? AG_GRID_TO_DATE_OPERATION_MAP[type] || internalOperation
124
+ : internalOperation;
125
+ // Use type predicate to validate and narrow the type
126
+ if (mappedOperation && isDateFilterOperation(mappedOperation)) {
127
+ const dateFilter = {
128
+ filterType: 'date',
129
+ type: mappedOperation,
130
+ filter: toIsoDateString(dateFrom || String(value || ''))
131
+ };
132
+ // Handle date range operations (AG Grid uses dateFrom/dateTo)
133
+ if (mappedOperation === 'dateRange' && dateTo) {
134
+ ;
135
+ dateFilter.filterTo = toIsoDateString(dateTo);
136
+ }
137
+ filterState[column] = dateFilter;
138
+ }
26
139
  }
27
140
  }
28
141
  return filterState;
@@ -34,6 +147,72 @@ export function getFilterModel(config) {
34
147
  return {};
35
148
  }
36
149
  }
150
+ /**
151
+ * Converts internal filter to AG Grid filter format
152
+ */
153
+ function convertToAGGridFilter(filter) {
154
+ if (filter.filterType === 'date') {
155
+ // Handle date range operations
156
+ if (filter.type === 'dateRange') {
157
+ return {
158
+ filterType: 'date',
159
+ type: 'inRange', // AG Grid uses 'inRange' for date ranges
160
+ dateFrom: filter.filter,
161
+ dateTo: filter.filterTo || null
162
+ };
163
+ }
164
+ // Handle blank operations for dates
165
+ if (filter.type === 'blank' || filter.type === 'notBlank') {
166
+ return {
167
+ filterType: 'date',
168
+ type: filter.type,
169
+ dateFrom: null,
170
+ dateTo: null
171
+ };
172
+ }
173
+ // Map date-specific operations to AG Grid generic operations
174
+ const dateOperationMapping = {
175
+ dateBefore: 'lessThan',
176
+ dateBeforeOrEqual: 'lessThanOrEqual',
177
+ dateAfter: 'greaterThan',
178
+ dateAfterOrEqual: 'greaterThanOrEqual'
179
+ };
180
+ const agOperation = dateOperationMapping[filter.type] ||
181
+ AG_GRID_OPERATION_NAMES[filter.type];
182
+ return {
183
+ filterType: 'date',
184
+ type: agOperation,
185
+ dateFrom: filter.filter,
186
+ dateTo: null
187
+ };
188
+ }
189
+ if (filter.filterType === 'number') {
190
+ const agGridOperation = AG_GRID_OPERATION_NAMES[filter.type];
191
+ if (filter.type === 'inRange') {
192
+ const baseFilter = {
193
+ filterType: 'number',
194
+ type: agGridOperation,
195
+ filter: filter.filter
196
+ };
197
+ // Only include filterTo if it's defined
198
+ return filter.filterTo !== undefined
199
+ ? { ...baseFilter, filterTo: filter.filterTo }
200
+ : baseFilter;
201
+ }
202
+ return {
203
+ filterType: 'number',
204
+ type: agGridOperation,
205
+ filter: filter.filter
206
+ };
207
+ }
208
+ // Text filter handling
209
+ const agGridOperation = AG_GRID_OPERATION_NAMES[filter.type];
210
+ return {
211
+ filterType: 'text',
212
+ type: agGridOperation,
213
+ filter: filter.filter
214
+ };
215
+ }
37
216
  /**
38
217
  * Applies a filter state to AG Grid
39
218
  */
@@ -41,20 +220,26 @@ export function applyFilterModel(filterState, config) {
41
220
  try {
42
221
  const model = {};
43
222
  for (const [column, filter] of Object.entries(filterState)) {
44
- if (filter &&
45
- typeof filter === 'object' &&
46
- filter.filterType === 'text') {
47
- // Convert our internal operation to AG Grid operation using centralized mapping
48
- const agGridOperation = AG_GRID_OPERATION_NAMES[filter.type];
49
- if (agGridOperation) {
50
- model[column] = {
51
- filterType: filter.filterType,
52
- type: agGridOperation,
53
- filter: filter.filter
54
- };
55
- }
223
+ if (!filter || typeof filter !== 'object')
224
+ continue;
225
+ // Detect expected filter type for this column
226
+ const expectedType = detectColumnFilterType(config.gridApi, column);
227
+ // Validate filter type compatibility
228
+ if (filter.filterType === 'date' && expectedType !== 'date') {
229
+ console.warn(`Column '${column}' expects a ${expectedType} filter but received a date filter. This may indicate a configuration mismatch in the column definition or filter application.`);
230
+ continue;
231
+ }
232
+ if (filter.filterType === 'number' && expectedType !== 'number') {
233
+ console.warn(`Column '${column}' expects a ${expectedType} filter but received a number filter. This may indicate a configuration mismatch in the column definition or filter application.`);
234
+ continue;
235
+ }
236
+ // Convert to AG Grid filter model
237
+ const agGridFilter = convertToAGGridFilter(filter);
238
+ if (agGridFilter) {
239
+ model[column] = agGridFilter;
56
240
  }
57
241
  }
242
+ // Apply the filter model to AG Grid
58
243
  config.gridApi.setFilterModel(model);
59
244
  }
60
245
  catch (error) {
@@ -1 +1 @@
1
- {"version":3,"file":"grid-integration.js","sourceRoot":"","sources":["../../src/core/grid-integration.ts"],"names":[],"mappings":"AACA,OAAO,EACL,uBAAuB,EACvB,+BAA+B,EAChC,MAAM,YAAY,CAAA;AAEnB;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,MAAsB;IACnD,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,cAAc,EAAE,CAAA;QAC7C,MAAM,WAAW,GAAgB,EAAE,CAAA;QAEnC,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YACxC,OAAO,WAAW,CAAA;QACpB,CAAC;QAED,KAAK,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACrD,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ;gBAAE,SAAQ;YAEnD,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,MAAa,CAAA;YACzD,IAAI,UAAU,KAAK,MAAM;gBAAE,SAAQ;YAEnC,mFAAmF;YACnF,MAAM,iBAAiB,GACrB,+BAA+B,CAC7B,IAAoD,CACrD,CAAA;YAEH,IAAI,iBAAiB,EAAE,CAAC;gBACtB,WAAW,CAAC,MAAM,CAAC,GAAG;oBACpB,UAAU,EAAE,MAAM;oBAClB,IAAI,EAAE,iBAAoC;oBAC1C,MAAM,EAAE,KAAK,IAAI,EAAE;iBACpB,CAAA;YACH,CAAC;QACH,CAAC;QAED,OAAO,WAAW,CAAA;IACpB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,MAAM,CAAC,YAAY,EAAE,CAAC;YACxB,MAAM,CAAC,YAAY,CAAC,KAAc,CAAC,CAAA;QACrC,CAAC;QACD,OAAO,EAAE,CAAA;IACX,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAC9B,WAAwB,EACxB,MAAsB;IAEtB,IAAI,CAAC;QACH,MAAM,KAAK,GAAQ,EAAE,CAAA;QAErB,KAAK,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC;YAC3D,IACE,MAAM;gBACN,OAAO,MAAM,KAAK,QAAQ;gBAC1B,MAAM,CAAC,UAAU,KAAK,MAAM,EAC5B,CAAC;gBACD,gFAAgF;gBAChF,MAAM,eAAe,GACnB,uBAAuB,CACrB,MAAM,CAAC,IAA4C,CACpD,CAAA;gBACH,IAAI,eAAe,EAAE,CAAC;oBACpB,KAAK,CAAC,MAAM,CAAC,GAAG;wBACd,UAAU,EAAE,MAAM,CAAC,UAAU;wBAC7B,IAAI,EAAE,eAAe;wBACrB,MAAM,EAAE,MAAM,CAAC,MAAM;qBACtB,CAAA;gBACH,CAAC;YACH,CAAC;QACH,CAAC;QAED,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,KAAK,CAAC,CAAA;IACtC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,MAAM,CAAC,YAAY,EAAE,CAAC;YACxB,MAAM,CAAC,YAAY,CAAC,KAAc,CAAC,CAAA;QACrC,CAAC;IACH,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"grid-integration.js","sourceRoot":"","sources":["../../src/core/grid-integration.ts"],"names":[],"mappings":"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"}