-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Rewrite table to functional component
- Loading branch information
Showing
14 changed files
with
652 additions
and
456 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import React from 'react' | ||
import { Table } from '@oacore/design' | ||
|
||
import TableRow from './row' | ||
import NoDataFoundRow from './no-data-found-row' | ||
|
||
const Body = React.memo( | ||
({ handleRowClick, handleDoubleRowClick, data, isClickable, columns }) => ( | ||
<Table.Body onClick={handleRowClick} onDoubleClick={handleDoubleRowClick}> | ||
{data === null && ( | ||
<Table.Row> | ||
<Table.Cell colSpan={1000}>Loading data</Table.Cell> | ||
</Table.Row> | ||
)} | ||
|
||
{data !== null && | ||
data.map((row, index) => { | ||
const props = { | ||
id: row.id, | ||
index, | ||
context: row, | ||
columns, | ||
isClickable, | ||
} | ||
|
||
return <TableRow key={row.id} {...props} /> | ||
})} | ||
{data !== null && data.length === 0 && <NoDataFoundRow />} | ||
</Table.Body> | ||
) | ||
) | ||
export default Body |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import React from 'react' | ||
|
||
import Pagination from './pagination' | ||
import styles from './styles.module.css' | ||
|
||
import { Button, Table } from 'design' | ||
|
||
const Footer = React.memo( | ||
({ | ||
children, | ||
isFirstPageLoaded, | ||
isLastPageLoaded, | ||
totalLength, | ||
fetchData, | ||
size, | ||
isLoading, | ||
}) => ( | ||
<Table.Footer className={styles.footer}> | ||
<Table.Row> | ||
<Table.Cell colSpan={1000}> | ||
<div className={styles.footerLeft}>{children}</div> | ||
<div className={styles.footerRight}> | ||
{isFirstPageLoaded && ( | ||
<Pagination | ||
size={size} | ||
total={totalLength} | ||
isAllLoaded={isLastPageLoaded} | ||
/> | ||
)} | ||
{!isLastPageLoaded && ( | ||
<Button | ||
disabled={isLoading} | ||
className={styles.loadNextPage} | ||
onClick={() => fetchData({ next: true })} | ||
> | ||
Show more | ||
</Button> | ||
)} | ||
</div> | ||
</Table.Cell> | ||
</Table.Row> | ||
</Table.Footer> | ||
) | ||
) | ||
|
||
export default Footer |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import React from 'react' | ||
import { Table } from '@oacore/design' | ||
|
||
const Header = React.memo( | ||
({ columns, handleColumnOrderChange, columnOrder }) => ( | ||
<Table.Head> | ||
<Table.Row> | ||
{columns.map( | ||
({ props: { id, className: columnClassName, display } }) => ( | ||
<Table.HeadCell | ||
key={id} | ||
order={columnOrder[id]} | ||
onClick={(event) => { | ||
event.preventDefault() | ||
handleColumnOrderChange(id) | ||
}} | ||
className={columnClassName} | ||
> | ||
{display} | ||
</Table.HeadCell> | ||
) | ||
)} | ||
</Table.Row> | ||
</Table.Head> | ||
) | ||
) | ||
|
||
export default Header |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
import { useCallback, useReducer } from 'react' | ||
|
||
const dataStateReducer = (state, action) => { | ||
switch (action.type) { | ||
case 'CHANGE_DATA_STATE': | ||
return { ...state, ...(action.value || {}) } | ||
default: | ||
throw new Error('Wrong action type for dataStateReducer') | ||
} | ||
} | ||
|
||
const useDynamicTableData = ({ pages, defaultSize = 100 }) => { | ||
const [dataState, changeDataState] = useReducer(dataStateReducer, { | ||
data: null, | ||
size: defaultSize, | ||
isLastPageLoaded: false, | ||
totalLength: null, | ||
}) | ||
|
||
const fetchData = useCallback( | ||
async ({ | ||
next = false, | ||
force = false, | ||
columnOrder, | ||
searchTerm, | ||
selectedOption: type, | ||
resetData = true, | ||
} = {}) => { | ||
let newSize = dataState.size | ||
|
||
if (next) newSize += 100 | ||
else newSize = defaultSize | ||
|
||
let newState = {} | ||
if (force) { | ||
pages.reset({ | ||
columnOrder, | ||
searchTerm, | ||
type, | ||
}) | ||
|
||
newState = { | ||
isLoading: true, | ||
isLastPageLoaded: false, | ||
} | ||
} | ||
|
||
newState.isLoading = true | ||
if (resetData) newState.data = null | ||
|
||
changeDataState({ | ||
type: 'CHANGE_DATA_STATE', | ||
value: newState, | ||
}) | ||
|
||
const newData = await pages.slice(0, newSize) | ||
|
||
changeDataState({ | ||
type: 'CHANGE_DATA_STATE', | ||
value: { | ||
isLoading: false, | ||
data: newData, | ||
size: newSize, | ||
isLastPageLoaded: pages.isLastPageLoaded, | ||
totalLength: pages.totalLength, | ||
}, | ||
}) | ||
}, | ||
[dataState.size] | ||
) | ||
return [dataState, fetchData] | ||
} | ||
|
||
export default useDynamicTableData |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import React, { useMemo } from 'react' | ||
|
||
import Column from '../column' | ||
import Sidebar from '../sidebar' | ||
import Action from '../action' | ||
|
||
const useTableConfig = (children) => | ||
// recompute value only if children changes. | ||
useMemo(() => { | ||
const childrenArray = React.Children.toArray(children) | ||
|
||
const columns = childrenArray.filter( | ||
(child) => | ||
child.type === Column || | ||
Object.prototype.isPrototypeOf.call(Column, child.type) | ||
) | ||
|
||
const columnOrder = Object.fromEntries( | ||
columns.map((column) => { | ||
const { id, order = null } = column.props | ||
return [id, order] | ||
}) | ||
) | ||
|
||
return { | ||
sidebar: childrenArray.find((item) => item.type === Sidebar), | ||
action: childrenArray.find((item) => item.type === Action), | ||
columnOrder, | ||
columns, | ||
} | ||
}, [children]) | ||
|
||
export default useTableConfig |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
import { useCallback, useReducer, useRef } from 'react' | ||
|
||
import useDebouncedEffect from 'utils/hooks/use-debounced-effect' | ||
|
||
const getNextOrder = (order) => { | ||
if (order === 'asc') return 'desc' | ||
if (order === 'desc') return 'asc' | ||
return 'asc' | ||
} | ||
|
||
const changeOrder = ({ id, columnOrder }) => { | ||
if (columnOrder[id] == null) return columnOrder | ||
|
||
return Object.fromEntries( | ||
Object.entries(columnOrder).map(([currId, currOrder]) => { | ||
if (currId === id) return [currId, getNextOrder(currOrder)] | ||
if (currOrder == null) return [currId, null] | ||
return [currId, 'any'] | ||
}) | ||
) | ||
} | ||
|
||
const tableStateReducer = (state, action) => { | ||
switch (action.type) { | ||
case 'CLOSE_SIDEBAR': | ||
return { ...state, expandedRowId: null } | ||
case 'CHANGE_SELECTED_OPTION': | ||
return { ...state, selectedOption: action.value } | ||
case 'SEARCH_CHANGED': | ||
return { ...state, searchTerm: action.value } | ||
case 'CHANGE_TABLE_STATE': | ||
return { ...state, ...(action.value || {}) } | ||
case 'CHANGE_COLUMN_ORDER': | ||
return { | ||
...state, | ||
columnOrder: changeOrder({ | ||
id: action.value, | ||
columnOrder: state.columnOrder, | ||
}), | ||
} | ||
default: | ||
throw new Error('Invalid action type for Table') | ||
} | ||
} | ||
|
||
const useTableState = ({ fetchDataProp, data, ...rest }) => { | ||
const tableRowClickTimeout = useRef(null) | ||
const [state, changeState] = useReducer(tableStateReducer, { | ||
searchTerm: '', | ||
expandedRowId: null, | ||
selectedOption: '', | ||
...rest, | ||
}) | ||
|
||
// Event handlers | ||
const handleSidebarClose = useCallback( | ||
() => changeState({ type: 'CLOSE_SIDEBAR' }), | ||
[] | ||
) | ||
|
||
const handleRowClick = useCallback( | ||
(event) => { | ||
if (event.detail === 1) { | ||
const clickedRow = event.target.closest('tr') | ||
tableRowClickTimeout.current = setTimeout(() => { | ||
// ignore copy event | ||
const selection = window.getSelection().toString() | ||
if (selection.length) return | ||
|
||
const rowId = clickedRow.dataset.id | ||
changeState({ | ||
type: 'CHANGE_TABLE_STATE', | ||
value: { | ||
expandedRowId: data?.find((e) => e.id === rowId), | ||
}, | ||
}) | ||
}, 200) | ||
} | ||
}, | ||
[data] | ||
) | ||
|
||
const handleDoubleRowClick = useCallback(() => { | ||
// Don't expand row on double click. Let user to copy value | ||
clearTimeout(tableRowClickTimeout.current) | ||
}, []) | ||
|
||
const handleSelectedOption = useCallback( | ||
(value) => | ||
changeState({ | ||
type: 'CHANGE_SELECTED_OPTION', | ||
value, | ||
}), | ||
[] | ||
) | ||
|
||
const handleColumnOrderChange = useCallback( | ||
(value) => | ||
changeState({ | ||
type: 'CHANGE_COLUMN_ORDER', | ||
value, | ||
}), | ||
[] | ||
) | ||
|
||
const handleSearchChange = useCallback( | ||
({ target: { value } }) => | ||
changeState({ | ||
type: 'SEARCH_CHANGED', | ||
value, | ||
}), | ||
[] | ||
) | ||
|
||
const { columnOrder, searchTerm, selectedOption } = state | ||
|
||
const fetchData = (arg) => | ||
fetchDataProp({ ...arg, columnOrder, searchTerm, selectedOption }) | ||
|
||
useDebouncedEffect(() => { | ||
fetchData({ force: true }) | ||
}, [searchTerm, selectedOption]) | ||
|
||
useDebouncedEffect(() => { | ||
fetchData({ force: true, resetData: false }) | ||
}, [columnOrder]) | ||
|
||
return [ | ||
state, | ||
{ | ||
handleSidebarClose, | ||
handleRowClick, | ||
handleDoubleRowClick, | ||
handleSelectedOption, | ||
handleColumnOrderChange, | ||
handleSearchChange, | ||
fetchData, | ||
}, | ||
] | ||
} | ||
|
||
export default useTableState |
Oops, something went wrong.