ag-grid-url-sync 0.5.0 โ 0.5.2
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 +339 -66
- package/dist/core/ag-grid-url-sync.d.ts +16 -9
- package/dist/core/ag-grid-url-sync.js +23 -19
- package/dist/core/ag-grid-url-sync.js.map +1 -1
- package/dist/core/codecs.d.ts +28 -0
- package/dist/core/codecs.js +39 -0
- package/dist/core/codecs.js.map +1 -0
- package/dist/core/data-issue-codes.d.ts +3 -0
- package/dist/core/data-issue-codes.js +10 -0
- package/dist/core/data-issue-codes.js.map +1 -0
- package/dist/core/grid-integration.d.ts +38 -2
- package/dist/core/grid-integration.js +169 -128
- package/dist/core/grid-integration.js.map +1 -1
- package/dist/core/grid-url-sync.d.ts +62 -0
- package/dist/core/grid-url-sync.js +738 -0
- package/dist/core/grid-url-sync.js.map +1 -0
- package/dist/core/index.d.ts +4 -1
- package/dist/core/index.js +4 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/individual-params.d.ts +12 -5
- package/dist/core/individual-params.js +38 -62
- package/dist/core/individual-params.js.map +1 -1
- package/dist/core/issue-columns.d.ts +2 -0
- package/dist/core/issue-columns.js +11 -0
- package/dist/core/issue-columns.js.map +1 -0
- package/dist/core/issues.d.ts +34 -0
- package/dist/core/issues.js +51 -0
- package/dist/core/issues.js.map +1 -0
- package/dist/core/operators.d.ts +189 -0
- package/dist/core/operators.js +60 -0
- package/dist/core/operators.js.map +1 -0
- package/dist/core/options.d.ts +21 -0
- package/dist/core/options.js +21 -0
- package/dist/core/options.js.map +1 -0
- package/dist/core/own-write.d.ts +138 -0
- package/dist/core/own-write.js +133 -0
- package/dist/core/own-write.js.map +1 -0
- package/dist/core/schemas.d.ts +51 -0
- package/dist/core/schemas.js +59 -0
- package/dist/core/schemas.js.map +1 -0
- package/dist/core/serialization/formats.d.ts +7 -7
- package/dist/core/serialization/formats.js +34 -28
- package/dist/core/serialization/formats.js.map +1 -1
- package/dist/core/serialization/grouped.d.ts +9 -12
- package/dist/core/serialization/grouped.js +24 -17
- package/dist/core/serialization/grouped.js.map +1 -1
- package/dist/core/to-error.d.ts +5 -0
- package/dist/core/to-error.js +4 -0
- package/dist/core/to-error.js.map +1 -0
- package/dist/core/types.d.ts +296 -43
- package/dist/core/types.js +39 -108
- package/dist/core/types.js.map +1 -1
- package/dist/core/url-generator.d.ts +4 -4
- package/dist/core/url-generator.js +9 -7
- package/dist/core/url-generator.js.map +1 -1
- package/dist/core/url-parser.d.ts +4 -4
- package/dist/core/url-parser.js +6 -4
- package/dist/core/url-parser.js.map +1 -1
- package/dist/core/validation.d.ts +21 -14
- package/dist/core/validation.js +58 -54
- package/dist/core/validation.js.map +1 -1
- package/dist/core/view-storage.js +21 -19
- package/dist/core/view-storage.js.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/react/index.d.ts +3 -2
- package/dist/react/index.js +1 -0
- package/dist/react/index.js.map +1 -1
- package/dist/react/sync-holder.d.ts +22 -0
- package/dist/react/sync-holder.js +68 -0
- package/dist/react/sync-holder.js.map +1 -0
- package/dist/react/sync-options.d.ts +17 -0
- package/dist/react/sync-options.js +22 -0
- package/dist/react/sync-options.js.map +1 -0
- package/dist/react/types.d.ts +101 -14
- package/dist/react/use-ag-grid-url-sync.d.ts +3 -0
- package/dist/react/use-ag-grid-url-sync.js +344 -499
- package/dist/react/use-ag-grid-url-sync.js.map +1 -1
- package/dist/react/use-grid-url-sync.d.ts +14 -0
- package/dist/react/use-grid-url-sync.js +206 -0
- package/dist/react/use-grid-url-sync.js.map +1 -0
- package/package.json +35 -15
package/README.md
CHANGED
|
@@ -145,7 +145,7 @@ The library provides two main entry points:
|
|
|
145
145
|
|
|
146
146
|
```typescript
|
|
147
147
|
// Core functionality - works with any framework
|
|
148
|
-
import {
|
|
148
|
+
import { createGridUrlSync } from 'ag-grid-url-sync'
|
|
149
149
|
|
|
150
150
|
// React hook - for React applications
|
|
151
151
|
import { useAGGridUrlSync } from 'ag-grid-url-sync/react'
|
|
@@ -195,16 +195,13 @@ import type {
|
|
|
195
195
|
|
|
196
196
|
```typescript
|
|
197
197
|
import { parseUrlFilters } from 'ag-grid-url-sync/url-parser'
|
|
198
|
-
import {
|
|
199
|
-
import type { FilterState, ParseConfig } from 'ag-grid-url-sync/types'
|
|
198
|
+
import type { FilterState } from 'ag-grid-url-sync/types'
|
|
200
199
|
|
|
201
200
|
function getFiltersFromUrl(url: string): FilterState {
|
|
202
|
-
|
|
203
|
-
|
|
201
|
+
// Options that you leave out get their defaults. No grid is needed.
|
|
202
|
+
return parseUrlFilters(url, {
|
|
204
203
|
onParseError: error => console.error('Parse error:', error)
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
return parseUrlFilters(url, config)
|
|
204
|
+
})
|
|
208
205
|
}
|
|
209
206
|
```
|
|
210
207
|
|
|
@@ -227,22 +224,78 @@ function getFiltersFromUrl(url: string): FilterState {
|
|
|
227
224
|
|
|
228
225
|
```typescript
|
|
229
226
|
import { generateUrl } from 'ag-grid-url-sync/url-generator'
|
|
230
|
-
import {
|
|
231
|
-
import type { FilterState, ParseConfig } from 'ag-grid-url-sync/types'
|
|
227
|
+
import type { FilterState } from 'ag-grid-url-sync/types'
|
|
232
228
|
|
|
233
229
|
function createShareableUrl(filters: FilterState, baseUrl: string): string {
|
|
234
|
-
|
|
235
|
-
...DEFAULT_CONFIG, // gridApi is not needed for generation
|
|
236
|
-
maxValueLength: 500
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
return generateUrl(baseUrl, filters, config)
|
|
230
|
+
return generateUrl(baseUrl, filters, { maxValueLength: 500 })
|
|
240
231
|
}
|
|
241
232
|
```
|
|
242
233
|
|
|
234
|
+
#### Example: Read and Write the Grid Filters
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
import {
|
|
238
|
+
getFilterModel,
|
|
239
|
+
applyFilterModel
|
|
240
|
+
} from 'ag-grid-url-sync/grid-integration'
|
|
241
|
+
|
|
242
|
+
const filters = getFilterModel(gridApi, { onParseError: console.warn })
|
|
243
|
+
applyFilterModel(filters, otherGridApi)
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
The config form (`getFilterModel({ ...DEFAULT_CONFIG, gridApi })`) still works, but it is deprecated.
|
|
247
|
+
|
|
243
248
|
**๐ก Pro Tip**: Use granular imports in libraries and applications where bundle size matters. All existing import patterns remain fully supported for backward compatibility.
|
|
244
249
|
|
|
245
|
-
###
|
|
250
|
+
### Vanilla JS: `createGridUrlSync`
|
|
251
|
+
|
|
252
|
+
One call gives full sync. `createGridUrlSync` applies the address bar's filters to the grid, follows the grid's filter changes, can write them to the address bar, and removes its listeners when the grid is destroyed.
|
|
253
|
+
|
|
254
|
+
```typescript
|
|
255
|
+
import { createGridUrlSync, individual, grouped } from 'ag-grid-url-sync'
|
|
256
|
+
|
|
257
|
+
const sync = createGridUrlSync({
|
|
258
|
+
api: gridApi,
|
|
259
|
+
codec: individual({ prefix: 'f_' }), // or grouped({ param: 'gf', format: 'json' })
|
|
260
|
+
history: 'replace', // 'none' (default) | 'replace' | 'push' | (url) => router.replace(url)
|
|
261
|
+
onIssue: ({ issue, source }) => {
|
|
262
|
+
if (issue.severity === 'warning') showToast(issue.message)
|
|
263
|
+
}
|
|
264
|
+
})
|
|
265
|
+
|
|
266
|
+
// The current state: { status, url, hasFilters, issues, views, activeViewId }
|
|
267
|
+
const { url, hasFilters } = sync.getSnapshot()
|
|
268
|
+
|
|
269
|
+
// Called after each change of the snapshot. Returns an unsubscribe function.
|
|
270
|
+
const unsubscribe = sync.subscribe(() => render(sync.getSnapshot()))
|
|
271
|
+
|
|
272
|
+
sync.applyUrl('https://app.com/data?f_name_contains=jo') // { issues }
|
|
273
|
+
sync.getUrl('https://app.com/share') // a share link for the grid's filters
|
|
274
|
+
sync.applyFilters({
|
|
275
|
+
name: { filterType: 'text', type: 'contains', filter: 'jo' }
|
|
276
|
+
}) // { issues }
|
|
277
|
+
sync.destroy()
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
`applyFilters` writes filters to the grid as the app's own write. When they land, the sync writes the URL in the `history` mode, as it does for a user edit. `applyUrl` writes no history, because the URL already holds those filters.
|
|
281
|
+
|
|
282
|
+
| Option | Default | Meaning |
|
|
283
|
+
| ------------------ | ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
284
|
+
| `api` | (required) | The AG Grid API |
|
|
285
|
+
| `codec` | `individual()` | How the filters are written in the URL. `individual({ prefix })` writes one param for each filter; `grouped({ param, format })` writes all filters in one param |
|
|
286
|
+
| `maxValueLength` | `200` | The longest text value that is kept |
|
|
287
|
+
| `history` | `'none'` | How a filter change goes to the address bar. `'replace'` and `'push'` use `window.history`; a function gets the URL, for a router. The page's other params, the `#hash` and `history.state` stay |
|
|
288
|
+
| `popstate` | `true` only with `'push'` | Apply the URL when the back or forward button changes it |
|
|
289
|
+
| `applyUrlOnCreate` | `true` | Apply the address bar's filters on create. A URL with no filter params keeps the grid's filters (for example from `initialState`), and with `history` they are written to the URL with replace |
|
|
290
|
+
| `views` | none | The saved views. Give `createViewStore(key)` from `ag-grid-url-sync/view-storage`. See [Saved Views](#saved-views) |
|
|
291
|
+
| `throwOnError` | `false` | Throw an issue instead of reporting it, in the methods that the app calls (`applyUrl`, `getUrl`, `applyFilters`, `saveView`, `loadView`, `deleteView`). `createGridUrlSync` and its listeners never throw |
|
|
292
|
+
| `onIssue` | none | Gets each issue one time, as `{ issue, source }`. `source` is `'url'` (the sync read the address bar), `'grid'` (it read the grid) or `'api'` (a method that the app called) |
|
|
293
|
+
|
|
294
|
+
The snapshot `status` is `'active'` from create, and `'destroyed'` after `destroy()` or after the grid is destroyed. After that, `applyUrl`, `applyFilters`, `saveView` and `loadView` report `NOT_READY` to `onIssue`. `deleteView` needs only storage, so it still works. `IDLE_SNAPSHOT` (`status: 'idle'`) is the snapshot for a time when there is no sync yet. `getSnapshot()` returns the same object until something changes, so it works with React's `useSyncExternalStore`. The sync writes no history for an `applyUrl` apply, because the URL holds those filters already. `applyFilters` and `loadView` write the URL as a user edit does. Nothing reads `window` when the module loads, and a sync made with no `window` does nothing to the address bar.
|
|
295
|
+
|
|
296
|
+
### Basic Example (deprecated `createUrlSync`)
|
|
297
|
+
|
|
298
|
+
`createUrlSync` and `AGGridUrlSync` will be removed in 0.6.0. Use `createGridUrlSync`.
|
|
246
299
|
|
|
247
300
|
```typescript
|
|
248
301
|
import { createUrlSync } from 'ag-grid-url-sync'
|
|
@@ -435,38 +488,83 @@ import {
|
|
|
435
488
|
|
|
436
489
|
---
|
|
437
490
|
|
|
438
|
-
### Error Handling
|
|
491
|
+
### Error Handling
|
|
492
|
+
|
|
493
|
+
The library never writes to the console. Each problem goes to one callback, and each error that the library makes has a `code` that you can switch on:
|
|
494
|
+
|
|
495
|
+
| Code | Meaning | Callback |
|
|
496
|
+
| ------------------------------------------------------------- | -------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
|
|
497
|
+
| `INVALID_PARAM` | A param name that is not valid, or an unknown operation | `onParseError` |
|
|
498
|
+
| `INVALID_NUMBER` | A value that is not a number, or that would be rounded | `onParseError` |
|
|
499
|
+
| `INVALID_DATE` | A date that does not exist, or a date range in the wrong order | `onParseError` |
|
|
500
|
+
| `VALUE_TOO_LONG` | A text value over `maxValueLength` | `onParseError` |
|
|
501
|
+
| `INVALID_GROUPED` | A grouped value that does not decode (bad JSON or base64) | `onParseError` |
|
|
502
|
+
| `TYPE_MISMATCH` | A number or date filter on a column of another type | `onParseError` |
|
|
503
|
+
| `INVALID_URL` | A URL that the parser cannot read | Thrown by `parseUrlFilters` and `applyFromUrl`. The React hook catches it and sends it to `onParseError` |
|
|
504
|
+
| `GRID_ERROR` | The AG Grid API threw | `onParseError` in the core, `onError` in the React hook |
|
|
505
|
+
| `NOT_READY` | A hook operation called before the grid is ready | `onError` (React) |
|
|
506
|
+
| `VIEW_NOT_FOUND`, `VIEW_NAME_REQUIRED` | A saved view operation that cannot run | `onError` (React) |
|
|
507
|
+
| `STORAGE_UNAVAILABLE`, `STORAGE_FULL`, `STORAGE_WRITE_FAILED` | Browser storage for saved views failed | `onError` (React) |
|
|
508
|
+
| `HISTORY_WRITE_FAILED` | The sync could not write the address bar | `onIssue` in the core, `onError` in the React hook |
|
|
439
509
|
|
|
440
|
-
|
|
510
|
+
Each error has a `severity`: `'warning'` for the first seven rows, `'error'` for the others. `createGridUrlSync` sends every issue to `onIssue`; the callbacks in the table are for the deprecated API and the React hook.
|
|
511
|
+
|
|
512
|
+
The codes in the first seven rows are data issues: a filter that is dropped or is not valid. The library skips that filter and keeps the others. When the library knows the column, it is in `error.column`. `onParseError` is typed with `Error`, so check `instanceof URLSyncError` before you read `code` or `column`.
|
|
513
|
+
|
|
514
|
+
Every error is a `URLSyncError`. The classes from earlier versions (`InvalidFilterError`, `InvalidDateError`, `InvalidURLError`, `InvalidSerializationError`) are subclasses of it, so an `instanceof` check still works.
|
|
441
515
|
|
|
442
516
|
```typescript
|
|
443
|
-
import {
|
|
517
|
+
import { createUrlSync, URLSyncError } from 'ag-grid-url-sync'
|
|
444
518
|
|
|
445
519
|
const urlSync = createUrlSync(gridApi, {
|
|
446
|
-
serialization: 'grouped',
|
|
447
|
-
format: 'json',
|
|
448
520
|
onParseError: error => {
|
|
449
|
-
if (error instanceof
|
|
450
|
-
|
|
451
|
-
alert('Invalid filter data in URL!')
|
|
452
|
-
} else {
|
|
453
|
-
// Handle other errors
|
|
454
|
-
console.error(error)
|
|
521
|
+
if (error instanceof URLSyncError && error.code === 'VALUE_TOO_LONG') {
|
|
522
|
+
showToast(`The link leaves out the filter on ${error.column}`)
|
|
455
523
|
}
|
|
456
524
|
}
|
|
457
525
|
})
|
|
458
526
|
```
|
|
459
527
|
|
|
528
|
+
In grouped mode, an error that comes from the grouped URL value is an `InvalidSerializationError`, and the original error is in `error.cause`. When the original error is an issue with one filter (for example a value over `maxValueLength`), the wrapper has the same `code` and `column`. So a check on `error.code` works the same in both modes. `INVALID_GROUPED` means that the grouped value itself does not decode.
|
|
529
|
+
|
|
460
530
|
In every grouped format (`querystring`, `json`, `base64`), a text value over `maxValueLength` (default 200) is dropped and reported through `onParseError`, both when the URL is generated and when it is parsed.
|
|
461
531
|
|
|
462
|
-
|
|
532
|
+
#### Dropped Filters in the Result
|
|
533
|
+
|
|
534
|
+
A caller does not need a callback to see dropped filters. `getUrlState()` and the apply methods return the issues of each call. They are the same objects that `onParseError` gets.
|
|
463
535
|
|
|
464
536
|
```typescript
|
|
465
|
-
|
|
537
|
+
const { url, hasFilters, issues } = urlSync.getUrlState()
|
|
466
538
|
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
539
|
+
if (issues.length > 0) {
|
|
540
|
+
// The link leaves out these filters, so the recipient sees other rows
|
|
541
|
+
showToast(`The link leaves out ${issues.length} filter(s)`)
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
const { issues: urlIssues } = urlSync.applyFromUrl()
|
|
545
|
+
```
|
|
546
|
+
|
|
547
|
+
In React, the hook returns `issues`: the issues of the latest URL apply (`applyUrlFilters`, `applyFilters`, auto-apply), followed by the issues of the latest URL write. The apply issues stay until the next apply, `clearFilters` or `loadView`. Operation failures (`onError`) are not in the list:
|
|
548
|
+
|
|
549
|
+
```tsx
|
|
550
|
+
const { shareUrl, issues } = useAGGridUrlSync(gridApi)
|
|
551
|
+
|
|
552
|
+
return issues.length > 0 ? (
|
|
553
|
+
<Warning>The link leaves out a filter</Warning>
|
|
554
|
+
) : null
|
|
555
|
+
```
|
|
556
|
+
|
|
557
|
+
#### `throwOnError`
|
|
558
|
+
|
|
559
|
+
Like TanStack Query, the library can throw an issue instead of reporting it. Set `throwOnError: true` to throw every issue, or give a function that picks issues. In the core, a thrown issue does not go to `onParseError`, and the first one stops the call. In React, the hook keeps reporting each issue to `onParseError` one time, and then throws the picked issue during render, so an error boundary catches it. The default is `false`.
|
|
560
|
+
|
|
561
|
+
Only `createGridUrlSync` or the hook with `history` changes the address bar. Before an error boundary resets, remove the bad param from the URL (or the filter from the grid), or the same issue is thrown again.
|
|
562
|
+
|
|
563
|
+
```tsx
|
|
564
|
+
useAGGridUrlSync(gridApi, {
|
|
565
|
+
// Throw only when a link would leave out a filter
|
|
566
|
+
throwOnError: issue => issue.code === 'VALUE_TOO_LONG'
|
|
567
|
+
})
|
|
470
568
|
```
|
|
471
569
|
|
|
472
570
|
### Exports
|
|
@@ -486,7 +584,7 @@ Refer to the [package.json](./package.json) for the full export map.
|
|
|
486
584
|
|
|
487
585
|
## React Integration
|
|
488
586
|
|
|
489
|
-
For React applications, use the
|
|
587
|
+
For React applications, use the `useGridUrlSync` hook. It is a thin adapter on [`createGridUrlSync`](#vanilla-js-creategridurlsync): it takes the same options, and the grid behaves as with the vanilla sync. The older `useAGGridUrlSync` hook is deprecated and will be removed in 0.6.0 (see [Migrating from `useAGGridUrlSync`](#migrating-from-useaggridurlsync)).
|
|
490
588
|
|
|
491
589
|
### Installation for React
|
|
492
590
|
|
|
@@ -494,7 +592,90 @@ For React applications, use the dedicated `useAGGridUrlSync` hook that provides
|
|
|
494
592
|
npm install ag-grid-url-sync
|
|
495
593
|
```
|
|
496
594
|
|
|
497
|
-
|
|
595
|
+
The hook works with React 16.8 and later. It uses the `use-sync-external-store` package, which npm installs with the library. On React 18 and later, that package uses React's own `useSyncExternalStore`.
|
|
596
|
+
|
|
597
|
+
### `useGridUrlSync({ api, ...options })`
|
|
598
|
+
|
|
599
|
+
```tsx
|
|
600
|
+
import { useState } from 'react'
|
|
601
|
+
import { AgGridReact } from 'ag-grid-react'
|
|
602
|
+
import type { GridApi } from 'ag-grid-community'
|
|
603
|
+
import { useGridUrlSync } from 'ag-grid-url-sync/react'
|
|
604
|
+
import { individual } from 'ag-grid-url-sync'
|
|
605
|
+
|
|
606
|
+
function GridComponent() {
|
|
607
|
+
const [api, setApi] = useState<GridApi | null>(null)
|
|
608
|
+
|
|
609
|
+
const { status, hasFilters, issues, getUrl, applyFilters } = useGridUrlSync({
|
|
610
|
+
api,
|
|
611
|
+
codec: individual({ prefix: 'filter_' }),
|
|
612
|
+
history: 'push', // each filter change adds a history entry; back and forward apply the URL
|
|
613
|
+
onIssue: ({ issue, source }) => console.warn(source, issue.code)
|
|
614
|
+
})
|
|
615
|
+
|
|
616
|
+
return (
|
|
617
|
+
<div>
|
|
618
|
+
<button
|
|
619
|
+
disabled={status !== 'active'}
|
|
620
|
+
onClick={() => navigator.clipboard.writeText(getUrl())}
|
|
621
|
+
>
|
|
622
|
+
Share filters
|
|
623
|
+
</button>
|
|
624
|
+
<button disabled={!hasFilters} onClick={() => applyFilters({})}>
|
|
625
|
+
Clear filters
|
|
626
|
+
</button>
|
|
627
|
+
{issues.length > 0 && (
|
|
628
|
+
<p>The link leaves out {issues.length} filter(s)</p>
|
|
629
|
+
)}
|
|
630
|
+
<AgGridReact onGridReady={params => setApi(params.api)} {...gridProps} />
|
|
631
|
+
</div>
|
|
632
|
+
)
|
|
633
|
+
}
|
|
634
|
+
```
|
|
635
|
+
|
|
636
|
+
The options are the [`createGridUrlSync` options](#vanilla-js-creategridurlsync) (`codec`, `maxValueLength`, `history`, `popstate`, `applyUrlOnCreate`, `throwOnError`, `onIssue`), with these changes:
|
|
637
|
+
|
|
638
|
+
| Option | Default | Description |
|
|
639
|
+
| -------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
640
|
+
| `api` | none | The AG Grid API, or `null` while the grid starts |
|
|
641
|
+
| `storageKey` | none | Enables saved views, kept in `localStorage` under this key (in place of the core's `views` store). With no key, the view methods do nothing |
|
|
642
|
+
| `enabled` | `true` | With `false`, the hook makes no sync: `status` is `'idle'`, and a method reports `NOT_READY`. `views` still shows what is in storage. Back to `true` starts again, as a new sync does: the URL is applied again |
|
|
643
|
+
| `throwOnError` | `false` | As in TanStack Query: the hook throws a picked issue during render, so an error boundary catches it. The methods do not throw |
|
|
644
|
+
|
|
645
|
+
The hook returns the snapshot and the methods of the sync:
|
|
646
|
+
|
|
647
|
+
| Property | Description |
|
|
648
|
+
| ------------------------------------ | -------------------------------------------------------------------------------- |
|
|
649
|
+
| `status` | `'idle'` (no grid, no sync yet, or a server render), `'active'` or `'destroyed'` |
|
|
650
|
+
| `url`, `hasFilters`, `issues` | The URL with the grid's filters, whether there are filters, the latest issues |
|
|
651
|
+
| `views`, `activeViewId` | The saved views and the active one |
|
|
652
|
+
| `applyUrl(url?)`, `applyFilters(f)` | Apply a URL or filters. They return `{ issues }` |
|
|
653
|
+
| `getUrl(baseUrl?)` | The URL with the grid's filters, for example to share |
|
|
654
|
+
| `saveView`, `loadView`, `deleteView` | The saved view methods of the sync |
|
|
655
|
+
|
|
656
|
+
- **The core's rules hold.** On mount, a URL with filters wins over the stored view, a URL with no filters keeps the grid's filters, and a URL apply writes no history.
|
|
657
|
+
- **Options can change after mount.** A new codec, `maxValueLength`, history mode, `popstate` or `storageKey` makes a new sync on the same grid. It does not apply the URL again, and it keeps the active view. A new `storageKey` restores that store's view. A new codec object with the same settings, a new `onIssue` or a new `history` function makes no new sync.
|
|
658
|
+
- **The result is stable.** It is the same object until a snapshot value changes, and the methods never change.
|
|
659
|
+
- **A method with no sync** (no grid yet, `enabled: false`): `applyUrl` and `applyFilters` report `NOT_READY` to `onIssue`, and so do `saveView` and `loadView` with a `storageKey`. `getUrl` returns the base URL. `deleteView` still works with storage only.
|
|
660
|
+
- **Issues stay across a new sync.** After an option change or a `StrictMode` remount, `issues` still shows the latest apply's issues until the next apply. `onIssue` gets each issue one time.
|
|
661
|
+
- `StrictMode`, React's `<Activity>` and server rendering are supported.
|
|
662
|
+
|
|
663
|
+
### Migrating from `useAGGridUrlSync`
|
|
664
|
+
|
|
665
|
+
| `useAGGridUrlSync` | `useGridUrlSync` |
|
|
666
|
+
| --------------------------------------------------------------- | ------------------------------------------------------------------------------------- |
|
|
667
|
+
| `useAGGridUrlSync(gridApi, options)` | `useGridUrlSync({ api: gridApi, ...options })` |
|
|
668
|
+
| `paramPrefix`, `serialization`, `groupedParam`, `format` | `codec: individual({ prefix })` or `grouped({ param, format })` |
|
|
669
|
+
| `autoApplyOnMount` (default `false`) | `applyUrlOnCreate` (default `true`). A URL with no filters keeps the grid's filters |
|
|
670
|
+
| `enabledWhenReady` | `enabled` |
|
|
671
|
+
| `onParseError`, `onError(error, context)` | `onIssue({ issue, source })` |
|
|
672
|
+
| `isReady` | `status === 'active'` |
|
|
673
|
+
| `currentUrl`, `shareUrl(baseUrl?)` | `url`, `getUrl(baseUrl?)` |
|
|
674
|
+
| `applyUrlFilters(url?)`, `clearFilters()` | `applyUrl(url?)`, `applyFilters({})` |
|
|
675
|
+
| `getQueryParams()` | `new URL(getUrl()).search`, while `status` is `'active'` |
|
|
676
|
+
| `parseUrlFilters`, `getFiltersAsFormat`, `getSerializationMode` | The module functions `parseUrlFilters` and `serializeGrouped` from `ag-grid-url-sync` |
|
|
677
|
+
|
|
678
|
+
### Basic React Usage (deprecated `useAGGridUrlSync`)
|
|
498
679
|
|
|
499
680
|
```tsx
|
|
500
681
|
import React, { useState } from 'react'
|
|
@@ -572,7 +753,7 @@ function GridComponent() {
|
|
|
572
753
|
}
|
|
573
754
|
```
|
|
574
755
|
|
|
575
|
-
### React Hook API
|
|
756
|
+
### React Hook API (deprecated `useAGGridUrlSync`)
|
|
576
757
|
|
|
577
758
|
#### `useAGGridUrlSync(gridApi, options?)`
|
|
578
759
|
|
|
@@ -587,13 +768,39 @@ const {
|
|
|
587
768
|
isReady,
|
|
588
769
|
currentUrl,
|
|
589
770
|
hasFilters,
|
|
771
|
+
issues,
|
|
590
772
|
parseUrlFilters,
|
|
591
773
|
applyFilters,
|
|
592
774
|
getFiltersAsFormat,
|
|
593
|
-
|
|
775
|
+
getSerializationMode
|
|
594
776
|
} = useAGGridUrlSync(gridApi, options)
|
|
595
777
|
```
|
|
596
778
|
|
|
779
|
+
The hook runs on [`createGridUrlSync`](#vanilla-js-creategridurlsync) and reads its state with `useSyncExternalStore`:
|
|
780
|
+
|
|
781
|
+
- **Options can change after mount.** A change to `paramPrefix`, `maxValueLength`, `serialization`, `groupedParam`, `format`, `history` (the mode) or `popstate` makes a new sync. The grid keeps its filters, and `currentUrl` uses the new format. The URL is not applied again. A new options object with the same values, or a new callback, makes no new sync.
|
|
782
|
+
- **The returned object is stable.** It is the same object across renders until a value in it changes.
|
|
783
|
+
- **A grid-read issue goes to `onParseError` one time** while it stays. It is reported again only after it goes away and comes back.
|
|
784
|
+
- **After AG Grid destroys the grid**, `isReady` is `false` and `currentUrl` is `''`, as with no grid. `applyUrlFilters`, `clearFilters`, `applyFilters`, `saveView` and `loadView` then report `NOT_READY` to `onError`. `shareUrl`, `getQueryParams` and `getFiltersAsFormat` return their fallback values.
|
|
785
|
+
- **The hook writes to the address bar only with `history`.** With the default `'none'`, use `currentUrl` or `shareUrl()` to update it.
|
|
786
|
+
|
|
787
|
+
#### Address bar (`history` and `popstate`)
|
|
788
|
+
|
|
789
|
+
The hook takes the `history` and `popstate` options of [`createGridUrlSync`](#vanilla-js-creategridurlsync), so a React app gets the same address-bar sync with one option:
|
|
790
|
+
|
|
791
|
+
```tsx
|
|
792
|
+
// Each filter change adds a history entry, and back and forward apply the URL
|
|
793
|
+
useAGGridUrlSync(gridApi, { autoApplyOnMount: true, history: 'push' })
|
|
794
|
+
|
|
795
|
+
// Or give the URL to a router. A new function on each render is fine.
|
|
796
|
+
useAGGridUrlSync(gridApi, { history: url => router.replace(url) })
|
|
797
|
+
```
|
|
798
|
+
|
|
799
|
+
- A user edit, `applyFilters`, `clearFilters` and `loadView` write the URL. `'push'` adds one entry for each of them.
|
|
800
|
+
- A URL apply writes nothing, because the URL holds those filters already: auto-apply on mount, `applyUrlFilters` and the back button.
|
|
801
|
+
- The restore of a stored view on mount replaces the entry. A new sync for an option change, or a `StrictMode` remount, adds no entry.
|
|
802
|
+
- `popstate` is `true` by default with `'push'`. On back or forward, the hook applies the new URL. The view is then no longer active, and the issues of that apply are in `issues`. A grid error goes to `onError` with the context `'popstate'`. A failed address-bar write is `HISTORY_WRITE_FAILED`, to `onError` with the context `'filter-change'`.
|
|
803
|
+
|
|
597
804
|
#### Hook Options
|
|
598
805
|
|
|
599
806
|
```tsx
|
|
@@ -602,11 +809,16 @@ interface UseAGGridUrlSyncOptions {
|
|
|
602
809
|
paramPrefix?: string // Default: 'f_'
|
|
603
810
|
maxValueLength?: number // Default: 200
|
|
604
811
|
onParseError?: (error: Error) => void
|
|
812
|
+
throwOnError?: boolean | ((issue: URLSyncError) => boolean) // Default: false
|
|
813
|
+
|
|
814
|
+
// Address bar (see Address bar above)
|
|
815
|
+
history?: 'none' | 'replace' | 'push' | ((url: string) => void) // Default: 'none'
|
|
816
|
+
popstate?: boolean // Default: true with 'push', else false
|
|
605
817
|
|
|
606
818
|
// React-specific options
|
|
607
819
|
autoApplyOnMount?: boolean // Default: false
|
|
608
820
|
enabledWhenReady?: boolean // Default: true
|
|
609
|
-
onError?: (error: Error, context: string) => void //
|
|
821
|
+
onError?: (error: Error, context: string) => void // Operation failures (see Error Handling)
|
|
610
822
|
|
|
611
823
|
// Saved views: setting a key enables the feature and namespaces its storage
|
|
612
824
|
storageKey?: string // Default: undefined (saved views disabled)
|
|
@@ -615,19 +827,21 @@ interface UseAGGridUrlSyncOptions {
|
|
|
615
827
|
|
|
616
828
|
#### Hook Return Values
|
|
617
829
|
|
|
618
|
-
| Property
|
|
619
|
-
|
|
|
620
|
-
| `shareUrl`
|
|
621
|
-
| `getQueryParams`
|
|
622
|
-
| `applyUrlFilters`
|
|
623
|
-
| `clearFilters`
|
|
624
|
-
| `isReady`
|
|
625
|
-
| `currentUrl`
|
|
626
|
-
| `hasFilters`
|
|
627
|
-
| `
|
|
628
|
-
| `
|
|
629
|
-
| `
|
|
630
|
-
| `
|
|
830
|
+
| Property | Type | Description |
|
|
831
|
+
| ---------------------- | --------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
|
|
832
|
+
| `shareUrl` | `(baseUrl?: string) => string` | Generate shareable URL with current filters |
|
|
833
|
+
| `getQueryParams` | `() => string` | Get filter state as query parameters |
|
|
834
|
+
| `applyUrlFilters` | `(url?: string) => void` | Apply filters from URL to grid |
|
|
835
|
+
| `clearFilters` | `() => void` | Clear all text filters |
|
|
836
|
+
| `isReady` | `boolean` | Whether the grid API is available and the hook is ready. `false` after AG Grid destroys the grid. |
|
|
837
|
+
| `currentUrl` | `string` | Current URL with filters applied |
|
|
838
|
+
| `hasFilters` | `boolean` | Whether the grid has supported filters |
|
|
839
|
+
| `issues` | `readonly URLSyncError[]` | Filters that the latest URL apply or URL write dropped (see Error Handling) |
|
|
840
|
+
| `parseUrlFilters` | `(url: string) => FilterState` | Parse filters from URL without applying |
|
|
841
|
+
| `applyFilters` | `(filters: FilterState) => void` | Apply filter state object to grid |
|
|
842
|
+
| `getFiltersAsFormat` | `(format: 'querystring' \| 'json' \| 'base64') => string` | Serialize filters to any supported format (for sharing/export) |
|
|
843
|
+
| `getSerializationMode` | `() => 'individual' \| 'grouped'` | Get the current serialization mode (individual or grouped) |
|
|
844
|
+
| `getCurrentFormat` | `() => 'individual' \| 'grouped'` | **Deprecated** (removed in 0.6.0): use `getSerializationMode` |
|
|
631
845
|
|
|
632
846
|
Saved views (inert unless `storageKey` is set):
|
|
633
847
|
|
|
@@ -639,10 +853,58 @@ Saved views (inert unless `storageKey` is set):
|
|
|
639
853
|
| `loadView` | `(id: string \| null) => void` | Apply a saved view, or pass `null` to reset to unfiltered |
|
|
640
854
|
| `deleteView` | `(id: string) => void` | Delete a saved view |
|
|
641
855
|
|
|
642
|
-
> **Note:** `getFiltersAsFormat` and `
|
|
856
|
+
> **Note:** `getFiltersAsFormat` and `getSerializationMode` are especially useful for grouped serialization, format conversion, and advanced sharing scenarios.
|
|
643
857
|
|
|
644
858
|
### Saved Views
|
|
645
859
|
|
|
860
|
+
The views are part of `createGridUrlSync`. The React hook uses the same code, so a vanilla app and a React app get the same rules.
|
|
861
|
+
|
|
862
|
+
#### Saved views in vanilla JS
|
|
863
|
+
|
|
864
|
+
Give the `views` option a view store. `createViewStore` is in its own entry, so an app with no saved views does not get the storage code.
|
|
865
|
+
|
|
866
|
+
```ts
|
|
867
|
+
import { createGridUrlSync } from 'ag-grid-url-sync'
|
|
868
|
+
import { createViewStore } from 'ag-grid-url-sync/view-storage'
|
|
869
|
+
|
|
870
|
+
const sync = createGridUrlSync({
|
|
871
|
+
api: gridApi,
|
|
872
|
+
history: 'push',
|
|
873
|
+
views: createViewStore('orders')
|
|
874
|
+
})
|
|
875
|
+
|
|
876
|
+
const { views, activeViewId } = sync.getSnapshot()
|
|
877
|
+
|
|
878
|
+
const view = sync.saveView('Open orders') // GridView, or null if the save failed
|
|
879
|
+
sync.loadView(view.id)
|
|
880
|
+
sync.loadView(null) // clear the filters
|
|
881
|
+
sync.deleteView(view.id)
|
|
882
|
+
```
|
|
883
|
+
|
|
884
|
+
You can also give your own `ViewStore`, for example one that talks to a server. With no `views` option, `views` is `[]`, `activeViewId` is `null`, and the three methods do nothing.
|
|
885
|
+
|
|
886
|
+
Rules for the vanilla API:
|
|
887
|
+
|
|
888
|
+
- **`activeViewId` is tracked.** `saveView`, `loadView` and the restore of the stored view set it. A load sets it before the grid write, so a snapshot never shows `null` while the write is still pending.
|
|
889
|
+
- **Any other filter model clears it.** A user edit, `applyUrl`, `applyFilters` and a back-button apply all set `activeViewId` to `null` and clear the stored pointer. A view that AG Grid changes when it applies it stays active.
|
|
890
|
+
- **A hand edit does not make a view active.** If the user types the filters of a view, `activeViewId` stays `null`. When two views have the same model, the active one is the one the user loaded.
|
|
891
|
+
- **A view load writes the URL as a user edit does.** With `history: 'push'`, the back button goes back to the URL from before the load. If the URL does not change, the sync writes no entry. Set and multi filters are kept in a view, but the URL cannot hold them. A view that differs only in those filters adds no entry.
|
|
892
|
+
- **A URL apply writes no history.** This holds for `applyUrl`, the apply on create and the back button.
|
|
893
|
+
- **A failed grid write restores `activeViewId`.** The failure goes to `onIssue` as `GRID_ERROR`.
|
|
894
|
+
- **Deleting is conservative.** If the deleted view is active and the grid still shows it, the grid is cleared. If the user edited the filters, they stay.
|
|
895
|
+
- **Failures go to `onIssue`**, not to `snapshot.issues`. They are `NOT_READY`, `GRID_ERROR`, `VIEW_NOT_FOUND`, `VIEW_NAME_REQUIRED`, `STORAGE_FULL`, `STORAGE_UNAVAILABLE` and `STORAGE_WRITE_FAILED`. `loadView` with an unknown id reads the store again, then reports `VIEW_NOT_FOUND`. After `destroy`, `saveView` and `loadView` report `NOT_READY`, and `deleteView` still deletes from the store.
|
|
896
|
+
|
|
897
|
+
On create (when `applyUrlOnCreate` is not `false`), the sync picks the starting filters in this order:
|
|
898
|
+
|
|
899
|
+
1. The URL holds the filters of the stored view (compared in URL form). The sync restores the view with its full model, and keeps `activeViewId` and the pointer. This is how a reload keeps a view that was loaded with `history` on.
|
|
900
|
+
2. The URL has at least one filter. The URL wins. `activeViewId` is `null` and the pointer is cleared. A URL with only bad params does not win.
|
|
901
|
+
3. A stored view exists. The sync restores it. With `history` on, the URL is written with replace, so page load adds no back-button step.
|
|
902
|
+
4. Else, the grid keeps its filters.
|
|
903
|
+
|
|
904
|
+
A stored pointer that names a missing view is ignored.
|
|
905
|
+
|
|
906
|
+
#### Saved views in React
|
|
907
|
+
|
|
646
908
|
Pass a `storageKey` to let users name and revisit filter sets. The key both enables
|
|
647
909
|
the feature and namespaces its storage, so two grids on the same origin must use
|
|
648
910
|
different keys:
|
|
@@ -707,7 +969,7 @@ Behaviour worth knowing:
|
|
|
707
969
|
`onError` with context `'save-view'`. Reads degrade to an empty list instead of
|
|
708
970
|
throwing, so server rendering is safe.
|
|
709
971
|
|
|
710
|
-
|
|
972
|
+
The store is also available directly:
|
|
711
973
|
|
|
712
974
|
```ts
|
|
713
975
|
import { createViewStore } from 'ag-grid-url-sync/view-storage'
|
|
@@ -721,8 +983,9 @@ store.saveView('Engineering', gridApi.getFilterModel())
|
|
|
721
983
|
- **๐ Automatic State Management**: Tracks grid state and filter changes automatically
|
|
722
984
|
- **โก Ready State**: `isReady` indicates when the grid API is available
|
|
723
985
|
- **๐ฏ Auto-Apply**: `autoApplyOnMount` applies URL filters when component mounts.
|
|
724
|
-
It is attempted once per grid: a failure is reported
|
|
725
|
-
`onParseError` and is not
|
|
986
|
+
It is attempted once per grid: a failure is reported one time (a bad filter
|
|
987
|
+
through `onParseError`, any other failure through `onError`) and is not
|
|
988
|
+
retried on later renders
|
|
726
989
|
- **๐งน Cleanup**: Automatically cleans up resources when component unmounts
|
|
727
990
|
- **๐ก๏ธ Error Boundaries**: Graceful error handling with configurable callbacks
|
|
728
991
|
- **๐ Filter Status**: `hasFilters` tracks whether any filters are active
|
|
@@ -885,9 +1148,13 @@ Parameter structure:
|
|
|
885
1148
|
|
|
886
1149
|
## API Reference
|
|
887
1150
|
|
|
888
|
-
### `
|
|
1151
|
+
### `createGridUrlSync(options)`
|
|
1152
|
+
|
|
1153
|
+
See [Vanilla JS: `createGridUrlSync`](#vanilla-js-creategridurlsync) for the options. It returns `{ getSnapshot, subscribe, applyUrl, applyFilters, getUrl, saveView, loadView, deleteView, destroy }`. The snapshot is `{ status, url, hasFilters, issues, views, activeViewId }`. See [Saved Views](#saved-views) for the view methods.
|
|
889
1154
|
|
|
890
|
-
|
|
1155
|
+
### `createUrlSync(gridApi, config?)` (deprecated)
|
|
1156
|
+
|
|
1157
|
+
Factory function to create a new AGGridUrlSync instance. Deprecated: use `createGridUrlSync`. It will be removed in 0.6.0.
|
|
891
1158
|
|
|
892
1159
|
```typescript
|
|
893
1160
|
import { createUrlSync } from 'ag-grid-url-sync'
|
|
@@ -899,7 +1166,9 @@ const urlSync = createUrlSync(gridApi, {
|
|
|
899
1166
|
})
|
|
900
1167
|
```
|
|
901
1168
|
|
|
902
|
-
### `AGGridUrlSync` Class
|
|
1169
|
+
### `AGGridUrlSync` Class (deprecated)
|
|
1170
|
+
|
|
1171
|
+
Deprecated: use `createGridUrlSync`. It will be removed in 0.6.0.
|
|
903
1172
|
|
|
904
1173
|
#### Constructor
|
|
905
1174
|
|
|
@@ -918,7 +1187,7 @@ const url = urlSync.generateUrl('https://app.com/page')
|
|
|
918
1187
|
// https://app.com/page?f_name_contains=john
|
|
919
1188
|
```
|
|
920
1189
|
|
|
921
|
-
##### `getUrlState(baseUrl?: string): { url: string; hasFilters: boolean }`
|
|
1190
|
+
##### `getUrlState(baseUrl?: string): { url: string; hasFilters: boolean; issues: readonly URLSyncError[] }`
|
|
922
1191
|
|
|
923
1192
|
Generates the same URL as `generateUrl`, and tells if the grid has filters that the library supports. It reads the grid one time for both values. `hasFilters` comes from the filter state, not from the URL, because the URL also keeps the page's own params.
|
|
924
1193
|
|
|
@@ -927,6 +1196,8 @@ const { url, hasFilters } = urlSync.getUrlState('https://app.com/page?tab=1')
|
|
|
927
1196
|
// url: https://app.com/page?tab=1 (no filters), hasFilters: false
|
|
928
1197
|
```
|
|
929
1198
|
|
|
1199
|
+
`issues` holds the filters that this call dropped, for example a text value over `maxValueLength`.
|
|
1200
|
+
|
|
930
1201
|
##### `getQueryParams(): string`
|
|
931
1202
|
|
|
932
1203
|
Gets the current filter state as URL query parameters.
|
|
@@ -936,17 +1207,17 @@ const params = urlSync.getQueryParams()
|
|
|
936
1207
|
// ?f_name_contains=john
|
|
937
1208
|
```
|
|
938
1209
|
|
|
939
|
-
##### `applyFromUrl(url?: string):
|
|
1210
|
+
##### `applyFromUrl(url?: string): { issues: readonly URLSyncError[] }`
|
|
940
1211
|
|
|
941
|
-
Applies filters from a URL to the grid.
|
|
1212
|
+
Applies filters from a URL to the grid, and returns the filters in the URL that were dropped.
|
|
942
1213
|
|
|
943
1214
|
```typescript
|
|
944
1215
|
urlSync.applyFromUrl('https://app.com/page?f_name_contains=john')
|
|
945
1216
|
```
|
|
946
1217
|
|
|
947
|
-
##### `applyFilters(filterState: FilterState):
|
|
1218
|
+
##### `applyFilters(filterState: FilterState): { issues: readonly URLSyncError[] }`
|
|
948
1219
|
|
|
949
|
-
Applies a filter state object to the grid.
|
|
1220
|
+
Applies a filter state object to the grid, and returns the filters that were dropped (for example a number filter on a text column).
|
|
950
1221
|
|
|
951
1222
|
```typescript
|
|
952
1223
|
urlSync.applyFilters({
|
|
@@ -958,7 +1229,7 @@ urlSync.applyFilters({
|
|
|
958
1229
|
})
|
|
959
1230
|
```
|
|
960
1231
|
|
|
961
|
-
##### `clearFilters():
|
|
1232
|
+
##### `clearFilters(): { issues: readonly URLSyncError[] }`
|
|
962
1233
|
|
|
963
1234
|
Clears all text filters from the grid.
|
|
964
1235
|
|
|
@@ -992,8 +1263,10 @@ interface AGGridUrlSyncConfig {
|
|
|
992
1263
|
// Parameter name for grouped mode (default: 'grid_filters')
|
|
993
1264
|
groupedParam?: string
|
|
994
1265
|
|
|
995
|
-
// Prefix for URL parameters (default: 'f_')
|
|
996
|
-
//
|
|
1266
|
+
// Prefix for URL parameters in individual mode (default: 'f_')
|
|
1267
|
+
// A prefix without a trailing '_' gets one ('filter' -> 'filter_name_contains').
|
|
1268
|
+
// An empty prefix means the default. Grouped mode always uses 'f_' inside the
|
|
1269
|
+
// grouped value, so grouped URLs do not change with this option.
|
|
997
1270
|
paramPrefix?: string
|
|
998
1271
|
|
|
999
1272
|
// Maximum length for filter values (default: 200)
|