@reactorui/datagrid 1.0.19 → 1.0.21

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.
@@ -15,9 +15,10 @@ export interface Column<T = BaseRowData> {
15
15
  align?: 'left' | 'center' | 'right';
16
16
  render?: (value: any, row: T, index: number) => ReactNode;
17
17
  }
18
+ export type FilterOperator = 'eq' | 'neq' | 'contains' | 'startsWith' | 'endsWith' | 'gt' | 'gte' | 'lt' | 'lte';
18
19
  export interface ActiveFilter {
19
20
  column: string;
20
- operator: string;
21
+ operator: FilterOperator | string;
21
22
  value: any;
22
23
  dataType: string;
23
24
  label: string;
@@ -35,38 +36,27 @@ export interface PaginationInfo {
35
36
  end: number;
36
37
  hasNext: boolean;
37
38
  hasPrevious: boolean;
38
- continuationToken?: string;
39
39
  }
40
- export interface ServerRequest {
41
- page: number;
42
- pageSize: number;
43
- search: string;
44
- filters: ActiveFilter[];
45
- continuationToken?: string;
46
- }
47
- export interface ServerResponse<T = BaseRowData> {
48
- Items: T[];
49
- ContinuationToken?: string;
50
- HasMore: boolean;
51
- Count: number;
52
- }
53
- export interface HttpConfig {
54
- bearerToken?: string;
55
- apiKey?: string;
56
- customHeaders?: Record<string, string>;
57
- method?: 'GET' | 'POST';
58
- postDataFormat?: 'form' | 'json';
59
- withCredentials?: boolean;
60
- timeout?: number;
40
+ export interface LoadingState {
41
+ /** Data is being fetched (shows table skeleton, disables all controls) */
42
+ data?: boolean;
43
+ /** Filter is being applied */
44
+ filter?: boolean;
45
+ /** Search is being executed */
46
+ search?: boolean;
47
+ /** Refresh is in progress */
48
+ refresh?: boolean;
49
+ /** Delete operation is in progress */
50
+ delete?: boolean;
61
51
  }
62
- export type OnDataLoadCallback<T = BaseRowData> = (data: ServerResponse<T>) => void;
63
- export type OnDataErrorCallback = (error: Error, context: string) => void;
64
- export type OnLoadingStateChangeCallback = (loading: boolean, context: string) => void;
65
52
  export type OnPageChangeCallback = (page: number, paginationInfo: PaginationInfo) => void;
66
- export type OnPageSizeChangeCallback = (pageSize: number, paginationInfo: PaginationInfo) => void;
53
+ export type OnPageSizeChangeCallback = (pageSize: number) => void;
67
54
  export type OnSortChangeCallback = (sortConfig: SortConfig) => void;
68
- export type OnFilterChangeCallback = (filters: ActiveFilter[]) => void;
69
55
  export type OnSearchChangeCallback = (searchTerm: string) => void;
56
+ export type OnApplyFilterCallback = (filter: ActiveFilter, allFilters: ActiveFilter[]) => void;
57
+ export type OnRemoveFilterCallback = (removedFilter: ActiveFilter, remainingFilters: ActiveFilter[]) => void;
58
+ export type OnClearFiltersCallback = () => void;
59
+ export type OnFilterChangeCallback = (filters: ActiveFilter[]) => void;
70
60
  export type OnTableRowClickCallback<T = BaseRowData> = (row: T, event: React.MouseEvent) => void;
71
61
  export type OnTableRowDoubleClickCallback<T = BaseRowData> = (row: T, event: React.MouseEvent) => boolean | void;
72
62
  export type OnRowSelectCallback<T = BaseRowData> = (row: T, isSelected: boolean) => void;
@@ -76,38 +66,86 @@ export type OnCellClickCallback<T = BaseRowData> = (value: any, row: T, column:
76
66
  export type OnTableRefreshCallback = () => void;
77
67
  export type OnBulkDeleteCallback<T = BaseRowData> = (selectedRows: T[]) => void;
78
68
  export interface DataGridProps<T = BaseRowData> extends Omit<HTMLAttributes<HTMLDivElement>, 'onError'> {
79
- data?: T[];
80
- endpoint?: string;
69
+ /** The data array to display - parent is responsible for fetching/filtering */
70
+ data: T[];
71
+ /** Column definitions - auto-detected if not provided */
81
72
  columns?: Column<T>[];
73
+ /**
74
+ * Granular loading states for different actions.
75
+ * @example { data: true } - Shows table skeleton
76
+ * @example { filter: true } - Shows spinner on Apply Filter button
77
+ * @example { refresh: true } - Shows spinner on Refresh button
78
+ */
79
+ loadingState?: LoadingState;
80
+ /**
81
+ * Simple loading flag - sets loadingState.data = true.
82
+ * Use this OR loadingState, not both.
83
+ * @deprecated Prefer loadingState for granular control
84
+ */
85
+ loading?: boolean;
86
+ /** Total records for pagination display (server-side) */
87
+ totalRecords?: number;
88
+ /** Whether more data is available (server-side) */
89
+ hasMore?: boolean;
90
+ /** Error message to display */
91
+ error?: string | null;
82
92
  enableSearch?: boolean;
83
93
  enableSorting?: boolean;
84
94
  enableFilters?: boolean;
85
95
  enableSelection?: boolean;
86
96
  enableDelete?: boolean;
97
+ enableRefresh?: boolean;
87
98
  deleteConfirmation?: boolean;
88
99
  pageSize?: number;
89
- serverPageSize?: number;
90
100
  pageSizeOptions?: number[];
91
- enableRefresh?: boolean;
92
- httpConfig?: HttpConfig;
101
+ /** Current page (controlled mode for server-side) */
102
+ currentPage?: number;
103
+ /**
104
+ * Set a max height for the grid - enables scrollable table body.
105
+ * Can be any CSS value: '400px', '50vh', 'calc(100vh - 200px)'
106
+ */
107
+ maxHeight?: string | number;
108
+ /**
109
+ * Keep table header visible while scrolling (sticky header).
110
+ * Automatically enabled when maxHeight is set.
111
+ */
112
+ stickyHeader?: boolean;
93
113
  className?: string;
94
114
  variant?: 'default' | 'striped' | 'bordered';
95
115
  size?: 'sm' | 'md' | 'lg';
96
- onDataLoad?: OnDataLoadCallback<T>;
97
- onDataError?: OnDataErrorCallback;
98
- onLoadingStateChange?: OnLoadingStateChangeCallback;
99
116
  onPageChange?: OnPageChangeCallback;
100
117
  onPageSizeChange?: OnPageSizeChangeCallback;
101
118
  onSortChange?: OnSortChangeCallback;
102
- onFilterChange?: OnFilterChangeCallback;
103
119
  onSearchChange?: OnSearchChangeCallback;
104
- onTableRefresh?: OnTableRefreshCallback;
120
+ /** Called when Apply Filter button is clicked */
121
+ onApplyFilter?: OnApplyFilterCallback;
122
+ /** Called when a filter tag is removed */
123
+ onRemoveFilter?: OnRemoveFilterCallback;
124
+ /** Called when Clear All is clicked */
125
+ onClearFilters?: OnClearFiltersCallback;
126
+ /** Called whenever filters change (convenience callback) */
127
+ onFilterChange?: OnFilterChangeCallback;
105
128
  onTableRowClick?: OnTableRowClickCallback<T>;
106
129
  onTableRowDoubleClick?: OnTableRowDoubleClickCallback<T>;
107
130
  onRowSelect?: OnRowSelectCallback<T>;
108
131
  onSelectionChange?: OnSelectionChangeCallback<T>;
109
132
  onTableRowHover?: OnTableRowHoverCallback<T>;
110
133
  onCellClick?: OnCellClickCallback<T>;
134
+ onTableRefresh?: OnTableRefreshCallback;
111
135
  onBulkDelete?: OnBulkDeleteCallback<T>;
112
136
  }
137
+ /** Request object that parent can use to build API calls */
138
+ export interface DataGridRequest {
139
+ page: number;
140
+ pageSize: number;
141
+ search: string;
142
+ filters: ActiveFilter[];
143
+ sort: SortConfig;
144
+ }
145
+ /** Helper type for building API responses */
146
+ export interface DataGridResponse<T = BaseRowData> {
147
+ items: T[];
148
+ totalRecords: number;
149
+ hasMore?: boolean;
150
+ }
113
151
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"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"}
@@ -1 +1,2 @@
1
+ // File: src/types/index.ts
1
2
  export {};
@@ -1,12 +1,21 @@
1
- import { HttpConfig, ServerRequest, ServerResponse } from '../types';
1
+ import type { ActiveFilter, SortConfig, DataGridRequest } from '../types';
2
2
  export declare const formatters: {
3
3
  date: (value: string, includeTime?: boolean) => string;
4
- currency: (value: number, currency?: string) => string;
5
- number: (value: number, decimals?: number) => string;
4
+ currency: (value: number, currency?: string, locale?: string) => string;
5
+ number: (value: number, decimals?: number, locale?: string) => string;
6
6
  truncate: (text: string, length: number) => string;
7
+ percentage: (value: number, decimals?: number) => string;
7
8
  };
8
9
  export declare const compareValues: (dataValue: any, filterValue: any, operator: string, dataType: string) => boolean;
9
- export declare const createApiRequest: <T>(endpoint: string, request: ServerRequest, config?: HttpConfig) => Promise<ServerResponse<T>>;
10
- export declare const mapServerResponse: <T>(data: any) => ServerResponse<T>;
11
10
  export declare const sortData: <T>(data: T[], sortColumn: string, direction: "asc" | "desc") => T[];
11
+ export declare const inferDataType: (value: any) => "string" | "number" | "boolean" | "date" | "datetime";
12
+ export declare const inferColumns: <T extends Record<string, any>>(data: T[]) => Array<{
13
+ key: string;
14
+ label: string;
15
+ sortable: boolean;
16
+ filterable: boolean;
17
+ dataType: "string" | "number" | "boolean" | "date" | "datetime";
18
+ }>;
19
+ export declare const generateRowId: <T extends Record<string, any>>(row: T, index: number) => string;
20
+ export declare const buildRequest: (page: number, pageSize: number, search: string, filters: ActiveFilter[], sort: SortConfig) => DataGridRequest;
12
21
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAGrE,eAAO,MAAM,UAAU;kBACP,MAAM;sBAMF,MAAM;oBAOR,MAAM;qBAOL,MAAM,UAAU,MAAM;CAIxC,CAAC;AAGF,eAAO,MAAM,aAAa,GACxB,WAAW,GAAG,EACd,aAAa,GAAG,EAChB,UAAU,MAAM,EAChB,UAAU,MAAM,KACf,OAkEF,CAAC;AAGF,eAAO,MAAM,gBAAgB,GAAU,CAAC,EACtC,UAAU,MAAM,EAChB,SAAS,aAAa,EACtB,SAAQ,UAAe,KACtB,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,CA2D3B,CAAC;AAGF,eAAO,MAAM,iBAAiB,GAAI,CAAC,EAAE,MAAM,GAAG,KAAG,cAAc,CAAC,CAAC,CA6DhE,CAAC;AAGF,eAAO,MAAM,QAAQ,GAAI,CAAC,EAAE,MAAM,CAAC,EAAE,EAAE,YAAY,MAAM,EAAE,WAAW,KAAK,GAAG,MAAM,KAAG,CAAC,EAiBvF,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAM1E,eAAO,MAAM,UAAU;kBACP,MAAM,4BAAwB,MAAM;sBAMhC,MAAM,yCAAuC,MAAM;oBAOrD,MAAM,yCAAmC,MAAM;qBAO9C,MAAM,UAAU,MAAM,KAAG,MAAM;wBAK5B,MAAM,wBAAiB,MAAM;CAGlD,CAAC;AAMF,eAAO,MAAM,aAAa,GACxB,WAAW,GAAG,EACd,aAAa,GAAG,EAChB,UAAU,MAAM,EAChB,UAAU,MAAM,KACf,OAgBF,CAAC;AA0EF,eAAO,MAAM,QAAQ,GAAI,CAAC,EAAE,MAAM,CAAC,EAAE,EAAE,YAAY,MAAM,EAAE,WAAW,KAAK,GAAG,MAAM,KAAG,CAAC,EA6BvF,CAAC;AAMF,eAAO,MAAM,aAAa,GACxB,OAAO,GAAG,KACT,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,GAAG,UAiB7C,CAAC;AAMF,eAAO,MAAM,YAAY,GAAI,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACxD,MAAM,CAAC,EAAE,KACR,KAAK,CAAC;IACP,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,EAAE,OAAO,CAAC;IACpB,QAAQ,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,GAAG,UAAU,CAAC;CACjE,CAWA,CAAC;AAcF,eAAO,MAAM,aAAa,GAAI,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,KAAK,CAAC,EAAE,OAAO,MAAM,KAAG,MAYpF,CAAC;AAMF,eAAO,MAAM,YAAY,GACvB,MAAM,MAAM,EACZ,UAAU,MAAM,EAChB,QAAQ,MAAM,EACd,SAAS,YAAY,EAAE,EACvB,MAAM,UAAU,KACf,eAMD,CAAC"}
@@ -1,4 +1,7 @@
1
- // Data formatting utilities (keeping existing ones)
1
+ // File: src/utils/index.ts
2
+ // ============================================================================
3
+ // Data Formatting Utilities
4
+ // ============================================================================
2
5
  export const formatters = {
3
6
  date: (value, includeTime = false) => {
4
7
  if (!value)
@@ -6,14 +9,14 @@ export const formatters = {
6
9
  const date = new Date(value);
7
10
  return includeTime ? date.toLocaleString() : date.toLocaleDateString();
8
11
  },
9
- currency: (value, currency = 'USD') => {
10
- return new Intl.NumberFormat('en-US', {
12
+ currency: (value, currency = 'USD', locale = 'en-US') => {
13
+ return new Intl.NumberFormat(locale, {
11
14
  style: 'currency',
12
15
  currency,
13
16
  }).format(value);
14
17
  },
15
- number: (value, decimals = 0) => {
16
- return new Intl.NumberFormat('en-US', {
18
+ number: (value, decimals = 0, locale = 'en-US') => {
19
+ return new Intl.NumberFormat(locale, {
17
20
  minimumFractionDigits: decimals,
18
21
  maximumFractionDigits: decimals,
19
22
  }).format(value);
@@ -23,187 +26,194 @@ export const formatters = {
23
26
  return text;
24
27
  return text.substring(0, length) + '...';
25
28
  },
29
+ percentage: (value, decimals = 0) => {
30
+ return `${(value * 100).toFixed(decimals)}%`;
31
+ },
26
32
  };
27
- // Filter comparison functions (keeping existing implementation)
33
+ // ============================================================================
34
+ // Filter Comparison Functions
35
+ // ============================================================================
28
36
  export const compareValues = (dataValue, filterValue, operator, dataType) => {
29
37
  if (dataValue == null)
30
38
  return false;
31
39
  switch (dataType) {
32
40
  case 'string':
33
- const str = dataValue.toString().toLowerCase();
34
- const filter = filterValue.toString().toLowerCase();
35
- switch (operator) {
36
- case 'eq':
37
- return str === filter;
38
- case 'contains':
39
- return str.includes(filter);
40
- case 'startsWith':
41
- return str.startsWith(filter);
42
- case 'endsWith':
43
- return str.endsWith(filter);
44
- default:
45
- return str.includes(filter);
46
- }
41
+ return compareString(dataValue, filterValue, operator);
47
42
  case 'number':
48
- const num = parseFloat(dataValue);
49
- const filterNum = parseFloat(filterValue);
50
- switch (operator) {
51
- case 'eq':
52
- return num === filterNum;
53
- case 'gt':
54
- return num > filterNum;
55
- case 'gte':
56
- return num >= filterNum;
57
- case 'lt':
58
- return num < filterNum;
59
- case 'lte':
60
- return num <= filterNum;
61
- default:
62
- return num === filterNum;
63
- }
43
+ return compareNumber(dataValue, filterValue, operator);
64
44
  case 'date':
65
45
  case 'datetime':
66
- const date = new Date(dataValue).getTime();
67
- const filterDate = new Date(filterValue).getTime();
68
- switch (operator) {
69
- case 'eq':
70
- return date === filterDate;
71
- case 'gt':
72
- return date > filterDate;
73
- case 'gte':
74
- return date >= filterDate;
75
- case 'lt':
76
- return date < filterDate;
77
- case 'lte':
78
- return date <= filterDate;
79
- default:
80
- return date === filterDate;
81
- }
46
+ return compareDate(dataValue, filterValue, operator);
82
47
  case 'boolean':
83
- return Boolean(dataValue) === Boolean(filterValue);
48
+ return Boolean(dataValue) === (filterValue === 'true' || filterValue === true);
84
49
  default:
85
50
  return String(dataValue).toLowerCase().includes(String(filterValue).toLowerCase());
86
51
  }
87
52
  };
88
- // Simplified API utilities
89
- export const createApiRequest = async (endpoint, request, config = {}) => {
90
- const { method = 'GET', bearerToken, apiKey, customHeaders = {}, withCredentials = false, timeout = 30000, } = config;
91
- // Build headers
92
- const headers = {
93
- 'Content-Type': 'application/json',
94
- ...customHeaders,
95
- };
96
- if (bearerToken) {
97
- headers.Authorization = `Bearer ${bearerToken}`;
98
- }
99
- if (apiKey) {
100
- headers['X-API-Key'] = apiKey;
101
- }
102
- // Build request
103
- const requestOptions = {
104
- method,
105
- headers,
106
- credentials: withCredentials ? 'include' : 'same-origin',
107
- };
108
- let url = endpoint;
109
- if (method === 'POST') {
110
- requestOptions.body = JSON.stringify({ request });
111
- }
112
- else {
113
- const params = new URLSearchParams({ request: JSON.stringify(request) });
114
- url = `${endpoint}?${params.toString()}`;
115
- }
116
- // Create timeout
117
- const controller = new AbortController();
118
- const timeoutId = setTimeout(() => controller.abort(), timeout);
119
- requestOptions.signal = controller.signal;
120
- try {
121
- const response = await fetch(url, requestOptions);
122
- clearTimeout(timeoutId);
123
- if (!response.ok) {
124
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
125
- }
126
- const data = await response.json();
127
- return mapServerResponse(data);
128
- }
129
- catch (error) {
130
- clearTimeout(timeoutId);
131
- throw error;
53
+ const compareString = (value, filter, operator) => {
54
+ const str = String(value).toLowerCase();
55
+ const filterStr = String(filter).toLowerCase();
56
+ switch (operator) {
57
+ case 'eq':
58
+ return str === filterStr;
59
+ case 'neq':
60
+ return str !== filterStr;
61
+ case 'contains':
62
+ return str.includes(filterStr);
63
+ case 'startsWith':
64
+ return str.startsWith(filterStr);
65
+ case 'endsWith':
66
+ return str.endsWith(filterStr);
67
+ default:
68
+ return str.includes(filterStr);
132
69
  }
133
70
  };
134
- // Simplified server response mapping with flexible casing
135
- export const mapServerResponse = (data) => {
136
- // Helper to get value with flexible casing
137
- const getValue = (obj, keys) => {
138
- for (const key of keys) {
139
- if (obj[key] !== undefined)
140
- return obj[key];
141
- }
142
- return undefined;
143
- };
144
- // Get items with flexible casing
145
- const items = getValue(data, ['Items', 'items', 'data', 'Data']);
146
- // Get continuation token with flexible casing
147
- const continuationToken = getValue(data, [
148
- 'ContinuationToken',
149
- 'continuationToken',
150
- 'continuationtoken',
151
- 'nextToken',
152
- 'next_token',
153
- 'NextToken',
154
- ]);
155
- // Get hasMore with flexible casing
156
- const hasMore = getValue(data, [
157
- 'HasMore',
158
- 'hasMore',
159
- 'hasmore',
160
- 'has_more',
161
- 'hasNextPage',
162
- 'has_next_page',
163
- ]);
164
- // Get count with flexible casing
165
- const count = getValue(data, ['Count', 'count', 'COUNT', 'total', 'Total', 'length', 'size']);
166
- // Direct mapping for arrays
167
- if (Array.isArray(data)) {
168
- return {
169
- Items: data,
170
- HasMore: false,
171
- Count: data.length,
172
- };
71
+ const compareNumber = (value, filter, operator) => {
72
+ const num = parseFloat(value);
73
+ const filterNum = parseFloat(filter);
74
+ if (isNaN(num) || isNaN(filterNum))
75
+ return false;
76
+ switch (operator) {
77
+ case 'eq':
78
+ return num === filterNum;
79
+ case 'neq':
80
+ return num !== filterNum;
81
+ case 'gt':
82
+ return num > filterNum;
83
+ case 'gte':
84
+ return num >= filterNum;
85
+ case 'lt':
86
+ return num < filterNum;
87
+ case 'lte':
88
+ return num <= filterNum;
89
+ default:
90
+ return num === filterNum;
173
91
  }
174
- // AWS DynamoDB style response (special case)
175
- if (data.Items && data.LastEvaluatedKey) {
176
- return {
177
- Items: data.Items,
178
- ContinuationToken: JSON.stringify(data.LastEvaluatedKey),
179
- HasMore: !!data.LastEvaluatedKey,
180
- Count: data.Count || data.Items.length,
181
- };
92
+ };
93
+ const compareDate = (value, filter, operator) => {
94
+ const date = new Date(value).getTime();
95
+ const filterDate = new Date(filter).getTime();
96
+ if (isNaN(date) || isNaN(filterDate))
97
+ return false;
98
+ switch (operator) {
99
+ case 'eq':
100
+ return date === filterDate;
101
+ case 'neq':
102
+ return date !== filterDate;
103
+ case 'gt':
104
+ return date > filterDate;
105
+ case 'gte':
106
+ return date >= filterDate;
107
+ case 'lt':
108
+ return date < filterDate;
109
+ case 'lte':
110
+ return date <= filterDate;
111
+ default:
112
+ return date === filterDate;
182
113
  }
183
- // Standard response mapping
184
- return {
185
- Items: Array.isArray(items) ? items : [],
186
- ContinuationToken: continuationToken,
187
- HasMore: Boolean(hasMore),
188
- Count: Number(count) || 0,
189
- };
190
114
  };
191
- // Sorting utilities (keeping existing implementation)
115
+ // ============================================================================
116
+ // Sorting Utilities
117
+ // ============================================================================
192
118
  export const sortData = (data, sortColumn, direction) => {
193
- if (!sortColumn)
119
+ if (!sortColumn || data.length === 0)
194
120
  return data;
195
121
  return [...data].sort((a, b) => {
196
122
  const aVal = a[sortColumn];
197
123
  const bVal = b[sortColumn];
124
+ // Handle nulls - always sort to end
198
125
  if (aVal == null && bVal == null)
199
126
  return 0;
200
127
  if (aVal == null)
201
128
  return 1;
202
129
  if (bVal == null)
203
130
  return -1;
204
- const aStr = String(aVal).toLowerCase();
205
- const bStr = String(bVal).toLowerCase();
206
- const result = aStr.localeCompare(bStr, undefined, { numeric: true });
131
+ // Compare based on type
132
+ let result;
133
+ if (typeof aVal === 'number' && typeof bVal === 'number') {
134
+ result = aVal - bVal;
135
+ }
136
+ else if (aVal instanceof Date && bVal instanceof Date) {
137
+ result = aVal.getTime() - bVal.getTime();
138
+ }
139
+ else {
140
+ // String comparison with natural sorting
141
+ result = String(aVal).localeCompare(String(bVal), undefined, {
142
+ numeric: true,
143
+ sensitivity: 'base',
144
+ });
145
+ }
207
146
  return direction === 'desc' ? -result : result;
208
147
  });
209
148
  };
149
+ // ============================================================================
150
+ // Data Type Inference
151
+ // ============================================================================
152
+ export const inferDataType = (value) => {
153
+ if (value == null)
154
+ return 'string';
155
+ if (typeof value === 'boolean')
156
+ return 'boolean';
157
+ if (typeof value === 'number')
158
+ return 'number';
159
+ if (typeof value === 'string') {
160
+ // Check for ISO date format
161
+ const isoDateRegex = /^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2})?/;
162
+ if (isoDateRegex.test(value)) {
163
+ const date = new Date(value);
164
+ if (!isNaN(date.getTime()) && date.getFullYear() > 1900) {
165
+ return value.includes('T') ? 'datetime' : 'date';
166
+ }
167
+ }
168
+ }
169
+ return 'string';
170
+ };
171
+ // ============================================================================
172
+ // Column Auto-Detection
173
+ // ============================================================================
174
+ export const inferColumns = (data) => {
175
+ if (data.length === 0)
176
+ return [];
177
+ const firstRow = data[0];
178
+ return Object.keys(firstRow).map((key) => ({
179
+ key,
180
+ label: formatColumnLabel(key),
181
+ sortable: true,
182
+ filterable: true,
183
+ dataType: inferDataType(firstRow[key]),
184
+ }));
185
+ };
186
+ const formatColumnLabel = (key) => {
187
+ return key
188
+ .replace(/([A-Z])/g, ' $1') // Add space before capitals
189
+ .replace(/[_-]/g, ' ') // Replace underscores/hyphens with spaces
190
+ .replace(/^\w/, (c) => c.toUpperCase()) // Capitalize first letter
191
+ .trim();
192
+ };
193
+ // ============================================================================
194
+ // Row ID Utilities
195
+ // ============================================================================
196
+ export const generateRowId = (row, index) => {
197
+ // Prefer existing id field
198
+ if (row.id !== undefined)
199
+ return String(row.id);
200
+ if (row._id !== undefined)
201
+ return String(row._id);
202
+ if (row.key !== undefined)
203
+ return String(row.key);
204
+ // Generate stable ID from content
205
+ const contentHash = JSON.stringify(row)
206
+ .slice(0, 50)
207
+ .replace(/[^a-zA-Z0-9]/g, '');
208
+ return `row-${index}-${contentHash}`;
209
+ };
210
+ // ============================================================================
211
+ // Export Helper for Building Requests (for parent component use)
212
+ // ============================================================================
213
+ export const buildRequest = (page, pageSize, search, filters, sort) => ({
214
+ page,
215
+ pageSize,
216
+ search,
217
+ filters,
218
+ sort,
219
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reactorui/datagrid",
3
- "version": "1.0.19",
3
+ "version": "1.0.21",
4
4
  "description": "High-performance React data grid with TypeScript support, server-side integration, and continuation token pagination",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",