diff --git a/package.json b/package.json index afe03c0a..d6d7c34e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ka-table", - "version": "8.11.0", + "version": "8.12.0", "license": "MIT", "repository": "github:komarovalexander/ka-table", "homepage": "https://komarovalexander.github.io/ka-table/#/overview", diff --git a/src/Demos/AddRowDemo/AddRowDemo.tsx b/src/Demos/AddRowDemo/AddRowDemo.tsx index a04cad86..78f1805a 100644 --- a/src/Demos/AddRowDemo/AddRowDemo.tsx +++ b/src/Demos/AddRowDemo/AddRowDemo.tsx @@ -1,10 +1,8 @@ import './AddRowDemo.scss'; -import React from 'react'; +import { DataType, Table, useTableInstance } from '../../lib'; -import { DataType, Table } from '../../lib'; -import { hideNewRow, saveNewRow, showNewRow } from '../../lib/actionCreators'; -import { ICellEditorProps, IHeadCellProps } from '../../lib/props'; +import React from 'react'; const dataArray = Array(7).fill(undefined).map( (_, index) => ({ @@ -22,29 +20,27 @@ const generateNewId = () => { return maxValue; }; -const AddButton: React.FC = ({ - dispatch, -}) => { +const AddButton = () => { + const table = useTableInstance(); return (
Add New Row dispatch(showNewRow())} + onClick={() => table.showNewRow()} />
); }; -const SaveButton: React.FC = ({ - dispatch -}) => { +const SaveButton = () => { + const table = useTableInstance(); const saveNewData = () => { const rowKeyValue = generateNewId(); - dispatch(saveNewRow(rowKeyValue, { + table.saveNewRow(rowKeyValue, { validate: true - })); + }); }; return (
@@ -60,7 +56,7 @@ const SaveButton: React.FC = ({ className='close-cell-button' alt='Cancel' title='Cancel' - onClick={() => dispatch(hideNewRow())} + onClick={() => table.hideNewRow()} />
); @@ -95,14 +91,14 @@ const AddRowDemo: React.FC = () => { cellEditor: { content: (props) => { if (props.column.key === 'addColumn'){ - return ; + return ; } } }, headCell: { content: (props) => { if (props.column.key === 'addColumn'){ - return ; + return ; } } } diff --git a/src/Demos/AlertCellDemo/AlertCellDemo.tsx b/src/Demos/AlertCellDemo/AlertCellDemo.tsx index 8f9ffa65..051e147e 100644 --- a/src/Demos/AlertCellDemo/AlertCellDemo.tsx +++ b/src/Demos/AlertCellDemo/AlertCellDemo.tsx @@ -16,9 +16,9 @@ const dataArray = Array(10).fill(undefined).map( ); -const AlertCell: React.FC = ({ +const AlertCell = ({ rowData, -}) => { +}: ICellTextProps) => { return ( = ({ ); }; -const AlertCellDemo: React.FC = () => { +const AlertCellDemo = () => { return ( ({ @@ -17,54 +16,7 @@ const dataArray = Array(119).fill(undefined).map( }), ); -const bootstrapChildComponents: ChildComponents = { - table: { - elementAttributes: () => ({ - className: 'table table-striped table-hover table-bordered table-primary' - }) - }, - tableHead: { - elementAttributes: () => ({ - className: 'thead-dark' - }) - }, - noDataRow: { - content: () => 'No Data Found' - }, - filterRowCell: { - content: (props) => { - const getEditor = () => { - switch (props.column.key){ - case 'column1': return ; - case 'column2': return <>; - case 'column3': return ; - case 'column4': return ; - } - } - return ( -
{getEditor()}
- ) - } - }, - cellEditorInput: { - elementAttributes: ({column}) => ({ - className: column.dataType === DataType.Boolean ? 'form-check-input' : undefined - }), - }, - pagingIndex: { - elementAttributes: ({ isActive }) => ({ - className: `page-item ${(isActive ? 'active' : '')}` - }), - content: ({ text, isActive }) =>
{text}
- }, - pagingPages: { - elementAttributes: () => ({ - className: 'pagination' - }), - } -}; - -const BootstrapDemo: React.FC = () => { +const BootstrapDemo = () => { return (
{ rowKeyField={'id'} sortingMode={SortingMode.Single} filteringMode={FilteringMode.FilterRow} - childComponents={bootstrapChildComponents} + + // bootstrap overrides for child components + childComponents={{ + table: { + elementAttributes: () => ({ + className: 'table table-striped table-hover table-bordered table-primary' + }) + }, + tableHead: { + elementAttributes: () => ({ + className: 'thead-dark' + }) + }, + noDataRow: { + content: () => 'No Data Found' + }, + filterRowCell: { + content: (props) => { + const getEditor = () => { + switch (props.column.key){ + case 'column1': return ; + case 'column2': return <>; + case 'column3': return ; + case 'column4': return ; + } + } + return ( +
{getEditor()}
+ ) + } + }, + cellEditorInput: { + elementAttributes: ({column}) => ({ + className: column.dataType === DataType.Boolean ? 'form-check-input' : undefined + }), + }, + pagingIndex: { + elementAttributes: ({ isActive }) => ({ + className: `page-item ${(isActive ? 'active' : '')}` + }), + content: ({ text, isActive }) =>
{text}
+ }, + pagingPages: { + elementAttributes: () => ({ + className: 'pagination' + }), + } + }} /> ); diff --git a/src/Demos/CustomAttributesDemo/CustomAttributesDemo.tsx b/src/Demos/CustomAttributesDemo/CustomAttributesDemo.tsx index e1097b4b..004fb571 100644 --- a/src/Demos/CustomAttributesDemo/CustomAttributesDemo.tsx +++ b/src/Demos/CustomAttributesDemo/CustomAttributesDemo.tsx @@ -1,9 +1,9 @@ -import React from 'react'; - import { DataType, Table } from '../../lib'; + +import React from 'react'; import { SortingMode } from '../../lib/enums'; -const dataArray: any[] = [ +const dataArray = [ { id: 1, name: 'Mike Wazowski', score: 80, passed: true, tryDate: new Date(2021, 10, 9) }, { id: 2, name: 'Billi Bob', score: 55, passed: false, tryDate: new Date(2021, 10, 8) }, { id: 3, name: 'Tom Williams', score: 45, passed: false, tryDate: new Date(2019, 11, 8) }, @@ -24,7 +24,7 @@ const CustomAttributesDemo: React.FC = () => { { key: 'passed', title: 'Passed', dataType: DataType.Boolean }, { dataType: DataType.Date, key: 'tryDate', title: 'Date', filterRowOperator: '<' }, ]} - format= {({ column, value }) => { + format= {({ column, value, rowData }) => { if (column.dataType === DataType.Date){ return value && value.toLocaleDateString('en', {month: '2-digit', day: '2-digit', year: 'numeric' }); } diff --git a/src/Demos/CustomCellDemo/CustomCellDemo.tsx b/src/Demos/CustomCellDemo/CustomCellDemo.tsx index 6def4dd2..dcfc417e 100644 --- a/src/Demos/CustomCellDemo/CustomCellDemo.tsx +++ b/src/Demos/CustomCellDemo/CustomCellDemo.tsx @@ -7,11 +7,11 @@ import { ICellTextProps } from '../../lib/props'; import React from 'react'; import dataArray from './data'; -const CustomCell: React.FC = ({ +const CustomCell = ({ column, rowKeyValue, value, -}) => { +}: ICellTextProps) => { const table = useTableInstance(); return (
{ @@ -22,7 +22,7 @@ const CustomCell: React.FC = ({ ); }; -const CustomCellDemo: React.FC = () => { +const CustomCellDemo = () => { return (
= ({rowData, columns}) => { - return ( - - ); -}; - -const CustomDataRowDemo: React.FC = () => { +const CustomDataRowDemo = () => { return (
-
- {rowData.name}: {rowData.score} ({rowData.passed ? 'Passed' : 'Failed'}) -
-
{ sortingMode={SortingMode.Single} childComponents={{ dataRow: { - content: (props) => , + content: ({rowData, columns}) => { + return ( + + ); + } } }} /> diff --git a/src/Demos/CustomEditorDemo/CustomEditorDemo.tsx b/src/Demos/CustomEditorDemo/CustomEditorDemo.tsx index d5017c35..d5136a77 100644 --- a/src/Demos/CustomEditorDemo/CustomEditorDemo.tsx +++ b/src/Demos/CustomEditorDemo/CustomEditorDemo.tsx @@ -1,9 +1,8 @@ import './CustomEditorDemo.scss'; +import { DataType, Table, useTableInstance } from '../../lib'; import React, { useState } from 'react'; -import { DataType, Table } from '../../lib'; -import { closeEditor, updateCellValue } from '../../lib/actionCreators'; import { EditingMode } from '../../lib/enums'; import { ICellEditorProps } from '../../lib/props'; @@ -16,11 +15,12 @@ const dataArray: any[] = [ { id: 6, name: 'Sunny Fox', score: 33, passed: false, nextTry: new Date(2019, 10, 9, 10) }, ]; -const CustomEditor: React.FC = ({ - column, rowKeyValue, dispatch, value, -}) => { +const CustomEditor = ({ + column, rowKeyValue, value, +}: ICellEditorProps) => { + const table = useTableInstance(); const close = () => { - dispatch(closeEditor(rowKeyValue, column.key)); + table.closeEditor(rowKeyValue, column.key); }; const [editorValue, setValue] = useState(value); return ( @@ -32,7 +32,7 @@ const CustomEditor: React.FC = ({ onChange={(event) => setValue(event.currentTarget.value)}/> @@ -40,12 +40,10 @@ const CustomEditor: React.FC = ({ ); }; -const CustomLookupEditor: React.FC = ({ +const CustomLookupEditor = ({ column, dispatch, rowKeyValue, value, -}) => { - const close = () => { - dispatch(closeEditor(rowKeyValue, column.key)); - }; +}: ICellEditorProps) => { + const table = useTableInstance(); const [editorValue, setValue] = useState(value); return (
@@ -54,8 +52,8 @@ const CustomLookupEditor: React.FC = ({ autoFocus={true} defaultValue={editorValue} onBlur={() => { - dispatch(updateCellValue(rowKeyValue, column.key, editorValue)); - close(); + table.updateCellValue(rowKeyValue, column.key, editorValue); + table.closeEditor(rowKeyValue, column.key); }} onChange={(event) => { setValue(event.currentTarget.value === 'true'); diff --git a/src/Demos/CustomHeaderCellDemo/CustomHeaderCellDemo.tsx b/src/Demos/CustomHeaderCellDemo/CustomHeaderCellDemo.tsx index 21c07915..2497e16d 100644 --- a/src/Demos/CustomHeaderCellDemo/CustomHeaderCellDemo.tsx +++ b/src/Demos/CustomHeaderCellDemo/CustomHeaderCellDemo.tsx @@ -1,7 +1,6 @@ +import { IHeadCellProps } from '../../lib/props'; import React from 'react'; - import { Table } from '../../lib'; -import { IHeadCellProps } from '../../lib/props'; const dataArray = Array(7).fill(undefined).map( (_, index) => ({ @@ -11,9 +10,9 @@ const dataArray = Array(7).fill(undefined).map( }), ); -const HeadCell: React.FC = ({ +const HeadCell = ({ column: { title }, -}) => { +}: IHeadCellProps) => { return (
{title} - (Custom) @@ -21,7 +20,7 @@ const HeadCell: React.FC = ({ ); }; -const CustomHeaderCellDemo: React.FC = () => { +const CustomHeaderCellDemo = () => { return (
+
+ {rowData.name}: {rowData.score} ({rowData.passed ? 'Passed' : 'Failed'}) +
+
({ @@ -16,7 +16,7 @@ const dataArray = Array(180).fill(undefined).map( }), ); -const PageSizeSelector: React.FC = ({ pageSize, pageSizes, dispatch }) => ( +const PageSizeSelector = ({ pageSize, pageSizes, dispatch }: IPagingProps) => ( <> Page Size: = ({ pageIndex, pagesCount, dispatch ) -const CustomPagingDemo: React.FC = () => { +const CustomPagingDemo = () => { return (
({ @@ -16,9 +16,9 @@ const dataArray = Array(10).fill(undefined).map( }), ); -const DeleteRow: React.FC = ({ +const DeleteRow = ({ dispatch, rowKeyValue, -}) => { +}: ICellTextProps) => { return ( = ({ ); }; -const DeleteRowDemo: React.FC = () => { +const DeleteRowDemo = () => { return (
= ({ cases }) => { diff --git a/src/Demos/DetailsRowDemo/DetailsRowDemo.tsx b/src/Demos/DetailsRowDemo/DetailsRowDemo.tsx index 7b0a02b9..35595027 100644 --- a/src/Demos/DetailsRowDemo/DetailsRowDemo.tsx +++ b/src/Demos/DetailsRowDemo/DetailsRowDemo.tsx @@ -13,11 +13,10 @@ const dataArray = Array(10).fill(undefined).map( }), ); -const DetailsButton: React.FC = ({ - dispatch, +const DetailsButton = ({ rowKeyValue, isDetailsRowShown, -}) => { +}: ICellTextProps) => { const table = useTableInstance(); return (
{ - const [data, setData] = useState(dataArray); + const [data, setData] = useState(dataArray); const table = useTable({ onDispatch: (action, tableProps) => { setData(tableProps.data); diff --git a/src/Demos/EventsDemo/EventsDemo.tsx b/src/Demos/EventsDemo/EventsDemo.tsx index 1866a5dd..86fcb454 100644 --- a/src/Demos/EventsDemo/EventsDemo.tsx +++ b/src/Demos/EventsDemo/EventsDemo.tsx @@ -1,9 +1,9 @@ import './EventsDemo.scss'; +import { DataType, EditingMode, FilteringMode, SortingMode } from '../../lib/enums'; import React, { useState } from 'react'; - import { Table, useTable } from '../../lib'; -import { DataType, EditingMode, FilteringMode, SortingMode } from '../../lib/enums'; + import { EventsLog } from './EventsLog'; const dataArray = Array(20).fill(undefined).map( @@ -17,8 +17,8 @@ const dataArray = Array(20).fill(undefined).map( ); -const EventsDemo: React.FC = () => { - const [events, changeEvents] = useState([] as any []); +const EventsDemo = () => { + const [events, changeEvents] = useState([]); const table = useTable({ onDispatch: (action) => { changeEvents((prevValue) => ([{ type: action.type, data: action, date: new Date(), showData: false }, ...prevValue])); diff --git a/src/Demos/ExportDemo/ExportDemo.tsx b/src/Demos/ExportDemo/ExportDemo.tsx index 1586b001..4bddf581 100644 --- a/src/Demos/ExportDemo/ExportDemo.tsx +++ b/src/Demos/ExportDemo/ExportDemo.tsx @@ -13,7 +13,7 @@ const dataArray = Array(7).fill(undefined).map( }), ); -const ExportDemo: React.FC = () => { +const ExportDemo = () => { const data = dataArray; const columns = [ { key: 'column1', title: 'Column 1', dataType: DataType.String }, diff --git a/src/Demos/FilterExtendedDemo/FilterExtendedDemo.tsx b/src/Demos/FilterExtendedDemo/FilterExtendedDemo.tsx index a4bf3226..cc8fc339 100644 --- a/src/Demos/FilterExtendedDemo/FilterExtendedDemo.tsx +++ b/src/Demos/FilterExtendedDemo/FilterExtendedDemo.tsx @@ -6,7 +6,7 @@ import FilterControl from 'react-filter-control'; import { IFilterControlFilterValue } from 'react-filter-control/interfaces'; import { filterData } from './filterData'; -const dataArray: any[] = [ +const dataArray = [ { id: 1, name: 'Mike Wazowski', score: 80, passed: true }, { id: 2, name: 'Billi Bob', score: 55, passed: false }, { id: 3, name: 'Tom Parker', score: 45, passed: false }, diff --git a/src/Demos/FilterRowCustomEditorDemo/FilterRowCustomEditorDemo.tsx b/src/Demos/FilterRowCustomEditorDemo/FilterRowCustomEditorDemo.tsx index 69cb3c7b..f0ae51ae 100644 --- a/src/Demos/FilterRowCustomEditorDemo/FilterRowCustomEditorDemo.tsx +++ b/src/Demos/FilterRowCustomEditorDemo/FilterRowCustomEditorDemo.tsx @@ -1,14 +1,14 @@ -import React from 'react'; - import { DataType, Table } from '../../lib'; -import { updateFilterRowOperator, updateFilterRowValue } from '../../lib/actionCreators'; import { EditingMode, FilteringMode } from '../../lib/enums'; +import { updateFilterRowOperator, updateFilterRowValue } from '../../lib/actionCreators'; + import { Column } from '../../lib/models'; -import { IFilterRowEditorProps } from '../../lib/props'; import { DispatchFunc } from '../../lib/types'; +import { IFilterRowEditorProps } from '../../lib/props'; +import React from 'react'; import { kaDateUtils } from '../../lib/utils'; -const dataArray: any[] = [ +const dataArray = [ { id: 1, name: 'Mike Wazowski', score: 80, passed: true, nextTry: new Date(2021, 10, 9) }, { id: 2, name: 'Billi Bob', score: 55, passed: false, nextTry: new Date(2021, 12, 9) }, { id: 3, name: 'Tom Williams', score: 45, passed: false, nextTry: new Date(2021, 7, 9) }, @@ -18,9 +18,9 @@ const dataArray: any[] = [ { id: 7, name: 'Alex Brzowsky', score: 48, passed: false, nextTry: new Date(2021, 11, 11) }, ]; -const CustomLookupEditor: React.FC = ({ +const CustomLookupEditor = ({ column, dispatch, -}) => { +}: IFilterRowEditorProps) => { const toNullableBoolean = (value: any) => { switch (value) { case 'true': return true; @@ -44,9 +44,9 @@ const CustomLookupEditor: React.FC = ({ ); }; -const FilterOperators: React.FC<{ column: Column; dispatch: DispatchFunc }> = ({ +const FilterOperators = ({ column, dispatch, -}) => { +}: { column: Column; dispatch: DispatchFunc }) => { return (
{ +const FilterRowDemo = () => { return (
({ @@ -20,7 +20,7 @@ const dataArray = Array(30).fill(undefined).map( }, { id: index }), ); -const FixedColumnDemo: React.FC = () => { +const FixedColumnDemo = () => { return (
{ +const GetDataByPropsDemo = () => { const [printData, setPrintData] = useState(); const [searchText, setSearchText] = useState(); const table = useTable(); diff --git a/src/Demos/GroupedColumnsDemo/GroupedColumnsDemo.tsx b/src/Demos/GroupedColumnsDemo/GroupedColumnsDemo.tsx index 463b8016..85c5ceb1 100644 --- a/src/Demos/GroupedColumnsDemo/GroupedColumnsDemo.tsx +++ b/src/Demos/GroupedColumnsDemo/GroupedColumnsDemo.tsx @@ -1,10 +1,10 @@ import './GroupedColumnsDemo.scss'; -import React from 'react'; - import { DataType, Table } from '../../lib'; -import { SortingMode } from '../../lib/enums'; + import { Column } from '../../lib/models'; +import React from 'react'; +import { SortingMode } from '../../lib/enums'; const columns: Column[] = Array(15).fill(undefined).map( (_, index) => ({ @@ -22,7 +22,7 @@ const dataArray = Array(30).fill(undefined).map( }), { id: index }), ); -const GroupedColumnsDemo: React.FC = () => { +const GroupedColumnsDemo = () => { return (
{ +const GroupingCustomCellDemo = () => { return (
= ({ ); -const GroupingCustomRowDemo: React.FC = () => { +const GroupingCustomRowDemo = () => { return (
{ +const GroupingDemo = () => { return (
{ +const GroupingSummaryDemo = () => { return (
{ +const HeaderFilterDemo = () => { return (
{ return filterValues?.some(x => value?.some(v => v.id === x.id)); }, headerFilterListItems: ({ data }) => { - const departments: any[] | undefined = data?.reduce((acc, item) => [...acc, ...item.departments || []], []); + const departments = data?.reduce<{ name: string, id: number }[]>((acc, item) => [...acc, ...(item.departments || [])], []); const departmentsUniqueByKey = departments?.filter((item: any, index) => { return departments?.findIndex(i => i.id === item.id) === index; }); diff --git a/src/Demos/HoverRowDemo/HoverRowDemo.tsx b/src/Demos/HoverRowDemo/HoverRowDemo.tsx index 120e5bcc..8b4faa1e 100644 --- a/src/Demos/HoverRowDemo/HoverRowDemo.tsx +++ b/src/Demos/HoverRowDemo/HoverRowDemo.tsx @@ -1,36 +1,16 @@ import './HoverRowDemo.scss'; +import { DataType, Table } from '../../lib'; import React, { useState } from 'react'; -import { DataType, Table } from '../../lib'; -import { ChildComponents } from '../../lib/models'; import dataArray from './data'; const ROW_MOUSE_ENTER = 'ROW_MOUSE_ENTER'; const ROW_MOUSE_LEAVE = 'ROW_MOUSE_LEAVE'; -const childAttributes: ChildComponents = { - dataRow: { - elementAttributes: (props) => ({ - title: `${props.rowData.name} ${props.rowData.phoneNumber}`, - onMouseEnter: (event, extendedEvent) => { - const { - childProps: { - rowKeyValue, - }, - dispatch, - } = extendedEvent; - dispatch({ type: ROW_MOUSE_ENTER, rowKeyValue }); - }, - onMouseLeave: (event, { dispatch }) => { - dispatch({ type: ROW_MOUSE_LEAVE }); - }, - }), - }, -}; -const HoverRowDemo: React.FC = () => { - const [selectedItem] = useState(); +const HoverRowDemo = () => { + const [selectedItem] = useState(); return (
{ ]} data={dataArray} rowKeyField={'id'} - childComponents={childAttributes} + childComponents={{ + dataRow: { + elementAttributes: (props) => ({ + title: `${props.rowData.name} ${props.rowData.phoneNumber}`, + onMouseEnter: (event, extendedEvent) => { + const { + childProps: { + rowKeyValue, + }, + dispatch, + } = extendedEvent; + dispatch({ type: ROW_MOUSE_ENTER, rowKeyValue }); + }, + onMouseLeave: (event, { dispatch }) => { + dispatch({ type: ROW_MOUSE_LEAVE }); + }, + }), + } + }} /> { selectedItem && (
diff --git a/src/Demos/InfiniteScrollingDemo/InfiniteScrollingDemo.tsx b/src/Demos/InfiniteScrollingDemo/InfiniteScrollingDemo.tsx index 8a261fa2..79e19aa3 100644 --- a/src/Demos/InfiniteScrollingDemo/InfiniteScrollingDemo.tsx +++ b/src/Demos/InfiniteScrollingDemo/InfiniteScrollingDemo.tsx @@ -5,7 +5,7 @@ import serverEmulator from './serverEmulator'; const LOAD_MORE_DATA = 'LOAD_MORE_DATA'; -const InfiniteScrollingDemo: React.FC = () => { +const InfiniteScrollingDemo = () => { const [pageIndex, changePageIndex] = useState(0); const [data, changeData] = useState([]); diff --git a/src/Demos/InsertRowDemo/InsertRowDemo.tsx b/src/Demos/InsertRowDemo/InsertRowDemo.tsx index 6c7e7e5e..6eeedfa0 100644 --- a/src/Demos/InsertRowDemo/InsertRowDemo.tsx +++ b/src/Demos/InsertRowDemo/InsertRowDemo.tsx @@ -1,8 +1,8 @@ -import React from 'react'; - import { DataType, Table, useTable } from '../../lib'; import { EditingMode, InsertRowPosition } from '../../lib/enums'; +import React from 'react'; + const dataArray = Array(7).fill(undefined).map( (_, index) => ({ column1: `column:1 rowId:${index}`, @@ -18,7 +18,7 @@ const generateNewId = () => { return maxValue; }; -const InsertRowDemo: React.FC = () => { +const InsertRowDemo = () => { const table = useTable(); return ( diff --git a/src/Demos/KeyboardNavigationDemo/KeyboardNavigationDemo.tsx b/src/Demos/KeyboardNavigationDemo/KeyboardNavigationDemo.tsx index 4dccdcc8..38f2bcb1 100644 --- a/src/Demos/KeyboardNavigationDemo/KeyboardNavigationDemo.tsx +++ b/src/Demos/KeyboardNavigationDemo/KeyboardNavigationDemo.tsx @@ -1,10 +1,10 @@ import './KeyboardNavigation.scss'; -import React from 'react'; - import { DataType, Table, useTable } from '../../lib'; import { EditingMode, SortingMode } from '../../lib/enums'; +import React from 'react'; + const dataArray = Array(100).fill(undefined).map( (_, index) => ({ column1: `column:1 row:${index}`, @@ -15,7 +15,7 @@ const dataArray = Array(100).fill(undefined).map( }), ); -const KeyboardNavigationDemo: React.FC = () => { +const KeyboardNavigationDemo = () => { const table = useTable(); return (
@@ -62,6 +62,7 @@ const KeyboardNavigationDemo: React.FC = () => { case 13: table.openEditor(cell.rowKeyValue, cell.columnKey); table.setFocused({ cellEditorInput: cell }); + e.stopPropagation(); break; } }, diff --git a/src/Demos/LoadingDemo/LoadingDemo.tsx b/src/Demos/LoadingDemo/LoadingDemo.tsx index ed888b5f..915a15b8 100644 --- a/src/Demos/LoadingDemo/LoadingDemo.tsx +++ b/src/Demos/LoadingDemo/LoadingDemo.tsx @@ -13,7 +13,7 @@ const dataArray = Array(10) id: index, })); -const LoadingDemo: React.FC = () => { +const LoadingDemo = () => { const [loading, setLoading] = useState(true); return ( diff --git a/src/Demos/ManyColumnsDemo/ManyColumnsDemo.tsx b/src/Demos/ManyColumnsDemo/ManyColumnsDemo.tsx index e1555e40..3abdac84 100644 --- a/src/Demos/ManyColumnsDemo/ManyColumnsDemo.tsx +++ b/src/Demos/ManyColumnsDemo/ManyColumnsDemo.tsx @@ -1,8 +1,8 @@ -import React from 'react'; - import { DataType, Table } from '../../lib'; import { EditingMode, SortingMode } from '../../lib/enums'; + import { Column } from '../../lib/models'; +import React from 'react'; const columns: Column[] = Array(100).fill(undefined).map( (_, index) => ({ @@ -20,7 +20,7 @@ const dataArray = Array(30).fill(undefined).map( }, { id: index }), ); -const ManyColumnsDemo: React.FC = () => { +const ManyColumnsDemo = () => { return (
{ +const ManyRowsDemo = () => { return (
{ +const useDynamicRowsOptions = () => { const [renderedRowSizes] = useState({}); let estimatedItemSize = 40; - const addRowHeight = (rowData: any, height?: number) => { + const addRowHeight = (rowKeyValue: any, height?: number) => { if (height) { - renderedRowSizes[rowData[rowKeyField]] = height; + renderedRowSizes[rowKeyValue] = height; } }; const totalHeight = Object.keys(renderedRowSizes).reduce((sum, key) => sum + parseFloat(renderedRowSizes[key] || 0), 0); @@ -51,30 +36,36 @@ const useDynamicRowsOptions = ({ rowKeyField }: ITableProps) => { : estimatedItemSize; return { addRowHeight, - itemHeight: (rowData: any) => renderedRowSizes[rowData[rowKeyField]] || estimatedItemSize, + itemHeight: (rowKeyField: string) => (rowData: any) => renderedRowSizes[rowData[rowKeyField]] || estimatedItemSize, }; }; -const ManyRowsDynamicDemo: React.FC = () => { - const [tableProps, changeTableProps] = useState(tablePropsInit); - const { itemHeight, addRowHeight } = useDynamicRowsOptions(tableProps); - - const dispatch: DispatchFunc = (action) => { - changeTableProps((prevState: ITableProps) => kaReducer(prevState, action)); - }; - +const ManyRowsDynamicDemo = () => { + const { itemHeight, addRowHeight } = useDynamicRowsOptions(); + const table = useTable({ + customReducer: (nextState, action, prevState) => { + if (nextState.virtualScrolling) nextState.virtualScrolling.itemHeight = itemHeight('id'); + return nextState; + } + }); return (
({ - ref: (ref: any) => addRowHeight(rowData, ref?.offsetHeight), + elementAttributes: ({ rowKeyValue }) => ({ + ref: (ref: any) => addRowHeight(rowKeyValue, ref?.offsetHeight), }), }, tableWrapper: { diff --git a/src/Demos/ManyRowsGroupingDemo/ManyRowsGroupingDemo.tsx b/src/Demos/ManyRowsGroupingDemo/ManyRowsGroupingDemo.tsx index 26ae173e..e3d8e635 100644 --- a/src/Demos/ManyRowsGroupingDemo/ManyRowsGroupingDemo.tsx +++ b/src/Demos/ManyRowsGroupingDemo/ManyRowsGroupingDemo.tsx @@ -1,6 +1,6 @@ -import React from 'react'; - import { DataType, Table } from '../../lib'; + +import React from 'react'; import { SortingMode } from '../../lib/enums'; const dataArray = Array(10000).fill(undefined).map( @@ -13,7 +13,7 @@ const dataArray = Array(10000).fill(undefined).map( }), ); -const ManyRowsGroupingDemo: React.FC = () => { +const ManyRowsGroupingDemo = () => { return (
{ const DataRowContentMemo = React.memo((props: IDataRowProps) => , () => true); -const ManyRowsMemoDemo: React.FC = () => { +const ManyRowsMemoDemo = () => { return (
{ +const MaterialDemo = () => { const table = useTable(); return (
diff --git a/src/Demos/NullableCellDataDemo/NullableCellDataDemo.tsx b/src/Demos/NullableCellDataDemo/NullableCellDataDemo.tsx index 77f4a4c5..b05a8bd5 100644 --- a/src/Demos/NullableCellDataDemo/NullableCellDataDemo.tsx +++ b/src/Demos/NullableCellDataDemo/NullableCellDataDemo.tsx @@ -6,9 +6,9 @@ import { IFilterRowEditorProps } from '../../lib/props'; import dataArray from './data'; import { kaDateUtils } from '../../lib/utils'; -const CustomDateFilterEditor: React.FC = ({ +const CustomDateFilterEditor = ({ column, -}) => { +}: IFilterRowEditorProps) => { const fieldValue = column.filterRowValue; const value = fieldValue && kaDateUtils.getDateInputValue(fieldValue); const table = useTableInstance(); @@ -28,7 +28,7 @@ const CustomDateFilterEditor: React.FC = ({ ); }; -const NullableCellDataDemo: React.FC = () => { +const NullableCellDataDemo = () => { const [searchText, setSearchText] = useState('i'); return ( <> diff --git a/src/Demos/OverviewDemo/OverviewDemo.tsx b/src/Demos/OverviewDemo/OverviewDemo.tsx index f9016db8..89a9c5e0 100644 --- a/src/Demos/OverviewDemo/OverviewDemo.tsx +++ b/src/Demos/OverviewDemo/OverviewDemo.tsx @@ -1,7 +1,7 @@ -import React from 'react'; +import { DataType, EditingMode, SortingMode } from '../../lib/enums'; +import React from 'react'; import { Table } from '../../lib'; -import { DataType, EditingMode, SortingMode } from '../../lib/enums'; const dataArray = Array(10).fill(undefined).map( (_, index) => ({ @@ -14,7 +14,7 @@ const dataArray = Array(10).fill(undefined).map( ); -const OverviewDemo: React.FC = () => { +const OverviewDemo = () => { return (
({ @@ -13,7 +13,7 @@ const dataArray = Array(180).fill(undefined).map( }), ); -const PagingDemo: React.FC = () => { +const PagingDemo = () => { const table = useTable(); return ( diff --git a/src/Demos/PrintDemo/PrintDemo.tsx b/src/Demos/PrintDemo/PrintDemo.tsx index ac7269eb..7e7517a4 100644 --- a/src/Demos/PrintDemo/PrintDemo.tsx +++ b/src/Demos/PrintDemo/PrintDemo.tsx @@ -1,9 +1,9 @@ import './PrintDemo.scss'; +import { DataType, Table } from '../../lib'; import React, { useRef } from 'react'; -import ReactToPrint from 'react-to-print'; -import { DataType, Table } from '../../lib'; +import ReactToPrint from 'react-to-print'; const dataArray = Array(180).fill(undefined).map( (_, index) => ({ @@ -15,7 +15,7 @@ const dataArray = Array(180).fill(undefined).map( }), ); -const PrintDemo: React.FC = () => { +const PrintDemo = () => { const componentRef = useRef(); return ( diff --git a/src/Demos/ReduxDemo/ReduxDemo.tsx b/src/Demos/ReduxDemo/ReduxDemo.tsx index 5c083370..19be6728 100644 --- a/src/Demos/ReduxDemo/ReduxDemo.tsx +++ b/src/Demos/ReduxDemo/ReduxDemo.tsx @@ -1,9 +1,9 @@ -import React from 'react'; +import { DataType, EditingMode, SortingMode } from '../../lib/enums'; +import { ITableProps, Table, kaReducer } from '../../lib'; import { Provider, useDispatch, useSelector } from 'react-redux'; import { combineReducers, createStore } from 'redux'; -import { ITableProps, kaReducer, Table } from '../../lib'; -import { DataType, EditingMode, SortingMode } from '../../lib/enums'; +import React from 'react'; const dataArray = Array(30).fill(undefined).map( (_, index) => ({ @@ -47,7 +47,7 @@ const ReduxTableComponent = () => { ); }; -const ReduxDemo: React.FC = () => { +const ReduxDemo = () => { return ( diff --git a/src/Demos/ResponsiveDemo/ResponsiveDemo.tsx b/src/Demos/ResponsiveDemo/ResponsiveDemo.tsx index 99306a8e..e326f22d 100644 --- a/src/Demos/ResponsiveDemo/ResponsiveDemo.tsx +++ b/src/Demos/ResponsiveDemo/ResponsiveDemo.tsx @@ -1,10 +1,10 @@ // open TS Example or JS Example to see how to override styles import './ResponsiveDemo.scss'; -import React from 'react'; - import { DataType, Table } from '../../lib'; + import { EditingMode } from '../../lib/enums'; +import React from 'react'; const dataArray = Array(10).fill(undefined).map( (_, index) => ({ @@ -16,7 +16,7 @@ const dataArray = Array(10).fill(undefined).map( }), ); -const ResponsiveDemo: React.FC = () => { +const ResponsiveDemo = () => { return (
({ @@ -13,7 +13,7 @@ const dataArray = Array(10).fill(undefined).map( }), ); -const RowReorderingDemo: React.FC = () => { +const RowReorderingDemo = () => { return (
{ +const SearchDemo = () => { const [searchText, setSearchText] = useState('Billi Bob'); return ( <> diff --git a/src/Demos/SelectionDemo/SelectionDemo.tsx b/src/Demos/SelectionDemo/SelectionDemo.tsx index 45295da6..a8cbe94c 100644 --- a/src/Demos/SelectionDemo/SelectionDemo.tsx +++ b/src/Demos/SelectionDemo/SelectionDemo.tsx @@ -17,7 +17,7 @@ const dataArray = Array(64) id: index, })); -const SelectionCell: React.FC = ({ rowKeyValue, isSelectedRow, selectedRows }) => { +const SelectionCell = ({ rowKeyValue, isSelectedRow, selectedRows }: ICellTextProps) => { const table = useTableInstance(); return ( { ); }; -const SelectionDemo: React.FC = () => { +const SelectionDemo = () => { return (
{ +const SelectionSingleDemo = () => { const [selectedData, changeSelectedData] = useState(); const table = useTable({ onDispatch: (action, tableProps) => { diff --git a/src/Demos/SortingCustomLogicDemo/SortingCustomLogicDemo.tsx b/src/Demos/SortingCustomLogicDemo/SortingCustomLogicDemo.tsx index e543b2db..aac19929 100644 --- a/src/Demos/SortingCustomLogicDemo/SortingCustomLogicDemo.tsx +++ b/src/Demos/SortingCustomLogicDemo/SortingCustomLogicDemo.tsx @@ -3,7 +3,7 @@ import { SortDirection, SortingMode } from '../../lib/enums'; import React from 'react'; -const dataArray: any[] = [ +const dataArray = [ { id: 1, name: 'Mike Wazowski', score: 80, prevScores: [59, 65, 70], passed: true }, { id: 2, name: 'Billi Bob', score: 55, prevScores: [60, 43, 50], passed: false }, { id: 3, name: 'Tom Williams', score: 45, prevScores: [62, 61, 60], passed: false }, diff --git a/src/Demos/SortingDemo/SortingDemo.tsx b/src/Demos/SortingDemo/SortingDemo.tsx index 2f3fceb9..4be94117 100644 --- a/src/Demos/SortingDemo/SortingDemo.tsx +++ b/src/Demos/SortingDemo/SortingDemo.tsx @@ -1,9 +1,9 @@ -import React from 'react'; - import { DataType, Table } from '../../lib'; import { SortDirection, SortingMode } from '../../lib/enums'; -const dataArray: any[] = [ +import React from 'react'; + +const dataArray = [ { id: 1, name: 'Mike Wazowski', score: 80, passed: true, faculty: 'Economics', comment: 'Well done!' }, { id: 2, name: 'Billi Bob', score: 55, passed: false, faculty: 'Engineering', comment: 'almost did it, keep going' }, { id: 3, name: 'Tom Williams', score: 45, passed: false, faculty: 'Engineering', comment: 'you can do it better' }, @@ -12,7 +12,7 @@ const dataArray: any[] = [ { id: 6, name: 'Sunny Fox', score: 33, passed: false, faculty: 'Mathematics', comment: 'It was just a bad day :)' }, ]; -const SortingDemo: React.FC = () => { +const SortingDemo = () => { return (
{ +const SortingModesDemo = () => { const [tableProps, changeTableProps] = useState(tablePropsInit); const dispatch: DispatchFunc = (action) => { changeTableProps((prevState: ITableProps) => kaReducer(prevState, action)); diff --git a/src/Demos/StateStoringDemo/StateStoringDemo.tsx b/src/Demos/StateStoringDemo/StateStoringDemo.tsx index 09e68762..44169235 100644 --- a/src/Demos/StateStoringDemo/StateStoringDemo.tsx +++ b/src/Demos/StateStoringDemo/StateStoringDemo.tsx @@ -14,7 +14,7 @@ const initDataArray = [ const OPTION_KEY = 'state-storing-demo-table-option'; const savedOptions = {...JSON.parse(localStorage.getItem(OPTION_KEY) || '0')}; -const StateStoringDemo: React.FC = () => { +const StateStoringDemo = () => { const table = useTable({ onDispatch: (action, newProps) => { const { data, ...settingsWithoutData } = newProps; diff --git a/src/Demos/SummaryDemo/SummaryDemo.tsx b/src/Demos/SummaryDemo/SummaryDemo.tsx index d2e05c00..e770b62f 100644 --- a/src/Demos/SummaryDemo/SummaryDemo.tsx +++ b/src/Demos/SummaryDemo/SummaryDemo.tsx @@ -1,9 +1,9 @@ -import React from 'react'; - import { DataType, Table } from '../../lib'; + +import React from 'react'; import { SortingMode } from '../../lib/enums'; -const dataArray: any[] = [ +const dataArray = [ { id: 1, name: 'Mike Wazowski', score: 80, passed: true }, { id: 2, name: 'Billi Bob', score: 55, passed: false }, { id: 3, name: 'Tom Williams', score: 45, passed: false }, @@ -12,7 +12,7 @@ const dataArray: any[] = [ { id: 6, name: 'Sunny Fox', score: 33, passed: false }, ]; -const SummaryDemo: React.FC = () => { +const SummaryDemo = () => { return (
{ +const TabIndexDemo = () => { const kaFocusRef = useKaFocusRef({ columnKey: 'column2', rowKeyValue: 2 diff --git a/src/Demos/TreeModeDemo/TreeModeDemo.tsx b/src/Demos/TreeModeDemo/TreeModeDemo.tsx index 5cf5eca7..7e4d8467 100644 --- a/src/Demos/TreeModeDemo/TreeModeDemo.tsx +++ b/src/Demos/TreeModeDemo/TreeModeDemo.tsx @@ -18,7 +18,7 @@ const data = [ { treeGroupId: 7, id: 12, name: 'Squad C', productivity: 7 }, ]; -const TreeModeDemo: React.FC = () => { +const TreeModeDemo = () => { return (
{ +const ValidationDemo = () => { return ( <>
>; onDispatch: OnDispatchFunc; dispatch: DispatchFunc; + customReducer?: CustomReducerFunc; } export interface ITableProps { columnReordering?: boolean; columnResizing?: boolean; - columns: Column[]; + columns: Column[]; groupedColumns?: GroupedColumn[]; data?: TData[]; detailsRows?: any[]; editableCells?: EditableCell[]; editingMode?: EditingMode; - extendedFilter?: (data: any[]) => any[]; - extendedSort?: (data: any[], columns: Column[]) => any[]; - filter?: FilterFunc; + extendedFilter?: (data: TData[]) => TData[]; + extendedSort?: (data: TData[], columns: Column[]) => TData[]; + filter?: FilterFunc; filteringMode?: FilteringMode; focused?: Focused; - format?: FormatFunc; + format?: FormatFunc; groups?: Group[]; groupsExpanded?: any[][]; groupPanel?: GroupPanelSettings; @@ -50,14 +51,14 @@ export interface ITableProps { treeGroupsExpanded?: any[]; treeExpandButtonColumnKey?: string; rowReordering?: boolean; - search?: SearchFunc; + search?: SearchFunc; searchText?: string; selectedRows?: any[]; singleAction?: any; sort?: SortFunc; noData?: NoData, sortingMode?: SortingMode; - validation?: ValidationFunc; + validation?: ValidationFunc; virtualScrolling?: VirtualScrolling; width?: number | string; controlledPropsKeys?: ControlledPropsKeys; diff --git a/src/lib/Components/TableUncontrolled/TableUncontrolled.tsx b/src/lib/Components/TableUncontrolled/TableUncontrolled.tsx index 07702ba1..574a2d96 100644 --- a/src/lib/Components/TableUncontrolled/TableUncontrolled.tsx +++ b/src/lib/Components/TableUncontrolled/TableUncontrolled.tsx @@ -22,7 +22,8 @@ export const TableUncontrolled: React.FunctionComponent { changeTableProps((prevState: ITableProps) => { - const nextState = kaReducer(prevState, action); + const nextStateDefault = kaReducer(prevState, action); + const nextState = props.table?.customReducer ? (props.table.customReducer(nextStateDefault, action, prevState) ?? nextStateDefault) : nextStateDefault; setTimeout(() => { props.table?.onDispatch?.(action, nextState); }, 0); diff --git a/src/lib/Models/Column.ts b/src/lib/Models/Column.ts index cf097fc8..9b94a5b1 100644 --- a/src/lib/Models/Column.ts +++ b/src/lib/Models/Column.ts @@ -3,7 +3,7 @@ import { DataType, SortDirection } from '../enums'; import { Field } from '../types'; import { PopupPosition } from './PopupPosition'; -export class Column { +export class Column { public colGroup?: React.ColHTMLAttributes; public dataType?: DataType; public field?: Field; @@ -11,8 +11,8 @@ export class Column { public filterRowValue?: any; public headerFilterValues?: any[]; public headerFilterPopupPosition?: PopupPosition; - public headerFilterListItems?: (props: { data?: any[] }) => any[]; - public filter?: (value: any, filterValue: any, rowData?: any) => boolean; + public headerFilterListItems?: (props: { data?: TData[] }) => any[]; + public filter?: (value: any, filterValue: any, rowData?: TData) => boolean; public isHeaderFilterPopupShown?: boolean; public isEditable?: boolean; public isFilterable?: boolean; diff --git a/src/lib/hooks/UseTable.test.tsx b/src/lib/hooks/UseTable.test.tsx new file mode 100644 index 00000000..0dda6bd1 --- /dev/null +++ b/src/lib/hooks/UseTable.test.tsx @@ -0,0 +1,34 @@ +import * as React from 'react'; + +import { Table } from '..'; +import { act } from '@testing-library/react'; +import { createRoot } from 'react-dom/client'; +import { useTable } from './UseTable'; + +jest.useFakeTimers(); + +describe('UseTable', () => { + it('customReducer', () => { + const customReducer = jest.fn().mockImplementation((state, action) => { + if (action.type === 'CUSTOM_ACTION'){ + return {...state, data: [{id : 2}]} + } + }); + const table = useTable({ + customReducer + }); + + const div = document.createElement('div'); + const root = createRoot(div!); + act(() => { + root.render(
); + }); + act(() => { + table.dispatch({ type: 'CUSTOM_ACTION' }); + }); + jest.runAllTimers(); + expect(customReducer).toHaveBeenCalledTimes(2); + expect(customReducer.mock.calls[1]).toMatchSnapshot(); + expect(customReducer.mock.results[1]).toMatchSnapshot(); + }); +}); diff --git a/src/lib/hooks/UseTable.tsx b/src/lib/hooks/UseTable.tsx index 8ff50426..d854c9b1 100644 --- a/src/lib/hooks/UseTable.tsx +++ b/src/lib/hooks/UseTable.tsx @@ -1,14 +1,14 @@ import * as actionCreators from '../actionCreators'; +import { CustomReducerFunc, OnDispatchFunc } from '../types'; import { ITableInstance, ITableProps } from '../Components/Table/Table'; -import { OnDispatchFunc } from '../types'; - export const getTable = (options?: { changeProps?: React.Dispatch>; onDispatch?: OnDispatchFunc; + customReducer?: CustomReducerFunc; }): ITableInstance => { - const { changeProps, onDispatch } = options || {}; + const { changeProps, onDispatch, customReducer } = options || {}; const propsResult = {} as any; return { ...(Object.keys(actionCreators).reduce((acc, key) => { @@ -21,9 +21,11 @@ export const getTable = (options?: { changeProps: changeProps || (() => {}), dispatch: () => {}, onDispatch: onDispatch || ((() => {}) as OnDispatchFunc), + customReducer }; }; -export const useTable = (options?: { onDispatch?: OnDispatchFunc }): ITableInstance => { +export const useTable = (options?: { onDispatch?: OnDispatchFunc; + customReducer?: CustomReducerFunc; }): ITableInstance => { return getTable(options); }; diff --git a/src/lib/hooks/__snapshots__/UseTable.test.tsx.snap b/src/lib/hooks/__snapshots__/UseTable.test.tsx.snap new file mode 100644 index 00000000..fcade1e1 --- /dev/null +++ b/src/lib/hooks/__snapshots__/UseTable.test.tsx.snap @@ -0,0 +1,46 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`UseTable customReducer 1`] = ` +Array [ + Object { + "columns": Array [ + Object { + "key": "id", + }, + ], + "data": Array [], + "rowKeyField": "id", + }, + Object { + "type": "CUSTOM_ACTION", + }, + Object { + "columns": Array [ + Object { + "key": "id", + }, + ], + "data": Array [], + "rowKeyField": "id", + }, +] +`; + +exports[`UseTable customReducer 2`] = ` +Object { + "type": "return", + "value": Object { + "columns": Array [ + Object { + "key": "id", + }, + ], + "data": Array [ + Object { + "id": 2, + }, + ], + "rowKeyField": "id", + }, +} +`; diff --git a/src/lib/types.ts b/src/lib/types.ts index 0093f85a..fe8f4a5f 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -17,11 +17,12 @@ type ElementAttributes = React.AllHTMLAttributes; export type ChildAttributesItem = WithExtraParameters, T> & { ref?: any }; export type DispatchFunc = (action: any) => void; +export type CustomReducerFunc = (nextState: ITableProps, action: any, prevState: ITableProps) => ITableProps; export type OnDispatchFunc = (action: any, tableProps: ITableProps) => void; export type ControlledPropsKeys = (keyof ITableProps)[]; export type Field = string; -export type FormatFunc = (props: { value: any, column: Column, rowData?: any; }) => any; -export type FilterFunc = (props: { column: Column }) => ((value: any, filterRowValue: any, rowData?: any) => boolean) | void; +export type FormatFunc = (props: { value: any, column: Column, rowData?: TData; }) => any; +export type FilterFunc = (props: { column: Column }) => ((value: any, filterRowValue: any, rowData?: TData) => boolean) | void; export type SortFunc = (props: { column: Column }) => ((value1: any, value2: any) => 0 | 1 | -1) | void; -export type SearchFunc = (props: { searchText: string, rowData: any, column: Column }) => boolean; -export type ValidationFunc = (props: { value: any, rowData: any, column: Column }) => string | void; +export type SearchFunc = (props: { searchText: string, rowData: TData, column: Column }) => boolean | void; +export type ValidationFunc = (props: { value: any, rowData: TData, column: Column }) => string | void;