react-restyle-components 0.4.35 → 0.4.36

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.
@@ -2,7 +2,7 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import React, { forwardRef, useState, useCallback, useMemo, useEffect, useRef, } from 'react';
4
4
  import { TableRoot, Toolbar, ToolbarGroup, SearchInput, ToolbarButton, TableWrapper, StyledTable, TableHeader, HeaderRow, HeaderCell, TableBody, TableRow, TableCell, Checkbox, ExpandButton, ExpandedRow, ExpandedCell, TableFooter, FooterRow, FooterCell, PaginationWrapper, PaginationInfo, PaginationControls, PageButton, PageSizeSelect, QuickJumper, EmptyState, LoadingOverlay, LoadingSpinner, EditableCell, CellEditor, ColumnTogglePanel, ColumnToggleHeader, ColumnToggleSearch, ColumnToggleList, ColumnToggleItem, SelectionIndicator, SelectionCount, } from './elements';
5
- import { useSortState, useFilterState, usePaginationState, useRowSelection, useRowExpansion, useColumnVisibility, useTableDebounce, sortData, filterData, paginateData, getNestedValue, exportToCSV, } from './hooks';
5
+ import { useSortState, useFilterState, usePaginationState, useRowSelection, useRowExpansion, useColumnVisibility, useTableDebounce, sortData, filterData, paginateData, getNestedValue, exportToCSV, exportToExcel, } from './hooks';
6
6
  import { getFilterComponent } from './filters';
7
7
  import { Tooltip } from '../Tooltip';
8
8
  // Icons
@@ -50,9 +50,9 @@ const RefreshIcon = () => (_jsxs("svg", { viewBox: "0 0 24 24", fill: "none", st
50
50
  const PrintIcon = () => (_jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [_jsx("path", { d: "M6 9V2h12v7M6 18H4a2 2 0 01-2-2v-5a2 2 0 012-2h16a2 2 0 012 2v5a2 2 0 01-2 2h-2", strokeLinecap: "round", strokeLinejoin: "round" }), _jsx("rect", { x: "6", y: "14", width: "12", height: "8" })] }));
51
51
  const ErrorIcon = () => (_jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [_jsx("circle", { cx: "12", cy: "12", r: "10" }), _jsx("path", { d: "M12 8v4M12 16h.01", strokeLinecap: "round", strokeLinejoin: "round" })] }));
