ag-grid-url-sync 0.1.1 → 0.2.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/LICENSE +7 -0
- package/README.md +435 -52
- 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 +14 -0
- package/dist/core/grid-integration.js +156 -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 +107 -0
- package/dist/core/types.js +117 -0
- package/dist/core/types.js.map +1 -0
- package/dist/core/url-generator.d.ts +9 -0
- package/dist/core/url-generator.js +88 -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 +164 -0
- package/dist/core/url-parser.js.map +1 -0
- package/dist/core/validation.d.ts +47 -0
- package/dist/core/validation.js +87 -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 +97 -8
- 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,164 @@
|
|
|
1
|
+
import { InvalidFilterError, InvalidURLError, OPERATION_MAP } from './types.js';
|
|
2
|
+
import { validateFilterValue, validateAndParseNumber, validateNumberRange, validateNumberFilter, DEFAULT_CONFIG } from './validation.js';
|
|
3
|
+
/**
|
|
4
|
+
* Detects if an operation is number-specific
|
|
5
|
+
*/
|
|
6
|
+
function isNumberOperation(operation) {
|
|
7
|
+
return [
|
|
8
|
+
'lessThan',
|
|
9
|
+
'lessThanOrEqual',
|
|
10
|
+
'greaterThan',
|
|
11
|
+
'greaterThanOrEqual',
|
|
12
|
+
'inRange'
|
|
13
|
+
].includes(operation);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Detects if an operation works with both text and number filters
|
|
17
|
+
*/
|
|
18
|
+
function isSharedOperation(operation) {
|
|
19
|
+
return ['eq', 'notEqual', 'blank', 'notBlank'].includes(operation);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Extracts column name and operation from a filter parameter
|
|
23
|
+
*/
|
|
24
|
+
function extractColumnAndOperation(param, expectedPrefix) {
|
|
25
|
+
const ensureTrailingUnderscore = (prefix) => prefix.endsWith('_') ? prefix : prefix + '_';
|
|
26
|
+
const sanitizedPrefix = expectedPrefix
|
|
27
|
+
? ensureTrailingUnderscore(expectedPrefix)
|
|
28
|
+
: ensureTrailingUnderscore(DEFAULT_CONFIG.paramPrefix);
|
|
29
|
+
const prefixToCheck = sanitizedPrefix.slice(0, -1);
|
|
30
|
+
if (!param.startsWith(prefixToCheck + '_')) {
|
|
31
|
+
throw new InvalidFilterError(`Invalid filter prefix in parameter: ${param}`);
|
|
32
|
+
}
|
|
33
|
+
// Remove prefix and find the last underscore to separate column from operation
|
|
34
|
+
const withoutPrefix = param.substring(prefixToCheck.length + 1);
|
|
35
|
+
const lastUnderscoreIndex = withoutPrefix.lastIndexOf('_');
|
|
36
|
+
if (lastUnderscoreIndex === -1) {
|
|
37
|
+
throw new InvalidFilterError(`Invalid filter parameter format: ${param}`);
|
|
38
|
+
}
|
|
39
|
+
const column = withoutPrefix.substring(0, lastUnderscoreIndex);
|
|
40
|
+
const operation = withoutPrefix.substring(lastUnderscoreIndex + 1);
|
|
41
|
+
if (!column.trim()) {
|
|
42
|
+
throw new InvalidFilterError(`Empty column name in parameter: ${param}`);
|
|
43
|
+
}
|
|
44
|
+
return { column, operation };
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Parses a URL parameter into a column filter
|
|
48
|
+
*/
|
|
49
|
+
export function parseFilterParam(param, value, expectedPrefix) {
|
|
50
|
+
const { operation } = extractColumnAndOperation(param, expectedPrefix);
|
|
51
|
+
const filterOp = OPERATION_MAP[operation];
|
|
52
|
+
if (!filterOp) {
|
|
53
|
+
throw new InvalidFilterError(`Unsupported filter operation: ${operation}`);
|
|
54
|
+
}
|
|
55
|
+
// Handle range operations
|
|
56
|
+
if (filterOp === 'inRange') {
|
|
57
|
+
const rangeParts = value.split(',');
|
|
58
|
+
if (rangeParts.length !== 2) {
|
|
59
|
+
throw new InvalidFilterError('Range operation requires exactly two values separated by comma');
|
|
60
|
+
}
|
|
61
|
+
const min = validateAndParseNumber(rangeParts[0] || '');
|
|
62
|
+
const max = validateAndParseNumber(rangeParts[1] || '');
|
|
63
|
+
const rangeValidation = validateNumberRange(min, max);
|
|
64
|
+
if (!rangeValidation.valid) {
|
|
65
|
+
throw new InvalidFilterError(rangeValidation.error || 'Invalid range values');
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
filterType: 'number',
|
|
69
|
+
type: filterOp,
|
|
70
|
+
filter: min,
|
|
71
|
+
filterTo: max
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
// Handle number operations
|
|
75
|
+
if (isNumberOperation(filterOp)) {
|
|
76
|
+
const numValue = validateAndParseNumber(value);
|
|
77
|
+
return {
|
|
78
|
+
filterType: 'number',
|
|
79
|
+
type: filterOp,
|
|
80
|
+
filter: numValue
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
// Handle blank operations first (work for both text and number but default to text)
|
|
84
|
+
if (filterOp === 'blank' || filterOp === 'notBlank') {
|
|
85
|
+
return {
|
|
86
|
+
filterType: 'text',
|
|
87
|
+
type: filterOp,
|
|
88
|
+
filter: '' // Blank operations don't use the value
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
// Handle shared operations (eq, notEqual) - detect if they should be number based on value
|
|
92
|
+
if (isSharedOperation(filterOp)) {
|
|
93
|
+
// Try to parse as number first
|
|
94
|
+
try {
|
|
95
|
+
const numValue = parseFloat(value);
|
|
96
|
+
if (!isNaN(numValue) && Number.isFinite(numValue)) {
|
|
97
|
+
const validation = validateNumberFilter(numValue);
|
|
98
|
+
if (validation.valid) {
|
|
99
|
+
return {
|
|
100
|
+
filterType: 'number',
|
|
101
|
+
type: filterOp,
|
|
102
|
+
filter: numValue
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
// Fall through to text handling
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
filterType: 'text',
|
|
112
|
+
type: filterOp,
|
|
113
|
+
filter: value || ''
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
// Handle text operations (existing logic)
|
|
117
|
+
return {
|
|
118
|
+
filterType: 'text',
|
|
119
|
+
type: filterOp,
|
|
120
|
+
filter: value || ''
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Converts URL search params into a filter state object
|
|
125
|
+
*/
|
|
126
|
+
export function parseUrlFilters(url, config) {
|
|
127
|
+
try {
|
|
128
|
+
const urlObj = new URL(url);
|
|
129
|
+
const filterState = {};
|
|
130
|
+
for (const [param, value] of urlObj.searchParams.entries()) {
|
|
131
|
+
if (!param.startsWith(config.paramPrefix))
|
|
132
|
+
continue;
|
|
133
|
+
try {
|
|
134
|
+
// Extract column name and parse the filter parameter
|
|
135
|
+
const { column: columnName } = extractColumnAndOperation(param, config.paramPrefix);
|
|
136
|
+
const filterParam = parseFilterParam(param, value, config.paramPrefix);
|
|
137
|
+
// Apply validation based on filter type
|
|
138
|
+
if (filterParam.filterType === 'text') {
|
|
139
|
+
const validatedValue = validateFilterValue(value, config, filterParam.type);
|
|
140
|
+
filterState[columnName] = {
|
|
141
|
+
...filterParam,
|
|
142
|
+
filter: validatedValue
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
// Number filters are already validated in parseFilterParam
|
|
147
|
+
filterState[columnName] = filterParam;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
catch (err) {
|
|
151
|
+
if (config.onParseError) {
|
|
152
|
+
config.onParseError(err);
|
|
153
|
+
}
|
|
154
|
+
// Skip invalid filters but continue processing
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return filterState;
|
|
159
|
+
}
|
|
160
|
+
catch (err) {
|
|
161
|
+
throw new InvalidURLError(`Failed to parse URL: ${err.message}`);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
//# 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,47 @@
|
|
|
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
|
+
* Validation result interface
|
|
11
|
+
*/
|
|
12
|
+
export interface ValidationResult {
|
|
13
|
+
valid: boolean;
|
|
14
|
+
error?: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Validates a text filter value against configuration constraints
|
|
18
|
+
*
|
|
19
|
+
* @param value - The filter value to validate
|
|
20
|
+
* @param config - Internal configuration with validation rules
|
|
21
|
+
* @param operation - Optional filter operation type. Blank operations (blank/notBlank) bypass value validation
|
|
22
|
+
* @returns The validated filter value (empty string for blank operations)
|
|
23
|
+
* @throws InvalidFilterError if value exceeds maximum length
|
|
24
|
+
*/
|
|
25
|
+
export declare function validateFilterValue(value: string, config: InternalConfig, operation?: FilterOperation): string;
|
|
26
|
+
/**
|
|
27
|
+
* Validates a number filter value
|
|
28
|
+
*
|
|
29
|
+
* @param value - The number value to validate
|
|
30
|
+
* @returns Validation result
|
|
31
|
+
*/
|
|
32
|
+
export declare function validateNumberFilter(value: number): ValidationResult;
|
|
33
|
+
/**
|
|
34
|
+
* Validates a number range filter
|
|
35
|
+
*
|
|
36
|
+
* @param min - The minimum value
|
|
37
|
+
* @param max - The maximum value
|
|
38
|
+
* @returns Validation result
|
|
39
|
+
*/
|
|
40
|
+
export declare function validateNumberRange(min: number, max: number): ValidationResult;
|
|
41
|
+
/**
|
|
42
|
+
* Validates a parsed number from string input
|
|
43
|
+
*
|
|
44
|
+
* @param value - String representation of number
|
|
45
|
+
* @returns Parsed number or throws InvalidFilterError
|
|
46
|
+
*/
|
|
47
|
+
export declare function validateAndParseNumber(value: string): number;
|
|
@@ -0,0 +1,87 @@
|
|
|
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 text 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
|
+
/**
|
|
29
|
+
* Validates a number filter value
|
|
30
|
+
*
|
|
31
|
+
* @param value - The number value to validate
|
|
32
|
+
* @returns Validation result
|
|
33
|
+
*/
|
|
34
|
+
export function validateNumberFilter(value) {
|
|
35
|
+
// Basic number validation
|
|
36
|
+
if (!Number.isFinite(value)) {
|
|
37
|
+
return { valid: false, error: 'Filter value must be a finite number' };
|
|
38
|
+
}
|
|
39
|
+
// Range validation for precision
|
|
40
|
+
if (Math.abs(value) > Number.MAX_SAFE_INTEGER) {
|
|
41
|
+
return { valid: false, error: 'Number value exceeds safe integer range' };
|
|
42
|
+
}
|
|
43
|
+
return { valid: true };
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Validates a number range filter
|
|
47
|
+
*
|
|
48
|
+
* @param min - The minimum value
|
|
49
|
+
* @param max - The maximum value
|
|
50
|
+
* @returns Validation result
|
|
51
|
+
*/
|
|
52
|
+
export function validateNumberRange(min, max) {
|
|
53
|
+
const minResult = validateNumberFilter(min);
|
|
54
|
+
if (!minResult.valid)
|
|
55
|
+
return minResult;
|
|
56
|
+
const maxResult = validateNumberFilter(max);
|
|
57
|
+
if (!maxResult.valid)
|
|
58
|
+
return maxResult;
|
|
59
|
+
if (min > max) {
|
|
60
|
+
return {
|
|
61
|
+
valid: false,
|
|
62
|
+
error: 'Range minimum must be less than or equal to maximum'
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
return { valid: true };
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Validates a parsed number from string input
|
|
69
|
+
*
|
|
70
|
+
* @param value - String representation of number
|
|
71
|
+
* @returns Parsed number or throws InvalidFilterError
|
|
72
|
+
*/
|
|
73
|
+
export function validateAndParseNumber(value) {
|
|
74
|
+
if (!value || value.trim() === '') {
|
|
75
|
+
throw new InvalidFilterError('Number filter value cannot be empty');
|
|
76
|
+
}
|
|
77
|
+
const numValue = parseFloat(value);
|
|
78
|
+
if (isNaN(numValue)) {
|
|
79
|
+
throw new InvalidFilterError(`Invalid number format: ${value}`);
|
|
80
|
+
}
|
|
81
|
+
const validation = validateNumberFilter(numValue);
|
|
82
|
+
if (!validation.valid) {
|
|
83
|
+
throw new InvalidFilterError(validation.error || 'Invalid number value');
|
|
84
|
+
}
|
|
85
|
+
return numValue;
|
|
86
|
+
}
|
|
87
|
+
//# 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;AAUV;;;;;;;;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;AAED;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,KAAa;IAChD,0BAA0B;IAC1B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QAC5B,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,sCAAsC,EAAE,CAAA;IACxE,CAAC;IAED,iCAAiC;IACjC,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC,gBAAgB,EAAE,CAAC;QAC9C,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,yCAAyC,EAAE,CAAA;IAC3E,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAA;AACxB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,mBAAmB,CACjC,GAAW,EACX,GAAW;IAEX,MAAM,SAAS,GAAG,oBAAoB,CAAC,GAAG,CAAC,CAAA;IAC3C,IAAI,CAAC,SAAS,CAAC,KAAK;QAAE,OAAO,SAAS,CAAA;IAEtC,MAAM,SAAS,GAAG,oBAAoB,CAAC,GAAG,CAAC,CAAA;IAC3C,IAAI,CAAC,SAAS,CAAC,KAAK;QAAE,OAAO,SAAS,CAAA;IAEtC,IAAI,GAAG,GAAG,GAAG,EAAE,CAAC;QACd,OAAO;YACL,KAAK,EAAE,KAAK;YACZ,KAAK,EAAE,qDAAqD;SAC7D,CAAA;IACH,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAA;AACxB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,sBAAsB,CAAC,KAAa;IAClD,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QAClC,MAAM,IAAI,kBAAkB,CAAC,qCAAqC,CAAC,CAAA;IACrE,CAAC;IAED,MAAM,QAAQ,GAAG,UAAU,CAAC,KAAK,CAAC,CAAA;IAClC,IAAI,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC;QACpB,MAAM,IAAI,kBAAkB,CAAC,0BAA0B,KAAK,EAAE,CAAC,CAAA;IACjE,CAAC;IAED,MAAM,UAAU,GAAG,oBAAoB,CAAC,QAAQ,CAAC,CAAA;IACjD,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;QACtB,MAAM,IAAI,kBAAkB,CAAC,UAAU,CAAC,KAAK,IAAI,sBAAsB,CAAC,CAAA;IAC1E,CAAC;IAED,OAAO,QAAQ,CAAA;AACjB,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;
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import { useState, useEffect, useRef, useCallback, useMemo } from 'react';
|
|
2
|
+
import { AGGridUrlSync } from '../core/ag-grid-url-sync.js';
|
|
3
|
+
import { parseUrlFilters as parseFilters } from '../core/url-parser.js';
|
|
4
|
+
import { DEFAULT_CONFIG } from '../core/validation.js';
|
|
5
|
+
/**
|
|
6
|
+
* React hook for AG Grid URL synchronization
|
|
7
|
+
*
|
|
8
|
+
* @param gridApi - AG Grid API instance (can be null during initialization)
|
|
9
|
+
* @param options - Configuration options for the hook
|
|
10
|
+
* @returns Hook API for URL synchronization
|
|
11
|
+
*/
|
|
12
|
+
export function useAGGridUrlSync(gridApi, options = {}) {
|
|
13
|
+
const { autoApplyOnMount = false, enabledWhenReady = true, ...coreOptions } = options;
|
|
14
|
+
// Internal state
|
|
15
|
+
const [isReady, setIsReady] = useState(false);
|
|
16
|
+
const [currentUrl, setCurrentUrl] = useState('');
|
|
17
|
+
const [hasFilters, setHasFilters] = useState(false);
|
|
18
|
+
// Refs to track state and prevent memory leaks
|
|
19
|
+
const urlSyncRef = useRef(null);
|
|
20
|
+
const autoAppliedRef = useRef(false);
|
|
21
|
+
const lastGridApiRef = useRef(null);
|
|
22
|
+
// Initialize or update URL sync instance when grid API changes
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
if (gridApi && enabledWhenReady) {
|
|
25
|
+
// Clean up previous instance if grid API changed
|
|
26
|
+
if (lastGridApiRef.current && lastGridApiRef.current !== gridApi) {
|
|
27
|
+
urlSyncRef.current?.destroy();
|
|
28
|
+
urlSyncRef.current = null;
|
|
29
|
+
autoAppliedRef.current = false;
|
|
30
|
+
}
|
|
31
|
+
// Create new instance if needed
|
|
32
|
+
if (!urlSyncRef.current) {
|
|
33
|
+
try {
|
|
34
|
+
urlSyncRef.current = new AGGridUrlSync(gridApi, coreOptions);
|
|
35
|
+
setIsReady(true);
|
|
36
|
+
lastGridApiRef.current = gridApi;
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
console.error('Failed to initialize AG Grid URL Sync:', error);
|
|
40
|
+
setIsReady(false);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
// Clean up when grid API is null or disabled
|
|
46
|
+
if (urlSyncRef.current) {
|
|
47
|
+
urlSyncRef.current.destroy();
|
|
48
|
+
urlSyncRef.current = null;
|
|
49
|
+
autoAppliedRef.current = false;
|
|
50
|
+
}
|
|
51
|
+
setIsReady(false);
|
|
52
|
+
lastGridApiRef.current = null;
|
|
53
|
+
}
|
|
54
|
+
}, [gridApi, enabledWhenReady, coreOptions]);
|
|
55
|
+
// Auto-apply URL filters on mount if enabled
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
if (isReady &&
|
|
58
|
+
autoApplyOnMount &&
|
|
59
|
+
!autoAppliedRef.current &&
|
|
60
|
+
urlSyncRef.current) {
|
|
61
|
+
try {
|
|
62
|
+
urlSyncRef.current.applyFromUrl();
|
|
63
|
+
autoAppliedRef.current = true;
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
console.error('Failed to auto-apply URL filters:', error);
|
|
67
|
+
coreOptions.onParseError?.(error);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}, [isReady, autoApplyOnMount, coreOptions]);
|
|
71
|
+
// Update current URL and filter state on filter changes
|
|
72
|
+
useEffect(() => {
|
|
73
|
+
if (!isReady || !urlSyncRef.current || !gridApi) {
|
|
74
|
+
setCurrentUrl('');
|
|
75
|
+
setHasFilters(false);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const updateState = () => {
|
|
79
|
+
try {
|
|
80
|
+
const newUrl = urlSyncRef.current.generateUrl();
|
|
81
|
+
setCurrentUrl(newUrl);
|
|
82
|
+
// Check if there are active filters by comparing query params
|
|
83
|
+
const queryParams = urlSyncRef.current.getQueryParams();
|
|
84
|
+
const searchParams = new URLSearchParams(queryParams);
|
|
85
|
+
setHasFilters([...searchParams.entries()].length > 0); // Check if there are any query parameters
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
console.error('Failed to update URL state:', error);
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
// Attach event listener for filter changes
|
|
92
|
+
const onFilterChanged = () => updateState();
|
|
93
|
+
gridApi.addEventListener('filterChanged', onFilterChanged);
|
|
94
|
+
// Initial state update
|
|
95
|
+
updateState();
|
|
96
|
+
// Cleanup event listener on unmount or gridApi change
|
|
97
|
+
return () => {
|
|
98
|
+
gridApi.removeEventListener('filterChanged', onFilterChanged);
|
|
99
|
+
};
|
|
100
|
+
}, [isReady, gridApi]);
|
|
101
|
+
// Cleanup on unmount
|
|
102
|
+
useEffect(() => {
|
|
103
|
+
return () => {
|
|
104
|
+
if (urlSyncRef.current) {
|
|
105
|
+
urlSyncRef.current.destroy();
|
|
106
|
+
urlSyncRef.current = null;
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
}, []);
|
|
110
|
+
// Memoized API methods
|
|
111
|
+
const shareUrl = useCallback((baseUrl) => {
|
|
112
|
+
if (!urlSyncRef.current) {
|
|
113
|
+
return (baseUrl || (typeof window !== 'undefined' ? window.location.href : ''));
|
|
114
|
+
}
|
|
115
|
+
try {
|
|
116
|
+
return urlSyncRef.current.generateUrl(baseUrl);
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
console.error('Failed to generate share URL:', error);
|
|
120
|
+
return (baseUrl || (typeof window !== 'undefined' ? window.location.href : ''));
|
|
121
|
+
}
|
|
122
|
+
}, []);
|
|
123
|
+
const getQueryParams = useCallback(() => {
|
|
124
|
+
if (!urlSyncRef.current) {
|
|
125
|
+
return '';
|
|
126
|
+
}
|
|
127
|
+
try {
|
|
128
|
+
return urlSyncRef.current.getQueryParams();
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
console.error('Failed to get query params:', error);
|
|
132
|
+
return '';
|
|
133
|
+
}
|
|
134
|
+
}, []);
|
|
135
|
+
const applyUrlFilters = useCallback((url) => {
|
|
136
|
+
if (!urlSyncRef.current) {
|
|
137
|
+
console.warn('AG Grid URL Sync not ready');
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
try {
|
|
141
|
+
urlSyncRef.current.applyFromUrl(url);
|
|
142
|
+
}
|
|
143
|
+
catch (error) {
|
|
144
|
+
console.error('Failed to apply URL filters:', error);
|
|
145
|
+
coreOptions.onParseError?.(error);
|
|
146
|
+
}
|
|
147
|
+
}, [coreOptions]);
|
|
148
|
+
const clearFilters = useCallback(() => {
|
|
149
|
+
if (!urlSyncRef.current) {
|
|
150
|
+
console.warn('AG Grid URL Sync not ready');
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
try {
|
|
154
|
+
urlSyncRef.current.clearFilters();
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
console.error('Failed to clear filters:', error);
|
|
158
|
+
}
|
|
159
|
+
}, []);
|
|
160
|
+
const parseUrlFilters = useCallback((url) => {
|
|
161
|
+
if (!gridApi) {
|
|
162
|
+
console.warn('AG Grid API not available');
|
|
163
|
+
return {};
|
|
164
|
+
}
|
|
165
|
+
try {
|
|
166
|
+
const config = {
|
|
167
|
+
gridApi,
|
|
168
|
+
paramPrefix: coreOptions.paramPrefix ?? DEFAULT_CONFIG.paramPrefix,
|
|
169
|
+
maxValueLength: coreOptions.maxValueLength ?? DEFAULT_CONFIG.maxValueLength,
|
|
170
|
+
onParseError: coreOptions.onParseError ?? (() => { })
|
|
171
|
+
};
|
|
172
|
+
return parseFilters(url, config);
|
|
173
|
+
}
|
|
174
|
+
catch (error) {
|
|
175
|
+
console.error('Failed to parse URL filters:', error);
|
|
176
|
+
coreOptions.onParseError?.(error);
|
|
177
|
+
return {};
|
|
178
|
+
}
|
|
179
|
+
}, [coreOptions, gridApi]);
|
|
180
|
+
const applyFilters = useCallback((filters) => {
|
|
181
|
+
if (!urlSyncRef.current) {
|
|
182
|
+
console.warn('AG Grid URL Sync not ready');
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
try {
|
|
186
|
+
urlSyncRef.current.applyFilters(filters);
|
|
187
|
+
}
|
|
188
|
+
catch (error) {
|
|
189
|
+
console.error('Failed to apply filters:', error);
|
|
190
|
+
}
|
|
191
|
+
}, []);
|
|
192
|
+
// Return the hook API
|
|
193
|
+
return useMemo(() => ({
|
|
194
|
+
shareUrl,
|
|
195
|
+
getQueryParams,
|
|
196
|
+
applyUrlFilters,
|
|
197
|
+
clearFilters,
|
|
198
|
+
isReady,
|
|
199
|
+
currentUrl,
|
|
200
|
+
hasFilters,
|
|
201
|
+
parseUrlFilters,
|
|
202
|
+
applyFilters
|
|
203
|
+
}), [
|
|
204
|
+
shareUrl,
|
|
205
|
+
getQueryParams,
|
|
206
|
+
applyUrlFilters,
|
|
207
|
+
clearFilters,
|
|
208
|
+
isReady,
|
|
209
|
+
currentUrl,
|
|
210
|
+
hasFilters,
|
|
211
|
+
parseUrlFilters,
|
|
212
|
+
applyFilters
|
|
213
|
+
]);
|
|
214
|
+
}
|
|
215
|
+
//# sourceMappingURL=use-ag-grid-url-sync.js.map
|