react-restyle-components 0.4.64 → 0.4.66

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.
@@ -1,4 +1,4 @@
1
1
  import React from 'react';
2
2
  export type { ModalSize, ModalPosition, ModalClassNames, ModalStyles, ModalProps } from './types';
3
3
  import type { ModalProps } from './types';
4
- export declare const Modal: ({ visible, title, className, contentClassName, headerClassName, style, contentStyle, onClose, isAutoClose, autoCloseDelay, isAutoCloseOutside, size, position, showCloseButton, closeButton, overlayOpacity, overlayColor, zIndex, closeOnOverlayClick, closeOnEscape, showHeader, headerBgColor, headerTextColor, borderRadius, maxWidth, contentPadding, headerPadding, animationDuration, classNames, styles, children, }: ModalProps) => React.JSX.Element | null;
4
+ export declare const Modal: ({ visible, title, className, contentClassName, headerClassName, style, contentStyle, onClose, isAutoClose, autoCloseDelay, isAutoCloseOutside, size, position, showCloseButton, closeButton, isExpand, expandButton, onExpand, overlayOpacity, overlayColor, zIndex, closeOnOverlayClick, closeOnEscape, showHeader, headerBgColor, headerTextColor, borderRadius, maxWidth, contentPadding, headerPadding, animationDuration, classNames, styles, children, }: ModalProps) => React.JSX.Element | null;
@@ -2,11 +2,12 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
2
2
  import { useEffect, useState } from 'react';
3
3
  import s from '../../../tc.module.css';
4
4
  import { cn } from '../../../utils';
