ag-grid-url-sync 0.1.1 → 0.2.1
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/README.md +407 -43
- package/dist/{ag-grid-url-sync.js → core/ag-grid-url-sync.js} +4 -2
- package/dist/core/ag-grid-url-sync.js.map +1 -0
- package/dist/core/grid-integration.d.ts +9 -0
- package/dist/core/grid-integration.js +66 -0
- package/dist/core/grid-integration.js.map +1 -0
- package/dist/core/index.d.ts +6 -0
- package/dist/core/index.js +11 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/types.d.ts +112 -0
- package/dist/core/types.js +79 -0
- package/dist/core/types.js.map +1 -0
- package/dist/core/url-generator.d.ts +9 -0
- package/dist/core/url-generator.js +42 -0
- package/dist/core/url-generator.js.map +1 -0
- package/dist/core/url-parser.d.ts +9 -0
- package/dist/core/url-parser.js +85 -0
- package/dist/core/url-parser.js.map +1 -0
- package/dist/core/validation.d.ts +18 -0
- package/dist/core/validation.js +28 -0
- package/dist/core/validation.js.map +1 -0
- package/dist/index.d.ts +6 -3
- package/dist/index.js +11 -3
- package/dist/index.js.map +1 -1
- package/dist/react/index.d.ts +3 -0
- package/dist/react/index.js +2 -0
- package/dist/react/index.js.map +1 -0
- package/dist/react/types.d.ts +64 -0
- package/dist/react/types.js +2 -0
- package/dist/react/types.js.map +1 -0
- package/dist/react/use-ag-grid-url-sync.d.ts +10 -0
- package/dist/react/use-ag-grid-url-sync.js +215 -0
- package/dist/react/use-ag-grid-url-sync.js.map +1 -0
- package/package.json +95 -6
- package/dist/ag-grid-url-sync.js.map +0 -1
- package/dist/types.d.ts +0 -69
- package/dist/types.js +0 -31
- package/dist/types.js.map +0 -1
- package/dist/utils.d.ts +0 -36
- package/dist/utils.js +0 -188
- package/dist/utils.js.map +0 -1
- /package/dist/{ag-grid-url-sync.d.ts → core/ag-grid-url-sync.d.ts} +0 -0
package/README.md
CHANGED
|
@@ -6,10 +6,11 @@ A lightweight TypeScript library for synchronizing AG Grid text filters with URL
|
|
|
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
10
|
- 🔗 Manual URL generation for sharing filter states
|
|
11
11
|
- ↔️ Bidirectional sync between grid and URL
|
|
12
12
|
- 🛠️ Framework agnostic - works with any AG Grid setup
|
|
13
|
+
- ⚛️ **React hook** - dedicated `useAGGridUrlSync` with state management
|
|
13
14
|
- 📝 Full TypeScript support with strict mode compliance
|
|
14
15
|
- 🚦 Graceful error handling with configurable error callbacks
|
|
15
16
|
- 🧹 Clean, human-readable URL format
|
|
@@ -18,6 +19,58 @@ A lightweight TypeScript library for synchronizing AG Grid text filters with URL
|
|
|
18
19
|
- 🛡️ Robust edge case handling (special characters, malformed URLs)
|
|
19
20
|
- 📦 Lightweight bundle size (~3KB gzipped)
|
|
20
21
|
|
|
22
|
+
## Supported Filter Types
|
|
23
|
+
|
|
24
|
+
Supports **all AG Grid text filter operations** (8 total) with clean, human-readable URL parameters:
|
|
25
|
+
|
|
26
|
+
| Filter Type | Operation | URL Format | Example | Description |
|
|
27
|
+
| ----------- | ------------ | ---------------------------- | -------------------------- | --------------------------- |
|
|
28
|
+
| **Text** | Contains | `f_column_contains=value` | `f_name_contains=john` | Text contains value |
|
|
29
|
+
| **Text** | Equals | `f_column_eq=value` | `f_status_eq=active` | Text equals value exactly |
|
|
30
|
+
| **Text** | Not Contains | `f_column_notContains=value` | `f_name_notContains=spam` | Text does not contain value |
|
|
31
|
+
| **Text** | Not Equal | `f_column_neq=value` | `f_status_neq=inactive` | Text does not equal value |
|
|
32
|
+
| **Text** | Starts With | `f_column_startsWith=value` | `f_email_startsWith=admin` | Text starts with value |
|
|
33
|
+
| **Text** | Ends With | `f_column_endsWith=value` | `f_file_endsWith=.pdf` | Text ends with value |
|
|
34
|
+
| **Text** | Blank | `f_column_blank=true` | `f_optional_blank=true` | Field is empty/null |
|
|
35
|
+
| **Text** | Not Blank | `f_column_notBlank=true` | `f_required_notBlank=true` | Field has any value |
|
|
36
|
+
|
|
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.
|
|
38
|
+
|
|
39
|
+
### Filter Detection
|
|
40
|
+
|
|
41
|
+
The library automatically works with:
|
|
42
|
+
|
|
43
|
+
- Columns configured with `filter: 'agTextColumnFilter'`
|
|
44
|
+
- 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
|
|
47
|
+
|
|
48
|
+
### URL Examples
|
|
49
|
+
|
|
50
|
+
**Simple filter:**
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
https://app.com/data?f_name_contains=john
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
**Multiple operations:**
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
https://app.com/data?f_name_contains=john&f_department_eq=Engineering&f_email_startsWith=admin&f_status_neq=inactive
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**Complex filtering with all operations:**
|
|
63
|
+
|
|
64
|
+
```
|
|
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
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**Blank operations:**
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
https://app.com/data?f_comments_blank=true&f_name_notBlank=true
|
|
72
|
+
```
|
|
73
|
+
|
|
21
74
|
## Installation
|
|
22
75
|
|
|
23
76
|
```bash
|
|
@@ -26,6 +79,96 @@ npm install ag-grid-url-sync
|
|
|
26
79
|
|
|
27
80
|
## Usage
|
|
28
81
|
|
|
82
|
+
### Importing the Library
|
|
83
|
+
|
|
84
|
+
The library provides two main entry points:
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
// Core functionality - works with any framework
|
|
88
|
+
import { createUrlSync } from 'ag-grid-url-sync'
|
|
89
|
+
|
|
90
|
+
// React hook - for React applications
|
|
91
|
+
import { useAGGridUrlSync } from 'ag-grid-url-sync/react'
|
|
92
|
+
|
|
93
|
+
// TypeScript types (available from both imports)
|
|
94
|
+
import type { FilterState, AGGridUrlSyncConfig } from 'ag-grid-url-sync'
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Tree-Shaking & Granular Imports
|
|
98
|
+
|
|
99
|
+
For optimal bundle sizes, you can import only the specific utilities you need:
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
import {
|
|
103
|
+
validateFilterValue,
|
|
104
|
+
DEFAULT_CONFIG
|
|
105
|
+
} from 'ag-grid-url-sync/validation'
|
|
106
|
+
|
|
107
|
+
import { parseUrlFilters, parseFilterParam } from 'ag-grid-url-sync/url-parser'
|
|
108
|
+
|
|
109
|
+
import { serializeFilters, generateUrl } from 'ag-grid-url-sync/url-generator'
|
|
110
|
+
|
|
111
|
+
import {
|
|
112
|
+
getFilterModel,
|
|
113
|
+
applyFilterModel
|
|
114
|
+
} from 'ag-grid-url-sync/grid-integration'
|
|
115
|
+
|
|
116
|
+
import type {
|
|
117
|
+
FilterState,
|
|
118
|
+
ColumnFilter,
|
|
119
|
+
FilterOperation
|
|
120
|
+
} from 'ag-grid-url-sync/types'
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
#### Bundle Size Comparison
|
|
124
|
+
|
|
125
|
+
| Import Pattern | Bundle Size | Use Case |
|
|
126
|
+
| ---------------- | ----------- | ---------------------- |
|
|
127
|
+
| Full library | 5KB | Complete functionality |
|
|
128
|
+
| Core class only | 3.2KB | Basic URL sync |
|
|
129
|
+
| React hook only | 7KB | React applications |
|
|
130
|
+
| URL parsing only | 1.05KB | Parse existing URLs |
|
|
131
|
+
| Validation only | 318B | Input validation |
|
|
132
|
+
| Types only | 0B | TypeScript types |
|
|
133
|
+
|
|
134
|
+
#### Example: Custom URL Parser
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
import { parseUrlFilters } from 'ag-grid-url-sync/url-parser'
|
|
138
|
+
import { DEFAULT_CONFIG } from 'ag-grid-url-sync/validation'
|
|
139
|
+
import type { FilterState } from 'ag-grid-url-sync/types'
|
|
140
|
+
|
|
141
|
+
function getFiltersFromUrl(url: string): FilterState {
|
|
142
|
+
const config = {
|
|
143
|
+
gridApi: null, // Not needed for parsing
|
|
144
|
+
...DEFAULT_CONFIG,
|
|
145
|
+
onParseError: error => console.error('Parse error:', error)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return parseUrlFilters(url, config)
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
#### Example: Custom URL Generator
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
import { generateUrl, serializeFilters } from 'ag-grid-url-sync/url-generator'
|
|
156
|
+
import type { FilterState } from 'ag-grid-url-sync/types'
|
|
157
|
+
|
|
158
|
+
function createShareableUrl(filters: FilterState, baseUrl: string): string {
|
|
159
|
+
const config = {
|
|
160
|
+
gridApi: null, // Not needed for generation
|
|
161
|
+
paramPrefix: 'f_',
|
|
162
|
+
maxValueLength: 200,
|
|
163
|
+
onParseError: () => {}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return generateUrl(baseUrl, filters, config)
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
**💡 Pro Tip**: Use granular imports in libraries and applications where bundle size matters. All existing import patterns remain fully supported for backward compatibility.
|
|
171
|
+
|
|
29
172
|
### Basic Example
|
|
30
173
|
|
|
31
174
|
```typescript
|
|
@@ -44,40 +187,257 @@ urlSync.applyFromUrl()
|
|
|
44
187
|
urlSync.clearFilters()
|
|
45
188
|
```
|
|
46
189
|
|
|
47
|
-
|
|
190
|
+
## React Integration
|
|
191
|
+
|
|
192
|
+
For React applications, use the dedicated `useAGGridUrlSync` hook that provides a complete solution with React-specific features and state management.
|
|
193
|
+
|
|
194
|
+
### Installation for React
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
npm install ag-grid-url-sync
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Basic React Usage
|
|
48
201
|
|
|
49
202
|
```tsx
|
|
50
|
-
import {
|
|
51
|
-
import {
|
|
203
|
+
import React, { useState } from 'react'
|
|
204
|
+
import { AgGridReact } from 'ag-grid-react'
|
|
205
|
+
import { useAGGridUrlSync } from 'ag-grid-url-sync/react'
|
|
206
|
+
|
|
207
|
+
function GridComponent() {
|
|
208
|
+
const [gridApi, setGridApi] = useState(null)
|
|
209
|
+
|
|
210
|
+
const {
|
|
211
|
+
shareUrl,
|
|
212
|
+
applyUrlFilters,
|
|
213
|
+
clearFilters,
|
|
214
|
+
hasFilters,
|
|
215
|
+
isReady,
|
|
216
|
+
currentUrl
|
|
217
|
+
} = useAGGridUrlSync(gridApi, {
|
|
218
|
+
autoApplyOnMount: true,
|
|
219
|
+
paramPrefix: 'filter_'
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
const handleShare = async () => {
|
|
223
|
+
const url = shareUrl()
|
|
224
|
+
await navigator.clipboard.writeText(url)
|
|
225
|
+
alert('Filter URL copied!')
|
|
226
|
+
}
|
|
52
227
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
228
|
+
return (
|
|
229
|
+
<div>
|
|
230
|
+
<div style={{ marginBottom: '10px' }}>
|
|
231
|
+
<button onClick={handleShare} disabled={!isReady}>
|
|
232
|
+
📋 Share Filters
|
|
233
|
+
</button>
|
|
234
|
+
<button onClick={clearFilters} disabled={!hasFilters}>
|
|
235
|
+
🗑️ Clear Filters
|
|
236
|
+
</button>
|
|
237
|
+
<button onClick={applyUrlFilters}>🔄 Apply URL Filters</button>
|
|
238
|
+
</div>
|
|
239
|
+
|
|
240
|
+
<div className="ag-theme-alpine" style={{ height: 400, width: '100%' }}>
|
|
241
|
+
<AgGridReact
|
|
242
|
+
onGridReady={params => setGridApi(params.api)}
|
|
243
|
+
rowData={rowData}
|
|
244
|
+
columnDefs={columnDefs}
|
|
245
|
+
defaultColDef={{
|
|
246
|
+
filter: 'agTextColumnFilter',
|
|
247
|
+
floatingFilter: true
|
|
248
|
+
}}
|
|
249
|
+
/>
|
|
250
|
+
</div>
|
|
251
|
+
</div>
|
|
57
252
|
)
|
|
253
|
+
}
|
|
254
|
+
```
|
|
58
255
|
|
|
59
|
-
|
|
256
|
+
### React Router Integration
|
|
60
257
|
|
|
61
|
-
|
|
258
|
+
For single-page applications using React Router, you can sync filters with the browser URL:
|
|
62
259
|
|
|
63
|
-
|
|
64
|
-
}
|
|
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
|
+
)
|
|
65
277
|
|
|
66
|
-
//
|
|
67
|
-
|
|
68
|
-
|
|
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
|
+
}
|
|
69
300
|
|
|
70
301
|
return (
|
|
71
302
|
<div>
|
|
72
|
-
<button
|
|
73
|
-
|
|
74
|
-
const url = shareUrl()
|
|
75
|
-
navigator.clipboard.writeText(url)
|
|
76
|
-
}}
|
|
77
|
-
>
|
|
78
|
-
Share Filters
|
|
303
|
+
<button onClick={updateUrlWithFilters} disabled={!isReady}>
|
|
304
|
+
Update URL with Filters
|
|
79
305
|
</button>
|
|
80
|
-
<button onClick={
|
|
306
|
+
<button onClick={clearFilters} disabled={!hasFilters}>
|
|
307
|
+
Clear All Filters
|
|
308
|
+
</button>
|
|
309
|
+
{/* Grid component */}
|
|
310
|
+
</div>
|
|
311
|
+
)
|
|
312
|
+
}
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
### React Hook API
|
|
316
|
+
|
|
317
|
+
#### `useAGGridUrlSync(gridApi, options?)`
|
|
318
|
+
|
|
319
|
+
```tsx
|
|
320
|
+
import { useAGGridUrlSync } from 'ag-grid-url-sync/react'
|
|
321
|
+
|
|
322
|
+
const {
|
|
323
|
+
shareUrl,
|
|
324
|
+
getQueryParams,
|
|
325
|
+
applyUrlFilters,
|
|
326
|
+
clearFilters,
|
|
327
|
+
isReady,
|
|
328
|
+
currentUrl,
|
|
329
|
+
hasFilters,
|
|
330
|
+
parseUrlFilters,
|
|
331
|
+
applyFilters
|
|
332
|
+
} = useAGGridUrlSync(gridApi, options)
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
#### Hook Options
|
|
336
|
+
|
|
337
|
+
```tsx
|
|
338
|
+
interface UseAGGridUrlSyncOptions {
|
|
339
|
+
// Core options (same as createUrlSync)
|
|
340
|
+
paramPrefix?: string // Default: 'f_'
|
|
341
|
+
maxValueLength?: number // Default: 200
|
|
342
|
+
onParseError?: (error: Error) => void
|
|
343
|
+
|
|
344
|
+
// React-specific options
|
|
345
|
+
autoApplyOnMount?: boolean // Default: false
|
|
346
|
+
enabledWhenReady?: boolean // Default: true
|
|
347
|
+
}
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
#### Hook Return Values
|
|
351
|
+
|
|
352
|
+
| Property | Type | Description |
|
|
353
|
+
| ----------------- | -------------------------------- | ----------------------------------------------- |
|
|
354
|
+
| `shareUrl` | `(baseUrl?: string) => string` | Generate shareable URL with current filters |
|
|
355
|
+
| `getQueryParams` | `() => string` | Get filter state as query parameters |
|
|
356
|
+
| `applyUrlFilters` | `(url?: string) => void` | Apply filters from URL to grid |
|
|
357
|
+
| `clearFilters` | `() => void` | Clear all text filters |
|
|
358
|
+
| `isReady` | `boolean` | Whether grid API is available and hook is ready |
|
|
359
|
+
| `currentUrl` | `string` | Current URL with filters applied |
|
|
360
|
+
| `hasFilters` | `boolean` | Whether grid has any active text filters |
|
|
361
|
+
| `parseUrlFilters` | `(url: string) => FilterState` | Parse filters from URL without applying |
|
|
362
|
+
| `applyFilters` | `(filters: FilterState) => void` | Apply filter state object to grid |
|
|
363
|
+
|
|
364
|
+
### React Features
|
|
365
|
+
|
|
366
|
+
- **🔄 Automatic State Management**: Tracks grid state and filter changes automatically
|
|
367
|
+
- **⚡ Ready State**: `isReady` indicates when the grid API is available
|
|
368
|
+
- **🎯 Auto-Apply**: `autoApplyOnMount` applies URL filters when component mounts
|
|
369
|
+
- **🧹 Cleanup**: Automatically cleans up resources when component unmounts
|
|
370
|
+
- **🛡️ Error Boundaries**: Graceful error handling with configurable callbacks
|
|
371
|
+
- **📊 Filter Status**: `hasFilters` tracks whether any filters are active
|
|
372
|
+
|
|
373
|
+
### Advanced React Example
|
|
374
|
+
|
|
375
|
+
```tsx
|
|
376
|
+
import React, { useState, useEffect } from 'react'
|
|
377
|
+
import { useAGGridUrlSync } from 'ag-grid-url-sync/react'
|
|
378
|
+
|
|
379
|
+
function AdvancedGridComponent() {
|
|
380
|
+
const [gridApi, setGridApi] = useState(null)
|
|
381
|
+
const [shareMessage, setShareMessage] = useState('')
|
|
382
|
+
|
|
383
|
+
const {
|
|
384
|
+
shareUrl,
|
|
385
|
+
applyUrlFilters,
|
|
386
|
+
clearFilters,
|
|
387
|
+
hasFilters,
|
|
388
|
+
isReady,
|
|
389
|
+
currentUrl,
|
|
390
|
+
parseUrlFilters
|
|
391
|
+
} = useAGGridUrlSync(gridApi, {
|
|
392
|
+
autoApplyOnMount: true,
|
|
393
|
+
paramPrefix: 'emp_',
|
|
394
|
+
maxValueLength: 100,
|
|
395
|
+
onParseError: error => {
|
|
396
|
+
console.warn('Filter parsing error:', error.message)
|
|
397
|
+
setShareMessage('Invalid filter URL detected')
|
|
398
|
+
}
|
|
399
|
+
})
|
|
400
|
+
|
|
401
|
+
// Watch for URL changes
|
|
402
|
+
useEffect(() => {
|
|
403
|
+
if (currentUrl !== window.location.href) {
|
|
404
|
+
setShareMessage('Filters have changed - URL ready to share')
|
|
405
|
+
}
|
|
406
|
+
}, [currentUrl])
|
|
407
|
+
|
|
408
|
+
const handleShareToEmail = () => {
|
|
409
|
+
const url = shareUrl()
|
|
410
|
+
const subject = 'Check out this filtered data'
|
|
411
|
+
const body = `View the filtered data here: ${url}`
|
|
412
|
+
window.open(
|
|
413
|
+
`mailto:?subject=${encodeURIComponent(subject)}&body=${encodeURIComponent(body)}`
|
|
414
|
+
)
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
const handlePreviewFilters = () => {
|
|
418
|
+
const url = prompt('Enter URL to preview filters:')
|
|
419
|
+
if (url) {
|
|
420
|
+
const filters = parseUrlFilters(url)
|
|
421
|
+
console.log('Parsed filters:', filters)
|
|
422
|
+
alert(`Found ${Object.keys(filters).length} filter(s)`)
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
return (
|
|
427
|
+
<div>
|
|
428
|
+
<div className="controls">
|
|
429
|
+
<button onClick={handleShareToEmail} disabled={!isReady}>
|
|
430
|
+
📧 Share via Email
|
|
431
|
+
</button>
|
|
432
|
+
<button onClick={handlePreviewFilters}>👁️ Preview URL Filters</button>
|
|
433
|
+
<button onClick={clearFilters} disabled={!hasFilters}>
|
|
434
|
+
Clear ({hasFilters ? 'has filters' : 'no filters'})
|
|
435
|
+
</button>
|
|
436
|
+
</div>
|
|
437
|
+
|
|
438
|
+
{shareMessage && <div className="message">{shareMessage}</div>}
|
|
439
|
+
|
|
440
|
+
{/* Grid component */}
|
|
81
441
|
</div>
|
|
82
442
|
)
|
|
83
443
|
}
|
|
@@ -203,35 +563,39 @@ interface AGGridUrlSyncConfig {
|
|
|
203
563
|
|
|
204
564
|
Check out the [examples](./examples) directory for comprehensive working demos:
|
|
205
565
|
|
|
206
|
-
###
|
|
566
|
+
### 🚀 [Vanilla JavaScript Demo](./examples/vanilla-js/demo.html)
|
|
207
567
|
|
|
208
|
-
|
|
568
|
+
Comprehensive demonstration showcasing all features:
|
|
209
569
|
|
|
210
|
-
-
|
|
211
|
-
-
|
|
212
|
-
-
|
|
213
|
-
-
|
|
570
|
+
- **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
|
|
573
|
+
- **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
|
|
214
577
|
|
|
215
|
-
###
|
|
578
|
+
### ⚛️ [React Integration](./examples/react-basic/basic-grid.tsx)
|
|
216
579
|
|
|
217
|
-
|
|
580
|
+
Clean React component showing the `useAGGridUrlSync` hook:
|
|
218
581
|
|
|
219
|
-
-
|
|
220
|
-
-
|
|
221
|
-
-
|
|
222
|
-
-
|
|
223
|
-
-
|
|
582
|
+
- React hook integration with AG Grid
|
|
583
|
+
- Automatic filter state management
|
|
584
|
+
- Share button with clipboard functionality
|
|
585
|
+
- Filter status indicators and controls
|
|
586
|
+
- Auto-apply filters on component mount
|
|
587
|
+
- Full TypeScript support
|
|
224
588
|
|
|
225
|
-
###
|
|
589
|
+
### 📊 [Shared Data](./examples/shared-data.js)
|
|
226
590
|
|
|
227
|
-
|
|
591
|
+
Common data and column definitions used across examples:
|
|
228
592
|
|
|
229
|
-
-
|
|
230
|
-
-
|
|
231
|
-
-
|
|
232
|
-
-
|
|
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
|
|
233
597
|
|
|
234
|
-
All examples work out-of-the-box
|
|
598
|
+
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.
|
|
235
599
|
|
|
236
600
|
## Contributing
|
|
237
601
|
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import { DEFAULT_CONFIG } from './
|
|
2
|
-
import { parseUrlFilters
|
|
1
|
+
import { DEFAULT_CONFIG } from './validation.js';
|
|
2
|
+
import { parseUrlFilters } from './url-parser.js';
|
|
3
|
+
import { generateUrl } from './url-generator.js';
|
|
4
|
+
import { getFilterModel, applyFilterModel } from './grid-integration.js';
|
|
3
5
|
/**
|
|
4
6
|
* AGGridUrlSync class for synchronizing AG Grid text filters with URL parameters
|
|
5
7
|
*/
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ag-grid-url-sync.js","sourceRoot":"","sources":["../../src/core/ag-grid-url-sync.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAChD,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAExE;;GAEG;AACH,MAAM,OAAO,aAAa;IAChB,MAAM,CAAgB;IAE9B;;;;OAIG;IACH,YAAY,OAAgB,EAAE,SAA8B,EAAE;QAC5D,IAAI,CAAC,MAAM,GAAG;YACZ,OAAO;YACP,WAAW,EAAE,MAAM,CAAC,WAAW,IAAI,cAAc,CAAC,WAAW;YAC7D,cAAc,EAAE,MAAM,CAAC,cAAc,IAAI,cAAc,CAAC,cAAc;YACtE,YAAY,EAAE,MAAM,CAAC,YAAY,IAAI,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC;SAChD,CAAA;IACH,CAAC;IAED;;;;OAIG;IACH,WAAW,CAAC,OAAgB;QAC1B,MAAM,WAAW,GAAG,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QAC/C,MAAM,GAAG,GAAG,OAAO,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAA;QAC3C,OAAO,WAAW,CAAC,GAAG,EAAE,WAAW,EAAE,IAAI,CAAC,MAAM,CAAC,CAAA;IACnD,CAAC;IAED;;;OAGG;IACH,cAAc;QACZ,MAAM,GAAG,GAAG,IAAI,CAAC,WAAW,EAAE,CAAA;QAC9B,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAA;QAC3B,OAAO,MAAM,CAAC,MAAM,CAAA;IACtB,CAAC;IAED;;;OAGG;IACH,YAAY,CAAC,GAAY;QACvB,MAAM,UAAU,GAAG,GAAG,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAA;QAC9C,MAAM,WAAW,GAAG,eAAe,CAAC,UAAU,EAAE,IAAI,CAAC,MAAM,CAAC,CAAA;QAC5D,IAAI,CAAC,YAAY,CAAC,WAAW,CAAC,CAAA;IAChC,CAAC;IAED;;;OAGG;IACH,YAAY,CAAC,WAAwB;QACnC,gBAAgB,CAAC,WAAW,EAAE,IAAI,CAAC,MAAM,CAAC,CAAA;IAC5C,CAAC;IAED;;OAEG;IACH,YAAY;QACV,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC,CAAA;IACvB,CAAC;IAED;;OAEG;IACH,OAAO;QACL,2DAA2D;IAC7D,CAAC;CACF;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAC3B,OAAgB,EAChB,MAA4B;IAE5B,OAAO,IAAI,aAAa,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;AAC3C,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { FilterState, InternalConfig } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Gets the current filter model from AG Grid
|
|
4
|
+
*/
|
|
5
|
+
export declare function getFilterModel(config: InternalConfig): FilterState;
|
|
6
|
+
/**
|
|
7
|
+
* Applies a filter state to AG Grid
|
|
8
|
+
*/
|
|
9
|
+
export declare function applyFilterModel(filterState: FilterState, config: InternalConfig): void;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { AG_GRID_OPERATION_NAMES, REVERSE_AG_GRID_OPERATION_NAMES } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Gets the current filter model from AG Grid
|
|
4
|
+
*/
|
|
5
|
+
export function getFilterModel(config) {
|
|
6
|
+
try {
|
|
7
|
+
const model = config.gridApi.getFilterModel();
|
|
8
|
+
const filterState = {};
|
|
9
|
+
if (!model || typeof model !== 'object') {
|
|
10
|
+
return filterState;
|
|
11
|
+
}
|
|
12
|
+
for (const [column, filter] of Object.entries(model)) {
|
|
13
|
+
if (!filter || typeof filter !== 'object')
|
|
14
|
+
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 || ''
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return filterState;
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
if (config.onParseError) {
|
|
32
|
+
config.onParseError(error);
|
|
33
|
+
}
|
|
34
|
+
return {};
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Applies a filter state to AG Grid
|
|
39
|
+
*/
|
|
40
|
+
export function applyFilterModel(filterState, config) {
|
|
41
|
+
try {
|
|
42
|
+
const model = {};
|
|
43
|
+
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
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
config.gridApi.setFilterModel(model);
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
if (config.onParseError) {
|
|
62
|
+
config.onParseError(error);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=grid-integration.js.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { AGGridUrlSync, createUrlSync } from './ag-grid-url-sync.js';
|
|
2
|
+
export * from './types.js';
|
|
3
|
+
export { validateFilterValue, DEFAULT_CONFIG } from './validation.js';
|
|
4
|
+
export { parseUrlFilters, parseFilterParam } from './url-parser.js';
|
|
5
|
+
export { serializeFilters, generateUrl } from './url-generator.js';
|
|
6
|
+
export { getFilterModel, applyFilterModel } from './grid-integration.js';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { AGGridUrlSync, createUrlSync } from './ag-grid-url-sync.js';
|
|
2
|
+
export * from './types.js';
|
|
3
|
+
// Validation utilities
|
|
4
|
+
export { validateFilterValue, DEFAULT_CONFIG } from './validation.js';
|
|
5
|
+
// URL parsing utilities
|
|
6
|
+
export { parseUrlFilters, parseFilterParam } from './url-parser.js';
|
|
7
|
+
// URL generation utilities
|
|
8
|
+
export { serializeFilters, generateUrl } from './url-generator.js';
|
|
9
|
+
// AG Grid integration utilities
|
|
10
|
+
export { getFilterModel, applyFilterModel } from './grid-integration.js';
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAA;AAEpE,cAAc,YAAY,CAAA;AAE1B,uBAAuB;AACvB,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAErE,wBAAwB;AACxB,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AAEnE,2BAA2B;AAC3B,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAElE,gCAAgC;AAChC,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA"}
|