ag-grid-url-sync 0.1.1 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +407 -43
- package/dist/{ag-grid-url-sync.js → core/ag-grid-url-sync.js} +4 -2
- package/dist/core/ag-grid-url-sync.js.map +1 -0
- package/dist/core/grid-integration.d.ts +9 -0
- package/dist/core/grid-integration.js +66 -0
- package/dist/core/grid-integration.js.map +1 -0
- package/dist/core/index.d.ts +6 -0
- package/dist/core/index.js +11 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/types.d.ts +112 -0
- package/dist/core/types.js +79 -0
- package/dist/core/types.js.map +1 -0
- package/dist/core/url-generator.d.ts +9 -0
- package/dist/core/url-generator.js +42 -0
- package/dist/core/url-generator.js.map +1 -0
- package/dist/core/url-parser.d.ts +9 -0
- package/dist/core/url-parser.js +85 -0
- package/dist/core/url-parser.js.map +1 -0
- package/dist/core/validation.d.ts +18 -0
- package/dist/core/validation.js +28 -0
- package/dist/core/validation.js.map +1 -0
- package/dist/index.d.ts +6 -3
- package/dist/index.js +11 -3
- package/dist/index.js.map +1 -1
- package/dist/react/index.d.ts +3 -0
- package/dist/react/index.js +2 -0
- package/dist/react/index.js.map +1 -0
- package/dist/react/types.d.ts +64 -0
- package/dist/react/types.js +2 -0
- package/dist/react/types.js.map +1 -0
- package/dist/react/use-ag-grid-url-sync.d.ts +10 -0
- package/dist/react/use-ag-grid-url-sync.js +215 -0
- package/dist/react/use-ag-grid-url-sync.js.map +1 -0
- package/package.json +95 -6
- package/dist/ag-grid-url-sync.js.map +0 -1
- package/dist/types.d.ts +0 -69
- package/dist/types.js +0 -31
- package/dist/types.js.map +0 -1
- package/dist/utils.d.ts +0 -36
- package/dist/utils.js +0 -188
- package/dist/utils.js.map +0 -1
- /package/dist/{ag-grid-url-sync.d.ts → core/ag-grid-url-sync.d.ts} +0 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import type { GridApi as AGGridApi } from 'ag-grid-community';
|
|
2
|
+
/**
|
|
3
|
+
* Configuration options for AG Grid URL Sync
|
|
4
|
+
*/
|
|
5
|
+
export interface AGGridUrlSyncConfig {
|
|
6
|
+
/**
|
|
7
|
+
* Prefix for URL parameters. Default: 'f_'
|
|
8
|
+
*/
|
|
9
|
+
paramPrefix?: string;
|
|
10
|
+
/**
|
|
11
|
+
* Maximum length for filter values. Default: 200
|
|
12
|
+
*/
|
|
13
|
+
maxValueLength?: number;
|
|
14
|
+
/**
|
|
15
|
+
* Optional error handler for parsing errors
|
|
16
|
+
*/
|
|
17
|
+
onParseError?: (error: Error) => void;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Supported filter operations
|
|
21
|
+
*/
|
|
22
|
+
export type FilterOperation = 'contains' | 'eq' | 'notContains' | 'notEqual' | 'startsWith' | 'endsWith' | 'blank' | 'notBlank';
|
|
23
|
+
/**
|
|
24
|
+
* Filter state for a single column
|
|
25
|
+
*/
|
|
26
|
+
export interface ColumnFilter {
|
|
27
|
+
filterType: 'text';
|
|
28
|
+
type: FilterOperation;
|
|
29
|
+
filter: string;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Complete filter state mapping
|
|
33
|
+
*/
|
|
34
|
+
export interface FilterState {
|
|
35
|
+
[columnId: string]: ColumnFilter;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Internal configuration with defaults
|
|
39
|
+
*/
|
|
40
|
+
export interface InternalConfig extends Required<AGGridUrlSyncConfig> {
|
|
41
|
+
gridApi: AGGridApi;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Error types for URL sync operations
|
|
45
|
+
*/
|
|
46
|
+
export declare class URLSyncError extends Error {
|
|
47
|
+
constructor(message: string);
|
|
48
|
+
}
|
|
49
|
+
export declare class InvalidFilterError extends URLSyncError {
|
|
50
|
+
constructor(message: string);
|
|
51
|
+
}
|
|
52
|
+
export declare class InvalidURLError extends URLSyncError {
|
|
53
|
+
constructor(message: string);
|
|
54
|
+
}
|
|
55
|
+
export type ParsedFilterParam = {
|
|
56
|
+
columnName: string;
|
|
57
|
+
operation: FilterOperation;
|
|
58
|
+
value: string;
|
|
59
|
+
action: 'apply' | 'remove';
|
|
60
|
+
};
|
|
61
|
+
export type GridApi = AGGridApi;
|
|
62
|
+
/**
|
|
63
|
+
* Operation Mapping Strategy:
|
|
64
|
+
*
|
|
65
|
+
* We use three different naming conventions for better user experience:
|
|
66
|
+
*
|
|
67
|
+
* 1. URL Parameters: Short, clean names for URLs (e.g., 'eq', 'neq')
|
|
68
|
+
* 2. Internal Types: Consistent naming for our FilterOperation type
|
|
69
|
+
* 3. AG Grid Operations: Exact names expected by AG Grid API (e.g., 'equals', 'notEqual')
|
|
70
|
+
*
|
|
71
|
+
* This approach keeps URLs readable while maintaining compatibility with AG Grid.
|
|
72
|
+
*/
|
|
73
|
+
export declare const OPERATION_MAP: {
|
|
74
|
+
readonly contains: "contains";
|
|
75
|
+
readonly eq: "eq";
|
|
76
|
+
readonly notContains: "notContains";
|
|
77
|
+
readonly neq: "notEqual";
|
|
78
|
+
readonly startsWith: "startsWith";
|
|
79
|
+
readonly endsWith: "endsWith";
|
|
80
|
+
readonly blank: "blank";
|
|
81
|
+
readonly notBlank: "notBlank";
|
|
82
|
+
};
|
|
83
|
+
export declare const INTERNAL_TO_URL_OPERATION_MAP: {
|
|
84
|
+
readonly contains: "contains";
|
|
85
|
+
readonly eq: "eq";
|
|
86
|
+
readonly notContains: "notContains";
|
|
87
|
+
readonly notEqual: "neq";
|
|
88
|
+
readonly startsWith: "startsWith";
|
|
89
|
+
readonly endsWith: "endsWith";
|
|
90
|
+
readonly blank: "blank";
|
|
91
|
+
readonly notBlank: "notBlank";
|
|
92
|
+
};
|
|
93
|
+
export declare const AG_GRID_OPERATION_NAMES: {
|
|
94
|
+
readonly contains: "contains";
|
|
95
|
+
readonly eq: "equals";
|
|
96
|
+
readonly notContains: "notContains";
|
|
97
|
+
readonly notEqual: "notEqual";
|
|
98
|
+
readonly startsWith: "startsWith";
|
|
99
|
+
readonly endsWith: "endsWith";
|
|
100
|
+
readonly blank: "blank";
|
|
101
|
+
readonly notBlank: "notBlank";
|
|
102
|
+
};
|
|
103
|
+
export declare const REVERSE_AG_GRID_OPERATION_NAMES: {
|
|
104
|
+
readonly contains: "contains";
|
|
105
|
+
readonly equals: "eq";
|
|
106
|
+
readonly notContains: "notContains";
|
|
107
|
+
readonly notEqual: "notEqual";
|
|
108
|
+
readonly startsWith: "startsWith";
|
|
109
|
+
readonly endsWith: "endsWith";
|
|
110
|
+
readonly blank: "blank";
|
|
111
|
+
readonly notBlank: "notBlank";
|
|
112
|
+
};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error types for URL sync operations
|
|
3
|
+
*/
|
|
4
|
+
export class URLSyncError extends Error {
|
|
5
|
+
constructor(message) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.name = 'URLSyncError';
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
export class InvalidFilterError extends URLSyncError {
|
|
11
|
+
constructor(message) {
|
|
12
|
+
super(message);
|
|
13
|
+
this.name = 'InvalidFilterError';
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export class InvalidURLError extends URLSyncError {
|
|
17
|
+
constructor(message) {
|
|
18
|
+
super(message);
|
|
19
|
+
this.name = 'InvalidURLError';
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Operation Mapping Strategy:
|
|
24
|
+
*
|
|
25
|
+
* We use three different naming conventions for better user experience:
|
|
26
|
+
*
|
|
27
|
+
* 1. URL Parameters: Short, clean names for URLs (e.g., 'eq', 'neq')
|
|
28
|
+
* 2. Internal Types: Consistent naming for our FilterOperation type
|
|
29
|
+
* 3. AG Grid Operations: Exact names expected by AG Grid API (e.g., 'equals', 'notEqual')
|
|
30
|
+
*
|
|
31
|
+
* This approach keeps URLs readable while maintaining compatibility with AG Grid.
|
|
32
|
+
*/
|
|
33
|
+
// URL parameter names to internal operation types
|
|
34
|
+
// Uses short names for cleaner URLs: 'eq' instead of 'equals', 'neq' instead of 'notEqual'
|
|
35
|
+
export const OPERATION_MAP = {
|
|
36
|
+
contains: 'contains',
|
|
37
|
+
eq: 'eq', // Short form for URL brevity
|
|
38
|
+
notContains: 'notContains',
|
|
39
|
+
neq: 'notEqual', // Short form: 'neq' -> 'notEqual'
|
|
40
|
+
startsWith: 'startsWith',
|
|
41
|
+
endsWith: 'endsWith',
|
|
42
|
+
blank: 'blank',
|
|
43
|
+
notBlank: 'notBlank'
|
|
44
|
+
};
|
|
45
|
+
// Internal operation types to URL parameter names
|
|
46
|
+
export const INTERNAL_TO_URL_OPERATION_MAP = {
|
|
47
|
+
contains: 'contains',
|
|
48
|
+
eq: 'eq',
|
|
49
|
+
notContains: 'notContains',
|
|
50
|
+
notEqual: 'neq', // Internal 'notEqual' -> URL 'neq'
|
|
51
|
+
startsWith: 'startsWith',
|
|
52
|
+
endsWith: 'endsWith',
|
|
53
|
+
blank: 'blank',
|
|
54
|
+
notBlank: 'notBlank'
|
|
55
|
+
};
|
|
56
|
+
// Internal operation types to AG Grid operation names
|
|
57
|
+
// AG Grid expects 'equals' not 'eq', 'notEqual' not 'neq'
|
|
58
|
+
export const AG_GRID_OPERATION_NAMES = {
|
|
59
|
+
contains: 'contains',
|
|
60
|
+
eq: 'equals', // Internal 'eq' -> AG Grid 'equals'
|
|
61
|
+
notContains: 'notContains',
|
|
62
|
+
notEqual: 'notEqual',
|
|
63
|
+
startsWith: 'startsWith',
|
|
64
|
+
endsWith: 'endsWith',
|
|
65
|
+
blank: 'blank',
|
|
66
|
+
notBlank: 'notBlank'
|
|
67
|
+
};
|
|
68
|
+
// AG Grid operation names to internal operation types (reverse mapping)
|
|
69
|
+
export const REVERSE_AG_GRID_OPERATION_NAMES = {
|
|
70
|
+
contains: 'contains',
|
|
71
|
+
equals: 'eq', // AG Grid 'equals' -> Internal 'eq'
|
|
72
|
+
notContains: 'notContains',
|
|
73
|
+
notEqual: 'notEqual',
|
|
74
|
+
startsWith: 'startsWith',
|
|
75
|
+
endsWith: 'endsWith',
|
|
76
|
+
blank: 'blank',
|
|
77
|
+
notBlank: 'notBlank'
|
|
78
|
+
};
|
|
79
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"AA0DA;;GAEG;AACH,MAAM,OAAO,YAAa,SAAQ,KAAK;IACrC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAA;QACd,IAAI,CAAC,IAAI,GAAG,cAAc,CAAA;IAC5B,CAAC;CACF;AAED,MAAM,OAAO,kBAAmB,SAAQ,YAAY;IAClD,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAA;QACd,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAA;IAClC,CAAC;CACF;AAED,MAAM,OAAO,eAAgB,SAAQ,YAAY;IAC/C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAA;QACd,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAA;IAC/B,CAAC;CACF;AAYD;;;;;;;;;;GAUG;AAEH,kDAAkD;AAClD,2FAA2F;AAC3F,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,QAAQ,EAAE,UAAU;IACpB,EAAE,EAAE,IAAI,EAAE,6BAA6B;IACvC,WAAW,EAAE,aAAa;IAC1B,GAAG,EAAE,UAAU,EAAE,kCAAkC;IACnD,UAAU,EAAE,YAAY;IACxB,QAAQ,EAAE,UAAU;IACpB,KAAK,EAAE,OAAO;IACd,QAAQ,EAAE,UAAU;CACZ,CAAA;AAEV,kDAAkD;AAClD,MAAM,CAAC,MAAM,6BAA6B,GAAG;IAC3C,QAAQ,EAAE,UAAU;IACpB,EAAE,EAAE,IAAI;IACR,WAAW,EAAE,aAAa;IAC1B,QAAQ,EAAE,KAAK,EAAE,mCAAmC;IACpD,UAAU,EAAE,YAAY;IACxB,QAAQ,EAAE,UAAU;IACpB,KAAK,EAAE,OAAO;IACd,QAAQ,EAAE,UAAU;CACZ,CAAA;AAEV,sDAAsD;AACtD,0DAA0D;AAC1D,MAAM,CAAC,MAAM,uBAAuB,GAAG;IACrC,QAAQ,EAAE,UAAU;IACpB,EAAE,EAAE,QAAQ,EAAE,oCAAoC;IAClD,WAAW,EAAE,aAAa;IAC1B,QAAQ,EAAE,UAAU;IACpB,UAAU,EAAE,YAAY;IACxB,QAAQ,EAAE,UAAU;IACpB,KAAK,EAAE,OAAO;IACd,QAAQ,EAAE,UAAU;CACZ,CAAA;AAEV,wEAAwE;AACxE,MAAM,CAAC,MAAM,+BAA+B,GAAG;IAC7C,QAAQ,EAAE,UAAU;IACpB,MAAM,EAAE,IAAI,EAAE,oCAAoC;IAClD,WAAW,EAAE,aAAa;IAC1B,QAAQ,EAAE,UAAU;IACpB,UAAU,EAAE,YAAY;IACxB,QAAQ,EAAE,UAAU;IACpB,KAAK,EAAE,OAAO;IACd,QAAQ,EAAE,UAAU;CACZ,CAAA"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { FilterState, InternalConfig } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Converts a filter state object into URL search parameters
|
|
4
|
+
*/
|
|
5
|
+
export declare function serializeFilters(filterState: FilterState, config: InternalConfig): URLSearchParams;
|
|
6
|
+
/**
|
|
7
|
+
* Generates a URL with the current filter state
|
|
8
|
+
*/
|
|
9
|
+
export declare function generateUrl(baseUrl: string, filterState: FilterState, config: InternalConfig): string;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { validateFilterValue } from './validation.js';
|
|
2
|
+
import { INTERNAL_TO_URL_OPERATION_MAP } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Converts a filter state object into URL search parameters
|
|
5
|
+
*/
|
|
6
|
+
export function serializeFilters(filterState, config) {
|
|
7
|
+
const params = new URLSearchParams();
|
|
8
|
+
for (const [column, filter] of Object.entries(filterState)) {
|
|
9
|
+
if (filter.filterType !== 'text')
|
|
10
|
+
continue;
|
|
11
|
+
const operation = INTERNAL_TO_URL_OPERATION_MAP[filter.type];
|
|
12
|
+
if (!operation)
|
|
13
|
+
continue;
|
|
14
|
+
const paramName = `${config.paramPrefix}${column}_${operation}`;
|
|
15
|
+
// Special handling for blank operations
|
|
16
|
+
if (filter.type === 'blank' || filter.type === 'notBlank') {
|
|
17
|
+
params.append(paramName, 'true');
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
const value = validateFilterValue(filter.filter, config, filter.type);
|
|
21
|
+
// URLSearchParams already handles encoding, so don't double-encode
|
|
22
|
+
params.append(paramName, value);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return params;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Generates a URL with the current filter state
|
|
29
|
+
*/
|
|
30
|
+
export function generateUrl(baseUrl, filterState, config) {
|
|
31
|
+
const url = new URL(baseUrl);
|
|
32
|
+
const filterParams = serializeFilters(filterState, config);
|
|
33
|
+
// Preserve existing non-filter parameters
|
|
34
|
+
for (const [key, value] of url.searchParams.entries()) {
|
|
35
|
+
if (!key.startsWith(config.paramPrefix)) {
|
|
36
|
+
filterParams.append(key, value);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
url.search = filterParams.toString();
|
|
40
|
+
return url.toString();
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=url-generator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"url-generator.js","sourceRoot":"","sources":["../../src/core/url-generator.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAA;AACrD,OAAO,EAAE,6BAA6B,EAAE,MAAM,YAAY,CAAA;AAE1D;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAC9B,WAAwB,EACxB,MAAsB;IAEtB,MAAM,MAAM,GAAG,IAAI,eAAe,EAAE,CAAA;IAEpC,KAAK,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC;QAC3D,IAAI,MAAM,CAAC,UAAU,KAAK,MAAM;YAAE,SAAQ;QAE1C,MAAM,SAAS,GACb,6BAA6B,CAC3B,MAAM,CAAC,IAAkD,CAC1D,CAAA;QACH,IAAI,CAAC,SAAS;YAAE,SAAQ;QAExB,MAAM,SAAS,GAAG,GAAG,MAAM,CAAC,WAAW,GAAG,MAAM,IAAI,SAAS,EAAE,CAAA;QAE/D,wCAAwC;QACxC,IAAI,MAAM,CAAC,IAAI,KAAK,OAAO,IAAI,MAAM,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;YAC1D,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAAA;QAClC,CAAC;aAAM,CAAC;YACN,MAAM,KAAK,GAAG,mBAAmB,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,CAAA;YACrE,mEAAmE;YACnE,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,KAAK,CAAC,CAAA;QACjC,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAA;AACf,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,WAAW,CACzB,OAAe,EACf,WAAwB,EACxB,MAAsB;IAEtB,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,CAAA;IAC5B,MAAM,YAAY,GAAG,gBAAgB,CAAC,WAAW,EAAE,MAAM,CAAC,CAAA;IAE1D,0CAA0C;IAC1C,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,GAAG,CAAC,YAAY,CAAC,OAAO,EAAE,EAAE,CAAC;QACtD,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC;YACxC,YAAY,CAAC,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;QACjC,CAAC;IACH,CAAC;IAED,GAAG,CAAC,MAAM,GAAG,YAAY,CAAC,QAAQ,EAAE,CAAA;IACpC,OAAO,GAAG,CAAC,QAAQ,EAAE,CAAA;AACvB,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { FilterState, ColumnFilter, InternalConfig } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Parses a URL parameter into a column filter
|
|
4
|
+
*/
|
|
5
|
+
export declare function parseFilterParam(param: string, value: string, expectedPrefix?: string): ColumnFilter;
|
|
6
|
+
/**
|
|
7
|
+
* Converts URL search params into a filter state object
|
|
8
|
+
*/
|
|
9
|
+
export declare function parseUrlFilters(url: string, config: InternalConfig): FilterState;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { InvalidFilterError, InvalidURLError, OPERATION_MAP } from './types.js';
|
|
2
|
+
import { validateFilterValue, DEFAULT_CONFIG } from './validation.js';
|
|
3
|
+
/**
|
|
4
|
+
* Extracts column name and operation from a filter parameter
|
|
5
|
+
*/
|
|
6
|
+
function extractColumnAndOperation(param, expectedPrefix) {
|
|
7
|
+
const ensureTrailingUnderscore = (prefix) => prefix.endsWith('_') ? prefix : prefix + '_';
|
|
8
|
+
const sanitizedPrefix = expectedPrefix
|
|
9
|
+
? ensureTrailingUnderscore(expectedPrefix)
|
|
10
|
+
: ensureTrailingUnderscore(DEFAULT_CONFIG.paramPrefix);
|
|
11
|
+
const prefixToCheck = sanitizedPrefix.slice(0, -1);
|
|
12
|
+
if (!param.startsWith(prefixToCheck + '_')) {
|
|
13
|
+
throw new InvalidFilterError(`Invalid filter prefix in parameter: ${param}`);
|
|
14
|
+
}
|
|
15
|
+
// Remove prefix and find the last underscore to separate column from operation
|
|
16
|
+
const withoutPrefix = param.substring(prefixToCheck.length + 1);
|
|
17
|
+
const lastUnderscoreIndex = withoutPrefix.lastIndexOf('_');
|
|
18
|
+
if (lastUnderscoreIndex === -1) {
|
|
19
|
+
throw new InvalidFilterError(`Invalid filter parameter format: ${param}`);
|
|
20
|
+
}
|
|
21
|
+
const column = withoutPrefix.substring(0, lastUnderscoreIndex);
|
|
22
|
+
const operation = withoutPrefix.substring(lastUnderscoreIndex + 1);
|
|
23
|
+
if (!column.trim()) {
|
|
24
|
+
throw new InvalidFilterError(`Empty column name in parameter: ${param}`);
|
|
25
|
+
}
|
|
26
|
+
return { column, operation };
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Parses a URL parameter into a column filter
|
|
30
|
+
*/
|
|
31
|
+
export function parseFilterParam(param, value, expectedPrefix) {
|
|
32
|
+
const { operation } = extractColumnAndOperation(param, expectedPrefix);
|
|
33
|
+
const filterOp = OPERATION_MAP[operation];
|
|
34
|
+
if (!filterOp) {
|
|
35
|
+
throw new InvalidFilterError(`Unsupported filter operation: ${operation}`);
|
|
36
|
+
}
|
|
37
|
+
// Special handling for blank operations - they don't use the value
|
|
38
|
+
if (filterOp === 'blank' || filterOp === 'notBlank') {
|
|
39
|
+
return {
|
|
40
|
+
filterType: 'text',
|
|
41
|
+
type: filterOp,
|
|
42
|
+
filter: '' // Blank operations don't use the value
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
filterType: 'text',
|
|
47
|
+
type: filterOp,
|
|
48
|
+
filter: value || ''
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Converts URL search params into a filter state object
|
|
53
|
+
*/
|
|
54
|
+
export function parseUrlFilters(url, config) {
|
|
55
|
+
try {
|
|
56
|
+
const urlObj = new URL(url);
|
|
57
|
+
const filterState = {};
|
|
58
|
+
for (const [param, value] of urlObj.searchParams.entries()) {
|
|
59
|
+
if (!param.startsWith(config.paramPrefix))
|
|
60
|
+
continue;
|
|
61
|
+
try {
|
|
62
|
+
// Extract column name and parse the filter parameter
|
|
63
|
+
const { column: columnName } = extractColumnAndOperation(param, config.paramPrefix);
|
|
64
|
+
const filterParam = parseFilterParam(param, value, config.paramPrefix);
|
|
65
|
+
const validatedValue = validateFilterValue(value, config, filterParam.type);
|
|
66
|
+
filterState[columnName] = {
|
|
67
|
+
...filterParam,
|
|
68
|
+
filter: validatedValue
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
catch (err) {
|
|
72
|
+
if (config.onParseError) {
|
|
73
|
+
config.onParseError(err);
|
|
74
|
+
}
|
|
75
|
+
// Skip invalid filters but continue processing
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return filterState;
|
|
80
|
+
}
|
|
81
|
+
catch (err) {
|
|
82
|
+
throw new InvalidURLError(`Failed to parse URL: ${err.message}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=url-parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"url-parser.js","sourceRoot":"","sources":["../../src/core/url-parser.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { InternalConfig, FilterOperation } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Default configuration values
|
|
4
|
+
*/
|
|
5
|
+
export declare const DEFAULT_CONFIG: {
|
|
6
|
+
readonly paramPrefix: "f_";
|
|
7
|
+
readonly maxValueLength: 200;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Validates a filter value against configuration constraints
|
|
11
|
+
*
|
|
12
|
+
* @param value - The filter value to validate
|
|
13
|
+
* @param config - Internal configuration with validation rules
|
|
14
|
+
* @param operation - Optional filter operation type. Blank operations (blank/notBlank) bypass value validation
|
|
15
|
+
* @returns The validated filter value (empty string for blank operations)
|
|
16
|
+
* @throws InvalidFilterError if value exceeds maximum length
|
|
17
|
+
*/
|
|
18
|
+
export declare function validateFilterValue(value: string, config: InternalConfig, operation?: FilterOperation): string;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { InvalidFilterError } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Default configuration values
|
|
4
|
+
*/
|
|
5
|
+
export const DEFAULT_CONFIG = {
|
|
6
|
+
paramPrefix: 'f_',
|
|
7
|
+
maxValueLength: 200
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Validates a filter value against configuration constraints
|
|
11
|
+
*
|
|
12
|
+
* @param value - The filter value to validate
|
|
13
|
+
* @param config - Internal configuration with validation rules
|
|
14
|
+
* @param operation - Optional filter operation type. Blank operations (blank/notBlank) bypass value validation
|
|
15
|
+
* @returns The validated filter value (empty string for blank operations)
|
|
16
|
+
* @throws InvalidFilterError if value exceeds maximum length
|
|
17
|
+
*/
|
|
18
|
+
export function validateFilterValue(value, config, operation) {
|
|
19
|
+
// Blank operations don't require value validation
|
|
20
|
+
if (operation === 'blank' || operation === 'notBlank') {
|
|
21
|
+
return '';
|
|
22
|
+
}
|
|
23
|
+
if (value.length > config.maxValueLength) {
|
|
24
|
+
throw new InvalidFilterError(`Filter value exceeds maximum length of ${config.maxValueLength} characters`);
|
|
25
|
+
}
|
|
26
|
+
return value;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=validation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.js","sourceRoot":"","sources":["../../src/core/validation.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAE/C;;GAEG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,WAAW,EAAE,IAAI;IACjB,cAAc,EAAE,GAAG;CACX,CAAA;AAEV;;;;;;;;GAQG;AACH,MAAM,UAAU,mBAAmB,CACjC,KAAa,EACb,MAAsB,EACtB,SAA2B;IAE3B,kDAAkD;IAClD,IAAI,SAAS,KAAK,OAAO,IAAI,SAAS,KAAK,UAAU,EAAE,CAAC;QACtD,OAAO,EAAE,CAAA;IACX,CAAC;IAED,IAAI,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,cAAc,EAAE,CAAC;QACzC,MAAM,IAAI,kBAAkB,CAC1B,0CAA0C,MAAM,CAAC,cAAc,aAAa,CAC7E,CAAA;IACH,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
-
export { AGGridUrlSync, createUrlSync } from './ag-grid-url-sync.js';
|
|
2
|
-
export * from './types.js';
|
|
3
|
-
export {
|
|
1
|
+
export { AGGridUrlSync, createUrlSync } from './core/ag-grid-url-sync.js';
|
|
2
|
+
export * from './core/types.js';
|
|
3
|
+
export { validateFilterValue, DEFAULT_CONFIG } from './core/validation.js';
|
|
4
|
+
export { parseUrlFilters, parseFilterParam } from './core/url-parser.js';
|
|
5
|
+
export { serializeFilters, generateUrl } from './core/url-generator.js';
|
|
6
|
+
export { getFilterModel, applyFilterModel } from './core/grid-integration.js';
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
export
|
|
3
|
-
export
|
|
1
|
+
// Export core functionality (vanilla JS)
|
|
2
|
+
export { AGGridUrlSync, createUrlSync } from './core/ag-grid-url-sync.js';
|
|
3
|
+
export * from './core/types.js';
|
|
4
|
+
// Validation utilities
|
|
5
|
+
export { validateFilterValue, DEFAULT_CONFIG } from './core/validation.js';
|
|
6
|
+
// URL parsing utilities
|
|
7
|
+
export { parseUrlFilters, parseFilterParam } from './core/url-parser.js';
|
|
8
|
+
// URL generation utilities
|
|
9
|
+
export { serializeFilters, generateUrl } from './core/url-generator.js';
|
|
10
|
+
// AG Grid integration utilities
|
|
11
|
+
export { getFilterModel, applyFilterModel } from './core/grid-integration.js';
|
|
4
12
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAA;AAEzE,cAAc,iBAAiB,CAAA;AAE/B,uBAAuB;AACvB,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAE1E,wBAAwB;AACxB,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AAExE,2BAA2B;AAC3B,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAA;AAEvE,gCAAgC;AAChC,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAA"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { AGGridUrlSyncConfig, FilterState } from '../core/types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Configuration options for the React hook
|
|
4
|
+
*/
|
|
5
|
+
export interface UseAGGridUrlSyncOptions extends AGGridUrlSyncConfig {
|
|
6
|
+
/**
|
|
7
|
+
* Automatically apply URL filters when the component mounts and grid API becomes ready
|
|
8
|
+
* Default: false
|
|
9
|
+
*/
|
|
10
|
+
autoApplyOnMount?: boolean;
|
|
11
|
+
/**
|
|
12
|
+
* Whether the hook should be enabled when the grid API is ready
|
|
13
|
+
* Default: true
|
|
14
|
+
*/
|
|
15
|
+
enabledWhenReady?: boolean;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Return type for the useAGGridUrlSync hook
|
|
19
|
+
*/
|
|
20
|
+
export interface UseAGGridUrlSyncReturn {
|
|
21
|
+
/**
|
|
22
|
+
* Generate a URL with current filter state
|
|
23
|
+
* @param baseUrl - Optional base URL (defaults to current URL)
|
|
24
|
+
* @returns URL string with filter parameters
|
|
25
|
+
*/
|
|
26
|
+
shareUrl: (baseUrl?: string) => string;
|
|
27
|
+
/**
|
|
28
|
+
* Get current filter state as query parameters string
|
|
29
|
+
* @returns Query parameter string
|
|
30
|
+
*/
|
|
31
|
+
getQueryParams: () => string;
|
|
32
|
+
/**
|
|
33
|
+
* Apply filters from a URL to the grid
|
|
34
|
+
* @param url - Optional URL to parse (defaults to current URL)
|
|
35
|
+
*/
|
|
36
|
+
applyUrlFilters: (url?: string) => void;
|
|
37
|
+
/**
|
|
38
|
+
* Clear all text filters from the grid
|
|
39
|
+
*/
|
|
40
|
+
clearFilters: () => void;
|
|
41
|
+
/**
|
|
42
|
+
* Whether the grid API is available and hook is ready
|
|
43
|
+
*/
|
|
44
|
+
isReady: boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Current generated URL with filters
|
|
47
|
+
*/
|
|
48
|
+
currentUrl: string;
|
|
49
|
+
/**
|
|
50
|
+
* Whether the grid has any active text filters
|
|
51
|
+
*/
|
|
52
|
+
hasFilters: boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Parse filters from a URL without applying them
|
|
55
|
+
* @param url - URL to parse
|
|
56
|
+
* @returns Parsed filter state
|
|
57
|
+
*/
|
|
58
|
+
parseUrlFilters: (url: string) => FilterState;
|
|
59
|
+
/**
|
|
60
|
+
* Apply a filter state object to the grid
|
|
61
|
+
* @param filters - Filter state to apply
|
|
62
|
+
*/
|
|
63
|
+
applyFilters: (filters: FilterState) => void;
|
|
64
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/react/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { GridApi } from 'ag-grid-community';
|
|
2
|
+
import type { UseAGGridUrlSyncOptions, UseAGGridUrlSyncReturn } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* React hook for AG Grid URL synchronization
|
|
5
|
+
*
|
|
6
|
+
* @param gridApi - AG Grid API instance (can be null during initialization)
|
|
7
|
+
* @param options - Configuration options for the hook
|
|
8
|
+
* @returns Hook API for URL synchronization
|
|
9
|
+
*/
|
|
10
|
+
export declare function useAGGridUrlSync(gridApi: GridApi | null, options?: UseAGGridUrlSyncOptions): UseAGGridUrlSyncReturn;
|