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 +7 -0
- package/README.md +138 -96
- package/dist/core/grid-integration.d.ts +5 -0
- package/dist/core/grid-integration.js +208 -23
- package/dist/core/grid-integration.js.map +1 -1
- package/dist/core/types.d.ts +204 -71
- package/dist/core/types.js +232 -52
- package/dist/core/types.js.map +1 -1
- package/dist/core/url-generator.js +96 -12
- package/dist/core/url-generator.js.map +1 -1
- package/dist/core/url-parser.js +223 -15
- package/dist/core/url-parser.js.map +1 -1
- package/dist/core/validation.d.ts +64 -2
- package/dist/core/validation.js +173 -2
- package/dist/core/validation.js.map +1 -1
- package/dist/react/types.d.ts +5 -0
- package/dist/react/use-ag-grid-url-sync.js +42 -27
- package/dist/react/use-ag-grid-url-sync.js.map +1 -1
- package/package.json +5 -5
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** (
|
|
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
|
-
|
|
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
|
-
-
|
|
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
|
-
**
|
|
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?
|
|
106
|
+
https://app.com/data?f_period_daterange=2024-01-01,2024-12-31
|
|
60
107
|
```
|
|
61
108
|
|
|
62
|
-
**
|
|
109
|
+
**Date before/after filter:**
|
|
63
110
|
|
|
64
111
|
```
|
|
65
|
-
https://app.com/data?
|
|
112
|
+
https://app.com/data?f_deadline_before=2024-12-31&f_created_after=2024-01-01
|
|
66
113
|
```
|
|
67
114
|
|
|
68
|
-
**
|
|
115
|
+
**Mixed text, number, and date filters:**
|
|
69
116
|
|
|
70
117
|
```
|
|
71
|
-
https://app.com/data?
|
|
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
|
-
//
|
|
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={
|
|
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
|
-
|
|
247
|
-
|
|
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
|
|
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
|
-
- **
|
|
572
|
-
- **
|
|
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
|
-
-
|
|
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
|
-
-
|
|
594
|
-
- Project data with
|
|
595
|
-
- Column definitions with complete text filter support
|
|
596
|
-
- All 8 text filter operations
|
|
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
|
|
652
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
611
653
|
|
|
612
654
|
[](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
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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":"
|
|
1
|
+
{"version":3,"file":"grid-integration.js","sourceRoot":"","sources":["../../src/core/grid-integration.ts"],"names":[],"mappings":"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"}
|