@reactorui/datagrid 1.0.0

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 (57) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +778 -0
  3. package/dist/components/DataGrid/DataGrid.d.ts +5 -0
  4. package/dist/components/DataGrid/DataGrid.d.ts.map +1 -0
  5. package/dist/components/DataGrid/DataGrid.js +87 -0
  6. package/dist/components/DataGrid/index.d.ts +3 -0
  7. package/dist/components/DataGrid/index.d.ts.map +1 -0
  8. package/dist/components/DataGrid/index.js +1 -0
  9. package/dist/components/Filter/FilterControls.d.ts +11 -0
  10. package/dist/components/Filter/FilterControls.d.ts.map +1 -0
  11. package/dist/components/Filter/FilterControls.js +78 -0
  12. package/dist/components/Filter/index.d.ts +2 -0
  13. package/dist/components/Filter/index.d.ts.map +1 -0
  14. package/dist/components/Filter/index.js +1 -0
  15. package/dist/components/Pagination/Pagination.d.ts +17 -0
  16. package/dist/components/Pagination/Pagination.d.ts.map +1 -0
  17. package/dist/components/Pagination/Pagination.js +12 -0
  18. package/dist/components/Pagination/index.d.ts +2 -0
  19. package/dist/components/Pagination/index.d.ts.map +1 -0
  20. package/dist/components/Pagination/index.js +1 -0
  21. package/dist/components/Search/SearchInput.d.ts +11 -0
  22. package/dist/components/Search/SearchInput.d.ts.map +1 -0
  23. package/dist/components/Search/SearchInput.js +9 -0
  24. package/dist/components/Search/index.d.ts +2 -0
  25. package/dist/components/Search/index.d.ts.map +1 -0
  26. package/dist/components/Search/index.js +1 -0
  27. package/dist/components/Table/TableBody.d.ts +20 -0
  28. package/dist/components/Table/TableBody.d.ts.map +1 -0
  29. package/dist/components/Table/TableBody.js +56 -0
  30. package/dist/components/Table/TableHeader.d.ts +13 -0
  31. package/dist/components/Table/TableHeader.d.ts.map +1 -0
  32. package/dist/components/Table/TableHeader.js +24 -0
  33. package/dist/components/Table/index.d.ts +3 -0
  34. package/dist/components/Table/index.d.ts.map +1 -0
  35. package/dist/components/Table/index.js +2 -0
  36. package/dist/hooks/index.d.ts +2 -0
  37. package/dist/hooks/index.d.ts.map +1 -0
  38. package/dist/hooks/index.js +1 -0
  39. package/dist/hooks/useDataGrid.d.ts +49 -0
  40. package/dist/hooks/useDataGrid.d.ts.map +1 -0
  41. package/dist/hooks/useDataGrid.js +356 -0
  42. package/dist/index.d.ts +7 -0
  43. package/dist/index.d.ts.map +1 -0
  44. package/dist/index.js +8 -0
  45. package/dist/setupTests.d.ts +12 -0
  46. package/dist/setupTests.d.ts.map +1 -0
  47. package/dist/setupTests.js +1 -0
  48. package/dist/themes/index.d.ts +22 -0
  49. package/dist/themes/index.d.ts.map +1 -0
  50. package/dist/themes/index.js +31 -0
  51. package/dist/types/index.d.ts +108 -0
  52. package/dist/types/index.d.ts.map +1 -0
  53. package/dist/types/index.js +1 -0
  54. package/dist/utils/index.d.ts +12 -0
  55. package/dist/utils/index.d.ts.map +1 -0
  56. package/dist/utils/index.js +209 -0
  57. package/package.json +80 -0