5
- export const Modal = ({ visible, title = '', className = '', contentClassName = '', headerClassName = '', style = {}, contentStyle = {}, onClose, isAutoClose = false, autoCloseDelay = 1000, isAutoCloseOutside, size = 'md', position = 'center', showCloseButton = true, closeButton, overlayOpacity = 0.5, overlayColor, zIndex = 11000, closeOnOverlayClick, closeOnEscape = true, showHeader = true, headerBgColor, headerTextColor, borderRadius = '0.5rem', maxWidth, contentPadding = '1rem', headerPadding = '1rem', animationDuration = 300, classNames = {}, styles = {}, children, }) => {
5
+ export const Modal = ({ visible, title = '', className = '', contentClassName = '', headerClassName = '', style = {}, contentStyle = {}, onClose, isAutoClose = false, autoCloseDelay = 1000, isAutoCloseOutside, size = 'md', position = 'center', showCloseButton = true, closeButton, isExpand = false, expandButton, onExpand, overlayOpacity = 0.5, overlayColor, zIndex = 11000, closeOnOverlayClick, closeOnEscape = true, showHeader = true, headerBgColor, headerTextColor, borderRadius = '0.5rem', maxWidth, contentPadding = '1rem', headerPadding = '1rem', animationDuration = 300, classNames = {}, styles = {}, children, }) => {
6
6
  // isAutoCloseOutside takes priority, then closeOnOverlayClick, default true
7
7
  const shouldCloseOnOutsideClick = isAutoCloseOutside ?? closeOnOverlayClick ?? true;
8
8
  const [showModal, setShowModal] = useState(visible);
9
9
  const [isAnimating, setIsAnimating] = useState(false);
10
+ const [isExpanded, setIsExpanded] = useState(false);
10
11
  useEffect(() => {
11
12
  if (visible) {
12
13
  setShowModal(true);
@@ -75,28 +76,46 @@ export const Modal = ({ visible, title = '', className = '', contentClassName =
75
76
  ...style,
76
77
  transition: `opacity ${animationDuration}ms ease-in-out`,
77
78
  opacity: isAnimating ? 1 : 0,
78
- zIndex: containerZIndex,
79
+ zIndex: isExpanded ? 2147483647 : containerZIndex,
80
+ overflow: isExpanded ? 'hidden' : undefined,
79
81
  ...styles.container,
80
- }, onClick: handleOverlayClick, children: _jsx("div", { className: cn(s['relative'], s['w-full'], s['my-5'], s['mx-auto'], classNames.wrapper), style: {
81
- maxWidth: modalMaxWidth,
82
- marginLeft: size === 'full' ? '1rem' : undefined,
83
- marginRight: size === 'full' ? '1rem' : undefined,
84
- transform: isAnimating
85
- ? position === 'center'
86
- ? 'scale(1)'
87
- : position === 'top'
88
- ? 'translateY(0)'
82
+ }, onClick: handleOverlayClick, children: _jsx("div", { className: cn(s['relative'], s['w-full'], !isExpanded && s['my-5'], !isExpanded && s['mx-auto'], classNames.wrapper), style: isExpanded
83
+ ? {
84
+ maxWidth: '100vw',
85
+ width: '100vw',
86
+ height: '100vh',
87
+ margin: 0,
88
+ position: 'fixed',
89
+ top: 0,
90
+ left: 0,
91
+ right: 0,
92
+ bottom: 0,
93
+ zIndex: 2147483647,
94
+ transform: 'none',
95
+ opacity: isAnimating ? 1 : 0,
96
+ transition: `opacity ${animationDuration}ms ease-out`,
97
+ ...styles.wrapper,
98
+ }
99
+ : {
100
+ maxWidth: modalMaxWidth,
101
+ marginLeft: size === 'full' ? '1rem' : undefined,
102
+ marginRight: size === 'full' ? '1rem' : undefined,
103
+ transform: isAnimating
104
+ ? position === 'center'
105
+ ? 'scale(1)'
89
106
  : 'translateY(0)'
90
- : position === 'center'
91
- ? 'scale(0.95)'
92
- : position === 'top'
93
- ? 'translateY(-20px)'
94
- : 'translateY(20px)',
95
- transition: `transform ${animationDuration}ms ease-out, opacity ${animationDuration}ms ease-out`,
96
- opacity: isAnimating ? 1 : 0,
97
- ...styles.wrapper,
98
- }, onClick: (e) => e.stopPropagation(), children: _jsxs("div", { className: cn(s['border-0'], s['shadow-xl'], s['relative'], s['flex'], s['flex-col'], s['bg-white'], s['outline-none'], s['focus:outline-none'], contentClassName, classNames.content), style: {
99
- borderRadius,
107
+ : position === 'center'
108
+ ? 'scale(0.95)'
109
+ : position === 'top'
110
+ ? 'translateY(-20px)'
111
+ : 'translateY(20px)',
112
+ transition: `transform ${animationDuration}ms ease-out, opacity ${animationDuration}ms ease-out`,
113
+ opacity: isAnimating ? 1 : 0,
114
+ ...styles.wrapper,
115
+ }, onClick: (e) => e.stopPropagation(), children: _jsxs("div", { className: cn(s['border-0'], s['shadow-xl'], s['relative'], s['flex'], s['flex-col'], s['bg-white'], s['outline-none'], s['focus:outline-none'], contentClassName, classNames.content), style: {
116
+ borderRadius: isExpanded ? 0 : borderRadius,
117
+ height: isExpanded ? '100%' : undefined,
118
+ overflow: isExpanded ? 'hidden' : undefined,
100
119
  ...contentStyle,
101
120
  ...styles.content,
102
121
  }, children: [showHeader && (_jsxs("div", { className: cn(s['flex'], s['items-center'], s['justify-between'], s['border-b'], s['border-solid'], s['border-gray-200'], headerClassName, classNames.header), style: {
@@ -106,7 +125,25 @@ export const Modal = ({ visible, title = '', className = '', contentClassName =
106
125
  borderTopRightRadius: borderRadius,
107
126
  borderBottom: title || showCloseButton ? undefined : 'none',
108
127
  ...styles.header,
109
- }, children: [title && (_jsx("h3", { className: cn(s['text-xl'], s['font-semibold'], s['m-0'], s['flex-1'], classNames.title), style: { color: headerTextColor, ...styles.title }, children: title })), showCloseButton && (_jsx("button", { type: "button", className: cn(s['p-1'], s['ml-4'], s['border-0'], s['bg-transparent'], s['cursor-pointer'], s['outline-none'], s['focus:outline-none'], s['transition-colors'], s['hover:bg-gray-100'], s['rounded'], classNames.closeButton), onClick: handleClose, "aria-label": "Close modal", style: {
128
+ }, children: [title && (_jsx("h3", { className: cn(s['text-xl'], s['font-semibold'], s['m-0'], s['flex-1'], classNames.title), style: { color: headerTextColor, ...styles.title }, children: title })), isExpand && (_jsx("button", { type: "button", className: cn(s['p-1'], s['ml-4'], s['border-0'], s['bg-transparent'], s['cursor-pointer'], s['outline-none'], s['focus:outline-none'], s['transition-colors'], s['hover:bg-gray-100'], s['rounded'], classNames.expandButton), onClick: () => {
129
+ const next = !isExpanded;
130
+ setIsExpanded(next);
131
+ onExpand && onExpand(next);
132
+ }, "aria-label": isExpanded ? 'Collapse modal' : 'Expand modal', style: {
133
+ marginLeft: title ? '1rem' : 0,
134
+ ...styles.expandButton,
135
+ }, children: expandButton || (_jsx("span", { style: {
136
+ color: headerTextColor || '#1f2937',
137
+ height: '1.5rem',
138
+ width: '1.5rem',
139
+ display: 'flex',
140
+ alignItems: 'center',
141
+ justifyContent: 'center',
142
+ }, children: isExpanded ? (
143
+ // Collapse icon
144
+ _jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("polyline", { points: "4 14 10 14 10 20" }), _jsx("polyline", { points: "20 10 14 10 14 4" }), _jsx("line", { x1: "10", y1: "14", x2: "3", y2: "21" }), _jsx("line", { x1: "21", y1: "3", x2: "14", y2: "10" })] })) : (
145
+ // Expand icon
146
+ _jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("polyline", { points: "15 3 21 3 21 9" }), _jsx("polyline", { points: "9 21 3 21 3 15" }), _jsx("line", { x1: "21", y1: "3", x2: "14", y2: "10" }), _jsx("line", { x1: "3", y1: "21", x2: "10", y2: "14" })] })) })) })), showCloseButton && (_jsx("button", { type: "button", className: cn(s['p-1'], s['ml-4'], s['border-0'], s['bg-transparent'], s['cursor-pointer'], s['outline-none'], s['focus:outline-none'], s['transition-colors'], s['hover:bg-gray-100'], s['rounded'], classNames.closeButton), onClick: handleClose, "aria-label": "Close modal", style: {
110
147
  marginLeft: title ? '1rem' : 0,
111
148
  marginRight: title ? 0 : 'auto',
112
149
  ...styles.closeButton,
@@ -118,8 +155,10 @@ export const Modal = ({ visible, title = '', className = '', contentClassName =
118
155
  alignItems: 'center',
119
156
  justifyContent: 'center',
120
157
  lineHeight: 1,
121
- }, children: "\u00D7" })) }))] })), _jsx("div", { className: cn(s['flex'], s['flex-col'], s['w-full'], s['h-auto'], s['overflow-y-auto'], classNames.body), style: {
158
+ }, children: "\u00D7" })) }))] })), _jsx("div", { className: cn(s['flex'], s['flex-col'], s['w-full'], !isExpanded && s['h-auto'], s['overflow-y-auto'], classNames.body), style: {
122
159
  padding: contentPadding,
160
+ flex: isExpanded ? '1 1 0' : undefined,
161
+ minHeight: isExpanded ? 0 : undefined,
123
162
  ...styles.body,
124
163
  }, children: children })] }) }) })] }));
125
164
  };
