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.
Files changed (43) hide show
  1. package/LICENSE +7 -0
  2. package/README.md +435 -52
  3. package/dist/{ag-grid-url-sync.js → core/ag-grid-url-sync.js} +4 -2
  4. package/dist/core/ag-grid-url-sync.js.map +1 -0
  5. package/dist/core/grid-integration.d.ts +14 -0
  6. package/dist/core/grid-integration.js +156 -0
  7. package/dist/core/grid-integration.js.map +1 -0
  8. package/dist/core/index.d.ts +6 -0
  9. package/dist/core/index.js +11 -0
  10. package/dist/core/index.js.map +1 -0
  11. package/dist/core/types.d.ts +107 -0
  12. package/dist/core/types.js +117 -0
  13. package/dist/core/types.js.map +1 -0
  14. package/dist/core/url-generator.d.ts +9 -0
  15. package/dist/core/url-generator.js +88 -0
  16. package/dist/core/url-generator.js.map +1 -0
  17. package/dist/core/url-parser.d.ts +9 -0
  18. package/dist/core/url-parser.js +164 -0
  19. package/dist/core/url-parser.js.map +1 -0
  20. package/dist/core/validation.d.ts +47 -0
  21. package/dist/core/validation.js +87 -0
  22. package/dist/core/validation.js.map +1 -0
  23. package/dist/index.d.ts +6 -3
  24. package/dist/index.js +11 -3
  25. package/dist/index.js.map +1 -1
  26. package/dist/react/index.d.ts +3 -0
  27. package/dist/react/index.js +2 -0
  28. package/dist/react/index.js.map +1 -0
  29. package/dist/react/types.d.ts +64 -0
  30. package/dist/react/types.js +2 -0
  31. package/dist/react/types.js.map +1 -0
  32. package/dist/react/use-ag-grid-url-sync.d.ts +10 -0
  33. package/dist/react/use-ag-grid-url-sync.js +215 -0
  34. package/dist/react/use-ag-grid-url-sync.js.map +1 -0
  35. package/package.json +97 -8
  36. package/dist/ag-grid-url-sync.js.map +0 -1
  37. package/dist/types.d.ts +0 -69
  38. package/dist/types.js +0 -31
  39. package/dist/types.js.map +0 -1
  40. package/dist/utils.d.ts +0 -36
  41. package/dist/utils.js +0 -188
  42. package/dist/utils.js.map +0 -1
  43. /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 { parseUrlFilters, serializeFilters, generateUrl, getFilterModel, applyFilterModel, validateFilterValue, parseFilterParam, DEFAULT_CONFIG } from './utils.js';
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
- export { AGGridUrlSync, createUrlSync } from './ag-grid-url-sync.js';
2
- export * from './types.js';
3
- export { parseUrlFilters, serializeFilters, generateUrl, getFilterModel, applyFilterModel, validateFilterValue, parseFilterParam, DEFAULT_CONFIG } from './utils.js';
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,uBAAuB,CAAA;AAEpE,cAAc,YAAY,CAAA;AAE1B,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,WAAW,EACX,cAAc,EACd,gBAAgB,EAChB,mBAAmB,EACnB,gBAAgB,EAChB,cAAc,EACf,MAAM,YAAY,CAAA"}
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,3 @@
1
+ export { useAGGridUrlSync } from './use-ag-grid-url-sync.js';
2
+ export type { UseAGGridUrlSyncOptions, UseAGGridUrlSyncReturn } from './types.js';
3
+ export type { FilterState, ColumnFilter, FilterOperation, AGGridUrlSyncConfig, GridApi } from '../core/types.js';
@@ -0,0 +1,2 @@
1
+ export { useAGGridUrlSync } from './use-ag-grid-url-sync.js';
2
+ //# sourceMappingURL=index.js.map
@@ -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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -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