@reactorui/datagrid 1.0.24 → 1.1.1
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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FilterControls.d.ts","sourceRoot":"","sources":["../../../src/components/Filter/FilterControls.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"FilterControls.d.ts","sourceRoot":"","sources":["../../../src/components/Filter/FilterControls.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,MAAM,EAAE,YAAY,EAAkB,MAAM,aAAa,CAAC;AAMnE,UAAU,mBAAmB,CAAC,CAAC;IAC7B,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;IACrB,aAAa,EAAE,YAAY,EAAE,CAAC;IAC9B,aAAa,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC,YAAY,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IAC7D,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AA2FD,eAAO,MAAM,cAAc,GAAI,CAAC,EAAG,qGAQhC,mBAAmB,CAAC,CAAC,CAAC,4CAkUxB,CAAC"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
// File: src/components/Filter/FilterControls.tsx
|
|
3
3
|
import { useState, useCallback, useMemo, useRef, useEffect } from 'react';
|
|
4
|
+
import { createPortal } from 'react-dom';
|
|
4
5
|
// ============================================================================
|
|
5
6
|
// Operator Configuration
|
|
6
7
|
// ============================================================================
|
|
@@ -41,14 +42,13 @@ const DEFAULT_OPERATORS = [{ value: 'eq', label: 'equals' }];
|
|
|
41
42
|
// Styles
|
|
42
43
|
// ============================================================================
|
|
43
44
|
const styles = {
|
|
44
|
-
select: 'w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md text-sm bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:focus:ring-blue-400 disabled:bg-gray-50 dark:disabled:bg-gray-700 disabled:text-gray-500 dark:disabled:text-gray-400 disabled:cursor-not-allowed',
|
|
45
|
-
input: 'w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md text-sm bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 placeholder-gray-500 dark:placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:focus:ring-blue-400 disabled:bg-gray-50 dark:disabled:bg-gray-700 disabled:cursor-not-allowed',
|
|
46
|
-
inputDisabled: 'w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md text-sm bg-gray-50 dark:bg-gray-700 text-gray-500 dark:text-gray-400 cursor-not-allowed',
|
|
47
|
-
buttonPrimary: 'w-full px-4 py-2 bg-blue-600 dark:bg-blue-700 text-white text-sm rounded-md hover:bg-blue-700 dark:hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:focus:ring-blue-400 disabled:bg-gray-300 dark:disabled:bg-gray-600 disabled:text-gray-500 dark:disabled:text-gray-400 disabled:cursor-not-allowed transition-colors duration-150',
|
|
48
|
-
|
|
49
|
-
filterTag: 'flex items-center
|
|
50
|
-
filterTagRemove: 'text-blue-600 dark:text-blue-300 hover:text-blue-800 dark:hover:text-blue-100 focus:outline-none
|
|
51
|
-
label: 'block text-xs font-medium text-gray-700 dark:text-gray-300 mb-1',
|
|
45
|
+
select: 'w-full px-3 py-2.5 border border-gray-300 dark:border-gray-600 rounded-md text-sm bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:focus:ring-blue-400 disabled:bg-gray-50 dark:disabled:bg-gray-700 disabled:text-gray-500 dark:disabled:text-gray-400 disabled:cursor-not-allowed',
|
|
46
|
+
input: 'w-full px-3 py-2.5 border border-gray-300 dark:border-gray-600 rounded-md text-sm bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 placeholder-gray-500 dark:placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:focus:ring-blue-400 disabled:bg-gray-50 dark:disabled:bg-gray-700 disabled:cursor-not-allowed',
|
|
47
|
+
inputDisabled: 'w-full px-3 py-2.5 border border-gray-300 dark:border-gray-600 rounded-md text-sm bg-gray-50 dark:bg-gray-700 text-gray-500 dark:text-gray-400 cursor-not-allowed',
|
|
48
|
+
buttonPrimary: 'w-full px-4 py-2.5 bg-blue-600 dark:bg-blue-700 text-white text-sm font-medium rounded-md hover:bg-blue-700 dark:hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:focus:ring-blue-400 disabled:bg-gray-300 dark:disabled:bg-gray-600 disabled:text-gray-500 dark:disabled:text-gray-400 disabled:cursor-not-allowed transition-colors duration-150',
|
|
49
|
+
label: 'block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1.5',
|
|
50
|
+
filterTag: 'inline-flex items-center gap-1.5 px-2.5 py-1 bg-blue-100 dark:bg-blue-900/30 text-blue-800 dark:text-blue-200 rounded-md text-xs font-medium whitespace-nowrap',
|
|
51
|
+
filterTagRemove: 'ml-0.5 text-blue-600 dark:text-blue-300 hover:text-blue-800 dark:hover:text-blue-100 focus:outline-none',
|
|
52
52
|
};
|
|
53
53
|
// ============================================================================
|
|
54
54
|
// Filter Icon
|
|
@@ -62,10 +62,40 @@ export const FilterControls = ({ columns, activeFilters, onApplyFilter, onRemove
|
|
|
62
62
|
const [filterColumn, setFilterColumn] = useState('');
|
|
63
63
|
const [filterOperator, setFilterOperator] = useState('eq');
|
|
64
64
|
const [filterValue, setFilterValue] = useState('');
|
|
65
|
-
const
|
|
65
|
+
const [popoverPosition, setPopoverPosition] = useState({ top: 0, left: 0 });
|
|
66
66
|
const buttonRef = useRef(null);
|
|
67
|
+
const popoverRef = useRef(null);
|
|
67
68
|
const isDisabled = disabled || filterLoading;
|
|
68
69
|
const filterCount = activeFilters.length;
|
|
70
|
+
// Calculate popover position relative to viewport with edge detection
|
|
71
|
+
const updatePopoverPosition = useCallback(() => {
|
|
72
|
+
if (buttonRef.current) {
|
|
73
|
+
const rect = buttonRef.current.getBoundingClientRect();
|
|
74
|
+
const popoverWidth = 320; // w-80
|
|
75
|
+
const viewportWidth = window.innerWidth;
|
|
76
|
+
// Adjust left position if popover would overflow right edge
|
|
77
|
+
let left = rect.left;
|
|
78
|
+
if (left + popoverWidth > viewportWidth - 16) {
|
|
79
|
+
left = Math.max(16, viewportWidth - popoverWidth - 16);
|
|
80
|
+
}
|
|
81
|
+
setPopoverPosition({
|
|
82
|
+
top: rect.bottom + 8,
|
|
83
|
+
left,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}, []);
|
|
87
|
+
// Update position when opening
|
|
88
|
+
useEffect(() => {
|
|
89
|
+
if (isOpen) {
|
|
90
|
+
updatePopoverPosition();
|
|
91
|
+
window.addEventListener('scroll', updatePopoverPosition, true);
|
|
92
|
+
window.addEventListener('resize', updatePopoverPosition);
|
|
93
|
+
return () => {
|
|
94
|
+
window.removeEventListener('scroll', updatePopoverPosition, true);
|
|
95
|
+
window.removeEventListener('resize', updatePopoverPosition);
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
}, [isOpen, updatePopoverPosition]);
|
|
69
99
|
// Close on outside click
|
|
70
100
|
useEffect(() => {
|
|
71
101
|
const handleClickOutside = (event) => {
|
|
@@ -126,12 +156,6 @@ export const FilterControls = ({ columns, activeFilters, onApplyFilter, onRemove
|
|
|
126
156
|
onApplyFilter,
|
|
127
157
|
isDisabled,
|
|
128
158
|
]);
|
|
129
|
-
const handleClear = useCallback(() => {
|
|
130
|
-
setFilterColumn('');
|
|
131
|
-
setFilterOperator('eq');
|
|
132
|
-
setFilterValue('');
|
|
133
|
-
onClearFilters();
|
|
134
|
-
}, [onClearFilters]);
|
|
135
159
|
const handleKeyDown = useCallback((e) => {
|
|
136
160
|
if (e.key === 'Enter' && canApply && !isDisabled) {
|
|
137
161
|
handleApply();
|
|
@@ -162,7 +186,13 @@ export const FilterControls = ({ columns, activeFilters, onApplyFilter, onRemove
|
|
|
162
186
|
return _jsx("input", { ...commonProps, type: "text", placeholder: "Enter value" });
|
|
163
187
|
}
|
|
164
188
|
};
|
|
165
|
-
return (_jsxs("div", { className: "
|
|
189
|
+
return (_jsxs("div", { className: "flex items-center gap-2 min-w-0 flex-1", children: [_jsxs("button", { ref: buttonRef, onClick: () => setIsOpen(!isOpen), disabled: isDisabled, title: filterCount > 0
|
|
166
190
|
? `${filterCount} filter${filterCount === 1 ? '' : 's'} active`
|
|
167
|
-
: 'Add filter', className: "relative p-2 text-gray-600 dark:text-gray-400 hover:text-gray-800 dark:hover:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 dark:focus:ring-blue-400 disabled:opacity-50 disabled:cursor-not-allowed transition-colors duration-150", children: [_jsx(FilterIcon, { className: "w-5 h-5" }), filterCount > 0 && (_jsx("span", { className: "absolute -top-1 -right-1 min-w-[18px] h-[18px] flex items-center justify-center px-1 text-xs font-medium text-white bg-blue-600 dark:bg-blue-500 rounded-full", children: filterCount }))] }),
|
|
191
|
+
: 'Add filter', className: "relative flex-shrink-0 p-2 text-gray-600 dark:text-gray-400 hover:text-gray-800 dark:hover:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 dark:focus:ring-blue-400 disabled:opacity-50 disabled:cursor-not-allowed transition-colors duration-150", children: [_jsx(FilterIcon, { className: "w-5 h-5" }), filterCount > 0 && (_jsx("span", { className: "absolute -top-1 -right-1 min-w-[18px] h-[18px] flex items-center justify-center px-1 text-xs font-medium text-white bg-blue-600 dark:bg-blue-500 rounded-full", children: filterCount }))] }), activeFilters.length > 0 && (_jsx("div", { className: "flex-1 min-w-0 overflow-x-auto", style: { scrollbarWidth: 'none', msOverflowStyle: 'none' }, children: _jsxs("div", { className: "flex items-center gap-1.5 py-0.5", children: [activeFilters.map((filter, index) => (_jsxs("span", { className: styles.filterTag, children: [filter.label, _jsx("button", { onClick: () => onRemoveFilter(index), disabled: isDisabled, className: styles.filterTagRemove, "aria-label": `Remove filter: ${filter.label}`, children: "\u00D7" })] }, `${filter.column}-${index}`))), activeFilters.length > 1 && (_jsx("button", { onClick: onClearFilters, disabled: isDisabled, className: "flex-shrink-0 text-xs text-red-600 dark:text-red-400 hover:text-red-800 dark:hover:text-red-300 whitespace-nowrap px-2 py-1 hover:bg-red-50 dark:hover:bg-red-900/20 rounded transition-colors", children: "Clear all" }))] }) })), isOpen &&
|
|
192
|
+
createPortal(_jsx("div", { ref: popoverRef, className: "fixed w-80 bg-white dark:bg-gray-800 rounded-lg shadow-xl border border-gray-200 dark:border-gray-700", style: {
|
|
193
|
+
top: popoverPosition.top,
|
|
194
|
+
padding: 14,
|
|
195
|
+
left: popoverPosition.left,
|
|
196
|
+
zIndex: 99999,
|
|
197
|
+
}, children: _jsxs("div", { className: "p-5 space-y-4", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("h3", { className: "text-base font-semibold text-gray-900 dark:text-gray-100", children: "Add Filter" }), _jsx("button", { onClick: () => setIsOpen(false), className: "p-1 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 rounded-md hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors", children: _jsx("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) })] }), _jsxs("div", { children: [_jsx("label", { className: styles.label, children: "Column" }), _jsxs("select", { value: filterColumn, onChange: (e) => handleColumnChange(e.target.value), disabled: isDisabled, className: styles.select, children: [_jsx("option", { value: "", children: "Select column" }), filterableColumns.map((col) => (_jsx("option", { value: String(col.key), children: col.label }, String(col.key))))] })] }), _jsxs("div", { children: [_jsx("label", { className: styles.label, children: "Operator" }), _jsx("select", { value: filterOperator, onChange: (e) => setFilterOperator(e.target.value), disabled: isDisabled || !filterColumn, className: styles.select, children: operatorOptions.map((op) => (_jsx("option", { value: op.value, children: op.label }, op.value))) })] }), _jsxs("div", { children: [_jsx("label", { className: styles.label, children: "Value" }), renderValueInput()] }), _jsx("button", { onClick: handleApply, disabled: isDisabled || !canApply, className: styles.buttonPrimary, children: "Apply Filter" })] }) }), document.body)] }));
|
|
168
198
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reactorui/datagrid",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "A flexible, high-performance React data grid component with TypeScript support, advanced filtering, pagination, sorting, and customizable theming",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.js",
|