{
pinned: true,
sortable: false,
resizable: false,
+ searchable: false,
cellRenderer: ButtonsCell,
editorCellRenderer: ButtonsEditorCell,
},
diff --git a/demo/src/views/sync.js b/demo/src/views/sync.js
index f8c9039..c2088cc 100644
--- a/demo/src/views/sync.js
+++ b/demo/src/views/sync.js
@@ -31,6 +31,7 @@ const MyAwesomeTable = () => {
let [columns, setColumns] = useState(getColumns({ setRowsData }));
let [isSettingsOpen, setIsSettingsOpen] = useState(false);
let [selectAllMode, setSelectAllMode] = useState("page");
+ let [searchByColumn, setSearchByColumn] = useState(true);
const controllers = {
columns: [columns, setColumns],
@@ -55,6 +56,7 @@ const MyAwesomeTable = () => {
minSearchChars: [minSearchChars, setMinSearchChars],
minColumnResizeWidth: [minColumnResizeWidth, setMinColumnWidth],
selectAllMode: [selectAllMode, setSelectAllMode],
+ searchByColumn: [searchByColumn, setSearchByColumn],
};
useEffect(() => {
@@ -114,6 +116,7 @@ const MyAwesomeTable = () => {
minSearchChars={minSearchChars}
minColumnResizeWidth={minColumnResizeWidth}
selectAllMode={selectAllMode}
+ searchByColumn={searchByColumn}
/>
diff --git a/src/components/CellContainer.jsx b/src/components/CellContainer.jsx
index 9da6f85..1f4c11e 100644
--- a/src/components/CellContainer.jsx
+++ b/src/components/CellContainer.jsx
@@ -17,6 +17,7 @@ const CellContainer = ({
id,
config: {
highlightSearch,
+ searchByColumn,
tableHasSelection,
additionalProps: { cellContainer: additionalProps = {} },
},
@@ -67,6 +68,9 @@ const CellContainer = ({
const getValue = () => {
let value;
+ const searchToHighlight = searchByColumn
+ ? column.searchText
+ : searchText;
switch (column.id) {
case "checkbox":
@@ -89,7 +93,7 @@ const CellContainer = ({
highlightSearch &&
valuePassesSearch(value, column)
)
- return getHighlightedText(value, searchText);
+ return getHighlightedText(value, searchToHighlight);
}
return value;
diff --git a/src/components/HeaderCell.jsx b/src/components/HeaderCell.jsx
index 23ee6d8..cf3626a 100644
--- a/src/components/HeaderCell.jsx
+++ b/src/components/HeaderCell.jsx
@@ -4,7 +4,10 @@ const HeaderCell = ({ column, tableManager }) => {
const {
config: {
additionalProps: { headerCell: additionalProps = {} },
+ components: { SearchColumn },
+ searchByColumn,
},
+ searchApi: { setColumnSearchText },
} = tableManager;
let classNames = (
@@ -12,13 +15,25 @@ const HeaderCell = ({ column, tableManager }) => {
).trim();
return (
-
- {column.label}
-
+
+
+ {column.label}
+
+ {searchByColumn && column.searchable && (
+
+ setColumnSearchText(column.field, value)
+ }
+ />
+ )}
+
);
};
diff --git a/src/components/HeaderCellContainer.jsx b/src/components/HeaderCellContainer.jsx
index b51d92b..032b279 100644
--- a/src/components/HeaderCellContainer.jsx
+++ b/src/components/HeaderCellContainer.jsx
@@ -21,11 +21,13 @@ const HeaderCellContainer = ({ index, column, tableManager }) => {
let {
config: {
isHeaderSticky,
+ searchByColumn,
components: { DragHandle },
additionalProps: { headerCellContainer: additionalProps = {} },
icons: {
sortAscending: sortAscendingIcon,
sortDescending: sortDescendingIcon,
+ sortUnsorted: sortUnsortedIcon,
},
},
sortApi: { sort, toggleSort },
@@ -79,7 +81,9 @@ const HeaderCellContainer = ({ index, column, tableManager }) => {
isPinnedRight
? " rgt-cell-header-pinned rgt-cell-header-pinned-right"
: ""
- } ${column.className}`.trim();
+ }${searchByColumn ? " rgt-cell-header-with-search" : ""} ${
+ column.className
+ }`.trim();
}
return (
@@ -144,7 +148,11 @@ const HeaderCellContainer = ({ index, column, tableManager }) => {
...selectionProps,
})
: column.headerCellRenderer(headerCellProps)}
- {sort.colId !== column.id ? null : sort.isAsc ? (
+ {!column.sortable ? null : sort.colId !== column.id ? (
+
+ {sortUnsortedIcon}
+
+ ) : sort.isAsc ? (
{sortAscendingIcon}
diff --git a/src/components/SearchColumn.jsx b/src/components/SearchColumn.jsx
new file mode 100644
index 0000000..a1bbbd2
--- /dev/null
+++ b/src/components/SearchColumn.jsx
@@ -0,0 +1,23 @@
+import React from "react";
+
+const SearchColumn = ({ tableManager, name, value, onChange }) => {
+ const {
+ config: {
+ texts: { search: searchText },
+ },
+ } = tableManager;
+
+ return (
+ onChange(event.target.value)}
+ onClick={(event) => event.stopPropagation()}
+ placeholder={searchText}
+ className="rgt-search-column-input"
+ />
+ );
+};
+
+export default SearchColumn;
diff --git a/src/components/index.js b/src/components/index.js
index a5f6254..7abbe47 100644
--- a/src/components/index.js
+++ b/src/components/index.js
@@ -14,6 +14,7 @@ import NoResults from "./NoResults";
import PopoverButton from "./PopoverButton";
import Row from "./Row";
import Search from "./Search";
+import SearchColumn from "./SearchColumn";
import Information from "./Information";
import PageSize from "./PageSize";
import Pagination from "./Pagination";
@@ -35,6 +36,7 @@ export {
PopoverButton,
Row,
Search,
+ SearchColumn,
Information,
PageSize,
Pagination,
diff --git a/src/defaults/icons.js b/src/defaults/icons.js
index c893025..e7c93b9 100644
--- a/src/defaults/icons.js
+++ b/src/defaults/icons.js
@@ -68,6 +68,7 @@ const MENU_ICON = (
const SORT_ASCENDING_ICON = ↑;
const SORT_DESCENDING_ICON = ↓;
+const SORT_UNSORTED_ICON = ⇅;
const SEARCH_ICON = ⚲;
@@ -77,5 +78,6 @@ export default {
columnVisibility: MENU_ICON,
sortAscending: SORT_ASCENDING_ICON,
sortDescending: SORT_DESCENDING_ICON,
+ sortUnsorted: SORT_UNSORTED_ICON,
search: SEARCH_ICON,
};
diff --git a/src/hooks/useColumns.jsx b/src/hooks/useColumns.jsx
index d05743c..afd7a2d 100644
--- a/src/hooks/useColumns.jsx
+++ b/src/hooks/useColumns.jsx
@@ -58,6 +58,7 @@ const useColumns = (props, tableManager) => {
editable: true,
sortable: true,
resizable: true,
+ searchText: "",
search: ({ value, searchText }) =>
value
.toString()
diff --git a/src/hooks/useSearch.jsx b/src/hooks/useSearch.jsx
index 82ad372..0931e1f 100644
--- a/src/hooks/useSearch.jsx
+++ b/src/hooks/useSearch.jsx
@@ -2,8 +2,8 @@ import { useState, useCallback, useRef } from "react";
const useSearch = (props, tableManager) => {
const {
- config: { minSearchChars },
- columnsApi: { columns },
+ config: { minSearchChars, searchByColumn },
+ columnsApi: { columns, setColumns },
} = tableManager;
const searchApi = useRef({}).current;
@@ -20,35 +20,65 @@ const useSearch = (props, tableManager) => {
props.onSearchTextChange?.(searchText, tableManager);
};
+ searchApi.setColumnSearchText = (columnField, searchText) => {
+ const columnsClone = [...columns];
+ const columnIndex = columnsClone.findIndex(
+ (col) => col.field === columnField
+ );
+
+ if (columnIndex !== -1) {
+ columnsClone[columnIndex].searchText = searchText;
+ setColumns(columnsClone);
+
+ props.onColumnSearchTextChange?.(
+ searchText,
+ columnField,
+ tableManager
+ );
+ }
+ };
+
searchApi.valuePassesSearch = (value, column) => {
if (!value) return false;
if (!column?.searchable) return false;
- if (searchApi.searchText.length < minSearchChars) return false;
+ if (!searchByColumn && searchApi.searchText.length < minSearchChars) {
+ return false;
+ } else if (
+ searchByColumn &&
+ column.searchText.length < minSearchChars
+ ) {
+ return false;
+ }
return column.search({
value: value.toString(),
- searchText: searchApi.searchText,
+ searchText: searchByColumn
+ ? column.searchText
+ : searchApi.searchText,
});
};
searchApi.searchRows = useCallback(
(rows) => {
- var cols = columns.reduce((cols, coldef) => {
- cols[coldef.field] = coldef;
- return cols;
- }, {});
+ const searchValue = ({ searchText, cellValue, column }) => {
+ const value = column.getValue({
+ value: cellValue,
+ column,
+ });
+ return column.search({
+ value: value?.toString() || "",
+ searchText,
+ });
+ };
if (searchApi.searchText.length >= minSearchChars) {
rows = rows.filter((item) =>
- Object.keys(item).some((key) => {
- if (cols[key] && cols[key].searchable) {
- const value = cols[key].getValue({
- value: item[key],
- column: cols[key],
- });
- return cols[key].search({
- value: value?.toString() || "",
+ columns.some((column) => {
+ if (column.searchable) {
+ return searchValue({
searchText: searchApi.searchText,
+ cellValue: item[column.field],
+ column,
});
}
return false;
@@ -56,9 +86,26 @@ const useSearch = (props, tableManager) => {
);
}
+ if (searchByColumn) {
+ columns.forEach((column) => {
+ if (
+ column.searchable &&
+ column.searchText.length >= minSearchChars
+ ) {
+ rows = rows.filter((item) => {
+ return searchValue({
+ searchText: column.searchText,
+ cellValue: item[column.field],
+ column,
+ });
+ });
+ }
+ });
+ }
+
return rows;
},
- [searchApi.searchText, columns, minSearchChars]
+ [searchApi.searchText, searchByColumn, columns, minSearchChars]
);
return searchApi;
diff --git a/src/hooks/useTableManager.jsx b/src/hooks/useTableManager.jsx
index abba823..5c583bd 100644
--- a/src/hooks/useTableManager.jsx
+++ b/src/hooks/useTableManager.jsx
@@ -51,6 +51,7 @@ const useTableManager = (props) => {
isPaginated: props.isPaginated,
enableColumnsReorder: props.enableColumnsReorder,
highlightSearch: props.highlightSearch,
+ searchByColumn: props.searchByColumn,
showSearch: props.showSearch,
showRowsInformation: props.showRowsInformation,
showColumnVisibilityManager: props.showColumnVisibilityManager,
diff --git a/src/index.css b/src/index.css
index 0d5bb73..5afa14f 100644
--- a/src/index.css
+++ b/src/index.css
@@ -103,6 +103,23 @@
border-bottom: var(--rgt-border);
}
+.rgt-cell-header-with-search {
+ display: flex;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ width: 100%;
+ z-index: 1;
+ min-height: 68px;
+ max-height: 68px;
+ border-bottom: var(--rgt-border);
+}
+
+.rgt-cell-header-with-search .rgt-cell-header-content-container {
+ gap: 5px;
+}
+
.rgt-cell-header-virtual-col {
border-bottom: var(--rgt-border);
background: var(--rgt-background-color);
@@ -129,6 +146,13 @@
justify-content: center;
}
+.rgt-cell-header-content-container {
+ display: inline-flex;
+ flex-direction: column;
+ flex: 1;
+ overflow: hidden;
+}
+
.rgt-placeholder-cell {
position: relative;
border-radius: 2px;
@@ -269,6 +293,13 @@
font-size: 16px;
margin-left: 5px;
display: inline-flex;
+ justify-content: center;
+ width: 12px;
+ line-height: 18px;
+}
+
+.rgt-sort-icon-unsorted {
+ font-size: 15px;
}
.rgt-container-overlay {
@@ -431,6 +462,12 @@
padding: 0;
}
+.rgt-search-column-input {
+ width: 100%;
+ outline: none;
+ border: 1px solid;
+}
+
.rgt-cell-editor-inner {
position: relative;
height: 30px;
diff --git a/src/index.js b/src/index.js
index 54737a3..ebced9a 100644
--- a/src/index.js
+++ b/src/index.js
@@ -128,6 +128,7 @@ GridTable.defaultProps = {
pageSizes: [20, 50, 100],
isHeaderSticky: true,
highlightSearch: true,
+ searchByColumn: false,
minSearchChars: 2,
isPaginated: true,
isVirtualScroll: true,
@@ -162,6 +163,7 @@ GridTable.propTypes = {
minColumnResizeWidth: PropTypes.number,
highlightSearch: PropTypes.bool,
showSearch: PropTypes.bool,
+ searchByColumn: PropTypes.bool,
showRowsInformation: PropTypes.bool,
showColumnVisibilityManager: PropTypes.bool,
minSearchChars: PropTypes.number,
@@ -177,6 +179,7 @@ GridTable.propTypes = {
selectAllMode: PropTypes.string,
// events
onColumnsChange: PropTypes.func,
+ onColumnSearchTextChange: PropTypes.func,
onSearchTextChange: PropTypes.func,
onSelectedRowsChange: PropTypes.func,
onSortChange: PropTypes.func,