@@ -14,6 +14,8 @@ export interface ModalClassNames {
14
14
  header?: string;
15
15
  /** Custom className for the title */
16
16
  title?: string;
17
+ /** Custom className for the expand button */
18
+ expandButton?: string;
17
19
  /** Custom className for the close button */
18
20
  closeButton?: string;
19
21
  /** Custom className for the body/children container */
@@ -32,6 +34,8 @@ export interface ModalStyles {
32
34
  header?: CSSProperties;
33
35
  /** Custom style for the title */
34
36
  title?: CSSProperties;
37
+ /** Custom style for the expand button */
38
+ expandButton?: CSSProperties;
35
39
  /** Custom style for the close button */
36
40
  closeButton?: CSSProperties;
37
41
  /** Custom style for the body/children container */
@@ -68,6 +72,12 @@ export interface ModalProps {
68
72
  showCloseButton?: boolean;
69
73
  /** Custom close button element */
70
74
  closeButton?: React.ReactNode;
75
+ /** Enable expand/collapse fullscreen button (shown before close button) */
76
+ isExpand?: boolean;
77
+ /** Custom expand button element */
78
+ expandButton?: React.ReactNode;
79
+ /** Callback fired when expand/collapse button is clicked; receives the new expanded state */
80
+ onExpand?: (isExpanded: boolean) => void;
71
81
  /** Overlay background opacity (0-1) */
72
82
  overlayOpacity?: number;
73
83
  /** Overlay background color */
@@ -3,7 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
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, TableCellContent, Checkbox, ExpandButton, ExpandedRow, ExpandedCell, TableFooter, FooterRow, FooterCell, PaginationWrapper, PaginationInfo, PaginationControls, PageButton, PageSizeInputWrapper, PageSizeInput, PageSizeDropdownIcon, PageSizeDropdown, PageSizeDropdownItem, QuickJumper, EmptyState, LoadingOverlay, LoadingSpinner, EditableCell, CellEditor, ColumnTogglePanel, ColumnToggleHeader, ColumnToggleSearch, ColumnToggleList, ColumnToggleItem, ColumnToggleDragHandle, SelectionIndicator, SelectionCount, SelectSortButton, } from './elements';
5
5
  import { useSortState, useFilterState, usePaginationState, useRowSelection, useRowExpansion, useColumnVisibility, useTableDebounce, sortData, filterData, paginateData, getNestedValue, exportToCSV, exportToExcel, } from './hooks';
6
- import { getFilterComponent } from './filters';
6
+ import { getFilterComponent, createCallableFilterHandle } from './filters';
7
7
  import { Tooltip } from '../Tooltip';
8
8
  import { LoadingAnimateSpin } from '../Loader/loader.component';
9
9
  import { useColumnResize, getColumnStyle } from './columnResize';
@@ -54,6 +54,31 @@ const RefreshIcon = () => (_jsxs("svg", { viewBox: "0 0 24 24", fill: "none", st
54
54
  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" })] }));
55
55
  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" })] }));
56
56
  const tableFilterCache = new Map();
57
+ /** Reads `getFilter` from TextFilter / NumberFilter / CustomFilter / DateFilter / SelectFilter factory `.props`. */
58
+ function getColumnFilterGetFilterCallback(filter) {
59
+ if (typeof filter !== 'function')
60
+ return undefined;
61
+ const props = filter.props;
62
+ return typeof props?.getFilter === 'function' ? props.getFilter : undefined;
63
+ }
64
+ /**
65
+ * When using `filterRenderer`, the factory `filter` component does not mount, so its `getFilter`
66
+ * never runs. Registers the same callable imperative API as built-in filters (via table state).
67
+ */
68
+ function FilterRendererGetFilterBridge({ getFilter, dataField, filters, handleFilterChange, }) {
69
+ const applyRef = useRef(() => { });
70
+ applyRef.current = (v) => handleFilterChange(dataField, v);
71
+ const getValueRef = useRef(() => filters[dataField]);
72
+ getValueRef.current = () => filters[dataField];
73
+ useEffect(() => {
74
+ getFilter(createCallableFilterHandle({
75
+ getValue: () => getValueRef.current(),
76
+ setValue: (value) => applyRef.current(value),
77
+ clear: () => applyRef.current(null),
78
+ }));
79
+ }, [getFilter]);
80
+ return null;
81
+ }
57
82
  export const Table = forwardRef(function TableComponent(props, ref) {
58
83
  const { id, data, columns, keyField = '_id', loading = false, loadingIndicator, isLoading = false, 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 = 'excel', exportSheetName, exportHeaderStyle, exportExtraSheets, exportSummaryRows, exportCharts, columnToggle = false, isFieldSelector = true, 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,
59
84
  // Quick configuration props
@@ -184,8 +209,35 @@ export const Table = forwardRef(function TableComponent(props, ref) {
184
209
  const [toggleDragOverColumn, setToggleDragOverColumn] = useState(null);
185
210
  // Internal state for reordered columns (used in popup and for rendering)
186
211
  const [reorderedColumns, setReorderedColumns] = useState(columns);
187
- // Sync reorderedColumns when columns prop structure changes (columns added/removed)
188
- // We only reset when the actual column dataFields change, not on every reference change
212
+ // A stable string fingerprint built from every serialisable column property.
213
+ // Using a primitive (string) as the useEffect dependency means the effect only
214
+ // re-runs when the column *content* meaningfully changes — not merely because
215
+ // the caller re-created the columns array inline on every render.
216
+ // When the fingerprint does change we re-run the effect and update reorderedColumns
217
+ // with the full column objects (picking up formatters, filters, and all functions).
218
+ const columnsFingerprint = useMemo(() => columns
219
+ .map((c) => [
220
+ c.dataField,
221
+ c.text ?? '',
222
+ c.headerText ?? '',
223
+ c.hidden ? '1' : '0',
224
+ c.sort ? '1' : '0',
225
+ String(c.width ?? ''),
226
+ String(c.minWidth ?? ''),
227
+ String(c.maxWidth ?? ''),
228
+ c.align ?? '',
229
+ c.headerAlign ?? '',
230
+ // editable can be boolean or function; stringify the boolean branch only
231
+ typeof c.editable === 'boolean' ? (c.editable ? '1' : '0') : 'fn',
232
+ c.pinned ?? '',
233
+ c.csvExport !== false ? '1' : '0',
234
+ c.editorType ?? '',
235
+ ].join('|'))
236
+ .join('~'), [columns]);
237
+ // Sync reorderedColumns when columns prop changes.
238
+ // Depends on columnsFingerprint (a string primitive) rather than the columns
239
+ // array reference so that inline column arrays that get re-created on every
240
+ // render do not trigger an infinite setState → re-render → setState loop.
189
241
  useEffect(() => {
190
242
  const currentDataFields = reorderedColumns
191
243
  .map((c) => c.dataField)
@@ -196,14 +248,12 @@ export const Table = forwardRef(function TableComponent(props, ref) {
196
248
  .sort()
197
249
  .join(',');
198
250
  if (currentDataFields !== newDataFields) {
199
- // Columns structure changed - merge new columns while preserving order where possible
251
+ // Columns structure changed merge while preserving user-defined order
200
252
  const newColumnMap = new Map(columns.map((c) => [c.dataField, c]));
201
253
  const existingDataFields = new Set(reorderedColumns.map((c) => c.dataField));
202
- // Keep existing order for columns that still exist, update their data
203
254
  const preserved = reorderedColumns
204
255
  .filter((c) => newColumnMap.has(c.dataField))
205
256
  .map((c) => newColumnMap.get(c.dataField));
206
- // Add new columns at the end
207
257
  const added = columns.filter((c) => !existingDataFields.has(c.dataField));
208
258
  setReorderedColumns([...preserved, ...added]);
209
259
  // Clean up filter component cache for removed columns
@@ -215,11 +265,16 @@ export const Table = forwardRef(function TableComponent(props, ref) {
215
265
  }
216
266
  }
217
267
  else {
218
- // Same columns but maybe different data - update column data while preserving order
268
+ // Same column structure update all column data (including formatter,
269
+ // filter, csvFormatter, etc.) so that reorderable-table rendering always
270
+ // uses the latest column definitions.
271
+ // This branch is only reached when columnsFingerprint actually changed,
272
+ // so there is no risk of an infinite loop here.
219
273
  const columnMap = new Map(columns.map((c) => [c.dataField, c]));
220
- setReorderedColumns(reorderedColumns.map((c) => columnMap.get(c.dataField) || c));
274
+ setReorderedColumns(reorderedColumns.map((c) => columnMap.get(c.dataField) ?? c));
221
275
  }
222
- }, [columns]);
276
+ // eslint-disable-next-line react-hooks/exhaustive-deps
277
+ }, [columnsFingerprint]);
223
278
  // Close column toggle panel on outside click
224
279
  useEffect(() => {
225
280
  if (!columnToggleOpen)
@@ -436,6 +491,28 @@ export const Table = forwardRef(function TableComponent(props, ref) {
436
491
  // Reset the flag after processing to prevent duplicate calls
437
492
  shouldCallOnFilter.current = false;
438
493
  }, [debouncedSearchValue, filters, page, pageSize]);
494
+ /** When remote filtering is used, pagination must re-send current criteria + new page/size. */
495
+ const getActiveRemoteFilterCallArgs = useCallback(() => {
496
+ const cleanFilters = {};
497
+ Object.keys(filters).forEach((key) => {
498
+ const value = filters[key];
499
+ if (value !== null && value !== undefined && value !== '') {
500
+ cleanFilters[key] = value;
501
+ }
502
+ });
503
+ const hasColumnFilters = Object.keys(cleanFilters).length > 0;
504
+ const hasSearch = Boolean(debouncedSearchValue);
505
+ if (!hasColumnFilters && !hasSearch)
506
+ return null;
507
+ const filterData = { ...cleanFilters };
508
+ if (hasSearch) {
509
+ filterData.srText = debouncedSearchValue;
510
+ }
511
+ const type = hasColumnFilters
512
+ ? 'filter'
513
+ : 'search';
514
+ return { type, filterData };
515
+ }, [filters, debouncedSearchValue]);
439
516
  // Track selection count changes for animation
440
517
  useEffect(() => {
441
518
  const currentCount = selectedKeys.size;
@@ -577,6 +654,15 @@ export const Table = forwardRef(function TableComponent(props, ref) {
577
654
  lastOnFilterCallRef.current = null;
578
655
  // Clear the cache so remounts also start fresh
579
656
  tableFilterCache.delete(id);
657
+ // Remote tables: clear-all must notify like column-filter clears. Search uses debounce and
658
+ // never sets shouldCallOnFilter here, so invoke onFilter immediately with empty criteria.
659
+ if (onFilter) {
660
+ prevSearchRef.current = '';
661
+ prevFiltersRef.current = JSON.stringify({});
662
+ shouldCallOnFilter.current = false;
663
+ filterTypeRef.current = 'filter';
664
+ onFilterRef.current?.('filter', {}, page + 1, pageSize);
665
+ }
580
666
  }, [
581
667
  id,
582
668
  clearFilters,
@@ -584,6 +670,9 @@ export const Table = forwardRef(function TableComponent(props, ref) {
584
670
  onFilterChange,
585
671
  onChange,
586
672
  resetEditingState,
673
+ onFilter,
674
+ page,
675
+ pageSize,
587
676
  ]);
588
677
  // Handle page change
589
678
  const handlePageChange = useCallback((newPage) => {
@@ -591,13 +680,24 @@ export const Table = forwardRef(function TableComponent(props, ref) {
591
680
  goToPage(newPage);
592
681
  // Pass 1-indexed page number to callbacks (user-friendly)
593
682
  const displayPage = newPage + 1;
594
- // onPageSizeChange is the primary callback
595
- onPageSizeChange?.(displayPage, pageSize);
596
- onPageChange?.(displayPage, pageSize);
597
- onChange?.({
598
- type: 'pagination',
599
- pagination: { page: displayPage, pageSize },
600
- });
683
+ const filterArgs = onFilter ? getActiveRemoteFilterCallArgs() : null;
684
+ // With active remote criteria, only onFilter should run (avoids double fetch vs onPageSizeChange)
685
+ if (filterArgs) {
686
+ onFilterRef.current?.(filterArgs.type, filterArgs.filterData, displayPage, pageSize);
687
+ // Keep onChange pagination for URL/sync; avoid duplicate fetch callbacks (onPageSizeChange / onPageChange)
688
+ onChange?.({
689
+ type: 'pagination',
690
+ pagination: { page: displayPage, pageSize },
691
+ });
692
+ }
693
+ else {
694
+ onPageSizeChange?.(displayPage, pageSize);
695
+ onPageChange?.(displayPage, pageSize);
696
+ onChange?.({
697
+ type: 'pagination',
698
+ pagination: { page: displayPage, pageSize },
699
+ });
700
+ }
601
701
  }, [
602
702
  goToPage,
603
703
  pageSize,
@@ -605,24 +705,37 @@ export const Table = forwardRef(function TableComponent(props, ref) {
605
705
  onPageChange,
606
706
  onChange,
607
707
  resetEditingState,
708
+ onFilter,
709
+ getActiveRemoteFilterCallArgs,
608
710
  ]);
609
711
  // Handle page size change
610
712
  const handlePageSizeChange = useCallback((newSize) => {
611
713
  resetEditingState();
612
714
  changePageSize(newSize);
613
- // Pass 1-indexed page number to callbacks (page 1 after size change)
614
- onPageSizeChange?.(1, newSize);
615
- onPageChange?.(1, newSize);
616
- onChange?.({
617
- type: 'pagination',
618
- pagination: { page: 1, pageSize: newSize },
619
- });
715
+ const filterArgs = onFilter ? getActiveRemoteFilterCallArgs() : null;
716
+ if (filterArgs) {
717
+ onFilterRef.current?.(filterArgs.type, filterArgs.filterData, 1, newSize);
718
+ onChange?.({
719
+ type: 'pagination',
720
+ pagination: { page: 1, pageSize: newSize },
721
+ });
722
+ }
723
+ else {
724
+ onPageSizeChange?.(1, newSize);
725
+ onPageChange?.(1, newSize);
726
+ onChange?.({
727
+ type: 'pagination',
728
+ pagination: { page: 1, pageSize: newSize },
729
+ });
730
+ }
620
731
  }, [
621
732
  changePageSize,
622
733
  onPageSizeChange,
623
734
  onPageChange,
624
735
  onChange,
625
736
  resetEditingState,
737
+ onFilter,
738
+ getActiveRemoteFilterCallArgs,
626
739
  ]);
627
740
  // Handle row click
628
741
  const handleRowClick = useCallback((row, rowIndex, e) => {
@@ -1064,7 +1177,7 @@ export const Table = forwardRef(function TableComponent(props, ref) {
1064
1177
  ? JSON.stringify(value)
1065
1178
  : String(value)
1066
1179
  : '';
1067
- return (_jsxs(_Fragment, { children: [formatterContent, editorRendererCacheRef.current.get(cacheKey)] }));
1180
+ return (_jsx(_Fragment, { children: editorRendererCacheRef.current.get(cacheKey) }));
1068
1181
  }
1069
1182
  // Default editor
1070
1183
  return (_jsx(CellEditor, { type: column.editorType === 'number' ? 'number' : 'text', value: editValue ?? '', onChange: (e) => setEditValue(e.target.value), onBlur: () => handleCellEditComplete(row, rowIndex, column), onKeyDown: (e) => {
@@ -1444,6 +1557,9 @@ export const Table = forwardRef(function TableComponent(props, ref) {
1444
1557
  // Non-null reference for JSX rendering (guarded by showFilter check below)
1445
1558
  const ResolvedFilter = FilterComponent;
1446
1559
  const onFilter = (value) => handleFilterChange(column.dataField, value);
1560
+ const factoryGetFilter = column.filterRenderer
1561
+ ? getColumnFilterGetFilterCallback(column.filter)
1562
+ : undefined;
1447
1563
  // Get resized column style
1448
1564
  const resizeStyle = resizable
1449
1565
  ? getColumnStyle(column, columnWidths, resizable, isResizing)
@@ -1568,7 +1684,10 @@ export const Table = forwardRef(function TableComponent(props, ref) {
1568
1684
  display: 'flex',
1569
1685
  alignItems: 'center',
1570
1686
  overflow: 'hidden',
1571
- }, onClick: (e) => e.stopPropagation(), children: column.filterRenderer ? (column.filterRenderer(onFilter, column)) : (_jsx("div", { onFocusCapture: () => {
1687
+ }, onClick: (e) => e.stopPropagation(), children: column.filterRenderer ? (_jsxs(_Fragment, { children: [factoryGetFilter ? (_jsx(FilterRendererGetFilterBridge, { getFilter: factoryGetFilter, dataField: column.dataField, filters: filters, handleFilterChange: handleFilterChange })) : null, column.filterRenderer(onFilter, column, {
1688
+ value: filters[column.dataField],
1689
+ onClear: () => handleFilterChange(column.dataField, null),
1690
+ })] })) : (_jsx("div", { onFocusCapture: () => {
1572
1691
  focusedFilterFieldRef.current =
1573
1692
  column.dataField;
1574
1693
  saveFilterCache({
@@ -175,26 +175,31 @@ export const ResizeLine = styled.div `
175
175
  // ============================================================================
176
176
  // Hook: useColumnResize
177
177
  // ============================================================================
178
- export function useColumnResize({ columns, config = {}, enabled = true, tableId, }) {
179
- const { minWidth = 50, maxWidth = 800, autoFit = true, onResize, onResizeStart, onResizeEnd, defaultWidths = {}, handleColor, handleHoverColor, handleWidth = 4, } = config;
178
+ // Module-level stable defaults so that destructuring in the hook never creates
179
+ // new object references on every render (which would re-trigger useEffect deps).
180
+ const EMPTY_RESIZE_CONFIG = {};
181
+ const EMPTY_WIDTHS = {};
182
+ export function useColumnResize({ columns, config = EMPTY_RESIZE_CONFIG, enabled = true, tableId, }) {
183
+ const { minWidth = 50, maxWidth = 800, autoFit = true, onResize, onResizeStart, onResizeEnd, defaultWidths, handleColor, handleHoverColor, handleWidth = 4, } = config;
184
+ // Stable reference — when the caller doesn't provide defaultWidths, always
185
+ // use the same empty object so that useEffect dependencies remain unchanged.
186
+ const stableDefaultWidths = defaultWidths || EMPTY_WIDTHS;
180
187
  // Initialize column widths - Column-level width takes FIRST priority
181
188
  const initialWidths = useMemo(() => {
182
189
  const widths = {};
183
190
  columns.forEach((col) => {
184
- // Priority 1: Column-level width (user-defined in column)
185
191
  if (typeof col.width === 'number') {
186
192
  widths[col.dataField] = col.width;
187
193
  }
188
194
  else if (typeof col.width === 'string' && col.width.endsWith('px')) {
189
195
  widths[col.dataField] = parseInt(col.width, 10);
190
196
  }
191
- // Priority 2: defaultWidths from resizeConfig (only if column width not set)
192
- else if (defaultWidths[col.dataField]) {
193
- widths[col.dataField] = defaultWidths[col.dataField];
197
+ else if (stableDefaultWidths[col.dataField]) {
198
+ widths[col.dataField] = stableDefaultWidths[col.dataField];
194
199
  }
195
200
  });
196
201
  return widths;
197
- }, [columns, defaultWidths]);
202
+ }, [columns, stableDefaultWidths]);
198
203
  const [columnWidths, setColumnWidths] = useState(initialWidths);
199
204
  const [isResizing, setIsResizing] = useState(false);
200
205
  const [resizingColumn, setResizingColumn] = useState(null);
@@ -203,29 +208,32 @@ export function useColumnResize({ columns, config = {}, enabled = true, tableId,
203
208
  const startWidthRef = useRef(0);
204
209
  const currentColumnRef = useRef(null);
205
210
  const headerRefMap = useRef(new Map());
206
- // Update widths when columns change - Column-level width takes FIRST priority
211
+ // Update widths when columns change - Column-level width takes FIRST priority.
212
+ // Returns `prev` (same reference) when nothing was added so React skips the
213
+ // re-render and avoids an infinite setState → render → setState loop.
207
214
  useEffect(() => {
208
215
  setColumnWidths((prev) => {
216
+ let changed = false;
209
217
  const newWidths = { ...prev };
210
218
  columns.forEach((col) => {
211
- // Only add if not already set (user hasn't resized it)
212
219
  if (!(col.dataField in newWidths)) {
213
- // Priority 1: Column-level width
214
220
  if (typeof col.width === 'number') {
215
221
  newWidths[col.dataField] = col.width;
222
+ changed = true;
216
223
  }
217
224
  else if (typeof col.width === 'string' && col.width.endsWith('px')) {
218
225
  newWidths[col.dataField] = parseInt(col.width, 10);
226
+ changed = true;
219
227
  }
220
- // Priority 2: defaultWidths from resizeConfig
221
- else if (defaultWidths[col.dataField]) {
222
- newWidths[col.dataField] = defaultWidths[col.dataField];
228
+ else if (stableDefaultWidths[col.dataField]) {
229
+ newWidths[col.dataField] = stableDefaultWidths[col.dataField];
230
+ changed = true;
223
231
  }
224
232
  }
225
233
  });
226
- return newWidths;
234
+ return changed ? newWidths : prev;
227
235
  });
228
- }, [columns, defaultWidths]);
236
+ }, [columns, stableDefaultWidths]);
229
237
  // Handle mouse move during resize
230
238
  const handleMouseMove = useCallback((e) => {
231
239
  if (!currentColumnRef.current)
@@ -175,6 +175,18 @@ export interface DateFilterInstance {
175
175
  /** Clear the filter */
176
176
  clear: () => void;
177
177
  }
178
+ /**
179
+ * Wraps `getFilter` instances so they are callable: `fn()`, `fn('')`, `fn(null)` clear;
180
+ * `fn(value)` sets. Still exposes `.value`, `.setValue`, and `.clear` like TextFilterInstance.
181
+ */
182
+ export declare function createCallableFilterHandle({ getValue, setValue, clear, }: {
183
+ getValue: () => any;
184
+ setValue: (value: any) => void;
185
+ clear: () => void;
186
+ }): ((v?: any) => void) & {
187
+ setValue: (value: any) => void;
188
+ clear: () => void;
189
+ };
178
190
  /**
179
191
  * Text filter - can be used as a component or factory function
180
192
  *
@@ -335,21 +347,21 @@ export interface CustomFilterInstance<T = any> {
335
347
  * })
336
348
  *
337
349
  * @example
338
- * // Using with column.filterRenderer (simple options pattern)
339
- * // This allows using existing filter components with custom configuration
350
+ * // With filterRenderer, mount the same factory component as `filter` so getFilter runs.
351
+ * // CustomFilter’s getFilter is not called if you only render a different component in filterRenderer.
352
+ * const PictureFilter = NumberFilter({
353
+ * placeholder: 'Picture',
354
+ * getFilter: (f) => {
355
+ * pictureFilterRef.current = f;
356
+ * },
357
+ * });
340
358
  * {
341
- * dataField: 'picture',
342
- * text: 'Picture',
343
- * filter: CustomFilter({
344
- * placeholder: 'Picture',
345
- * getFilter: (filter) => {
346
- * pictureFilterRef.current = filter;
347
- * },
348
- * }),
349
- * filterRenderer: (onFilter, column) => (
350
- * <NumberFilter onFilter={onFilter} column={column} />
359
+ * filter: PictureFilter,
360
+ * filterRenderer: (onFilter, column, {value, onClear}) => (
361
+ * <PictureFilter column={column} value={value} onChange={onFilter} />
351
362
  * ),
352
363
  * }
364
+ * // clearAllFilter: () => pictureFilterRef.current?.clear()
353
365
  *
354
366
  * @example
355
367
  * // Custom range filter
@@ -10,6 +10,33 @@ const filterSelectClass = cn(s['leading-4'], s['p-1'], s['focus:outline-none'],
10
10
  const comparatorSelectClass = cn(s['leading-4'], s['p-1'], s['focus:outline-none'], s['focus:ring'], s['shadow-sm'], s['text-xs'], s['font-normal'], s['border'], s['border-gray-300'], s['rounded-md'], s['text-black'], s['bg-white'], s['cursor-pointer'], s['w-12']);
11
11
  const dateInputClass = cn(s['leading-4'], s['p-1'], s['focus:outline-none'], s['focus:ring'], s['shadow-sm'], s['text-xs'], s['font-normal'], s['border'], s['border-gray-300'], s['rounded-md'], s['text-black'], s['bg-white'], s['flex-1'], s['transition-all']);
12
12
  const filterContainerClass = cn(s['flex'], s['flex-row'], s['gap-1'], s['items-center'], s['w-full']);
13
+ /**
14
+ * Wraps `getFilter` instances so they are callable: `fn()`, `fn('')`, `fn(null)` clear;
15
+ * `fn(value)` sets. Still exposes `.value`, `.setValue`, and `.clear` like TextFilterInstance.
16
+ */
17
+ export function createCallableFilterHandle({ getValue, setValue, clear, }) {
18
+ const handle = Object.assign(function callFilter(v) {
19
+ if (arguments.length === 0 ||
20
+ v === '' ||
21
+ (typeof v === 'string' && v.trim() === '') ||
22
+ v == null) {
23
+ clear();
24
+ return;
25
+ }
26
+ setValue(v);
27
+ }, {
28
+ setValue,
29
+ clear,
30
+ });
31
+ Object.defineProperty(handle, 'value', {
32
+ get() {
33
+ return getValue();
34
+ },
35
+ enumerable: true,
36
+ configurable: true,
37
+ });
38
+ return handle;
39
+ }
13
40
  /**
14
41
  * Internal Text filter component with options support
15
42
  */
@@ -57,10 +84,8 @@ const TextFilterComponent = ({ column, value, onChange, onFilter: onFilterProp,
57
84
  // Provide filter instance via getFilter callback - only on mount
58
85
  useEffect(() => {
59
86
  if (getFilter) {
60
- getFilter({
61
- get value() {
62
- return internalValueRef.current;
63
- },
87
+ getFilter(createCallableFilterHandle({
88
+ getValue: () => internalValueRef.current,
64
89
  setValue: (newValue) => {
65
90
  setInternalValue(newValue);
66
91
  onChangeRef.current(newValue || null);
@@ -71,7 +96,7 @@ const TextFilterComponent = ({ column, value, onChange, onFilter: onFilterProp,
71
96
  onChangeRef.current(null);
72
97
  onFilterRef.current?.('');
73
98
  },
74
- });
99
+ }));
75
100
  }
76
101
  // eslint-disable-next-line react-hooks/exhaustive-deps
77
102
  }, [getFilter]);
@@ -178,16 +203,21 @@ const NumberFilterComponent = ({ column, value, onChange, onFilter: onFilterProp
178
203
  // Provide filter instance via getFilter callback - only on mount
179
204
  useEffect(() => {
180
205
  if (getFilter) {
181
- getFilter({
182
- get value() {
183
- return numberRef.current
184
- ? { number: numberRef.current, comparator: comparatorRef.current }
185
- : null;
186
- },
206
+ getFilter(createCallableFilterHandle({
207
+ getValue: () => numberRef.current
208
+ ? {
209
+ number: numberRef.current,
210
+ comparator: comparatorRef.current,
211
+ }
212
+ : null,
187
213
  setValue: (newValue) => {
188
214
  if (newValue !== null && newValue !== undefined) {
189
- const num = typeof newValue === 'object' ? newValue.number : String(newValue);
190
- const comp = typeof newValue === 'object' ? newValue.comparator : comparatorRef.current;
215
+ const num = typeof newValue === 'object'
216
+ ? newValue.number
217
+ : String(newValue);
218
+ const comp = typeof newValue === 'object'
219
+ ? newValue.comparator
220
+ : comparatorRef.current;
191
221
  setNumber(num);
192
222
  setComparator(comp);
193
223
  onChangeRef.current({ number: num, comparator: comp });
@@ -205,7 +235,7 @@ const NumberFilterComponent = ({ column, value, onChange, onFilter: onFilterProp
205
235
  onChangeRef.current(null);
206
236
  onFilterRef.current?.(null);
207
237
  },
208
- });
238
+ }));
209
239
  }
210
240
  // eslint-disable-next-line react-hooks/exhaustive-deps
211
241
  }, [getFilter]);
@@ -353,8 +383,8 @@ const DateFilterComponent = ({ column, value, onChange, onFilter: onFilterProp,
353
383
  // Provide filter instance via getFilter callback - only on mount
354
384
  useEffect(() => {
355
385
  if (getFilter) {
356
- getFilter({
357
- get value() {
386
+ getFilter(createCallableFilterHandle({
387
+ getValue: () => {
358
388
  const { startDate: s, endDate: e, diffFlag: d, comparator: c, } = stateRef.current;
359
389
  return s || e
360
390
  ? { startDate: s, endDate: e, diffFlag: d, comparator: c }
@@ -384,7 +414,7 @@ const DateFilterComponent = ({ column, value, onChange, onFilter: onFilterProp,
384
414
  onChangeRef.current(null);
385
415
  onFilterRef.current?.(null);
386
416
  },
387
- });
417
+ }));
388
418
  }
389
419
  // eslint-disable-next-line react-hooks/exhaustive-deps
390
420
  }, [getFilter]);
@@ -479,10 +509,8 @@ const SelectFilterComponent = ({ column, value, onChange, onFilter: onFilterProp
479
509
  // Provide filter instance via getFilter callback - only on mount
480
510
  useEffect(() => {
481
511
  if (getFilter) {
482
- getFilter({
483
- get value() {
484
- return selectedValueRef.current || null;
485
- },
512
+ getFilter(createCallableFilterHandle({
513
+ getValue: () => selectedValueRef.current || null,
486
514
  setValue: (newValue) => {
487
515
  setSelectedValue(newValue || '');
488
516
  onChangeRef.current(newValue);
@@ -493,7 +521,7 @@ const SelectFilterComponent = ({ column, value, onChange, onFilter: onFilterProp
493
521
  onChangeRef.current(null);
494
522
  onFilterRef.current?.(null);
495
523
  },
496
- });
524
+ }));
497
525
  }
498
526
  // eslint-disable-next-line react-hooks/exhaustive-deps
499
527
  }, [getFilter]);
@@ -564,10 +592,8 @@ const CustomFilterComponent = ({ column, value, onChange, onFilter: onFilterProp
564
592
  // Provide filter instance via getFilter callback - only on mount
565
593
  useEffect(() => {
566
594
  if (getFilter) {
567
- getFilter({
568
- get value() {
569
- return filterValueRef.current;
570
- },
595
+ getFilter(createCallableFilterHandle({
596
+ getValue: () => filterValueRef.current,
571
597
  setValue: (newValue) => {
572
598
  setFilterValue(newValue);
573
599
  onChangeRef.current(newValue);
@@ -578,7 +604,7 @@ const CustomFilterComponent = ({ column, value, onChange, onFilter: onFilterProp
578
604
  onChangeRef.current(null);
579
605
  onFilterRef.current?.(null);
580
606
  },
581
- });
607
+ }));
582
608
  }
583
609
  // eslint-disable-next-line react-hooks/exhaustive-deps
584
610
  }, [getFilter]);
@@ -643,21 +669,21 @@ const CustomFilterComponent = ({ column, value, onChange, onFilter: onFilterProp
643
669
  * })
644
670
  *
645
671
  * @example
646
- * // Using with column.filterRenderer (simple options pattern)
647
- * // This allows using existing filter components with custom configuration
672
+ * // With filterRenderer, mount the same factory component as `filter` so getFilter runs.
673
+ * // CustomFilter’s getFilter is not called if you only render a different component in filterRenderer.
674
+ * const PictureFilter = NumberFilter({
675
+ * placeholder: 'Picture',
676
+ * getFilter: (f) => {
677
+ * pictureFilterRef.current = f;
678
+ * },
679
+ * });
648
680
  * {
649
- * dataField: 'picture',
650
- * text: 'Picture',
651
- * filter: CustomFilter({
652
- * placeholder: 'Picture',
653
- * getFilter: (filter) => {
654
- * pictureFilterRef.current = filter;
655
- * },
656
- * }),
657
- * filterRenderer: (onFilter, column) => (
658
- * <NumberFilter onFilter={onFilter} column={column} />
681
+ * filter: PictureFilter,
682
+ * filterRenderer: (onFilter, column, {value, onClear}) => (
683
+ * <PictureFilter column={column} value={value} onChange={onFilter} />
659
684
  * ),
660
685
  * }
686
+ * // clearAllFilter: () => pictureFilterRef.current?.clear()
661
687
  *
662
688
  * @example
663
689
  * // Custom range filter
@@ -1,7 +1,7 @@
1
1
  export { Table, default } from './Table';
2
2
  export * from './types';
3
3
  export { useSortState, useFilterState, usePaginationState, useRowSelection, useRowExpansion, useColumnVisibility, useTableDebounce, sortData, filterData, paginateData, getNestedValue, exportToCSV, exportToExcel, } from './hooks';
4
- export { TextFilter, NumberFilter, DateFilter, SelectFilter, CustomFilter, getFilterComponent, } from './filters';
4
+ export { TextFilter, NumberFilter, DateFilter, SelectFilter, CustomFilter, getFilterComponent, createCallableFilterHandle, } from './filters';
5
5
  export type { TextFilterOptions, TextFilterInstance, NumberFilterOptions, NumberFilterInstance, SelectFilterOptions, SelectFilterInstance, DateFilterOptions, DateFilterInstance, CustomFilterOptions, CustomFilterRenderProps, CustomFilterInstance, } from './filters';
6
6
  export { useColumnResize, getColumnStyle, ResizeHandle, ResizeLine, ResizableHeaderCell, } from './columnResize';
7
7
  export type { ColumnResizeConfig, UseColumnResizeReturn } from './columnResize';
@@ -1,6 +1,6 @@
1
1
  export { Table, default } from './Table';
2
2
  export * from './types';
3
3
  export { useSortState, useFilterState, usePaginationState, useRowSelection, useRowExpansion, useColumnVisibility, useTableDebounce, sortData, filterData, paginateData, getNestedValue, exportToCSV, exportToExcel, } from './hooks';
4
- export { TextFilter, NumberFilter, DateFilter, SelectFilter, CustomFilter, getFilterComponent, } from './filters';
4
+ export { TextFilter, NumberFilter, DateFilter, SelectFilter, CustomFilter, getFilterComponent, createCallableFilterHandle, } from './filters';
5
5
  export { useColumnResize, getColumnStyle, ResizeHandle, ResizeLine, ResizableHeaderCell, } from './columnResize';
6
6
  export { useColumnReorder, DragGhost, DropIndicator, DraggableHeader, ColumnReorderGhost, ColumnDropIndicator, getReorderableHeaderProps, } from './columnReorder';
@@ -155,6 +155,16 @@ export interface FieldConfig<T = any> {
155
155
  /** Field type configuration for dynamic rendering */
156
156
  export type FieldTypeConfig<T = any> = Record<string, FieldConfig<T>>;
157
157
  /** Column definition */
158
+ /**
159
+ * Passed as the third argument to {@link TableColumn.filterRenderer} so custom
160
+ * filters stay controlled with table state (including clear-all).
161
+ */
162
+ export interface TableFilterRendererContext {
163
+ /** Current value for this column from the table filter state */
164
+ value: any;
165
+ /** Clear this column’s filter only */
166
+ onClear: () => void;
167
+ }
158
168
  export interface TableColumn<T = any> {
159
169
  /** Unique field key */
160
170
  dataField: string;
@@ -193,8 +203,8 @@ export interface TableColumn<T = any> {
193
203
  }>;
194
204
  /** Custom filter component (alias for filter when using component) */
195
205
  filterComponent?: React.ComponentType<TableFilterProps>;
196
- /** Custom filter renderer */
197
- filterRenderer?: (onFilter: (value: any) => void, column: TableColumn<T>) => React.ReactNode;
206
+ /** Custom filter renderer (third arg is always passed when provided by Table; optional in signature for backward compatibility) */
207
+ filterRenderer?: (onFilter: (value: any) => void, column: TableColumn<T>, context?: TableFilterRendererContext) => React.ReactNode;
198
208
  /** Filter placeholder */
199
209
  filterPlaceholder?: string;
200
210
  /** Whether column is editable - boolean or function returning boolean/custom editor */
@@ -69,4 +69,17 @@
69
69
  /*! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com*/
70
70
  /*! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com*/
71
71
  /*! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com*/
72
+ /*! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com*/
73
+ /*! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com*/
74
+ /*! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com*/
75
+ /*! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com*/
76
+ /*! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com*/
77
+ /*! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com*/
78
+ /*! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com*/
79
+ /*! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com*/
80
+ /*! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com*/
81
+ /*! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com*/
82
+ /*! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com*/
83
+ /*! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com*/
84
+ /*! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com*/
72
85
  /*! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com*/.\!filter,.backdrop-blur-sm,.blur,.ring,.ring-2,.shadow,.shadow-inner,body{font-family:Arima Regular;font-size:14px}.dark\:bg-black:is(.dark *),.dark\:border-gray-600:is(.dark *){--tw-bg-opacity:1;background-color:rgb(0 0 0/var(--tw-bg-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*/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))}.\!visible{visibility:visible!important}.top-1\/2{top:50%}.-translate-x-1\/2,.-translate-x-1\/2,.-translate-y-1\/2{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-y-1\/2{--tw-translate-y:-50%}.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}.py-1\.5{padding-bottom:.375rem;padding-top:.375rem}.\!filter,.blur,.ring,.ring-2,.shadow,.shadow-inner,.backdrop-blur-sm,body{font-family:Arima Regular;font-size:14px}.hover\:bg-white\/20:hover{background-color:#fff3}.dark\:border-gray-600:is(.dark *),.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))}.\!visible{visibility:visible!important}.top-1\/2{top:50%}.flex-grow,.-translate-x-1\/2{--tw-translate-x:-50%}.-translate-x-1\/2,.-translate-y-1\/2{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-y-1\/2{--tw-translate-y:-50%}@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}.py-1\.5{padding-bottom:.375rem;padding-top:.375rem}.shadow,.shadow-inner,.shadow-md,.ring,.ring-2,.blur,.\!filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)!important}.backdrop-blur-sm,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,.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.64",
3
+ "version": "0.4.66",
4
4
  "private": false,
5
5
  "description": "Easy use restyle components",
6
6
  "author": {