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.
Files changed (39) hide show
  1. package/README.md +395 -23
  2. package/dist/core/ag-grid-url-sync.d.ts +16 -1
  3. package/dist/core/ag-grid-url-sync.js +19 -41
  4. package/dist/core/ag-grid-url-sync.js.map +1 -1
  5. package/dist/core/grid-integration.js +5 -67
  6. package/dist/core/grid-integration.js.map +1 -1
  7. package/dist/core/index.d.ts +1 -0
  8. package/dist/core/index.js +1 -4
  9. package/dist/core/index.js.map +1 -1
  10. package/dist/core/serialization/formats.d.ts +29 -0
  11. package/dist/core/serialization/formats.js +68 -0
  12. package/dist/core/serialization/formats.js.map +1 -0
  13. package/dist/core/serialization/grouped.d.ts +36 -0
  14. package/dist/core/serialization/grouped.js +105 -0
  15. package/dist/core/serialization/grouped.js.map +1 -0
  16. package/dist/core/serialization/index.d.ts +7 -0
  17. package/dist/core/serialization/index.js +3 -0
  18. package/dist/core/serialization/index.js.map +1 -0
  19. package/dist/core/types.d.ts +75 -5
  20. package/dist/core/types.js +7 -115
  21. package/dist/core/types.js.map +1 -1
  22. package/dist/core/url-generator.js +44 -38
  23. package/dist/core/url-generator.js.map +1 -1
  24. package/dist/core/url-parser.js +8 -52
  25. package/dist/core/url-parser.js.map +1 -1
  26. package/dist/core/validation.d.ts +13 -2
  27. package/dist/core/validation.js +34 -72
  28. package/dist/core/validation.js.map +1 -1
  29. package/dist/core/view-storage.d.ts +63 -0
  30. package/dist/core/view-storage.js +112 -0
  31. package/dist/core/view-storage.js.map +1 -0
  32. package/dist/index.d.ts +3 -0
  33. package/dist/index.js +2 -5
  34. package/dist/index.js.map +1 -1
  35. package/dist/react/index.d.ts +1 -0
  36. package/dist/react/types.d.ts +108 -1
  37. package/dist/react/use-ag-grid-url-sync.js +440 -42
  38. package/dist/react/use-ag-grid-url-sync.js.map +1 -1
  39. 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
- - ๐Ÿ”— Manual URL generation for sharing filter states
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 for multi-grid scenarios
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 | Type | Description |
385
- | ----------------- | -------------------------------- | ----------------------------------------------- |
386
- | `shareUrl` | `(baseUrl?: string) => string` | Generate shareable URL with current filters |
387
- | `getQueryParams` | `() => string` | Get filter state as query parameters |
388
- | `applyUrlFilters` | `(url?: string) => void` | Apply filters from URL to grid |
389
- | `clearFilters` | `() => void` | Clear all text filters |
390
- | `isReady` | `boolean` | Whether grid API is available and hook is ready |
391
- | `currentUrl` | `string` | Current URL with filters applied |
392
- | `hasFilters` | `boolean` | Whether grid has any active text filters |
393
- | `parseUrlFilters` | `(url: string) => FilterState` | Parse filters from URL without applying |
394
- | `applyFilters` | `(filters: FilterState) => void` | Apply filter state object to grid |
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 - useful for multi-grid scenarios)
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
- // Useful for multi-grid scenarios: 'emp_', 'proj_', etc.
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
- - **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
612
- - **URL sharing workflow** - Copy to clipboard, email, and Slack integration
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
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
- paramPrefix: config.paramPrefix ?? DEFAULT_CONFIG.paramPrefix,
19
- maxValueLength: config.maxValueLength ?? DEFAULT_CONFIG.maxValueLength,
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
- // Currently no cleanup needed, but included for future use
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":"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"}
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"}