52
52
  export const Table = forwardRef(function TableComponent(props, ref) {
53
- const { id, data, columns, keyField = '_id', loading = false, loadingIndicator, pagination = true, paginationConfig, totalSize, remote = true, defaultSort, sort: controlledSort, filterable = false, defaultFilters, filters: controlledFilters, defaultShowFilters = true, showFilters: controlledShowFilters, onShowFiltersChange, showFilterToggle = true, searchable = true, searchPlaceholder = 'Search...', defaultSearchValue = '', searchValue: controlledSearchValue, searchDebounce = 300, rowSelection, expandable, editMode = 'dblclick', showEditIcon = false, onCellEdit, exportable = true, exportFileName = 'table_export', columnToggle = false, bordered = true, striped = false, hover = true, compact = false, cellPadding, stickyHeader = false, maxHeight, rowClassName, rowStyle, classNames = {}, styles = {}, className, style, emptyText = 'No data available', onChange, onPageChange, onSortChange, onFilterChange, onSearch, onRowClick, onRowDoubleClick, onClearFilters, toolbar, hideToolbar = false, showFooter = false, caption,
53
+ const { id, data, columns, keyField = '_id', loading = false, loadingIndicator, pagination = true, paginationConfig, totalSize, remote = true, defaultSort, sort: controlledSort, filterable = false, defaultFilters, filters: controlledFilters, defaultShowFilters = true, showFilters: controlledShowFilters, onShowFiltersChange, showFilterToggle = true, searchable = true, searchPlaceholder = 'Search...', defaultSearchValue = '', searchValue: controlledSearchValue, searchDebounce = 300, rowSelection, expandable, editMode = 'dblclick', showEditIcon = false, onCellEdit, exportable = true, exportFileName = 'table_export', exportFormat = 'csv', columnToggle = false, bordered = true, striped = false, hover = true, compact = false, cellPadding, stickyHeader = false, maxHeight, rowClassName, rowStyle, classNames = {}, styles = {}, className, style, emptyText = 'No data available', onChange, onPageChange, onSortChange, onFilterChange, onSearch, onRowClick, onRowDoubleClick, onClearFilters, toolbar, hideToolbar = false, showFooter = false, caption,
54
54
  // Quick configuration props
55
- isDelete = false, isEditModify, isUpdate, isExport, isSelectRow, isView = false, fileName, hideExcelSheet = false,
55
+ isDelete = false, isEditModify, isUpdate, isExport, isSelectRow, getNonSelectableRows, nonSelectableStyle, isView = false, fileName, hideExcelSheet = false,
56
56
  // Quick callbacks
57
57
  onSelectedRow, selectedRowStyle, selectedRowClassName, onUpdateItem, onPageSizeChange, onFilter, clearAllFilter, onDelete, onEdit, onView,
58
58
  // Dynamic configuration
@@ -80,6 +80,17 @@ export const Table = forwardRef(function TableComponent(props, ref) {
80
80
  // Resolve aliases
81
81
  const resolvedExportable = isExport ?? exportable;
82
82
  const resolvedExportFileName = fileName ?? exportFileName;
83
+ // Auto-detect remote mode: if totalSize <= data.length, use client-side (remote=false)
84
+ const resolvedRemote = useMemo(() => {
85
+ // If totalSize is provided and is <= data.length, all data is loaded - use client-side
86
+ if (totalSize !== undefined &&
87
+ totalSize > 0 &&
88
+ totalSize <= data.length) {
89
+ return false;
90
+ }
91
+ // Otherwise use the prop value (default: true)
92
+ return remote;
93
+ }, [remote, totalSize, data.length]);
83
94
  // Handle isSelectRow shorthand
84
95
  const resolvedRowSelection = isSelectRow
85
96
  ? {
@@ -87,6 +98,16 @@ export const Table = forwardRef(function TableComponent(props, ref) {
87
98
  selectedRowStyle: selectedRowStyle || rowSelection?.selectedRowStyle,
88
99
  selectedRowClassName: selectedRowClassName || rowSelection?.selectedRowClassName,
89
100
  ...rowSelection,
101
+ // Merge getNonSelectableRows with existing getCheckboxProps
102
+ getCheckboxProps: (row) => {
103
+ const rowKey = row[keyField];
104
+ const isNonSelectable = getNonSelectableRows?.includes(rowKey);
105
+ const existingProps = rowSelection?.getCheckboxProps?.(row);
106
+ return {
107
+ ...existingProps,
108
+ disabled: isNonSelectable || existingProps?.disabled,
109
+ };
110
+ },
90
111
  onChange: (keys, rows) => {
91
112
  rowSelection?.onChange?.(keys, rows);
92
113
  // Note: onSelectedRow is only called when user clicks the "X Selected" button
@@ -156,12 +177,56 @@ export const Table = forwardRef(function TableComponent(props, ref) {
156
177
  const { sort, handleSort } = useSortState(defaultSort, controlledSort);
157
178
  // Filter state
158
179
  const { filters, setFilter, clearFilters } = useFilterState(defaultFilters, controlledFilters);
159
- // Debounced filters for onFilter callback
160
- const debouncedFilters = useTableDebounce(filters, 500);
161
180
  // Track if onFilter should be called (only after user interaction)
162
181
  const shouldCallOnFilter = useRef(false);
163
182
  const filterTypeRef = useRef('filter');
164
183
  const onFilterRef = useRef(onFilter);
184
+ // Track last called values to prevent duplicate calls
185
+ const lastOnFilterCallRef = useRef(null);
186
+ // Track focused filter field to restore focus after data changes
187
+ const focusedFilterFieldRef = useRef(null);
188
+ const tableContainerRef = useRef(null);
189
+ // Clear focus ref when clicking outside the table
190
+ useEffect(() => {
191
+ const handleClickOutside = (event) => {
192
+ if (tableContainerRef.current &&
193
+ !tableContainerRef.current.contains(event.target)) {
194
+ focusedFilterFieldRef.current = null;
195
+ }
196
+ };
197
+ document.addEventListener('mousedown', handleClickOutside);
198
+ return () => {
199
+ document.removeEventListener('mousedown', handleClickOutside);
200
+ };
201
+ }, []);
202
+ // Restore filter focus after data changes
203
+ useEffect(() => {
204
+ const restoreFocus = () => {
205
+ if (focusedFilterFieldRef.current && tableContainerRef.current) {
206
+ const filterInput = tableContainerRef.current.querySelector(`[data-filter-field="${focusedFilterFieldRef.current}"]`);
207
+ if (filterInput && document.activeElement !== filterInput) {
208
+ filterInput.focus();
209
+ // Move cursor to end of input
210
+ const len = filterInput.value?.length || 0;
211
+ filterInput.setSelectionRange(len, len);
212
+ }
213
+ }
214
+ };
215
+ // Try multiple times with increasing delays to handle async renders
216
+ const timers = [
217
+ requestAnimationFrame(() => restoreFocus()),
218
+ setTimeout(() => restoreFocus(), 50),
219
+ setTimeout(() => restoreFocus(), 150),
220
+ ];
221
+ return () => {
222
+ timers.forEach((timer) => {
223
+ if (typeof timer === 'number') {
224
+ cancelAnimationFrame(timer);
225
+ clearTimeout(timer);
226
+ }
227
+ });
228
+ };
229
+ }, [data]);
165
230
  // Pagination state
166
231
  const { page, pageSize, totalPages, goToPage, goToNextPage, goToPrevPage, goToFirstPage, goToLastPage, changePageSize, } = usePaginationState(resolvedPaginationConfig?.page || 0, resolvedPaginationConfig?.pageSize || 10, totalSize ?? data.length);
167
232
  // Row selection
@@ -174,23 +239,48 @@ export const Table = forwardRef(function TableComponent(props, ref) {
174
239
  useEffect(() => {
175
240
  onFilterRef.current = onFilter;
176
241
  }, [onFilter]);
177
- // Call onFilter when debounced values change (debounced)
242
+ // Track previous values to detect actual changes
243
+ const prevSearchRef = useRef(debouncedSearchValue);
244
+ const prevFiltersRef = useRef(JSON.stringify(filters));
245
+ // Call onFilter when filter/search values actually change
246
+ // Note: filter components already debounce internally, so we use filters directly
178
247
  useEffect(() => {
179
248
  if (!shouldCallOnFilter.current)
180
249
  return;
181
- const hasFilters = Object.keys(debouncedFilters).some((key) => debouncedFilters[key] !== null &&
182
- debouncedFilters[key] !== undefined &&
183
- debouncedFilters[key] !== '');
184
- if (debouncedSearchValue || hasFilters) {
185
- // For 'search' type, include srText; for 'filter' type, only pass filters
186
- const filterData = filterTypeRef.current === 'search'
187
- ? { ...debouncedFilters, srText: debouncedSearchValue }
188
- : debouncedFilters;
189
- onFilterRef.current?.(filterTypeRef.current, filterData, page, pageSize);
250
+ const currentFiltersStr = JSON.stringify(filters);
251
+ const searchChanged = prevSearchRef.current !== debouncedSearchValue;
252
+ const filtersChanged = prevFiltersRef.current !== currentFiltersStr;
253
+ // Only proceed if search or filters actually changed
254
+ if (!searchChanged && !filtersChanged) {
255
+ shouldCallOnFilter.current = false;
256
+ return;
257
+ }
258
+ // Update previous values
259
+ prevSearchRef.current = debouncedSearchValue;
260
+ prevFiltersRef.current = currentFiltersStr;
261
+ // Clean up filters - remove null, undefined, and empty string values
262
+ const cleanFilters = {};
263
+ Object.keys(filters).forEach((key) => {
264
+ const value = filters[key];
265
+ if (value !== null && value !== undefined && value !== '') {
266
+ cleanFilters[key] = value;
267
+ }
268
+ });
269
+ const hasFilters = Object.keys(cleanFilters).length > 0;
270
+ // Build filter data based on what type of filter changed
271
+ // For 'search' type, include srText if not empty; for 'filter' type, only pass filters
272
+ const filterData = { ...cleanFilters };
273
+ if (filterTypeRef.current === 'search' && debouncedSearchValue) {
274
+ filterData.srText = debouncedSearchValue;
190
275
  }
191
- // Reset the flag after calling onFilter to prevent duplicate calls
276
+ // Always call onFilter when filters change (including when cleared)
277
+ // Case 1: Global search cleared, column filters exist → filters only
278
+ // Case 2: Global search cleared, no column filters → empty filters (reload all)
279
+ // Case 3: Specific column cleared → remaining filters
280
+ onFilterRef.current?.(filterTypeRef.current, filterData, page, pageSize);
281
+ // Reset the flag after processing to prevent duplicate calls
192
282
  shouldCallOnFilter.current = false;
193
- }, [debouncedSearchValue, debouncedFilters, page, pageSize]);
283
+ }, [debouncedSearchValue, filters, page, pageSize]);
194
284
  // Track selection count changes for animation
195
285
  useEffect(() => {
196
286
  const currentCount = selectedKeys.size;
@@ -210,7 +300,7 @@ export const Table = forwardRef(function TableComponent(props, ref) {
210
300
  }, [selectedKeys.size]);
211
301
  // Process data (filter, sort, paginate)
212
302
  const processedData = useMemo(() => {
213
- if (remote) {
303
+ if (resolvedRemote) {
214
304
  // Server-side processing - data is already processed
215
305
  return data;
216
306
  }
@@ -231,7 +321,7 @@ export const Table = forwardRef(function TableComponent(props, ref) {
231
321
  page,
232
322
  pageSize,
233
323
  debouncedSearchValue,
234
- remote,
324
+ resolvedRemote,
235
325
  pagination,
236
326
  columns,
237
327
  ]);
@@ -240,10 +330,17 @@ export const Table = forwardRef(function TableComponent(props, ref) {
240
330
  // If totalSize is explicitly provided, use it for server-side pagination
241
331
  if (totalSize !== undefined && totalSize > 0)
242
332
  return totalSize;
243
- if (remote)
333
+ if (resolvedRemote)
244
334
  return data.length;
245
335
  return filterData(data, filters, columns, debouncedSearchValue).length;
246
- }, [data, filters, columns, debouncedSearchValue, remote, totalSize]);
336
+ }, [
337
+ data,
338
+ filters,
339
+ columns,
340
+ debouncedSearchValue,
341
+ resolvedRemote,
342
+ totalSize,
343
+ ]);
247
344
  // Handle search
248
345
  const handleSearchChange = useCallback((value) => {
249
346
  setInternalSearchValue(value);
@@ -279,6 +376,8 @@ export const Table = forwardRef(function TableComponent(props, ref) {
279
376
  clearAllFilter?.();
280
377
  onFilterChange?.({});
281
378
  onChange?.({ type: 'filter', filters: {} });
379
+ // Reset the tracking ref so subsequent filters can trigger onFilter
380
+ lastOnFilterCallRef.current = null;
282
381
  }, [clearFilters, onClearFilters, onFilterChange, onChange]);
283
382
  // Handle page change
284
383
  const handlePageChange = useCallback((newPage) => {
@@ -288,7 +387,10 @@ export const Table = forwardRef(function TableComponent(props, ref) {
288
387
  // onPageSizeChange is the primary callback
289
388
  onPageSizeChange?.(displayPage, pageSize);
290
389
  onPageChange?.(displayPage, pageSize);
291
- onChange?.({ type: 'pagination', pagination: { page: displayPage, pageSize } });
390
+ onChange?.({
391
+ type: 'pagination',
392
+ pagination: { page: displayPage, pageSize },
393
+ });
292
394
  }, [goToPage, pageSize, onPageSizeChange, onPageChange, onChange]);
293
395
  // Handle page size change
294
396
  const handlePageSizeChange = useCallback((newSize) => {
@@ -344,25 +446,32 @@ export const Table = forwardRef(function TableComponent(props, ref) {
344
446
  }, []);
345
447
  // Handle cell edit
346
448
  const handleCellDoubleClick = useCallback((row, rowIndex, column, colIndex, e) => {
347
- if (editMode === 'none')
348
- return;
349
- const { isEditable } = getCellEditableInfo(column, row, rowIndex, colIndex);
350
- if (!isEditable)
351
- return;
352
- if (editMode === 'dblclick') {
353
- setEditingCell({ row: rowIndex, field: column.dataField });
354
- setEditValue(getNestedValue(row, column.dataField));
449
+ // Call column's onDoubleClick event if defined
450
+ column.events?.onDoubleClick?.(e, row, rowIndex, column, colIndex);
451
+ // Handle cell editing
452
+ if (editMode !== 'none') {
453
+ const { isEditable } = getCellEditableInfo(column, row, rowIndex, colIndex);
454
+ if (isEditable && editMode === 'dblclick') {
455
+ setEditingCell({ row: rowIndex, field: column.dataField });
456
+ setEditValue(getNestedValue(row, column.dataField));
457
+ }
355
458
  }
459
+ // Call row double click callback
356
460
  onRowDoubleClick?.(row, rowIndex, e);
357
461
  }, [editMode, onRowDoubleClick, getCellEditableInfo]);
358
- const handleCellClick = useCallback((row, rowIndex, column, colIndex) => {
359
- if (editMode !== 'click')
360
- return;
361
- const { isEditable } = getCellEditableInfo(column, row, rowIndex, colIndex);
362
- if (!isEditable)
363
- return;
364
- setEditingCell({ row: rowIndex, field: column.dataField });
365
- setEditValue(getNestedValue(row, column.dataField));
462
+ const handleCellClick = useCallback((row, rowIndex, column, colIndex, e) => {
463
+ // Call column's onClick event if defined
464
+ if (e) {
465
+ column.events?.onClick?.(e, row, rowIndex, column, colIndex);
466
+ }
467
+ // Handle cell editing on click
468
+ if (editMode === 'click') {
469
+ const { isEditable } = getCellEditableInfo(column, row, rowIndex, colIndex);
470
+ if (isEditable) {
471
+ setEditingCell({ row: rowIndex, field: column.dataField });
472
+ setEditValue(getNestedValue(row, column.dataField));
473
+ }
474
+ }
366
475
  }, [editMode, getCellEditableInfo]);
367
476
  const handleCellEditComplete = useCallback((row, rowIndex, column) => {
368
477
  if (editingCell) {
@@ -387,7 +496,11 @@ export const Table = forwardRef(function TableComponent(props, ref) {
387
496
  };
388
497
  // Process field value based on fieldTypeConfig (matching reference implementation)
389
498
  const processFieldValue = useCallback((value, dataField, row) => {
390
- const config = fieldTypeConfig?.[dataField];
499
+ const configOrType = fieldTypeConfig?.[dataField];
500
+ // Handle both object config and string shorthand (e.g., 'boolean', 'date')
501
+ const config = typeof configOrType === 'string'
502
+ ? { type: configOrType }
503
+ : configOrType;
391
504
  const fieldType = config?.type || 'string';
392
505
  switch (fieldType) {
393
506
  case 'array':
@@ -475,8 +588,8 @@ export const Table = forwardRef(function TableComponent(props, ref) {
475
588
  }
476
589
  }, [fieldTypeConfig]);
477
590
  // Handle export
478
- const handleExport = useCallback(() => {
479
- const exportData = remote
591
+ const handleExport = useCallback(async () => {
592
+ const exportData = resolvedRemote
480
593
  ? data
481
594
  : filterData(data, filters, columns, debouncedSearchValue);
482
595
  // Filter columns based on hideExcelSheet array and csvExport !== false
@@ -487,7 +600,10 @@ export const Table = forwardRef(function TableComponent(props, ref) {
487
600
  // Also filter based on fieldTypeConfig hideFromExport or csvExport
488
601
  if (fieldTypeConfig) {
489
602
  exportColumns = exportColumns.filter((col) => {
490
- const config = fieldTypeConfig[col.dataField];
603
+ const configOrType = fieldTypeConfig[col.dataField];
604
+ const config = typeof configOrType === 'string'
605
+ ? { type: configOrType }
606
+ : configOrType;
491
607
  if (config?.hideFromExport)
492
608
  return false;
493
609
  if (config?.csvExport === false)
@@ -497,7 +613,10 @@ export const Table = forwardRef(function TableComponent(props, ref) {
497
613
  }
498
614
  // Create enhanced columns with export formatters
499
615
  const enhancedColumns = exportColumns.map((col) => {
500
- const config = fieldTypeConfig?.[col.dataField];
616
+ const configOrType = fieldTypeConfig?.[col.dataField];
617
+ const config = typeof configOrType === 'string'
618
+ ? { type: configOrType }
619
+ : configOrType;
501
620
  // If column already has csvFormatter, use it
502
621
  if (col.csvFormatter) {
503
622
  return col;
@@ -515,7 +634,13 @@ export const Table = forwardRef(function TableComponent(props, ref) {
515
634
  }
516
635
  return col;
517
636
  });
518
- exportToCSV(exportData, enhancedColumns, resolvedExportFileName);
637
+ // Export based on format
638
+ if (exportFormat === 'excel') {
639
+ await exportToExcel(exportData, enhancedColumns, resolvedExportFileName);
640
+ }
641
+ else {
642
+ exportToCSV(exportData, enhancedColumns, resolvedExportFileName);
643
+ }
519
644
  }, [
520
645
  data,
521
646
  filters,
@@ -526,7 +651,8 @@ export const Table = forwardRef(function TableComponent(props, ref) {
526
651
  fieldTypeConfig,
527
652
  processFieldValue,
528
653
  resolvedExportFileName,
529
- remote,
654
+ resolvedRemote,
655
+ exportFormat,
530
656
  ]);
531
657
  // Handle checkbox change
532
658
  const handleCheckboxChange = useCallback((row, e) => {
@@ -631,7 +757,10 @@ export const Table = forwardRef(function TableComponent(props, ref) {
631
757
  if (toolbar)
632
758
  return toolbar;
633
759
  const hasFilters = Object.keys(filters).length > 0 || searchValue;
634
- return (_jsxs(Toolbar, { className: classNames.toolbar, style: styles.toolbar, children: [_jsxs(ToolbarGroup, { children: [toolbarLeft, searchable && (_jsxs(SearchInput, { children: [_jsx(SearchIcon, {}), _jsx("input", { type: "text", value: searchValue, onChange: (e) => handleSearchChange(e.target.value), placeholder: searchPlaceholder })] })), searchable && (_jsx(ToolbarButton, { onClick: () => handleSearchChange(''), disabled: !searchValue, style: { opacity: searchValue ? 1 : 0.6 }, children: "Clear" })), _jsx(ToolbarButton, { onClick: handleClearFilters, disabled: !hasFilters, style: { opacity: hasFilters ? 1 : 0.6 }, children: "Clear all filters" }), resolvedExportable && hideExcelSheet !== true && (_jsxs(ToolbarButton, { onClick: handleExport, children: [_jsx(DownloadIcon, {}), "Export CSV"] })), showFilterToggle && (_jsxs("div", { ref: columnToggleRef, style: { position: 'relative' }, children: [_jsx(Tooltip, { content: "Show/Hide Columns", position: "bottom", children: _jsx(ToolbarButton, { "$active": columnToggleOpen, onClick: () => setColumnToggleOpen(!columnToggleOpen), "aria-label": "Toggle column visibility", style: { padding: '0 8px' }, children: _jsx(FilterIcon, {}) }) }), columnToggleOpen && (_jsxs(ColumnTogglePanel, { children: [_jsxs(ColumnToggleHeader, { children: [_jsx("span", { children: "Show/Hide Columns" }), _jsx("button", { onClick: () => setColumnToggleOpen(false), children: _jsx(CloseIcon, {}) })] }), _jsx(ColumnToggleSearch, { children: _jsx("input", { type: "text", value: columnSearch, onChange: (e) => setColumnSearch(e.target.value), placeholder: "Search columns..." }) }), _jsxs(ColumnToggleList, { children: [_jsxs(ColumnToggleItem, { style: {
760
+ return (_jsxs(Toolbar, { className: classNames.toolbar, style: styles.toolbar, children: [_jsxs(ToolbarGroup, { children: [toolbarLeft, searchable && (_jsxs(SearchInput, { children: [_jsx(SearchIcon, {}), _jsx("input", { type: "text", value: searchValue, onChange: (e) => handleSearchChange(e.target.value), onFocus: () => {
761
+ // Clear column filter focus when global search is focused
762
+ focusedFilterFieldRef.current = null;
763
+ }, placeholder: searchPlaceholder })] })), searchable && (_jsx(ToolbarButton, { onClick: () => handleSearchChange(''), disabled: !searchValue, style: { opacity: searchValue ? 1 : 0.6 }, children: "Clear" })), _jsx(ToolbarButton, { onClick: handleClearFilters, disabled: !hasFilters, style: { opacity: hasFilters ? 1 : 0.6 }, children: "Clear all filters" }), resolvedExportable && hideExcelSheet !== true && (_jsxs(ToolbarButton, { onClick: handleExport, children: [_jsx(DownloadIcon, {}), exportFormat === 'excel' ? 'Export Excel' : 'Export CSV'] })), showFilterToggle && (_jsxs("div", { ref: columnToggleRef, style: { position: 'relative' }, children: [_jsx(Tooltip, { content: "Show/Hide Columns", position: "bottom", children: _jsx(ToolbarButton, { "$active": columnToggleOpen, onClick: () => setColumnToggleOpen(!columnToggleOpen), "aria-label": "Toggle column visibility", style: { padding: '0 8px' }, children: _jsx(FilterIcon, {}) }) }), columnToggleOpen && (_jsxs(ColumnTogglePanel, { children: [_jsxs(ColumnToggleHeader, { children: [_jsx("span", { children: "Show/Hide Columns" }), _jsx("button", { onClick: () => setColumnToggleOpen(false), children: _jsx(CloseIcon, {}) })] }), _jsx(ColumnToggleSearch, { children: _jsx("input", { type: "text", value: columnSearch, onChange: (e) => setColumnSearch(e.target.value), placeholder: "Search columns..." }) }), _jsxs(ColumnToggleList, { children: [_jsxs(ColumnToggleItem, { style: {
635
764
  borderBottom: '1px solid #e5e7eb',
636
765
  paddingBottom: 8,
637
766
  marginBottom: 4,
@@ -658,7 +787,9 @@ export const Table = forwardRef(function TableComponent(props, ref) {
658
787
  const actualTotalPages = Math.ceil(calculatedTotal / pageSize) || 1;
659
788
  const startItem = calculatedTotal > 0 ? page * pageSize + 1 : 0;
660
789
  // Use actual data length for endItem to show correct count
661
- const actualDataCount = remote ? data.length : processedData.length;
790
+ const actualDataCount = resolvedRemote
791
+ ? data.length
792
+ : processedData.length;
662
793
  const endItem = Math.min(page * pageSize + actualDataCount, calculatedTotal);
663
794
  const showTotal = resolvedPaginationConfig?.showTotal === true
664
795
  ? `Showing ${startItem} to ${endItem} of ${calculatedTotal} Results`
@@ -706,35 +837,38 @@ export const Table = forwardRef(function TableComponent(props, ref) {
706
837
  resolvedRowSelection?.onChange?.(Array.from(selectedKeys), selectedRows);
707
838
  // Call onSelectedRow only when user clicks this button
708
839
  onSelectedRow?.(selectedRows);
709
- }, children: [_jsx(SelectionCount, { "$animate": selectionAnimation, children: selectedKeys.size }, selectedKeys.size), _jsx("span", { children: "Selected" })] })), resolvedPaginationConfig?.showSizeChanger !== false && (_jsx(PageSizeSelect, { value: pageSize, onChange: (e) => handlePageSizeChange(Number(e.target.value)), children: (resolvedPaginationConfig?.pageSizeOptions || [10, 20, 30, 50]).map((size) => (_jsx("option", { value: size, children: size }, size))) }))] }), _jsxs(PaginationControls, { children: [_jsx(PageButton, { onClick: () => handlePageChange(0), disabled: page === 0, children: _jsx(ChevronsLeftIcon, {}) }), _jsx(PageButton, { onClick: () => handlePageChange(page - 1), disabled: page === 0, children: _jsx(ChevronLeftIcon, {}) }), getPageNumbers().map((p, i) => typeof p === 'string' ? (_jsx("span", { style: { padding: '0 4px', color: 'white' }, children: p }, `ellipsis-${i}`)) : (_jsx(PageButton, { "$active": p === page, onClick: () => handlePageChange(p), children: p + 1 }, p))), _jsx(PageButton, { onClick: () => handlePageChange(page + 1), disabled: page >= actualTotalPages - 1, children: _jsx(ChevronRightIcon, {}) }), _jsx(PageButton, { onClick: () => handlePageChange(actualTotalPages - 1), disabled: page >= actualTotalPages - 1, children: _jsx(ChevronsRightIcon, {}) })] }), showTotal && _jsx(PaginationInfo, { children: showTotal }), resolvedPaginationConfig?.showQuickJumper && (() => {
710
- const handleQuickJump = (input) => {
711
- const pageNum = parseInt(input.value, 10);
712
- if (!isNaN(pageNum) && pageNum >= 1 && pageNum <= actualTotalPages) {
713
- handlePageChange(pageNum - 1); // Convert to 0-indexed
714
- input.value = '';
715
- return true;
716
- }
717
- return false;
718
- };
719
- return (_jsxs(QuickJumper, { children: ["Go to", _jsx("input", { type: "number", min: 1, max: actualTotalPages, placeholder: `1-${actualTotalPages}`, onKeyDown: (e) => {
720
- if (e.key === 'Enter') {
721
- e.preventDefault();
840
+ }, children: [_jsx(SelectionCount, { "$animate": selectionAnimation, children: selectedKeys.size }, selectedKeys.size), _jsx("span", { children: "Selected" })] })), resolvedPaginationConfig?.showSizeChanger !== false && (_jsx(PageSizeSelect, { value: pageSize, onChange: (e) => handlePageSizeChange(Number(e.target.value)), children: (resolvedPaginationConfig?.pageSizeOptions || [10, 20, 30, 50]).map((size) => (_jsx("option", { value: size, children: size }, size))) }))] }), _jsxs(PaginationControls, { children: [_jsx(PageButton, { onClick: () => handlePageChange(0), disabled: page === 0, children: _jsx(ChevronsLeftIcon, {}) }), _jsx(PageButton, { onClick: () => handlePageChange(page - 1), disabled: page === 0, children: _jsx(ChevronLeftIcon, {}) }), getPageNumbers().map((p, i) => typeof p === 'string' ? (_jsx("span", { style: { padding: '0 4px', color: 'white' }, children: p }, `ellipsis-${i}`)) : (_jsx(PageButton, { "$active": p === page, onClick: () => handlePageChange(p), children: p + 1 }, p))), _jsx(PageButton, { onClick: () => handlePageChange(page + 1), disabled: page >= actualTotalPages - 1, children: _jsx(ChevronRightIcon, {}) }), _jsx(PageButton, { onClick: () => handlePageChange(actualTotalPages - 1), disabled: page >= actualTotalPages - 1, children: _jsx(ChevronsRightIcon, {}) })] }), showTotal && _jsx(PaginationInfo, { children: showTotal }), resolvedPaginationConfig?.showQuickJumper &&
841
+ (() => {
842
+ const handleQuickJump = (input) => {
843
+ const pageNum = parseInt(input.value, 10);
844
+ if (!isNaN(pageNum) &&
845
+ pageNum >= 1 &&
846
+ pageNum <= actualTotalPages) {
847
+ handlePageChange(pageNum - 1); // Convert to 0-indexed
848
+ input.value = '';
849
+ return true;
850
+ }
851
+ return false;
852
+ };
853
+ return (_jsxs(QuickJumper, { children: ["Go to", _jsx("input", { type: "number", min: 1, max: actualTotalPages, placeholder: `1-${actualTotalPages}`, onKeyDown: (e) => {
854
+ if (e.key === 'Enter') {
855
+ e.preventDefault();
856
+ const target = e.target;
857
+ if (handleQuickJump(target)) {
858
+ target.blur();
859
+ }
860
+ }
861
+ }, onBlur: (e) => {
722
862
  const target = e.target;
723
- if (handleQuickJump(target)) {
724
- target.blur();
863
+ handleQuickJump(target);
864
+ } }), _jsx(PageButton, { onClick: () => {
865
+ const input = document.querySelector(`#${id} input[type="number"]`);
866
+ if (input) {
867
+ handleQuickJump(input);
868
+ input.blur();
725
869
  }
726
- }
727
- }, onBlur: (e) => {
728
- const target = e.target;
729
- handleQuickJump(target);
730
- } }), _jsx(PageButton, { onClick: () => {
731
- const input = document.querySelector(`#${id} input[type="number"]`);
732
- if (input) {
733
- handleQuickJump(input);
734
- input.blur();
735
- }
736
- }, style: { marginLeft: '4px', padding: '2px 8px' }, children: "Go" })] }));
737
- })()] }));
870
+ }, style: { marginLeft: '4px', padding: '2px 8px' }, children: "Go" })] }));
871
+ })()] }));
738
872
  };
739
873
  // Check if any columns have filters
740
874
  const hasFilterableColumns = filterable ||
@@ -743,7 +877,17 @@ export const Table = forwardRef(function TableComponent(props, ref) {
743
877
  c.filter === true);
744
878
  // Should show filter row - check if there are filterable columns AND filters are visible
745
879
  const shouldShowFilterRow = hasFilterableColumns && showFilterRow;
746
- return (_jsxs(TableRoot, { ref: ref, "$bordered": bordered, "$compact": compact, className: className || classNames.root, style: { ...styles.root, ...style, position: 'relative' }, "aria-label": rest['aria-label'], "aria-labelledby": rest['aria-labelledby'], children: [loading && (_jsx(LoadingOverlay, { className: classNames.loading, style: styles.loading, children: loadingIndicator || _jsx(LoadingSpinner, {}) })), renderToolbar(), _jsx(TableWrapper, { "$maxHeight": maxHeight, "$stickyHeader": stickyHeader, className: classNames.wrapper, style: styles.wrapper, children: _jsxs(StyledTable, { ref: tableRef, "$striped": striped, "$hover": hover, "$compact": compact, role: "grid", children: [caption && _jsx("caption", { className: "sr-only", children: caption }), _jsx(TableHeader, { "$sticky": stickyHeader, className: classNames.header, style: styles.header, children: _jsxs(HeaderRow, { className: classNames.headerRow, style: styles.headerRow, children: [resolvedRowSelection?.mode === 'checkbox' && (_jsx(HeaderCell, { "$align": "center", "$sortable": false, "$compact": compact, "$width": resolvedRowSelection.columnWidth || 40, children: !resolvedRowSelection.hideSelectAll && (_jsx(Checkbox, { checked: isAllSelected, ref: (el) => {
880
+ return (_jsxs(TableRoot, { ref: (node) => {
881
+ // Handle forwarded ref
882
+ if (typeof ref === 'function') {
883
+ ref(node);
884
+ }
885
+ else if (ref) {
886
+ ref.current = node;
887
+ }
888
+ // Also store in our container ref
889
+ tableContainerRef.current = node;
890
+ }, "$bordered": bordered, "$compact": compact, className: className || classNames.root, style: { ...styles.root, ...style, position: 'relative' }, "aria-label": rest['aria-label'], "aria-labelledby": rest['aria-labelledby'], children: [loading && (_jsx(LoadingOverlay, { className: classNames.loading, style: styles.loading, children: loadingIndicator || _jsx(LoadingSpinner, {}) })), renderToolbar(), _jsx(TableWrapper, { "$maxHeight": maxHeight, "$stickyHeader": stickyHeader, className: classNames.wrapper, style: styles.wrapper, children: _jsxs(StyledTable, { ref: tableRef, "$striped": striped, "$hover": hover, "$compact": compact, role: "grid", children: [caption && _jsx("caption", { className: "sr-only", children: caption }), _jsx(TableHeader, { "$sticky": stickyHeader, className: classNames.header, style: styles.header, children: _jsxs(HeaderRow, { className: classNames.headerRow, style: styles.headerRow, children: [resolvedRowSelection?.mode === 'checkbox' && (_jsx(HeaderCell, { "$align": "center", "$sortable": false, "$compact": compact, "$width": resolvedRowSelection.columnWidth || 40, children: !resolvedRowSelection.hideSelectAll && (_jsx(Checkbox, { checked: isAllSelected, ref: (el) => {
747
891
  if (el)
748
892
  el.indeterminate = isIndeterminate;
749
893
  }, onChange: handleSelectAllChange })) })), expandable && (_jsx(HeaderCell, { "$align": "center", "$sortable": false, "$compact": compact, "$width": expandable.columnWidth || 40 })), showRowNumber && (_jsx(HeaderCell, { "$align": "center", "$sortable": false, "$compact": compact, "$width": rowNumberWidth, children: rowNumberTitle })), visibleColumns.map((column, colIndex) => {
@@ -841,7 +985,10 @@ export const Table = forwardRef(function TableComponent(props, ref) {
841
985
  flexShrink: 1,
842
986
  display: 'flex',
843
987
  alignItems: 'center',
844
- }, onClick: (e) => e.stopPropagation(), children: column.filterRenderer ? (column.filterRenderer(onFilter, column)) : (_jsx(FilterComponent, { column: column, value: filters[column.dataField], onChange: onFilter, onClear: () => handleFilterChange(column.dataField, null) })) }), showSort && (_jsx("div", { style: {
988
+ }, onClick: (e) => e.stopPropagation(), children: column.filterRenderer ? (column.filterRenderer(onFilter, column)) : (_jsx("div", { onFocusCapture: () => {
989
+ focusedFilterFieldRef.current =
990
+ column.dataField;
991
+ }, "data-filter-wrapper": column.dataField, children: _jsx(FilterComponent, { column: column, value: filters[column.dataField], onChange: onFilter, onClear: () => handleFilterChange(column.dataField, null) }, `filter-${column.dataField}`) })) }), showSort && (_jsx("div", { style: {
845
992
  width: '20%',
846
993
  minWidth: 24,
847
994
  height: 24,
@@ -881,6 +1028,13 @@ export const Table = forwardRef(function TableComponent(props, ref) {
881
1028
  const hasCustomBgColor = rowSelected &&
882
1029
  selectedStyle &&
883
1030
  (selectedStyle.backgroundColor || selectedStyle.background);
1031
+ // Apply non-selectable style if row is disabled
1032
+ const disabledStyle = checkboxProps?.disabled
1033
+ ? nonSelectableStyle || {
1034
+ backgroundColor: '#f3f4f6',
1035
+ opacity: 0.7,
1036
+ }
1037
+ : undefined;
884
1038
  return (_jsxs(React.Fragment, { children: [_jsxs(TableRow, { "$selected": rowSelected, "$clickable": !!onRowClick ||
885
1039
  resolvedRowSelection?.mode === 'single' ||
886
1040
  expandable?.expandRowByClick === true, "$disabled": !!checkboxProps?.disabled, "$hasCustomSelectedStyle": !!hasCustomBgColor, "data-custom-selected": hasCustomBgColor ? 'true' : undefined, className: `${classNames.row || ''} ${rowClass || ''} ${rowSelected
@@ -888,7 +1042,16 @@ export const Table = forwardRef(function TableComponent(props, ref) {
888
1042
  'function'
889
1043
  ? resolvedRowSelection.selectedRowClassName(row)
890
1044
  : resolvedRowSelection?.selectedRowClassName || ''
891
- : ''}`, style: { ...styles.row, ...rowStyles, ...selectedStyle }, onClick: (e) => handleRowClick(row, rowIndex, e), onDoubleClick: (e) => handleCellDoubleClick(row, rowIndex, visibleColumns[0], 0, e), role: "row", "aria-selected": rowSelected, children: [resolvedRowSelection?.mode === 'checkbox' && (_jsx(TableCell, { "$align": "center", "$compact": compact, "$padding": cellPadding, children: _jsx(Checkbox, { checked: rowSelected, disabled: checkboxProps?.disabled, onChange: (e) => handleCheckboxChange(row, e), onClick: (e) => e.stopPropagation() }) })), expandable && (_jsx(TableCell, { "$align": "center", "$compact": compact, "$padding": cellPadding, children: isExpandable && (_jsx(ExpandButton, { "$expanded": rowExpanded, onClick: (e) => handleExpandClick(row, e), children: expandable.expandIcon ? (expandable.expandIcon({
1045
+ : ''}`, style: {
1046
+ ...styles.row,
1047
+ ...rowStyles,
1048
+ ...selectedStyle,
1049
+ ...disabledStyle,
1050
+ }, onClick: (e) => handleRowClick(row, rowIndex, e), onDoubleClick: (e) => {
1051
+ // Only trigger row-level double click if not handled by a cell
1052
+ // Cells handle their own double-clicks for editing
1053
+ onRowDoubleClick?.(row, rowIndex, e);
1054
+ }, role: "row", "aria-selected": rowSelected, children: [resolvedRowSelection?.mode === 'checkbox' && (_jsx(TableCell, { "$align": "center", "$compact": compact, "$padding": cellPadding, children: _jsx(Checkbox, { checked: rowSelected, disabled: checkboxProps?.disabled, onChange: (e) => handleCheckboxChange(row, e), onClick: (e) => e.stopPropagation() }) })), expandable && (_jsx(TableCell, { "$align": "center", "$compact": compact, "$padding": cellPadding, children: isExpandable && (_jsx(ExpandButton, { "$expanded": rowExpanded, onClick: (e) => handleExpandClick(row, e), children: expandable.expandIcon ? (expandable.expandIcon({
892
1055
  expanded: rowExpanded,
893
1056
  row,
894
1057
  onExpand: () => toggleExpand(row),
@@ -905,7 +1068,10 @@ export const Table = forwardRef(function TableComponent(props, ref) {
905
1068
  const cellStyle = typeof column.style === 'function'
906
1069
  ? column.style(getNestedValue(row, column.dataField), row, rowIndex, colIndex)
907
1070
  : column.style;
908
- return (_jsx(TableCell, { "$align": column.align || 'left', "$compact": compact, "$padding": cellPadding, "$pinned": column.pinned, "$hasCustomClass": !!cellClass, className: cellClass || classNames.cell, style: { ...styles.cell, ...cellStyle }, onClick: () => handleCellClick(row, rowIndex, column, colIndex), onDoubleClick: (e) => handleCellDoubleClick(row, rowIndex, column, colIndex, e), role: "gridcell", children: (() => {
1071
+ return (_jsx(TableCell, { "$align": column.align || 'left', "$compact": compact, "$padding": cellPadding, "$pinned": column.pinned, "$hasCustomClass": !!cellClass, className: cellClass || classNames.cell, style: { ...styles.cell, ...cellStyle }, onClick: (e) => handleCellClick(row, rowIndex, column, colIndex, e), onDoubleClick: (e) => {
1072
+ e.stopPropagation(); // Prevent row-level double-click from interfering
1073
+ handleCellDoubleClick(row, rowIndex, column, colIndex, e);
1074
+ }, role: "gridcell", children: (() => {
909
1075
  const editInfo = getCellEditableInfo(column, row, rowIndex, colIndex);
910
1076
  if (editInfo.isEditable &&
911
1077
  editMode !== 'none') {
@@ -145,6 +145,8 @@ const TextFilterComponent = ({ column, value, onChange, options }) => {
145
145
  const inputRef = useRef(null);
146
146
  const onChangeRef = useRef(onChange);
147
147
  const onFilterRef = useRef(onFilter);
148
+ // Track if the last change was from user input (internal) vs external (e.g., clear all)
149
+ const lastInternalValueRef = useRef(internalValue);
148
150
  // Keep refs in sync
149
151
  useEffect(() => {
150
152
  internalValueRef.current = internalValue;
@@ -153,10 +155,23 @@ const TextFilterComponent = ({ column, value, onChange, options }) => {
153
155
  onChangeRef.current = onChange;
154
156
  onFilterRef.current = onFilter;
155
157
  }, [onChange, onFilter]);
158
+ // Sync internal value when external value changes (e.g., from clear all filters)
159
+ // Only sync when external value differs from what we last sent to parent
160
+ useEffect(() => {
161
+ const externalValue = value || '';
162
+ // Only sync if external value is different from what we last propagated
163
+ // This prevents overwriting user input while they're typing
164
+ if (externalValue !== lastInternalValueRef.current) {
165
+ setInternalValue(externalValue);
166
+ lastInternalValueRef.current = externalValue;
167
+ }
168
+ }, [value]);
156
169
  // Debounce the internal value
157
170
  const [debouncedValue] = useDebouncedValue(internalValue, { wait: delay });
158
171
  // Propagate debounced value to parent
159
172
  useEffect(() => {
173
+ // Update the ref to track what we're sending to parent
174
+ lastInternalValueRef.current = debouncedValue || '';
160
175
  onChangeRef.current(debouncedValue || null);
161
176
  onFilterRef.current?.(debouncedValue);
162
177
  }, [debouncedValue]);
@@ -181,23 +196,43 @@ const TextFilterComponent = ({ column, value, onChange, options }) => {
181
196
  }
182
197
  // eslint-disable-next-line react-hooks/exhaustive-deps
183
198
  }, [getFilter]);
199
+ // Track if input should be focused (user is actively typing)
200
+ const hasFocusRef = useRef(false);
184
201
  // Simple change handler - just update local state
185
202
  const handleChange = useCallback((e) => {
186
203
  setInternalValue(e.target.value);
187
204
  }, []);
205
+ // Track focus state
206
+ const handleFocus = useCallback(() => {
207
+ hasFocusRef.current = true;
208
+ }, []);
209
+ const handleBlur = useCallback(() => {
210
+ hasFocusRef.current = false;
211
+ }, []);
212
+ // Restore focus after re-renders if it was focused
213
+ useEffect(() => {
214
+ if (hasFocusRef.current && inputRef.current) {
215
+ // Use requestAnimationFrame to ensure DOM is ready
216
+ requestAnimationFrame(() => {
217
+ if (hasFocusRef.current && inputRef.current) {
218
+ inputRef.current.focus();
219
+ }
220
+ });
221
+ }
222
+ });
188
223
  const inputStyle = {
189
224
  fontWeight: 400,
190
225
  ...style,
191
226
  };
192
227
  // If custom className is provided, use plain input to allow full CSS control
193
228
  if (className) {
194
- return (_jsx("input", { ref: inputRef, type: "text", id: id, value: internalValue, onChange: handleChange, placeholder: placeholder || column.filterPlaceholder || `Filter ${column.text}...`, className: className, style: {
229
+ return (_jsx("input", { ref: inputRef, type: "text", id: id, "data-filter-field": column.dataField, value: internalValue, onChange: handleChange, onFocus: handleFocus, onBlur: handleBlur, placeholder: placeholder || column.filterPlaceholder || `Filter ${column.text}...`, className: className, style: {
195
230
  width: '100%',
196
231
  fontWeight: 400,
197
232
  ...style,
198
233
  }, disabled: disabled }));
199
234
  }
200
- return (_jsx(FilterInputBase, { ref: inputRef, type: "text", id: id, value: internalValue, onChange: handleChange, placeholder: placeholder || column.filterPlaceholder || `Filter ${column.text}...`, style: inputStyle, disabled: disabled }));
235
+ return (_jsx(FilterInputBase, { ref: inputRef, type: "text", id: id, "data-filter-field": column.dataField, value: internalValue, onChange: handleChange, onFocus: handleFocus, onBlur: handleBlur, placeholder: placeholder || column.filterPlaceholder || `Filter ${column.text}...`, style: inputStyle, disabled: disabled }));
201
236
  };
202
237
  export function TextFilter(optionsOrProps) {
203
238
  // Check if it's being used as a factory function (options object without column/value/onChange)
@@ -227,6 +262,9 @@ const NumberFilterComponent = ({ column, value, onChange, options }) => {
227
262
  const inputRef = useRef(null);
228
263
  const onChangeRef = useRef(onChange);
229
264
  const onFilterRef = useRef(onFilter);
265
+ // Track last value sent to parent to prevent sync loops
266
+ const lastNumberRef = useRef(number);
267
+ const lastComparatorRef = useRef(comparator);
230
268
  // Keep refs in sync with state
231
269
  useEffect(() => {
232
270
  numberRef.current = number;
@@ -236,10 +274,27 @@ const NumberFilterComponent = ({ column, value, onChange, options }) => {
236
274
  onChangeRef.current = onChange;
237
275
  onFilterRef.current = onFilter;
238
276
  }, [onChange, onFilter]);
277
+ // Sync internal value when external value changes (e.g., from clear all filters)
278
+ useEffect(() => {
279
+ const externalNumber = value?.number || '';
280
+ const externalComparator = value?.comparator || defaultComparator;
281
+ // Only sync if different from what we last sent to parent
282
+ if (externalNumber !== lastNumberRef.current) {
283
+ setNumber(externalNumber);
284
+ lastNumberRef.current = externalNumber;
285
+ }
286
+ if (externalComparator !== lastComparatorRef.current) {
287
+ setComparator(externalComparator);
288
+ lastComparatorRef.current = externalComparator;
289
+ }
290
+ }, [value, defaultComparator]);
239
291
  // Debounce the number value
240
292
  const [debouncedNumber] = useDebouncedValue(number, { wait: delay });
241
293
  // Propagate debounced value to parent
242
294
  useEffect(() => {
295
+ // Update refs to track what we're sending to parent
296
+ lastNumberRef.current = debouncedNumber || '';
297
+ lastComparatorRef.current = comparatorRef.current;
243
298
  const newValue = debouncedNumber
244
299
  ? { number: debouncedNumber, comparator: comparatorRef.current }
245
300
  : null;
@@ -278,6 +333,25 @@ const NumberFilterComponent = ({ column, value, onChange, options }) => {
278
333
  }
279
334
  // eslint-disable-next-line react-hooks/exhaustive-deps
280
335
  }, [getFilter]);
336
+ // Track if input should be focused
337
+ const hasFocusRef = useRef(false);
338
+ // Track focus state
339
+ const handleFocus = useCallback(() => {
340
+ hasFocusRef.current = true;
341
+ }, []);
342
+ const handleBlur = useCallback(() => {
343
+ hasFocusRef.current = false;
344
+ }, []);
345
+ // Restore focus after re-renders if it was focused
346
+ useEffect(() => {
347
+ if (hasFocusRef.current && inputRef.current) {
348
+ requestAnimationFrame(() => {
349
+ if (hasFocusRef.current && inputRef.current) {
350
+ inputRef.current.focus();
351
+ }
352
+ });
353
+ }
354
+ });
281
355
  // Handle comparator change - trigger immediate filter update
282
356
  const handleComparatorChange = useCallback((newComparator) => {
283
357
  setComparator(newComparator);
@@ -303,6 +377,7 @@ const NumberFilterComponent = ({ column, value, onChange, options }) => {
303
377
  const inputProps = {
304
378
  type: 'text',
305
379
  id,
380
+ 'data-filter-field': column.dataField,
306
381
  value: number,
307
382
  onChange: (e) => {
308
383
  const val = e.target.value;
@@ -311,6 +386,8 @@ const NumberFilterComponent = ({ column, value, onChange, options }) => {
311
386
  setNumber(val);
312
387
  }
313
388
  },
389
+ onFocus: handleFocus,
390
+ onBlur: handleBlur,
314
391
  placeholder: placeholder || column.filterPlaceholder || 'Number...',
315
392
  disabled,
316
393
  };
@@ -341,6 +418,13 @@ const DateFilterComponent = ({ column, value, onChange, options }) => {
341
418
  const stateRef = useRef({ startDate, endDate, diffFlag, comparator });
342
419
  const onChangeRef = useRef(onChange);
343
420
  const onFilterRef = useRef(onFilter);
421
+ // Track last values sent to parent to prevent sync loops
422
+ const lastValuesRef = useRef({
423
+ startDate,
424
+ endDate,
425
+ diffFlag,
426
+ comparator,
427
+ });
344
428
  // Keep refs in sync with state
345
429
  useEffect(() => {
346
430
  stateRef.current = { startDate, endDate, diffFlag, comparator };
@@ -349,11 +433,42 @@ const DateFilterComponent = ({ column, value, onChange, options }) => {
349
433
  onChangeRef.current = onChange;
350
434
  onFilterRef.current = onFilter;
351
435
  }, [onChange, onFilter]);
436
+ // Sync internal value when external value changes (e.g., from clear all filters)
437
+ useEffect(() => {
438
+ const externalStartDate = value?.startDate || '';
439
+ const externalEndDate = value?.endDate || '';
440
+ const externalDiffFlag = value?.diffFlag ?? defaultRangeMode;
441
+ const externalComparator = value?.comparator || defaultComparator;
442
+ // Only sync if different from what we last sent to parent
443
+ if (externalStartDate !== lastValuesRef.current.startDate) {
444
+ setStartDate(externalStartDate);
445
+ lastValuesRef.current.startDate = externalStartDate;
446
+ }
447
+ if (externalEndDate !== lastValuesRef.current.endDate) {
448
+ setEndDate(externalEndDate);
449
+ lastValuesRef.current.endDate = externalEndDate;
450
+ }
451
+ if (externalDiffFlag !== lastValuesRef.current.diffFlag) {
452
+ setDiffFlag(externalDiffFlag);
453
+ lastValuesRef.current.diffFlag = externalDiffFlag;
454
+ }
455
+ if (externalComparator !== lastValuesRef.current.comparator) {
456
+ setComparator(externalComparator);
457
+ lastValuesRef.current.comparator = externalComparator;
458
+ }
459
+ }, [value, defaultRangeMode, defaultComparator]);
352
460
  // Debounce the date values
353
461
  const [debouncedStartDate] = useDebouncedValue(startDate, { wait: 500 });
354
462
  const [debouncedEndDate] = useDebouncedValue(endDate, { wait: 500 });
355
463
  // Propagate debounced value to parent
356
464
  useEffect(() => {
465
+ // Update refs to track what we're sending to parent
466
+ lastValuesRef.current = {
467
+ startDate: debouncedStartDate,
468
+ endDate: debouncedEndDate,
469
+ diffFlag,
470
+ comparator,
471
+ };
357
472
  const newValue = debouncedStartDate || debouncedEndDate
358
473
  ? {
359
474
  startDate: debouncedStartDate,
@@ -440,6 +555,8 @@ const SelectFilterComponent = ({ column, value, onChange, options }) => {
440
555
  const selectedValueRef = useRef(selectedValue);
441
556
  const onChangeRef = useRef(onChange);
442
557
  const onFilterRef = useRef(onFilter);
558
+ // Track last value sent to parent to prevent sync loops
559
+ const lastValueRef = useRef(selectedValue);
443
560
  // Keep refs in sync with state
444
561
  useEffect(() => {
445
562
  selectedValueRef.current = selectedValue;
@@ -448,10 +565,21 @@ const SelectFilterComponent = ({ column, value, onChange, options }) => {
448
565
  onChangeRef.current = onChange;
449
566
  onFilterRef.current = onFilter;
450
567
  }, [onChange, onFilter]);
568
+ // Sync internal value when external value changes (e.g., from clear all filters)
569
+ useEffect(() => {
570
+ const externalValue = value || '';
571
+ // Only sync if different from what we last sent to parent
572
+ if (externalValue !== lastValueRef.current) {
573
+ setSelectedValue(externalValue);
574
+ lastValueRef.current = externalValue;
575
+ }
576
+ }, [value]);
451
577
  // Debounce the selected value
452
578
  const [debouncedValue] = useDebouncedValue(selectedValue, { wait: delay });
453
579
  // Propagate debounced value to parent
454
580
  useEffect(() => {
581
+ // Update ref to track what we're sending to parent
582
+ lastValueRef.current = debouncedValue || '';
455
583
  onChangeRef.current(debouncedValue || null);
456
584
  onFilterRef.current?.(debouncedValue || null);
457
585
  }, [debouncedValue]);
@@ -104,4 +104,20 @@ export declare function getNestedValue(obj: any, path: string): any;
104
104
  * Export data to CSV
105
105
  */
106
106
  export declare function exportToCSV<T>(data: T[], columns: TableColumn<T>[], fileName: string): void;
107
+ /**
108
+ * Export data to Excel (.xlsx) - loads ExcelJS dynamically from CDN
109
+ */
110
+ export declare function exportToExcel<T>(data: T[], columns: TableColumn<T>[], fileName: string, options?: {
111
+ sheetName?: string;
112
+ headerStyle?: {
113
+ font?: {
114
+ bold?: boolean;
115
+ color?: string;
116
+ size?: number;
117
+ };
118
+ fill?: {
119
+ color?: string;
120
+ };
121
+ };
122
+ }): Promise<void>;
107
123
  //# sourceMappingURL=hooks.d.ts.map
@@ -424,11 +424,11 @@ export function exportToCSV(data, columns, fileName) {
424
424
  // Header row
425
425
  const header = exportColumns.map((c) => `"${c.text}"`).join(',');
426
426
  // Data rows
427
- const rows = data.map((row) => {
427
+ const rows = data.map((row, rowIndex) => {
428
428
  return exportColumns
429
429
  .map((col) => {
430
430
  const value = col.csvFormatter
431
- ? col.csvFormatter(getNestedValue(row, col.dataField), row)
431
+ ? col.csvFormatter(getNestedValue(row, col.dataField), row, rowIndex)
432
432
  : getNestedValue(row, col.dataField);
433
433
  if (value === null || value === undefined)
434
434
  return '""';
@@ -449,3 +449,88 @@ export function exportToCSV(data, columns, fileName) {
449
449
  link.click();
450
450
  URL.revokeObjectURL(url);
451
451
  }
452
+ /**
453
+ * Dynamically load ExcelJS from CDN (no bundle impact)
454
+ */
455
+ async function loadExcelJS() {
456
+ // Check if already loaded
457
+ if (window.ExcelJS) {
458
+ return window.ExcelJS;
459
+ }
460
+ return new Promise((resolve, reject) => {
461
+ const script = document.createElement('script');
462
+ script.src =
463
+ 'https://cdn.jsdelivr.net/npm/exceljs@4.4.0/dist/exceljs.min.js';
464
+ script.async = true;
465
+ script.onload = () => resolve(window.ExcelJS);
466
+ script.onerror = () => reject(new Error('Failed to load ExcelJS from CDN'));
467
+ document.head.appendChild(script);
468
+ });
469
+ }
470
+ /**
471
+ * Export data to Excel (.xlsx) - loads ExcelJS dynamically from CDN
472
+ */
473
+ export async function exportToExcel(data, columns, fileName, options) {
474
+ try {
475
+ const ExcelJS = await loadExcelJS();
476
+ const workbook = new ExcelJS.Workbook();
477
+ const worksheet = workbook.addWorksheet(options?.sheetName || 'Sheet1');
478
+ const exportColumns = columns.filter((c) => c.csvExport !== false);
479
+ // Set up columns with headers
480
+ worksheet.columns = exportColumns.map((col) => ({
481
+ header: col.text,
482
+ key: col.dataField,
483
+ width: Math.max(col.text.length + 5, 15),
484
+ }));
485
+ // Style header row
486
+ const headerRow = worksheet.getRow(1);
487
+ headerRow.font = {
488
+ bold: options?.headerStyle?.font?.bold ?? true,
489
+ size: options?.headerStyle?.font?.size ?? 12,
490
+ };
491
+ if (options?.headerStyle?.fill?.color) {
492
+ headerRow.fill = {
493
+ type: 'pattern',
494
+ pattern: 'solid',
495
+ fgColor: { argb: options.headerStyle.fill.color.replace('#', '') },
496
+ };
497
+ }
498
+ headerRow.commit();
499
+ // Add data rows
500
+ data.forEach((row, rowIndex) => {
501
+ const rowData = {};
502
+ exportColumns.forEach((col) => {
503
+ const value = col.csvFormatter
504
+ ? col.csvFormatter(getNestedValue(row, col.dataField), row, rowIndex)
505
+ : getNestedValue(row, col.dataField);
506
+ if (value === null || value === undefined) {
507
+ rowData[col.dataField] = '';
508
+ }
509
+ else if (typeof value === 'object') {
510
+ rowData[col.dataField] = JSON.stringify(value);
511
+ }
512
+ else {
513
+ rowData[col.dataField] = value;
514
+ }
515
+ });
516
+ worksheet.addRow(rowData);
517
+ });
518
+ // Generate and download
519
+ const buffer = await workbook.xlsx.writeBuffer();
520
+ const blob = new Blob([buffer], {
521
+ type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
522
+ });
523
+ const url = URL.createObjectURL(blob);
524
+ const link = document.createElement('a');
525
+ link.href = url;
526
+ link.download = `${fileName}_${new Date().toISOString().slice(0, 10)}.xlsx`;
527
+ link.click();
528
+ URL.revokeObjectURL(url);
529
+ }
530
+ catch (error) {
531
+ console.error('Excel export failed:', error);
532
+ // Fallback to CSV if Excel export fails
533
+ console.warn('Falling back to CSV export');
534
+ exportToCSV(data, columns, fileName);
535
+ }
536
+ }
@@ -1,6 +1,6 @@
1
1
  export { Table, default } from './Table';
2
2
  export * from './types';
3
- export { useSortState, useFilterState, usePaginationState, useRowSelection, useRowExpansion, useColumnVisibility, useTableDebounce, sortData, filterData, paginateData, getNestedValue, exportToCSV, } from './hooks';
3
+ export { useSortState, useFilterState, usePaginationState, useRowSelection, useRowExpansion, useColumnVisibility, useTableDebounce, sortData, filterData, paginateData, getNestedValue, exportToCSV, exportToExcel, } from './hooks';
4
4
  export { TextFilter, NumberFilter, DateFilter, SelectFilter, getFilterComponent, } from './filters';
5
5
  export type { TextFilterOptions, TextFilterInstance, NumberFilterOptions, NumberFilterInstance, SelectFilterOptions, SelectFilterInstance, DateFilterOptions, DateFilterInstance, } from './filters';
6
6
  //# sourceMappingURL=index.d.ts.map
@@ -1,4 +1,4 @@
1
1
  export { Table, default } from './Table';
2
2
  export * from './types';
3
- export { useSortState, useFilterState, usePaginationState, useRowSelection, useRowExpansion, useColumnVisibility, useTableDebounce, sortData, filterData, paginateData, getNestedValue, exportToCSV, } from './hooks';
3
+ export { useSortState, useFilterState, usePaginationState, useRowSelection, useRowExpansion, useColumnVisibility, useTableDebounce, sortData, filterData, paginateData, getNestedValue, exportToCSV, exportToExcel, } from './hooks';
4
4
  export { TextFilter, NumberFilter, DateFilter, SelectFilter, getFilterComponent, } from './filters';
@@ -116,7 +116,7 @@ export interface TableColumn<T = any> {
116
116
  /** Custom footer renderer */
117
117
  footerFormatter?: (column: TableColumn<T>, data: T[]) => React.ReactNode;
118
118
  /** CSV export formatter */
119
- csvFormatter?: (cell: any, row: T) => string;
119
+ csvFormatter?: (cell: any, row: T, rowIndex: number) => string;
120
120
  /** Whether to include in CSV export */
121
121
  csvExport?: boolean;
122
122
  /** Header CSS class */
@@ -145,8 +145,8 @@ export interface TableColumn<T = any> {
145
145
  footer?: string | ((column: TableColumn<T>, data: T[]) => React.ReactNode);
146
146
  /** Events */
147
147
  events?: {
148
- onClick?: (e: React.MouseEvent, cell: any, row: T, rowIndex: number) => void;
149
- onDoubleClick?: (e: React.MouseEvent, cell: any, row: T, rowIndex: number) => void;
148
+ onClick?: (e: React.MouseEvent, row: T, rowIndex: number, column: TableColumn<T>, columnIndex: number) => void;
149
+ onDoubleClick?: (e: React.MouseEvent, row: T, rowIndex: number, column: TableColumn<T>, columnIndex: number) => void;
150
150
  };
151
151
  }
152
152
  /** Filter props passed to custom filter components */
@@ -334,7 +334,8 @@ export interface TableProps<T = any> {
334
334
  paginationConfig?: Partial<TablePaginationConfig>;
335
335
  /** Total records (for server-side) */
336
336
  totalSize?: number;
337
- /** Server-side mode (default: true) - data is fetched from server via onPageSizeChange */
337
+ /** Server-side mode (default: true) - data is fetched from server via onPageSizeChange.
338
+ * Auto-detection: If totalSize <= data.length, automatically uses client-side mode (remote=false) */
338
339
  remote?: boolean;
339
340
  /** Default sort */
340
341
  defaultSort?: TableSortState;
@@ -376,10 +377,12 @@ export interface TableProps<T = any> {
376
377
  showEditIcon?: boolean;
377
378
  /** On cell edit */
378
379
  onCellEdit?: (value: any, dataField: string, row: T, rowIndex: number) => void;
379
- /** Enable CSV export */
380
+ /** Enable export button */
380
381
  exportable?: boolean;
381
382
  /** Export file name */
382
383
  exportFileName?: string;
384
+ /** Export format: 'csv' (default) or 'excel' */
385
+ exportFormat?: 'csv' | 'excel';
383
386
  /** Enable column toggle */
384
387
  columnToggle?: boolean;
385
388
  /** Enable column reorder */
@@ -465,6 +468,10 @@ export interface TableProps<T = any> {
465
468
  onView?: (row: T, rowIndex: number) => void;
466
469
  /** Enable row selection (shorthand) */
467
470
  isSelectRow?: boolean;
471
+ /** Array of row IDs (keyField values) that cannot be selected */
472
+ getNonSelectableRows?: string[];
473
+ /** Style for non-selectable rows */
474
+ nonSelectableStyle?: CSSProperties;
468
475
  /** Export file name (alias for exportFileName) */
469
476
  fileName?: string;
470
477
  /** Hide export sheet button or array of fields to exclude from export */
@@ -200,6 +200,20 @@
200
200
  /*! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com*/.-translate-x-1\/2,@keyframes pulse{50%{opacity:.5}}
201
201
  /*! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com*/.-translate-x-1\/2,@keyframes pulse{50%{opacity:.5}}
202
202
  /*! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com*/.-translate-x-1\/2,@keyframes pulse{50%{opacity:.5}}
203
+ /*! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com*/.-translate-x-1\/2,@keyframes pulse{50%{opacity:.5}}
204
+ /*! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com*/.-translate-x-1\/2,@keyframes pulse{50%{opacity:.5}}
205
+ /*! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com*/.-translate-x-1\/2,@keyframes pulse{50%{opacity:.5}}
206
+ /*! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com*/.-translate-x-1\/2,@keyframes pulse{50%{opacity:.5}}
207
+ /*! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com*/.-translate-x-1\/2,@keyframes pulse{50%{opacity:.5}}
208
+ /*! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com*/.-translate-x-1\/2,@keyframes pulse{50%{opacity:.5}}
209
+ /*! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com*/.-translate-x-1\/2,@keyframes pulse{50%{opacity:.5}}
210
+ /*! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com*/.-translate-x-1\/2,@keyframes pulse{50%{opacity:.5}}
211
+ /*! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com*/.-translate-x-1\/2,@keyframes pulse{50%{opacity:.5}}
212
+ /*! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com*/.-translate-x-1\/2,@keyframes pulse{50%{opacity:.5}}
213
+ /*! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com*/.-translate-x-1\/2,@keyframes pulse{50%{opacity:.5}}
214
+ /*! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com*/.-translate-x-1\/2,@keyframes pulse{50%{opacity:.5}}
215
+ /*! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com*/.-translate-x-1\/2,@keyframes pulse{50%{opacity:.5}}
216
+ /*! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com*/.-translate-x-1\/2,@keyframes pulse{50%{opacity:.5}}
203
217
  /*! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com*/.-translate-x-1\/2,@keyframes pulse{50%{opacity:.5}}*,.dark\:bg-black:is(.dark *),.dark\:border-gray-600:is(.dark *),:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#3b82f680;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }
204
218
  /*! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com*/.-translate-x-1\/2,@keyframes pulse{50%{opacity:.5}}.dark\:bg-black:is(.dark *),.dark\:border-gray-600:is(.dark *),*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#3b82f680;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#3b82f680;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }
205
219
  /*! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com*/fieldset,legend{padding:0}.container{width:100%}@media (min-width:0px){.container{max-width:0}}@media (min-width:20rem){.container{max-width:20rem}}@media (min-width:23.4375rem){.container{max-width:23.4375rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:90rem){.container{max-width:90rem}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.form-input,.form-input::-webkit-datetime-edit,.form-input::-webkit-datetime-edit-day-field,.form-input::-webkit-datetime-edit-hour-field,.form-input::-webkit-datetime-edit-meridiem-field,.form-input::-webkit-datetime-edit-millisecond-field,.form-input::-webkit-datetime-edit-minute-field,.form-input::-webkit-datetime-edit-month-field,.form-input::-webkit-datetime-edit-second-field,.form-input::placeholder,.form-input:focus,.form-multiselect,.form-multiselect:focus,.form-select,.form-select:focus,table{--tw-text-opacity:1;color:rgb(0 0 0/var(--tw-text-opacity,1))}table:is(.dark *){--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}table tr:hover:is(.dark *){--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1))}select:is(.dark *){--tw-text-opacity:1;color:rgb(0 0 0/var(--tw-text-opacity,1))}.-translate-x-1\/2{--tw-translate-x:-50%}.-translate-x-1\/2,@keyframes pulse{50%{opacity:.5}}.space-x-1>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(.25rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(.25rem*var(--tw-space-x-reverse))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-bottom-width:calc(1px*var(--tw-divide-y-reverse));border-top-width:calc(1px*(1 - var(--tw-divide-y-reverse)))}.divide-gray-100>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(243 244 246/var(--tw-divide-opacity,1))}.divide-teal-50>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(240 253 250/var(--tw-divide-opacity,1))}.bg-purple-900\/50{background-color:#581c8780}.p-0\.5{padding:.125rem}.blur,.filter,.ring,.ring-2,.shadow,.shadow-inner,.shadow-md,body{font-family:Arima Regular;font-size:14px}.hover\:bg-white\/20:hover{background-color:#fff3}.dark\:border-gray-600:is(.dark *),.focus\:ring-0:focus,.dark\:bg-black:is(.dark *){--tw-bg-opacity:1;background-color:rgb(0 0 0/var(--tw-bg-opacity,1))}.dark\:bg-boxdark:is(.dark *){--tw-bg-opacity:1;background-color:rgb(36 48 63/var(--tw-bg-opacity,1))}.dark\:bg-gray-700:is(.dark *){--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity,1))}.dark\:bg-gray-800:is(.dark *){--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity,1))}.dark\:text-black:is(.dark *){--tw-text-opacity:1;color:rgb(0 0 0/var(--tw-text-opacity,1))}.dark\:text-gray-100:is(.dark *){--tw-text-opacity:1;color:rgb(243 244 246/var(--tw-text-opacity,1))}.dark\:text-gray-200:is(.dark *){--tw-text-opacity:1;color:rgb(229 231 235/var(--tw-text-opacity,1))}.dark\:text-gray-300:is(.dark *){--tw-text-opacity:1;color:rgb(209 213 219/var(--tw-text-opacity,1))}.dark\:text-gray-400:is(.dark *){--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity,1))}.dark\:text-gray-500:is(.dark *){--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity,1))}.dark\:text-red-400:is(.dark *){--tw-text-opacity:1;color:rgb(248 113 113/var(--tw-text-opacity,1))}.dark\:text-white:is(.dark *){--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.dark\:placeholder-gray-400:is(.dark *)::placeholder{--tw-placeholder-opacity:1;color:rgb(156 163 175/var(--tw-placeholder-opacity,1))}.dark\:ring-offset-gray-800:is(.dark *){--tw-ring-offset-color:#1f2937}.dark\:hover\:bg-blue-900:hover:is(.dark *){--tw-bg-opacity:1;background-color:rgb(30 58 138/var(--tw-bg-opacity,1))}.dark\:hover\:bg-gray-600:hover:is(.dark *){--tw-bg-opacity:1;background-color:rgb(75 85 99/var(--tw-bg-opacity,1))}.dark\:hover\:bg-gray-700:hover:is(.dark *){--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity,1))}.dark\:focus\:ring-blue-600:focus:is(.dark *){--tw-ring-opacity:1;--tw-ring-color:rgb(37 99 235/var(--tw-ring-opacity,1))}*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#3b82f680;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#3b82f680;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }/*! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com*/*,:after,:before{border:0 solid #e5e7eb;box-sizing:border-box}:after,:before{--tw-content:""}:host,html{-webkit-text-size-adjust:100%;font-feature-settings:normal;-webkit-tap-highlight-color:transparent;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-variation-settings:normal;line-height:1.5;tab-size:4}body{line-height:inherit;margin:0}hr{border-top-width:1px;color:inherit;height:0}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-feature-settings:normal;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em;font-variation-settings:normal}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:initial}sub{bottom:-.25em}sup{top:-.5em}table{border-collapse:collapse;border-color:inherit;text-indent:0}button,input,optgroup,select,textarea{font-feature-settings:inherit;color:inherit;font-family:inherit;font-size:100%;font-variation-settings:inherit;font-weight:inherit;letter-spacing:inherit;line-height:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:initial;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:initial}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::placeholder,textarea::placeholder{color:#9ca3af;opacity:1}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{height:auto;max-width:100%}[hidden]:where(:not([hidden=until-found])){display:none}@font-face{font-family:ArimaRegular;src:url(library/assets/fonts/arima/arima-regular.ttf)}.container{width:100%}@media (min-width:0px){.container{max-width:0}}@media (min-width:20rem){.container{max-width:20rem}}@media (min-width:23.4375rem){.container{max-width:23.4375rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:90rem){.container{max-width:90rem}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.form-input,.form-multiselect,.form-select,.form-input:focus,.form-multiselect:focus,.form-select:focus,.form-input::placeholder,.form-input::-webkit-datetime-edit,.form-input::-webkit-datetime-edit-day-field,.form-input::-webkit-datetime-edit-hour-field,.form-input::-webkit-datetime-edit-meridiem-field,.form-input::-webkit-datetime-edit-millisecond-field,.form-input::-webkit-datetime-edit-minute-field,.form-input::-webkit-datetime-edit-month-field,.form-input::-webkit-datetime-edit-second-field,table{--tw-text-opacity:1;color:rgb(0 0 0/var(--tw-text-opacity,1))}table:is(.dark *){--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}table tr:hover:is(.dark *){--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1))}select:is(.dark *){--tw-text-opacity:1;color:rgb(0 0 0/var(--tw-text-opacity,1))}.-translate-x-1\/2{--tw-translate-x:-50%}.-translate-x-1\/2,@keyframes pulse{50%{opacity:.5}}@keyframes spin{to{transform:rotate(1turn)}}.space-x-1>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(.25rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(.25rem*var(--tw-space-x-reverse))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-bottom-width:calc(1px*var(--tw-divide-y-reverse));border-top-width:calc(1px*(1 - var(--tw-divide-y-reverse)))}.divide-gray-100>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(243 244 246/var(--tw-divide-opacity,1))}.divide-teal-50>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(240 253 250/var(--tw-divide-opacity,1))}.bg-purple-900\/50{background-color:#581c8780}.p-0\.5{padding:.125rem}.shadow,.shadow-inner,.shadow-md,.ring,.ring-2,.blur,.filter,body{font-family:Arima Regular;font-size:14px}.menu ul{list-style:none;margin:0;padding:0}.menu li{border-bottom:1px solid #ddd;padding:10px}.menu li:last-child{border-bottom:none}.hover\:bg-white\/20:hover{background-color:#fff3}.focus\:ring-0:focus,.focus\:ring-1:focus,.dark\:border-gray-600:is(.dark *){--tw-border-opacity:1;border-color:rgb(75 85 99/var(--tw-border-opacity,1))}.dark\:bg-black:is(.dark *){--tw-bg-opacity:1;background-color:rgb(0 0 0/var(--tw-bg-opacity,1))}.dark\:bg-boxdark:is(.dark *){--tw-bg-opacity:1;background-color:rgb(36 48 63/var(--tw-bg-opacity,1))}.dark\:bg-gray-700:is(.dark *){--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity,1))}.dark\:bg-gray-800:is(.dark *){--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity,1))}.dark\:text-black:is(.dark *){--tw-text-opacity:1;color:rgb(0 0 0/var(--tw-text-opacity,1))}.dark\:text-gray-100:is(.dark *){--tw-text-opacity:1;color:rgb(243 244 246/var(--tw-text-opacity,1))}.dark\:text-gray-200:is(.dark *){--tw-text-opacity:1;color:rgb(229 231 235/var(--tw-text-opacity,1))}.dark\:text-gray-300:is(.dark *){--tw-text-opacity:1;color:rgb(209 213 219/var(--tw-text-opacity,1))}.dark\:text-gray-400:is(.dark *){--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity,1))}.dark\:text-gray-500:is(.dark *){--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity,1))}.dark\:text-red-400:is(.dark *){--tw-text-opacity:1;color:rgb(248 113 113/var(--tw-text-opacity,1))}.dark\:text-white:is(.dark *){--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.dark\:placeholder-gray-400:is(.dark *)::placeholder{--tw-placeholder-opacity:1;color:rgb(156 163 175/var(--tw-placeholder-opacity,1))}.dark\:ring-offset-gray-800:is(.dark *){--tw-ring-offset-color:#1f2937}.dark\:hover\:bg-blue-900:hover:is(.dark *){--tw-bg-opacity:1;background-color:rgb(30 58 138/var(--tw-bg-opacity,1))}.dark\:hover\:bg-gray-600:hover:is(.dark *){--tw-bg-opacity:1;background-color:rgb(75 85 99/var(--tw-bg-opacity,1))}.dark\:hover\:bg-gray-700:hover:is(.dark *){--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity,1))}.dark\:focus\:ring-blue-600:focus:is(.dark *){--tw-ring-opacity:1;--tw-ring-color:rgb(37 99 235/var(--tw-ring-opacity,1))}@media (min-width:0px) and (max-width:767px){}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-restyle-components",
3
- "version": "0.4.35",
3
+ "version": "0.4.36",
4
4
  "private": false,
5
5
  "description": "Easy use restyle components",
6
6
  "author": {