@@ -0,0 +1,5 @@
1
+ import { DataGridProps } from '../../types';
2
+ export declare const DataGrid: <T extends {
3
+ id?: string | number;
4
+ } = any>({ data, endpoint, columns: columnsProp, enableSearch, enableSorting, enableFilters, enableSelection, pageSize, serverPageSize, pageSizeOptions, httpConfig, variant, size, className, onDataLoad, onDataError, onLoadingStateChange, onPageChange, onPageSizeChange, onSortChange, onFilterChange, onSearchChange, onTableRefresh, onTableRowClick, onTableRowDoubleClick, onRowSelect, onSelectionChange, onTableRowHover, onCellClick, ...rest }: DataGridProps<T>) => import("react/jsx-runtime").JSX.Element;
5
+ //# sourceMappingURL=DataGrid.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DataGrid.d.ts","sourceRoot":"","sources":["../../../src/components/DataGrid/DataGrid.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAU,MAAM,aAAa,CAAC;AAQpD,eAAO,MAAM,QAAQ,GAAI,CAAC,SAAS;IAAE,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,GAAG,GAAG,EAAE,obAkChE,aAAa,CAAC,CAAC,CAAC,4CAwLlB,CAAC"}
@@ -0,0 +1,87 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React, { useMemo } from 'react';
3
+ import { useDataGrid } from '../../hooks';
4
+ import { SearchInput } from '../Search';
5
+ import { FilterControls } from '../Filter';
6
+ import { TableHeader, TableBody } from '../Table';
7
+ import { Pagination } from '../Pagination';
8
+ import { getTheme } from '../../themes';
9
+ export const DataGrid = ({ data, endpoint, columns: columnsProp = [], enableSearch = true, enableSorting = true, enableFilters = true, enableSelection = true, pageSize = 10, serverPageSize = 100, pageSizeOptions = [5, 10, 25, 50, 100], httpConfig, variant = 'default', size = 'md', className = '',
10
+ // Event callbacks (renamed to avoid HTML conflicts)
11
+ onDataLoad, onDataError, onLoadingStateChange, onPageChange, onPageSizeChange, onSortChange, onFilterChange, onSearchChange, onTableRefresh, onTableRowClick, onTableRowDoubleClick, onRowSelect, onSelectionChange, onTableRowHover, onCellClick, ...rest }) => {
12
+ const theme = getTheme(variant);
13
+ // Use the data grid hook with all callbacks
14
+ const { data: sourceData, processedData, paginatedData, loading, error, searchTerm, activeFilters, sortConfig, selectedRows, currentPage, currentPageSize, setSearchTerm, setSort, setCurrentPage, setCurrentPageSize, addFilter, removeFilter, clearFilters, selectRow, selectAll, paginationInfo, selectedData, refresh, } = useDataGrid({
15
+ data,
16
+ endpoint,
17
+ httpConfig,
18
+ pageSize,
19
+ serverPageSize,
20
+ onDataLoad,
21
+ onDataError,
22
+ onLoadingStateChange,
23
+ onPageChange,
24
+ onPageSizeChange,
25
+ onSortChange,
26
+ onFilterChange,
27
+ onSearchChange,
28
+ });
29
+ // Auto-detect columns if not provided
30
+ const columns = useMemo(() => {
31
+ if (columnsProp.length > 0)
32
+ return columnsProp;
33
+ if (sourceData.length > 0) {
34
+ const firstRow = sourceData[0];
35
+ return Object.keys(firstRow).map(key => ({
36
+ key,
37
+ label: key.charAt(0).toUpperCase() + key.slice(1).replace(/([A-Z])/g, ' $1'),
38
+ sortable: true,
39
+ filterable: true,
40
+ dataType: inferDataType(firstRow[key]),
41
+ }));
42
+ }
43
+ return [];
44
+ }, [columnsProp, sourceData]);
45
+ // Handle selection changes
46
+ React.useEffect(() => {
47
+ if (onSelectionChange) {
48
+ onSelectionChange(selectedData);
49
+ }
50
+ }, [selectedData, onSelectionChange]);
51
+ // Handle row selection with callback
52
+ const handleRowSelect = (rowId, selected) => {
53
+ selectRow(rowId, selected);
54
+ if (onRowSelect) {
55
+ const row = sourceData.find(r => String(r.id) === rowId);
56
+ if (row) {
57
+ onRowSelect(row, selected);
58
+ }
59
+ }
60
+ };
61
+ // Handle refresh
62
+ const handleRefresh = () => {
63
+ refresh();
64
+ onTableRefresh?.();
65
+ };
66
+ if (error) {
67
+ return (_jsx("div", { className: `${theme.container} ${className}`, ...rest, children: _jsxs("div", { className: "px-4 py-8 text-center", children: [_jsx("div", { className: "text-red-600 mb-2", children: "Error loading data" }), _jsx("div", { className: "text-sm text-gray-600 mb-4", children: error }), _jsx("button", { onClick: handleRefresh, className: "px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700", children: "Try Again" })] }) }));
68
+ }
69
+ return (_jsxs("div", { className: `${theme.container} ${className}`, ...rest, children: [_jsxs("div", { className: "p-4 space-y-4", children: [enableFilters && (_jsx(FilterControls, { columns: columns, activeFilters: activeFilters, onAddFilter: addFilter, onRemoveFilter: removeFilter, onClearFilters: clearFilters })), _jsxs("div", { className: "flex flex-col sm:flex-row gap-4 justify-between items-start sm:items-center", children: [enableSearch && (_jsx("div", { className: "w-full sm:w-64", children: _jsx(SearchInput, { value: searchTerm, onChange: setSearchTerm, placeholder: "Search...", disabled: loading }) })), _jsx("button", { onClick: handleRefresh, disabled: loading, className: "px-4 py-2 bg-gray-100 text-gray-700 rounded hover:bg-gray-200 disabled:opacity-50", children: loading ? 'Loading...' : 'Refresh' })] })] }), _jsx("div", { className: "overflow-x-auto", children: _jsxs("table", { className: theme.table, children: [_jsx(TableHeader, { columns: columns, sortConfig: sortConfig, onSort: enableSorting ? setSort : undefined, enableSelection: enableSelection, selectedCount: selectedRows.size, totalCount: paginatedData.length, onSelectAll: enableSelection ? selectAll : undefined }), _jsx(TableBody, { columns: columns, data: paginatedData, selectedRows: selectedRows, onSelectRow: enableSelection ? handleRowSelect : undefined, onRowClick: onTableRowClick, onRowDoubleClick: onTableRowDoubleClick, onRowHover: onTableRowHover, onCellClick: onCellClick, enableSelection: enableSelection, loading: loading })] }) }), _jsx(Pagination, { currentPage: currentPage, totalPages: paginationInfo.totalPages, pageSize: currentPageSize, pageSizeOptions: pageSizeOptions, totalRecords: processedData.length, displayStart: paginationInfo.start, displayEnd: paginationInfo.end, onPageChange: setCurrentPage, onPageSizeChange: setCurrentPageSize, hasNext: paginationInfo.hasNext, hasPrevious: paginationInfo.hasPrevious })] }));
70
+ };
71
+ // Helper function to infer data type
72
+ function inferDataType(value) {
73
+ if (value == null)
74
+ return 'string';
75
+ if (typeof value === 'boolean')
76
+ return 'boolean';
77
+ if (typeof value === 'number')
78
+ return 'number';
79
+ if (typeof value === 'string') {
80
+ // Try to detect dates
81
+ const dateValue = new Date(value);
82
+ if (!isNaN(dateValue.getTime()) && dateValue.getFullYear() > 1900) {
83
+ return value.includes('T') || value.includes(' ') ? 'datetime' : 'date';
84
+ }
85
+ }
86
+ return 'string';
87
+ }
@@ -0,0 +1,3 @@
1
+ export { DataGrid } from './DataGrid';
2
+ export type { DataGridProps } from '../../types';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/DataGrid/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1 @@
1
+ export { DataGrid } from './DataGrid';
@@ -0,0 +1,11 @@
1
+ import { Column, ActiveFilter } from '../../types';
2
+ interface FilterControlsProps<T> {
3
+ columns: Column<T>[];
4
+ activeFilters: ActiveFilter[];
5
+ onAddFilter: (filter: Omit<ActiveFilter, 'label'>) => void;
6
+ onRemoveFilter: (index: number) => void;
7
+ onClearFilters: () => void;
8
+ }
9
+ export declare const FilterControls: <T>({ columns, activeFilters, onAddFilter, onRemoveFilter, onClearFilters, }: FilterControlsProps<T>) => import("react/jsx-runtime").JSX.Element;
10
+ export {};
11
+ //# sourceMappingURL=FilterControls.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FilterControls.d.ts","sourceRoot":"","sources":["../../../src/components/Filter/FilterControls.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEnD,UAAU,mBAAmB,CAAC,CAAC;IAC7B,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;IACrB,aAAa,EAAE,YAAY,EAAE,CAAC;IAC9B,WAAW,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC,YAAY,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IAC3D,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,cAAc,EAAE,MAAM,IAAI,CAAC;CAC5B;AAED,eAAO,MAAM,cAAc,GAAI,CAAC,EAAG,0EAMhC,mBAAmB,CAAC,CAAC,CAAC,4CAsLxB,CAAC"}
@@ -0,0 +1,78 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from 'react';
3
+ export const FilterControls = ({ columns, activeFilters, onAddFilter, onRemoveFilter, onClearFilters, }) => {
4
+ const [filterColumn, setFilterColumn] = useState('');
5
+ const [filterOperator, setFilterOperator] = useState('eq');
6
+ const [filterValue, setFilterValue] = useState('');
7
+ const filterableColumns = columns.filter((col) => col.filterable !== false);
8
+ const selectedColumn = filterableColumns.find((col) => col.key === filterColumn);
9
+ const getOperatorOptions = () => {
10
+ if (!selectedColumn)
11
+ return [{ value: 'eq', label: 'equals' }];
12
+ switch (selectedColumn.dataType) {
13
+ case 'number':
14
+ case 'date':
15
+ case 'datetime':
16
+ return [
17
+ { value: 'eq', label: 'equals' },
18
+ { value: 'gt', label: 'greater than' },
19
+ { value: 'gte', label: 'greater than or equal' },
20
+ { value: 'lt', label: 'less than' },
21
+ { value: 'lte', label: 'less than or equal' },
22
+ ];
23
+ case 'string':
24
+ return [
25
+ { value: 'eq', label: 'equals' },
26
+ { value: 'contains', label: 'contains' },
27
+ { value: 'startsWith', label: 'starts with' },
28
+ { value: 'endsWith', label: 'ends with' },
29
+ ];
30
+ case 'boolean':
31
+ return [{ value: 'eq', label: 'equals' }];
32
+ default:
33
+ return [{ value: 'eq', label: 'equals' }];
34
+ }
35
+ };
36
+ const renderFilterInput = () => {
37
+ if (!selectedColumn) {
38
+ return (_jsx("input", { type: "text", disabled: true, placeholder: "Select column first", className: "px-3 py-2 border border-gray-300 rounded-md text-sm bg-gray-50 cursor-not-allowed" }));
39
+ }
40
+ const baseProps = {
41
+ value: filterValue,
42
+ onChange: (e) => setFilterValue(e.target.value),
43
+ className: 'px-3 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-blue-500',
44
+ };
45
+ switch (selectedColumn.dataType) {
46
+ case 'boolean':
47
+ return (_jsxs("select", { ...baseProps, children: [_jsx("option", { value: "", children: "Select value" }), _jsx("option", { value: "true", children: "True" }), _jsx("option", { value: "false", children: "False" })] }));
48
+ case 'date':
49
+ return _jsx("input", { ...baseProps, type: "date" });
50
+ case 'datetime':
51
+ return _jsx("input", { ...baseProps, type: "datetime-local" });
52
+ case 'number':
53
+ return _jsx("input", { ...baseProps, type: "number", placeholder: "Enter number" });
54
+ default:
55
+ return _jsx("input", { ...baseProps, type: "text", placeholder: "Enter value" });
56
+ }
57
+ };
58
+ const handleApplyFilter = () => {
59
+ if (!filterColumn || !filterValue.trim())
60
+ return;
61
+ onAddFilter({
62
+ column: filterColumn,
63
+ operator: filterOperator,
64
+ value: filterValue.trim(),
65
+ dataType: selectedColumn?.dataType || 'string',
66
+ });
67
+ // Reset form
68
+ setFilterColumn('');
69
+ setFilterOperator('eq');
70
+ setFilterValue('');
71
+ };
72
+ const canApplyFilter = filterColumn && filterValue.trim();
73
+ return (_jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "flex flex-wrap gap-4 items-end", children: [_jsxs("div", { className: "min-w-40", children: [_jsx("label", { className: "block text-xs font-medium text-gray-700 mb-1", children: "Column" }), _jsxs("select", { value: filterColumn, onChange: (e) => {
74
+ setFilterColumn(e.target.value);
75
+ setFilterOperator('eq');
76
+ setFilterValue('');
77
+ }, className: "w-full px-3 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-blue-500", children: [_jsx("option", { value: "", children: "Select Column" }), filterableColumns.map((col) => (_jsx("option", { value: String(col.key), children: col.label }, String(col.key))))] })] }), _jsxs("div", { className: "min-w-32", children: [_jsx("label", { className: "block text-xs font-medium text-gray-700 mb-1", children: "Operator" }), _jsx("select", { value: filterOperator, onChange: (e) => setFilterOperator(e.target.value), disabled: !filterColumn, className: "w-full px-3 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:bg-gray-50 disabled:cursor-not-allowed", children: getOperatorOptions().map((op) => (_jsx("option", { value: op.value, children: op.label }, op.value))) })] }), _jsxs("div", { className: "min-w-40", children: [_jsx("label", { className: "block text-xs font-medium text-gray-700 mb-1", children: "Value" }), renderFilterInput()] }), _jsxs("div", { className: "flex gap-2", children: [_jsx("button", { onClick: handleApplyFilter, disabled: !canApplyFilter, className: "px-4 py-2 bg-blue-600 text-white text-sm rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:bg-gray-300 disabled:cursor-not-allowed", children: "Apply Filter" }), _jsx("button", { onClick: onClearFilters, className: "px-4 py-2 bg-gray-100 text-gray-700 text-sm rounded-md hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-gray-500", children: "Clear All" })] })] }), activeFilters.length > 0 && (_jsxs("div", { className: "flex flex-wrap gap-2 items-center", children: [_jsx("span", { className: "text-sm text-gray-600", children: "Active filters:" }), activeFilters.map((filter, index) => (_jsxs("span", { className: "inline-flex items-center gap-1 px-3 py-1 bg-blue-100 text-blue-800 rounded-full text-sm", children: [filter.label, _jsx("button", { onClick: () => onRemoveFilter(index), className: "ml-1 text-blue-600 hover:text-blue-800 focus:outline-none", children: "\u00D7" })] }, index)))] }))] }));
78
+ };
@@ -0,0 +1,2 @@
1
+ export { FilterControls } from './FilterControls';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/Filter/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1 @@
1
+ export { FilterControls } from './FilterControls';
@@ -0,0 +1,17 @@
1
+ import React from 'react';
2
+ interface PaginationProps {
3
+ currentPage: number;
4
+ totalPages: number;
5
+ pageSize: number;
6
+ pageSizeOptions: number[];
7
+ totalRecords: number;
8
+ displayStart: number;
9
+ displayEnd: number;
10
+ onPageChange: (page: number) => void;
11
+ onPageSizeChange: (size: number) => void;
12
+ hasNext: boolean;
13
+ hasPrevious: boolean;
14
+ }
15
+ export declare const Pagination: React.FC<PaginationProps>;
16
+ export {};
17
+ //# sourceMappingURL=Pagination.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Pagination.d.ts","sourceRoot":"","sources":["../../../src/components/Pagination/Pagination.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,UAAU,eAAe;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,gBAAgB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CA0EhD,CAAC"}
@@ -0,0 +1,12 @@
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ export const Pagination = ({ currentPage, totalPages, pageSize, pageSizeOptions, totalRecords, displayStart, displayEnd, onPageChange, onPageSizeChange, hasNext, hasPrevious, }) => {
3
+ const handlePageChange = (page) => {
4
+ if (page >= 1 && page <= totalPages) {
5
+ onPageChange(page);
6
+ }
7
+ };
8
+ const handlePageSizeChange = (newSize) => {
9
+ onPageSizeChange(newSize);
10
+ };
11
+ return (_jsxs("div", { className: "flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4 px-4 py-3 bg-white border-t border-gray-200", children: [_jsxs("div", { className: "text-sm text-gray-700", children: ["Showing ", displayStart, "-", displayEnd, " of ", totalRecords, " records"] }), _jsxs("div", { className: "flex items-center gap-6", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "text-sm text-gray-700", children: "Show:" }), _jsx("select", { value: pageSize, onChange: (e) => handlePageSizeChange(parseInt(e.target.value)), className: "px-2 py-1 border border-gray-300 rounded text-sm focus:outline-none focus:ring-2 focus:ring-blue-500", children: pageSizeOptions.map((size) => (_jsx("option", { value: size, children: size }, size))) }), _jsx("span", { className: "text-sm text-gray-700", children: "entries" })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("button", { onClick: () => handlePageChange(currentPage - 1), disabled: !hasPrevious, className: "px-3 py-1 text-sm border border-gray-300 rounded hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed", children: "Previous" }), _jsxs("span", { className: "text-sm text-gray-700", children: ["Page ", currentPage, " of ", totalPages] }), _jsx("button", { onClick: () => handlePageChange(currentPage + 1), disabled: !hasNext, className: "px-3 py-1 text-sm border border-gray-300 rounded hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed", children: "Next" })] })] })] }));
12
+ };
@@ -0,0 +1,2 @@
1
+ export { Pagination } from './Pagination';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/Pagination/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1 @@
1
+ export { Pagination } from './Pagination';
@@ -0,0 +1,11 @@
1
+ import React from 'react';
2
+ interface SearchInputProps {
3
+ value: string;
4
+ onChange: (value: string) => void;
5
+ placeholder?: string;
6
+ disabled?: boolean;
7
+ className?: string;
8
+ }
9
+ export declare const SearchInput: React.FC<SearchInputProps>;
10
+ export {};
11
+ //# sourceMappingURL=SearchInput.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SearchInput.d.ts","sourceRoot":"","sources":["../../../src/components/Search/SearchInput.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,UAAU,gBAAgB;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC,gBAAgB,CAuClD,CAAC"}
@@ -0,0 +1,9 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ export const SearchInput = ({ value, onChange, placeholder = 'Search...', disabled = false, className = '', }) => {
3
+ return (_jsxs("div", { className: "relative", children: [_jsx("input", { type: "text", value: value, onChange: (e) => onChange(e.target.value), placeholder: placeholder, disabled: disabled, className: `
4
+ w-full px-3 py-2 pl-10 border border-gray-300 rounded-md text-sm
5
+ focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent
6
+ disabled:bg-gray-50 disabled:text-gray-500
7
+ ${className}
8
+ ` }), _jsx("div", { className: "absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none", children: _jsx("svg", { className: "h-4 w-4 text-gray-400", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" }) }) })] }));
9
+ };
@@ -0,0 +1,2 @@
1
+ export { SearchInput } from './SearchInput';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/Search/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1 @@
1
+ export { SearchInput } from './SearchInput';
@@ -0,0 +1,20 @@
1
+ import React from 'react';
2
+ import { Column } from '../../types';
3
+ interface TableBodyProps<T> {
4
+ columns: Column<T>[];
5
+ data: T[];
6
+ selectedRows: Set<string>;
7
+ onSelectRow?: (rowId: string, selected: boolean) => void;
8
+ onRowClick?: (row: T, event: React.MouseEvent) => void;
9
+ onRowDoubleClick?: (row: T, event: React.MouseEvent) => void;
10
+ onRowHover?: (row: T | null, event: React.MouseEvent) => void;
11
+ onCellClick?: (value: any, row: T, column: Column<T>, event: React.MouseEvent) => void;
12
+ enableSelection: boolean;
13
+ loading: boolean;
14
+ emptyMessage?: string;
15
+ }
16
+ export declare const TableBody: <T extends {
17
+ id?: string | number;
18
+ }>({ columns, data, selectedRows, onSelectRow, onRowClick, onRowDoubleClick, onRowHover, onCellClick, enableSelection, loading, emptyMessage, }: TableBodyProps<T>) => import("react/jsx-runtime").JSX.Element;
19
+ export {};
20
+ //# sourceMappingURL=TableBody.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TableBody.d.ts","sourceRoot":"","sources":["../../../src/components/Table/TableBody.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,UAAU,cAAc,CAAC,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;IACrB,IAAI,EAAE,CAAC,EAAE,CAAC;IACV,YAAY,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC1B,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;IACzD,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,UAAU,KAAK,IAAI,CAAC;IACvD,gBAAgB,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,UAAU,KAAK,IAAI,CAAC;IAC7D,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,UAAU,KAAK,IAAI,CAAC;IAC9D,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,UAAU,KAAK,IAAI,CAAC;IACvF,eAAe,EAAE,OAAO,CAAC;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,eAAO,MAAM,SAAS,GAAI,CAAC,SAAS;IAAE,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,EAAE,8IAY3D,cAAc,CAAC,CAAC,CAAC,4CAmInB,CAAC"}
@@ -0,0 +1,56 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ export const TableBody = ({ columns, data, selectedRows, onSelectRow, onRowClick, onRowDoubleClick, onRowHover, onCellClick, enableSelection, loading, emptyMessage = 'No data available', }) => {
3
+ const renderCell = (row, column) => {
4
+ const value = row[column.key];
5
+ if (column.render) {
6
+ return column.render(value, row, 0);
7
+ }
8
+ return value?.toString() || '';
9
+ };
10
+ const getRowId = (row) => {
11
+ return String(row.id || Math.random());
12
+ };
13
+ const handleRowClick = (row, event) => {
14
+ // Don't trigger if clicking on interactive elements
15
+ if (event.target instanceof HTMLInputElement ||
16
+ event.target instanceof HTMLButtonElement ||
17
+ event.target.closest('button, input, a')) {
18
+ return;
19
+ }
20
+ onRowClick?.(row, event);
21
+ // Also handle selection if enabled
22
+ if (enableSelection && onSelectRow) {
23
+ const rowId = getRowId(row);
24
+ const isSelected = selectedRows.has(rowId);
25
+ onSelectRow(rowId, !isSelected);
26
+ }
27
+ };
28
+ const handleCellClick = (row, column, event) => {
29
+ if (onCellClick) {
30
+ const value = row[column.key];
31
+ onCellClick(value, row, column, event);
32
+ }
33
+ };
34
+ if (loading && data.length === 0) {
35
+ return (_jsx("tbody", { children: Array.from({ length: 5 }).map((_, index) => (_jsxs("tr", { className: "animate-pulse", children: [enableSelection && (_jsx("td", { className: "px-4 py-3", children: _jsx("div", { className: "w-4 h-4 bg-gray-200 rounded" }) })), columns.map((column) => (_jsx("td", { className: "px-4 py-3", children: _jsx("div", { className: "h-4 bg-gray-200 rounded" }) }, String(column.key))))] }, index))) }));
36
+ }
37
+ if (data.length === 0 && !loading) {
38
+ return (_jsx("tbody", { children: _jsx("tr", { children: _jsx("td", { colSpan: columns.length + (enableSelection ? 1 : 0), className: "px-4 py-8 text-center text-gray-500", children: emptyMessage }) }) }));
39
+ }
40
+ return (_jsx("tbody", { className: "bg-white divide-y divide-gray-200", children: data.map((row) => {
41
+ const rowId = getRowId(row);
42
+ const isSelected = selectedRows.has(rowId);
43
+ return (_jsxs("tr", { className: `transition-colors duration-150 cursor-pointer ${isSelected ? 'bg-blue-50 hover:bg-blue-100' : 'hover:bg-gray-50'}`, onClick: (e) => handleRowClick(row, e), onDoubleClick: (e) => onRowDoubleClick?.(row, e), onMouseEnter: (e) => onRowHover?.(row, e), onMouseLeave: (e) => onRowHover?.(null, e), children: [enableSelection && (_jsx("td", { className: "px-4 py-3", children: _jsx("input", { type: "checkbox", checked: isSelected, onChange: (e) => {
44
+ e.stopPropagation();
45
+ onSelectRow?.(rowId, e.target.checked);
46
+ }, className: "w-4 h-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500" }) })), columns.map((column) => (_jsx("td", { className: "px-4 py-3 text-sm text-gray-900", style: {
47
+ width: column.width,
48
+ minWidth: column.minWidth,
49
+ maxWidth: column.maxWidth,
50
+ textAlign: column.align || 'left',
51
+ }, onClick: (e) => {
52
+ e.stopPropagation();
53
+ handleCellClick(row, column, e);
54
+ }, children: renderCell(row, column) }, String(column.key))))] }, rowId));
55
+ }) }));
56
+ };
@@ -0,0 +1,13 @@
1
+ import { Column, SortConfig } from '../../types';
2
+ interface TableHeaderProps<T> {
3
+ columns: Column<T>[];
4
+ sortConfig: SortConfig;
5
+ onSort?: (column: string) => void;
6
+ enableSelection: boolean;
7
+ selectedCount: number;
8
+ totalCount: number;
9
+ onSelectAll?: (selected: boolean) => void;
10
+ }
11
+ export declare const TableHeader: <T>({ columns, sortConfig, onSort, enableSelection, selectedCount, totalCount, onSelectAll, }: TableHeaderProps<T>) => import("react/jsx-runtime").JSX.Element;
12
+ export {};
13
+ //# sourceMappingURL=TableHeader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TableHeader.d.ts","sourceRoot":"","sources":["../../../src/components/Table/TableHeader.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEjD,UAAU,gBAAgB,CAAC,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;IACrB,UAAU,EAAE,UAAU,CAAC;IACvB,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,eAAe,EAAE,OAAO,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;CAC3C;AAED,eAAO,MAAM,WAAW,GAAI,CAAC,EAAG,2FAQ7B,gBAAgB,CAAC,CAAC,CAAC,4CA8ErB,CAAC"}
@@ -0,0 +1,24 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ export const TableHeader = ({ columns, sortConfig, onSort, enableSelection, selectedCount, totalCount, onSelectAll, }) => {
3
+ const getSortIcon = (columnKey) => {
4
+ if (sortConfig.column !== columnKey) {
5
+ return (_jsx("svg", { className: "w-4 h-4 text-gray-400", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M7 16V4m0 0L3 8m4-4l4 4m6 0v12m0 0l4-4m-4 4l-4-4" }) }));
6
+ }
7
+ if (sortConfig.direction === 'asc') {
8
+ return (_jsx("svg", { className: "w-4 h-4 text-blue-500", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M5 15l7-7 7 7" }) }));
9
+ }
10
+ return (_jsx("svg", { className: "w-4 h-4 text-blue-500", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 9l-7 7-7-7" }) }));
11
+ };
12
+ return (_jsx("thead", { className: "bg-gray-50 border-b border-gray-200", children: _jsxs("tr", { children: [enableSelection && (_jsx("th", { className: "w-12 px-4 py-3 text-left", role: "columnheader", "aria-label": "Select all rows", children: _jsx("input", { type: "checkbox", checked: selectedCount > 0 && selectedCount === totalCount, ref: (el) => {
13
+ if (el) {
14
+ el.indeterminate = selectedCount > 0 && selectedCount < totalCount;
15
+ }
16
+ }, onChange: (e) => onSelectAll?.(e.target.checked), className: "w-4 h-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500", "aria-label": "Select all rows" }) })), columns.map((column) => (_jsx("th", { role: "columnheader", className: `px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider ${column.sortable && onSort ? 'cursor-pointer hover:bg-gray-100 select-none' : ''}`, onClick: () => column.sortable && onSort && onSort(String(column.key)), style: {
17
+ width: column.width,
18
+ minWidth: column.minWidth,
19
+ maxWidth: column.maxWidth,
20
+ textAlign: column.align || 'left',
21
+ }, "aria-sort": sortConfig.column === String(column.key)
22
+ ? sortConfig.direction === 'asc' ? 'ascending' : 'descending'
23
+ : 'none', "aria-label": `Sort by ${column.label}`, children: _jsxs("div", { className: "flex items-center gap-2", children: [column.label, column.sortable && onSort && getSortIcon(String(column.key))] }) }, String(column.key))))] }) }));
24
+ };
@@ -0,0 +1,3 @@
1
+ export { TableHeader } from './TableHeader';
2
+ export { TableBody } from './TableBody';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/Table/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { TableHeader } from './TableHeader';
2
+ export { TableBody } from './TableBody';
@@ -0,0 +1,2 @@
1
+ export { useDataGrid } from './useDataGrid';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1 @@
1
+ export { useDataGrid } from './useDataGrid';
@@ -0,0 +1,49 @@
1
+ import { BaseRowData, ServerResponse, ActiveFilter, SortConfig, HttpConfig, PaginationInfo } from '../types';
2
+ interface UseDataGridProps<T> {
3
+ data?: T[];
4
+ endpoint?: string;
5
+ httpConfig?: HttpConfig;
6
+ pageSize?: number;
7
+ serverPageSize?: number;
8
+ onDataLoad?: (data: ServerResponse<T>) => void;
9
+ onDataError?: (error: Error, context: string) => void;
10
+ onLoadingStateChange?: (loading: boolean, context: string) => void;
11
+ onPageChange?: (page: number, paginationInfo: PaginationInfo) => void;
12
+ onPageSizeChange?: (pageSize: number, paginationInfo: PaginationInfo) => void;
13
+ onSortChange?: (sortConfig: SortConfig) => void;
14
+ onFilterChange?: (filters: ActiveFilter[]) => void;
15
+ onSearchChange?: (searchTerm: string) => void;
16
+ }
17
+ export declare const useDataGrid: <T extends BaseRowData>({ data: staticData, endpoint, httpConfig, pageSize, serverPageSize, onDataLoad, onDataError, onLoadingStateChange, onPageChange, onPageSizeChange, onSortChange, onFilterChange, onSearchChange, }: UseDataGridProps<T>) => {
18
+ data: T[];
19
+ processedData: T[];
20
+ paginatedData: T[];
21
+ loading: boolean;
22
+ error: string | null;
23
+ searchTerm: string;
24
+ activeFilters: ActiveFilter[];
25
+ sortConfig: SortConfig;
26
+ selectedRows: Set<string>;
27
+ currentPage: number;
28
+ currentPageSize: number;
29
+ totalRecords: number;
30
+ hasMore: boolean;
31
+ continuationToken: string | undefined;
32
+ setSearchTerm: (term: string) => void;
33
+ setSort: (column: string) => void;
34
+ setCurrentPage: (page: number) => void;
35
+ setCurrentPageSize: (newPageSize: number) => void;
36
+ navigateNext: () => void;
37
+ navigatePrevious: () => void;
38
+ addFilter: (filter: Omit<ActiveFilter, "label">) => void;
39
+ removeFilter: (index: number) => void;
40
+ clearFilters: () => void;
41
+ selectRow: (rowId: string, selected: boolean) => void;
42
+ selectAll: (selected: boolean) => void;
43
+ refresh: () => void;
44
+ paginationInfo: PaginationInfo;
45
+ selectedData: T[];
46
+ hasSelection: boolean;
47
+ };
48
+ export {};
49
+ //# sourceMappingURL=useDataGrid.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useDataGrid.d.ts","sourceRoot":"","sources":["../../src/hooks/useDataGrid.ts"],"names":[],"mappings":"AACA,OAAO,EACL,WAAW,EAEX,cAAc,EACd,YAAY,EACZ,UAAU,EACV,UAAU,EACV,cAAc,EACf,MAAM,UAAU,CAAC;AAGlB,UAAU,gBAAgB,CAAC,CAAC;IAC1B,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;IACX,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;IAC/C,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACtD,oBAAoB,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACnE,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,cAAc,EAAE,cAAc,KAAK,IAAI,CAAC;IACtE,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,cAAc,EAAE,cAAc,KAAK,IAAI,CAAC;IAC9E,YAAY,CAAC,EAAE,CAAC,UAAU,EAAE,UAAU,KAAK,IAAI,CAAC;IAChD,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,YAAY,EAAE,KAAK,IAAI,CAAC;IACnD,cAAc,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;CAC/C;AAED,eAAO,MAAM,WAAW,GAAI,CAAC,SAAS,WAAW,EAAE,oMAchD,gBAAgB,CAAC,CAAC,CAAC;;;;;;;;;;;;;;;0BAsSX,MAAM;sBArFJ,MAAM;2BAkBR,MAAM;sCA2CC,MAAM;;;wBAuCX,IAAI,CAAC,YAAY,EAAE,OAAO,CAAC;0BAqB5B,MAAM;;uBA2BsB,MAAM,YAAY,OAAO;0BAalD,OAAO;;;;;CA6DrB,CAAC"}