ag-grid-url-sync 0.2.4 โ 0.4.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 +395 -23
- package/dist/core/ag-grid-url-sync.d.ts +16 -1
- package/dist/core/ag-grid-url-sync.js +19 -41
- package/dist/core/ag-grid-url-sync.js.map +1 -1
- package/dist/core/grid-integration.js +5 -67
- package/dist/core/grid-integration.js.map +1 -1
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.js +1 -4
- package/dist/core/index.js.map +1 -1
- package/dist/core/serialization/formats.d.ts +29 -0
- package/dist/core/serialization/formats.js +68 -0
- package/dist/core/serialization/formats.js.map +1 -0
- package/dist/core/serialization/grouped.d.ts +36 -0
- package/dist/core/serialization/grouped.js +105 -0
- package/dist/core/serialization/grouped.js.map +1 -0
- package/dist/core/serialization/index.d.ts +7 -0
- package/dist/core/serialization/index.js +3 -0
- package/dist/core/serialization/index.js.map +1 -0
- package/dist/core/types.d.ts +75 -5
- package/dist/core/types.js +7 -115
- package/dist/core/types.js.map +1 -1
- package/dist/core/url-generator.js +44 -38
- package/dist/core/url-generator.js.map +1 -1
- package/dist/core/url-parser.js +8 -52
- package/dist/core/url-parser.js.map +1 -1
- package/dist/core/validation.d.ts +13 -2
- package/dist/core/validation.js +34 -72
- package/dist/core/validation.js.map +1 -1
- package/dist/core/view-storage.d.ts +63 -0
- package/dist/core/view-storage.js +112 -0
- package/dist/core/view-storage.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +2 -5
- package/dist/index.js.map +1 -1
- package/dist/react/index.d.ts +1 -0
- package/dist/react/types.d.ts +108 -1
- package/dist/react/use-ag-grid-url-sync.js +440 -42
- package/dist/react/use-ag-grid-url-sync.js.map +1 -1
- package/package.json +101 -30
package/README.md
CHANGED
|
@@ -9,7 +9,8 @@ A lightweight TypeScript library for synchronizing AG Grid text and number filte
|
|
|
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
|
- ๐ข **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
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)
|
|
12
|
-
-
|
|
12
|
+
- ๏ฟฝ **Grouped serialization** - Package all filters into a single URL parameter with querystring, JSON, or base64 encoding
|
|
13
|
+
- ๏ฟฝ๐ Manual URL generation for sharing filter states
|
|
13
14
|
- โ๏ธ Bidirectional sync between grid and URL
|
|
14
15
|
- ๐ ๏ธ Framework agnostic - works with any AG Grid setup
|
|
15
16
|
- โ๏ธ **React hook** - dedicated `useAGGridUrlSync` with state management
|
|
@@ -17,7 +18,7 @@ A lightweight TypeScript library for synchronizing AG Grid text and number filte
|
|
|
17
18
|
- ๐ฆ Graceful error handling with configurable error callbacks
|
|
18
19
|
- ๐งน Clean, human-readable URL format
|
|
19
20
|
- โก High performance - handles 100+ filters efficiently (<20ms)
|
|
20
|
-
- ๐ง Configurable URL prefixes
|
|
21
|
+
- ๐ง Configurable URL prefixes
|
|
21
22
|
- ๐ก๏ธ Robust edge case handling (special characters, malformed URLs)
|
|
22
23
|
- ๐ฆ Lightweight bundle size (~3KB gzipped)
|
|
23
24
|
|
|
@@ -256,6 +257,204 @@ urlSync.applyFilters({
|
|
|
256
257
|
urlSync.clearFilters()
|
|
257
258
|
```
|
|
258
259
|
|
|
260
|
+
## Grouped Serialization
|
|
261
|
+
|
|
262
|
+
Grouped serialization provides an alternative to individual URL parameters by packaging all filters into a single URL parameter. This is particularly useful for applications with many filters or when you need more compact URLs.
|
|
263
|
+
|
|
264
|
+
### Why Use Grouped Serialization?
|
|
265
|
+
|
|
266
|
+
**Individual serialization** (default):
|
|
267
|
+
|
|
268
|
+
```
|
|
269
|
+
https://app.com/data?f_name_contains=john&f_age_gt=25&f_salary_gte=80000&f_created_after=2024-01-01
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
**Grouped serialization**:
|
|
273
|
+
|
|
274
|
+
```
|
|
275
|
+
https://app.com/data?grid_filters=f_name_contains%3Djohn%26f_age_gt%3D25%26f_salary_gte%3D80000%26f_created_after%3D2024-01-01
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
Benefits of grouped serialization:
|
|
279
|
+
|
|
280
|
+
- **Cleaner URLs** with a single parameter instead of many
|
|
281
|
+
- **Better for sharing** - easier to copy/paste and less prone to URL manipulation
|
|
282
|
+
|
|
283
|
+
- **Format flexibility** - choose between querystring, JSON, or base64 encoding
|
|
284
|
+
- **Future-proof** - easier to extend with additional metadata
|
|
285
|
+
|
|
286
|
+
### Configuration
|
|
287
|
+
|
|
288
|
+
```typescript
|
|
289
|
+
interface AGGridUrlSyncConfig {
|
|
290
|
+
// Serialization mode
|
|
291
|
+
serialization?: 'individual' | 'grouped' // default: 'individual'
|
|
292
|
+
|
|
293
|
+
// Format for grouped serialization
|
|
294
|
+
format?: 'querystring' | 'json' | 'base64' // default: 'querystring'
|
|
295
|
+
|
|
296
|
+
// Parameter name for grouped mode
|
|
297
|
+
groupedParam?: string // default: 'grid_filters'
|
|
298
|
+
|
|
299
|
+
// ... other existing options
|
|
300
|
+
}
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
### Format Options
|
|
304
|
+
|
|
305
|
+
#### QueryString Format (Default)
|
|
306
|
+
|
|
307
|
+
Filters are URL-encoded as a query string within the parameter:
|
|
308
|
+
|
|
309
|
+
```typescript
|
|
310
|
+
const urlSync = createUrlSync(gridApi, {
|
|
311
|
+
serialization: 'grouped',
|
|
312
|
+
format: 'querystring' // default
|
|
313
|
+
})
|
|
314
|
+
|
|
315
|
+
// Result: ?grid_filters=f_name_contains%3Djohn%26f_age_gt%3D25
|
|
316
|
+
// Decoded: f_name_contains=john&f_age_gt=25
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
#### JSON Format
|
|
320
|
+
|
|
321
|
+
Filters are JSON-encoded, ideal for complex filter states:
|
|
322
|
+
|
|
323
|
+
```typescript
|
|
324
|
+
const urlSync = createUrlSync(gridApi, {
|
|
325
|
+
serialization: 'grouped',
|
|
326
|
+
format: 'json'
|
|
327
|
+
})
|
|
328
|
+
|
|
329
|
+
// Result: ?grid_filters=%7B%22name%22%3A%7B%22filterType%22%3A%22text%22...
|
|
330
|
+
// Decoded: {"name":{"filterType":"text","type":"contains","filter":"john"}...}
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
#### Base64 Format
|
|
334
|
+
|
|
335
|
+
Filters are JSON-encoded then base64-encoded for maximum compactness:
|
|
336
|
+
|
|
337
|
+
```typescript
|
|
338
|
+
const urlSync = createUrlSync(gridApi, {
|
|
339
|
+
serialization: 'grouped',
|
|
340
|
+
format: 'base64'
|
|
341
|
+
})
|
|
342
|
+
|
|
343
|
+
// Result: ?grid_filters=eyJuYW1lIjp7ImZpbHRlclR5cGUiOiJ0ZXh0Ii...
|
|
344
|
+
// Most compact representation, ideal for sharing
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
### Basic Usage
|
|
348
|
+
|
|
349
|
+
```typescript
|
|
350
|
+
import { createUrlSync } from 'ag-grid-url-sync'
|
|
351
|
+
|
|
352
|
+
// Basic grouped serialization
|
|
353
|
+
const urlSync = createUrlSync(gridApi, {
|
|
354
|
+
serialization: 'grouped'
|
|
355
|
+
})
|
|
356
|
+
|
|
357
|
+
// Generate grouped URL
|
|
358
|
+
const url = urlSync.generateUrl('https://app.com/data')
|
|
359
|
+
// Result: https://app.com/data?grid_filters=f_name_contains%3Djohn%26f_age_gt%3D25
|
|
360
|
+
|
|
361
|
+
// Apply filters from grouped URL
|
|
362
|
+
urlSync.applyFromUrl(url)
|
|
363
|
+
|
|
364
|
+
// Get current serialization mode and format
|
|
365
|
+
console.log(urlSync.getSerializationMode()) // 'grouped'
|
|
366
|
+
console.log(urlSync.getCurrentFormat()) // 'querystring'
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
### Advanced Examples
|
|
370
|
+
|
|
371
|
+
#### Format Conversion Utility
|
|
372
|
+
|
|
373
|
+
```typescript
|
|
374
|
+
// Convert current filters to any format
|
|
375
|
+
const jsonFormat = urlSync.getFiltersAsFormat('json')
|
|
376
|
+
const base64Format = urlSync.getFiltersAsFormat('base64')
|
|
377
|
+
const querystringFormat = urlSync.getFiltersAsFormat('querystring')
|
|
378
|
+
|
|
379
|
+
console.log('JSON:', jsonFormat)
|
|
380
|
+
console.log('Base64:', base64Format)
|
|
381
|
+
console.log('QueryString:', querystringFormat)
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
#### Dynamic Format Switching
|
|
385
|
+
|
|
386
|
+
```typescript
|
|
387
|
+
// Start with individual mode
|
|
388
|
+
const urlSync = createUrlSync(gridApi, {
|
|
389
|
+
serialization: 'individual'
|
|
390
|
+
})
|
|
391
|
+
|
|
392
|
+
// Switch to grouped mode when needed
|
|
393
|
+
const groupedSync = createUrlSync(gridApi, {
|
|
394
|
+
serialization: 'grouped',
|
|
395
|
+
format: 'base64',
|
|
396
|
+
groupedParam: 'filters'
|
|
397
|
+
})
|
|
398
|
+
|
|
399
|
+
// Use utility to maintain filters across mode changes
|
|
400
|
+
const currentFilters = urlSync.getFiltersAsFormat('json')
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
### Granular Import: Grouped Serialization
|
|
404
|
+
|
|
405
|
+
You can import grouped serialization utilities and format serializers directly for advanced use cases or custom integrations:
|
|
406
|
+
|
|
407
|
+
```typescript
|
|
408
|
+
import {
|
|
409
|
+
serializeGrouped,
|
|
410
|
+
deserializeGrouped
|
|
411
|
+
} from 'ag-grid-url-sync/serialization'
|
|
412
|
+
import {
|
|
413
|
+
QueryStringSerializer,
|
|
414
|
+
JsonSerializer,
|
|
415
|
+
Base64Serializer
|
|
416
|
+
} from 'ag-grid-url-sync/serialization/formats'
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
---
|
|
420
|
+
|
|
421
|
+
### Error Handling for Grouped Serialization
|
|
422
|
+
|
|
423
|
+
You can provide a custom error handler to gracefully handle malformed grouped parameters or serialization errors. The library will throw an `InvalidSerializationError` for invalid formats:
|
|
424
|
+
|
|
425
|
+
```typescript
|
|
426
|
+
import { InvalidSerializationError } from 'ag-grid-url-sync/types'
|
|
427
|
+
|
|
428
|
+
const urlSync = createUrlSync(gridApi, {
|
|
429
|
+
serialization: 'grouped',
|
|
430
|
+
format: 'json',
|
|
431
|
+
onParseError: error => {
|
|
432
|
+
if (error instanceof InvalidSerializationError) {
|
|
433
|
+
// Handle grouped serialization error
|
|
434
|
+
alert('Invalid filter data in URL!')
|
|
435
|
+
} else {
|
|
436
|
+
// Handle other errors
|
|
437
|
+
console.error(error)
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
})
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
### Exports
|
|
444
|
+
|
|
445
|
+
The package supports granular subpath exports for advanced usage and optimal bundle size:
|
|
446
|
+
|
|
447
|
+
- `ag-grid-url-sync/serialization` โ grouped serialization utilities
|
|
448
|
+
- `ag-grid-url-sync/serialization/formats` โ format-specific serializers
|
|
449
|
+
- `ag-grid-url-sync/validation` โ validation helpers
|
|
450
|
+
- `ag-grid-url-sync/url-parser` โ URL parsing utilities
|
|
451
|
+
- `ag-grid-url-sync/url-generator` โ URL generation utilities
|
|
452
|
+
- `ag-grid-url-sync/grid-integration` โ AG Grid integration helpers
|
|
453
|
+
- `ag-grid-url-sync/view-storage` โ localStorage-backed saved views
|
|
454
|
+
- `ag-grid-url-sync/types` โ TypeScript types
|
|
455
|
+
|
|
456
|
+
Refer to the [package.json](./package.json) for the full export map.
|
|
457
|
+
|
|
259
458
|
## React Integration
|
|
260
459
|
|
|
261
460
|
For React applications, use the dedicated `useAGGridUrlSync` hook that provides a complete solution with React-specific features and state management.
|
|
@@ -360,7 +559,9 @@ const {
|
|
|
360
559
|
currentUrl,
|
|
361
560
|
hasFilters,
|
|
362
561
|
parseUrlFilters,
|
|
363
|
-
applyFilters
|
|
562
|
+
applyFilters,
|
|
563
|
+
getFiltersAsFormat,
|
|
564
|
+
getCurrentFormat
|
|
364
565
|
} = useAGGridUrlSync(gridApi, options)
|
|
365
566
|
```
|
|
366
567
|
|
|
@@ -376,31 +577,187 @@ interface UseAGGridUrlSyncOptions {
|
|
|
376
577
|
// React-specific options
|
|
377
578
|
autoApplyOnMount?: boolean // Default: false
|
|
378
579
|
enabledWhenReady?: boolean // Default: true
|
|
580
|
+
onError?: (error: Error, context: string) => void // Advanced error handling
|
|
581
|
+
|
|
582
|
+
// Saved views: setting a key enables the feature and namespaces its storage
|
|
583
|
+
storageKey?: string // Default: undefined (saved views disabled)
|
|
379
584
|
}
|
|
380
585
|
```
|
|
381
586
|
|
|
382
587
|
#### Hook Return Values
|
|
383
588
|
|
|
384
|
-
| Property
|
|
385
|
-
|
|
|
386
|
-
| `shareUrl`
|
|
387
|
-
| `getQueryParams`
|
|
388
|
-
| `applyUrlFilters`
|
|
389
|
-
| `clearFilters`
|
|
390
|
-
| `isReady`
|
|
391
|
-
| `currentUrl`
|
|
392
|
-
| `hasFilters`
|
|
393
|
-
| `parseUrlFilters`
|
|
394
|
-
| `applyFilters`
|
|
589
|
+
| Property | Type | Description |
|
|
590
|
+
| -------------------- | -------------------------------- | ----------------------------------------------- | ---------------------------------------------------------- | -------------------------------------------------------------- |
|
|
591
|
+
| `shareUrl` | `(baseUrl?: string) => string` | Generate shareable URL with current filters |
|
|
592
|
+
| `getQueryParams` | `() => string` | Get filter state as query parameters |
|
|
593
|
+
| `applyUrlFilters` | `(url?: string) => void` | Apply filters from URL to grid |
|
|
594
|
+
| `clearFilters` | `() => void` | Clear all text filters |
|
|
595
|
+
| `isReady` | `boolean` | Whether grid API is available and hook is ready |
|
|
596
|
+
| `currentUrl` | `string` | Current URL with filters applied |
|
|
597
|
+
| `hasFilters` | `boolean` | Whether grid has any active text filters |
|
|
598
|
+
| `parseUrlFilters` | `(url: string) => FilterState` | Parse filters from URL without applying |
|
|
599
|
+
| `applyFilters` | `(filters: FilterState) => void` | Apply filter state object to grid |
|
|
600
|
+
| `getFiltersAsFormat` | `(format: 'querystring' | 'json' | 'base64') => string` | Serialize filters to any supported format (for sharing/export) |
|
|
601
|
+
| `getCurrentFormat` | `() => 'individual' | 'grouped'` | Get the current serialization mode (individual or grouped) |
|
|
602
|
+
|
|
603
|
+
Saved views (inert unless `storageKey` is set):
|
|
604
|
+
|
|
605
|
+
| Property | Type | Description |
|
|
606
|
+
| -------------- | ------------------------------------ | ----------------------------------------------------------------------- |
|
|
607
|
+
| `views` | `GridView[]` | Saved views for this grid, in save order. Empty when views are disabled |
|
|
608
|
+
| `activeViewId` | `string \| null` | Id of the view currently applied to the grid, or `null` when none is |
|
|
609
|
+
| `saveView` | `(name: string) => GridView \| null` | Save current filters under a name; overwrites an existing name in place |
|
|
610
|
+
| `loadView` | `(id: string \| null) => void` | Apply a saved view, or pass `null` to reset to unfiltered |
|
|
611
|
+
| `deleteView` | `(id: string) => void` | Delete a saved view |
|
|
612
|
+
|
|
613
|
+
> **Note:** `getFiltersAsFormat` and `getCurrentFormat` are especially useful for grouped serialization, format conversion, and advanced sharing scenarios.
|
|
614
|
+
|
|
615
|
+
### Saved Views
|
|
616
|
+
|
|
617
|
+
Pass a `storageKey` to let users name and revisit filter sets. The key both enables
|
|
618
|
+
the feature and namespaces its storage, so two grids on the same origin must use
|
|
619
|
+
different keys:
|
|
620
|
+
|
|
621
|
+
```tsx
|
|
622
|
+
const { views, activeViewId, saveView, loadView, deleteView } =
|
|
623
|
+
useAGGridUrlSync(gridApi, { storageKey: 'employee-grid' })
|
|
624
|
+
|
|
625
|
+
// One handler each, as a user drives them: save whatever is currently filtered,
|
|
626
|
+
// then apply or remove a view by id in a later interaction.
|
|
627
|
+
const handleSave = () => saveView('Engineering, high salary')
|
|
628
|
+
const handleLoad = (id: string) => loadView(id)
|
|
629
|
+
const handleDelete = (id: string) => deleteView(id)
|
|
630
|
+
const handleReset = () => loadView(null) // back to an unfiltered grid
|
|
631
|
+
```
|
|
632
|
+
|
|
633
|
+
Render the list from `views` and drive the buttons from it. `views[i].id` is only
|
|
634
|
+
stale immediately after a mutation, not during render. If you do need the new
|
|
635
|
+
view's id in the same handler that saved it, use `saveView`'s return value rather
|
|
636
|
+
than reading it back out of `views`: that array is React state, so it does not
|
|
637
|
+
update until the next render.
|
|
638
|
+
|
|
639
|
+
Only the filter model is stored, using AG Grid's native format so set filters and
|
|
640
|
+
combined conditions survive a round trip. Column order, row selection and expanded
|
|
641
|
+
groups are not stored.
|
|
642
|
+
|
|
643
|
+
Behaviour worth knowing:
|
|
644
|
+
|
|
645
|
+
- **Names are unique.** Saving over an existing name updates that view in place,
|
|
646
|
+
keeping its id and list position, so "load, adjust, save under the same name" is
|
|
647
|
+
how you update one. Names are trimmed and must not be empty.
|
|
648
|
+
- **`activeViewId` describes the live grid**, not what is persisted. A stored view
|
|
649
|
+
is only applied on mount when `autoApplyOnMount` is set, so with it off this
|
|
650
|
+
stays `null` until you call `loadView` or `saveView`.
|
|
651
|
+
- **Filtering away from a view unloads it.** `activeViewId` returns to `null` as
|
|
652
|
+
soon as the grid stops showing that view, including through routes that never
|
|
653
|
+
mention views: `clearFilters`, `applyFilters`, `applyUrlFilters`, or the user
|
|
654
|
+
editing a filter in the grid's own UI. The stored pointer is cleared with it,
|
|
655
|
+
so `autoApplyOnMount` will not bring the view back on the next load. The grid
|
|
656
|
+
is compared against what it reported once the view was applied, so a view AG
|
|
657
|
+
Grid normalises (one naming a column the grid no longer has, say) stays loaded
|
|
658
|
+
and keeps its stored pointer. A write AG Grid defers, such as a restore made
|
|
659
|
+
before column data types are inferred, is recognised when it lands by the
|
|
660
|
+
event's `source: 'api'`; on AG Grid versions that do not report a source, a
|
|
661
|
+
deferred view the grid also normalises unloads when it lands. An API change
|
|
662
|
+
that filters a column the view does not, such as your own `setFilterModel`
|
|
663
|
+
over a view the grid ignored, is not taken for the view and unloads it. One
|
|
664
|
+
that filters only the view's columns cannot be told from the view landing.
|
|
665
|
+
Render the
|
|
666
|
+
active state from `activeViewId` and it follows the grid on its own. Replacing
|
|
667
|
+
`gridApi` also clears `activeViewId`, but keeps the stored pointer, so with
|
|
668
|
+
`autoApplyOnMount` the view is restored against the new grid.
|
|
669
|
+
- **URL filters win on mount.** With `autoApplyOnMount` set, an incoming URL that
|
|
670
|
+
carries filters takes precedence over the stored view, so a shared link shows the
|
|
671
|
+
sender's filters rather than the recipient's saved default. It wins once per
|
|
672
|
+
grid: switching `storageKey` later restores the new namespace's stored view
|
|
673
|
+
instead of applying the link again.
|
|
674
|
+
- **Deleting is conservative.** `deleteView` clears the grid only if it still shows
|
|
675
|
+
exactly that view's filters, so hand-edited filters survive.
|
|
676
|
+
- **Failures are reported, not silent.** A save that cannot be persisted (quota
|
|
677
|
+
exhausted, storage blocked by policy) returns `null` and reports through
|
|
678
|
+
`onError` with context `'save-view'`. Reads degrade to an empty list instead of
|
|
679
|
+
throwing, so server rendering is safe.
|
|
680
|
+
|
|
681
|
+
For non-React consumers the same store is available directly:
|
|
682
|
+
|
|
683
|
+
```ts
|
|
684
|
+
import { createViewStore } from 'ag-grid-url-sync/view-storage'
|
|
685
|
+
|
|
686
|
+
const store = createViewStore('employee-grid')
|
|
687
|
+
store.saveView('Engineering', gridApi.getFilterModel())
|
|
688
|
+
```
|
|
395
689
|
|
|
396
690
|
### React Features
|
|
397
691
|
|
|
398
692
|
- **๐ Automatic State Management**: Tracks grid state and filter changes automatically
|
|
399
693
|
- **โก Ready State**: `isReady` indicates when the grid API is available
|
|
400
|
-
- **๐ฏ Auto-Apply**: `autoApplyOnMount` applies URL filters when component mounts
|
|
694
|
+
- **๐ฏ Auto-Apply**: `autoApplyOnMount` applies URL filters when component mounts.
|
|
695
|
+
It is attempted once per grid: a failure is reported through `onError` and
|
|
696
|
+
`onParseError` and is not retried on later renders
|
|
401
697
|
- **๐งน Cleanup**: Automatically cleans up resources when component unmounts
|
|
402
698
|
- **๐ก๏ธ Error Boundaries**: Graceful error handling with configurable callbacks
|
|
403
699
|
- **๐ Filter Status**: `hasFilters` tracks whether any filters are active
|
|
700
|
+
- **๐พ Saved Views**: `storageKey` enables naming and revisiting filter sets
|
|
701
|
+
|
|
702
|
+
### React with Grouped Serialization
|
|
703
|
+
|
|
704
|
+
The React hook fully supports grouped serialization with all format options:
|
|
705
|
+
|
|
706
|
+
```tsx
|
|
707
|
+
import React, { useState } from 'react'
|
|
708
|
+
import { AgGridReact } from 'ag-grid-react'
|
|
709
|
+
import { useAGGridUrlSync } from 'ag-grid-url-sync/react'
|
|
710
|
+
|
|
711
|
+
function AdvancedGridComponent() {
|
|
712
|
+
const [gridApi, setGridApi] = useState(null)
|
|
713
|
+
|
|
714
|
+
// Grouped serialization with base64 format
|
|
715
|
+
const { shareUrl, applyUrlFilters, clearFilters, hasFilters, isReady } =
|
|
716
|
+
useAGGridUrlSync(gridApi, {
|
|
717
|
+
serialization: 'grouped',
|
|
718
|
+
format: 'base64',
|
|
719
|
+
groupedParam: 'grid_state',
|
|
720
|
+
autoApplyOnMount: true
|
|
721
|
+
})
|
|
722
|
+
|
|
723
|
+
const handleShareCompact = async () => {
|
|
724
|
+
// Base64 format creates very compact URLs
|
|
725
|
+
const url = shareUrl()
|
|
726
|
+
await navigator.clipboard.writeText(url)
|
|
727
|
+
alert('Compact filter URL copied!')
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
const handleShareJSON = async () => {
|
|
731
|
+
// Get filters in JSON format for debugging
|
|
732
|
+
const jsonFilters = urlSync?.getFiltersAsFormat?.('json')
|
|
733
|
+
console.log('Current filters as JSON:', jsonFilters)
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
return (
|
|
737
|
+
<div>
|
|
738
|
+
<div style={{ marginBottom: '10px' }}>
|
|
739
|
+
<button onClick={handleShareCompact} disabled={!isReady}>
|
|
740
|
+
๐ Share Compact URL
|
|
741
|
+
</button>
|
|
742
|
+
<button onClick={handleShareJSON} disabled={!isReady}>
|
|
743
|
+
๐ Debug Filters
|
|
744
|
+
</button>
|
|
745
|
+
<button onClick={clearFilters} disabled={!hasFilters}>
|
|
746
|
+
๐๏ธ Clear Filters
|
|
747
|
+
</button>
|
|
748
|
+
</div>
|
|
749
|
+
|
|
750
|
+
{/* Grid component */}
|
|
751
|
+
<div className="ag-theme-alpine" style={{ height: 400, width: '100%' }}>
|
|
752
|
+
<AgGridReact
|
|
753
|
+
onGridReady={params => setGridApi(params.api)}
|
|
754
|
+
// ... grid configuration
|
|
755
|
+
/>
|
|
756
|
+
</div>
|
|
757
|
+
</div>
|
|
758
|
+
)
|
|
759
|
+
}
|
|
760
|
+
```
|
|
404
761
|
|
|
405
762
|
### Advanced React Example
|
|
406
763
|
|
|
@@ -488,7 +845,7 @@ Mixed filters: https://app.com/page?f_name_contains=john&f_salary_gte=75000&f_ag
|
|
|
488
845
|
|
|
489
846
|
Parameter structure:
|
|
490
847
|
|
|
491
|
-
- Prefix: `f_` (configurable
|
|
848
|
+
- Prefix: `f_` (configurable)
|
|
492
849
|
- Format: `f_{columnName}_{operation}={value}`
|
|
493
850
|
- **Text Operations**: `contains`, `eq`, `neq`, `startsWith`, `endsWith`, `notContains`, `blank`, `notBlank`
|
|
494
851
|
- **Number Operations**: `eq`, `neq`, `gt`, `gte`, `lt`, `lte`, `range`, `blank`, `notBlank`
|
|
@@ -583,8 +940,22 @@ urlSync.destroy()
|
|
|
583
940
|
|
|
584
941
|
```typescript
|
|
585
942
|
interface AGGridUrlSyncConfig {
|
|
943
|
+
// Serialization mode (default: 'individual')
|
|
944
|
+
// 'individual': Each filter becomes a separate URL parameter
|
|
945
|
+
// 'grouped': All filters are packaged into a single URL parameter
|
|
946
|
+
serialization?: 'individual' | 'grouped'
|
|
947
|
+
|
|
948
|
+
// Format for grouped serialization (default: 'querystring')
|
|
949
|
+
// 'querystring': URL-encoded querystring format (most compatible)
|
|
950
|
+
// 'json': JSON-encoded format (good for complex filters)
|
|
951
|
+
// 'base64': Base64-encoded JSON (most compact)
|
|
952
|
+
format?: 'querystring' | 'json' | 'base64'
|
|
953
|
+
|
|
954
|
+
// Parameter name for grouped mode (default: 'grid_filters')
|
|
955
|
+
groupedParam?: string
|
|
956
|
+
|
|
586
957
|
// Prefix for URL parameters (default: 'f_')
|
|
587
|
-
//
|
|
958
|
+
// In grouped mode, this affects individual parameter names within the group
|
|
588
959
|
paramPrefix?: string
|
|
589
960
|
|
|
590
961
|
// Maximum length for filter values (default: 200)
|
|
@@ -607,12 +978,13 @@ Comprehensive demonstration showcasing all features:
|
|
|
607
978
|
- **Complete text filter support** - All 8 AG Grid text operations (contains, equals, not contains, not equal, starts with, ends with, blank, not blank)
|
|
608
979
|
- **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
980
|
- **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
|
-
- **
|
|
611
|
-
- **
|
|
612
|
-
- **
|
|
613
|
-
- **
|
|
614
|
-
- **
|
|
615
|
-
- **
|
|
981
|
+
- **Grouped serialization** - Package all filters into a single URL parameter with querystring, JSON, or base64 encoding
|
|
982
|
+
- **Manual URL generation** - Generate shareable URLs with current filter states
|
|
983
|
+
- **Bidirectional sync** - Automatic synchronization between AG Grid and URL
|
|
984
|
+
- **Framework agnostic** - Works with any AG Grid setup, no dependencies
|
|
985
|
+
- **High performance** - Efficiently handles 100+ filters with <20ms latency
|
|
986
|
+
- **Robust error handling** - Graceful handling of malformed URLs and special characters
|
|
987
|
+
- **Lightweight** - Minimal bundle size (~3KB gzipped), ideal for modern web applications
|
|
616
988
|
|
|
617
989
|
### โ๏ธ [React Integration](./examples/react-basic/basic-grid.tsx)
|
|
618
990
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { GridApi } from 'ag-grid-community';
|
|
2
|
-
import type { AGGridUrlSyncConfig, FilterState } from './types.js';
|
|
2
|
+
import type { AGGridUrlSyncConfig, FilterState, SerializationFormat, SerializationMode } from './types.js';
|
|
3
3
|
/**
|
|
4
4
|
* AGGridUrlSync class for synchronizing AG Grid text filters with URL parameters
|
|
5
5
|
*/
|
|
@@ -40,6 +40,21 @@ export declare class AGGridUrlSync {
|
|
|
40
40
|
* Cleans up any resources or event listeners
|
|
41
41
|
*/
|
|
42
42
|
destroy(): void;
|
|
43
|
+
/**
|
|
44
|
+
* Gets the current serialization mode
|
|
45
|
+
* @returns The current serialization mode
|
|
46
|
+
*/
|
|
47
|
+
getSerializationMode(): SerializationMode;
|
|
48
|
+
/**
|
|
49
|
+
* Gets the current format (for grouped mode)
|
|
50
|
+
* @returns The current format
|
|
51
|
+
*/
|
|
52
|
+
getCurrentFormat(): SerializationFormat; /**
|
|
53
|
+
* Gets filters in a specific format (useful for sharing/export)
|
|
54
|
+
* @param format - The format to serialize to
|
|
55
|
+
* @returns Serialized filter string
|
|
56
|
+
*/
|
|
57
|
+
getFiltersAsFormat(format: SerializationFormat): string;
|
|
43
58
|
}
|
|
44
59
|
/**
|
|
45
60
|
* Factory function to create a new AGGridUrlSync instance
|
|
@@ -2,78 +2,56 @@ import { DEFAULT_CONFIG } from './validation.js';
|
|
|
2
2
|
import { parseUrlFilters } from './url-parser.js';
|
|
3
3
|
import { generateUrl } from './url-generator.js';
|
|
4
4
|
import { getFilterModel, applyFilterModel } from './grid-integration.js';
|
|
5
|
-
|
|
6
|
-
* AGGridUrlSync class for synchronizing AG Grid text filters with URL parameters
|
|
7
|
-
*/
|
|
5
|
+
import { serializeGrouped } from './serialization/grouped.js';
|
|
8
6
|
export class AGGridUrlSync {
|
|
9
7
|
config;
|
|
10
|
-
/**
|
|
11
|
-
* Creates a new instance of AGGridUrlSync
|
|
12
|
-
* @param gridApi - AG Grid API instance
|
|
13
|
-
* @param config - Optional configuration options
|
|
14
|
-
*/
|
|
15
8
|
constructor(gridApi, config = {}) {
|
|
16
9
|
this.config = {
|
|
17
10
|
gridApi,
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
onParseError: config.onParseError ?? (() => { })
|
|
11
|
+
...DEFAULT_CONFIG,
|
|
12
|
+
...config
|
|
21
13
|
};
|
|
22
14
|
}
|
|
23
|
-
/**
|
|
24
|
-
* Generates a URL with the current filter state
|
|
25
|
-
* @param baseUrl - Optional base URL (defaults to current URL)
|
|
26
|
-
* @returns URL string with filter parameters
|
|
27
|
-
*/
|
|
28
15
|
generateUrl(baseUrl) {
|
|
29
16
|
const filterState = getFilterModel(this.config);
|
|
30
17
|
const url = baseUrl ?? window.location.href;
|
|
31
18
|
return generateUrl(url, filterState, this.config);
|
|
32
19
|
}
|
|
33
|
-
/**
|
|
34
|
-
* Gets the current filter state as URL query parameters
|
|
35
|
-
* @returns Query parameter string
|
|
36
|
-
*/
|
|
37
20
|
getQueryParams() {
|
|
38
21
|
const url = this.generateUrl();
|
|
39
22
|
const urlObj = new URL(url);
|
|
40
23
|
return urlObj.search;
|
|
41
24
|
}
|
|
42
|
-
/**
|
|
43
|
-
* Applies filters from a URL to the grid
|
|
44
|
-
* @param url - Optional URL to parse (defaults to current URL)
|
|
45
|
-
*/
|
|
46
25
|
applyFromUrl(url) {
|
|
47
26
|
const urlToParse = url ?? window.location.href;
|
|
48
27
|
const filterState = parseUrlFilters(urlToParse, this.config);
|
|
49
28
|
this.applyFilters(filterState);
|
|
50
29
|
}
|
|
51
|
-
/**
|
|
52
|
-
* Applies a filter state object to the grid
|
|
53
|
-
* @param filterState - Filter state to apply
|
|
54
|
-
*/
|
|
55
30
|
applyFilters(filterState) {
|
|
56
31
|
applyFilterModel(filterState, this.config);
|
|
57
32
|
}
|
|
58
|
-
/**
|
|
59
|
-
* Clears all text filters from the grid
|
|
60
|
-
*/
|
|
61
33
|
clearFilters() {
|
|
62
34
|
this.applyFilters({});
|
|
63
35
|
}
|
|
64
|
-
/**
|
|
65
|
-
* Cleans up any resources or event listeners
|
|
66
|
-
*/
|
|
67
36
|
destroy() {
|
|
68
|
-
|
|
37
|
+
}
|
|
38
|
+
getSerializationMode() {
|
|
39
|
+
return this.config.serialization;
|
|
40
|
+
}
|
|
41
|
+
getCurrentFormat() {
|
|
42
|
+
return this.config.format;
|
|
43
|
+
}
|
|
44
|
+
getFiltersAsFormat(format) {
|
|
45
|
+
const filterState = getFilterModel(this.config);
|
|
46
|
+
const tempConfig = {
|
|
47
|
+
...this.config,
|
|
48
|
+
format,
|
|
49
|
+
serialization: 'grouped'
|
|
50
|
+
};
|
|
51
|
+
const result = serializeGrouped(filterState, tempConfig);
|
|
52
|
+
return result.value;
|
|
69
53
|
}
|
|
70
54
|
}
|
|
71
|
-
/**
|
|
72
|
-
* Factory function to create a new AGGridUrlSync instance
|
|
73
|
-
* @param gridApi - AG Grid API instance
|
|
74
|
-
* @param config - Optional configuration options
|
|
75
|
-
* @returns AGGridUrlSync instance
|
|
76
|
-
*/
|
|
77
55
|
export function createUrlSync(gridApi, config) {
|
|
78
56
|
return new AGGridUrlSync(gridApi, config);
|
|
79
57
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ag-grid-url-sync.js","sourceRoot":"","sources":["../../src/core/ag-grid-url-sync.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ag-grid-url-sync.js","sourceRoot":"","sources":["../../src/core/ag-grid-url-sync.ts"],"names":[],"mappings":"AAQA,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;AACxE,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAA;AAK7D,MAAM,OAAO,aAAa;IAChB,MAAM,CAAgB;IAO9B,YAAY,OAAgB,EAAE,MAAM,GAAwB,EAAE;QAC5D,IAAI,CAAC,MAAM,GAAG;YACZ,OAAO;YACP,GAAG,cAAc;YACjB,GAAG,MAAM;SACV,CAAA;IACH,CAAC;IAOD,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;IAMD,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;IAMD,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;IAMD,YAAY,CAAC,WAAwB;QACnC,gBAAgB,CAAC,WAAW,EAAE,IAAI,CAAC,MAAM,CAAC,CAAA;IAC5C,CAAC;IAKD,YAAY;QACV,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC,CAAA;IACvB,CAAC;IAKD,OAAO;IAEP,CAAC;IAMD,oBAAoB;QAClB,OAAO,IAAI,CAAC,MAAM,CAAC,aAAa,CAAA;IAClC,CAAC;IAMD,gBAAgB;QACd,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM,CAAA;IAC3B,CAAC;IAKD,kBAAkB,CAAC,MAA2B;QAC5C,MAAM,WAAW,GAAG,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QAG/C,MAAM,UAAU,GAAG;YACjB,GAAG,IAAI,CAAC,MAAM;YACd,MAAM;YACN,aAAa,EAAE,SAAkB;SAClC,CAAA;QAGD,MAAM,MAAM,GAAG,gBAAgB,CAAC,WAAW,EAAE,UAAU,CAAC,CAAA;QAExD,OAAO,MAAM,CAAC,KAAK,CAAA;IACrB,CAAC;CACF;AAQD,MAAM,UAAU,aAAa,CAC3B,OAAgB,EAChB,MAA4B;IAE5B,OAAO,IAAI,aAAa,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;AAC3C,CAAC"}
|