Skip to content

Instantly share code, notes, and snippets.

@ifirmawan
Created February 4, 2021 09:20
Show Gist options
  • Save ifirmawan/aebe163025b1858834aba2ef2f068331 to your computer and use it in GitHub Desktop.
Save ifirmawan/aebe163025b1858834aba2ef2f068331 to your computer and use it in GitHub Desktop.
Create Table React Component with collapsable each row has a childs.
import React, { Fragment } from 'react'
import PropTypes from 'prop-types'
import { Icons } from '../assets'
export const ToggleTree = ({ children, toggle, ...dataProps }) => {
return (
<td {...dataProps}>
<button type='button' style={{ border: 'none', background: 'none' }}>
{toggle ? <Icons.MinusTree /> : <Icons.PlusTree />}
{children}
</button>
</td>
)
}
ToggleTree.propTypes = {
children: PropTypes.oneOfType([
PropTypes.string,
PropTypes.node
]).isRequired,
toggle: PropTypes.bool,
onClick: PropTypes.func
}
export const RowTree = ({ parent, 'data-id': dataID, children, ...rowProps }) => <tr parent={parent} data-id={dataID} {...rowProps}>{children}</tr>
RowTree.propTypes = {
parent: PropTypes.number.isRequired,
'data-id': PropTypes.number.isRequired,
children: PropTypes.oneOfType([
PropTypes.string,
PropTypes.element,
PropTypes.node
]).isRequired
}
RowTree.defaultProps = {
parent: 0
}
export class TableTree extends React.Component {
constructor (props) {
super(props)
this.state = {
rows: [],
active: null
}
this.handleUpdateRows = this.handleUpdateRows.bind(this)
this.handleOnClick = this.handleOnClick.bind(this)
}
handleUpdateRows (items) {
this.setState({
rows: items
})
}
handleOnClick (id, toggles) {
this.setState({
active: id
})
const { rows } = this.state
const items = rows.length > 0 ? rows : toggles
const changes = items.map(item => item.id === id ? { ...item, toggle: !item.toggle } : item)
this.handleUpdateRows(changes)
}
render () {
const { rows, active } = this.state
const { children } = this.props
const toggles = []
let parents = []
let padLeft = 3
return (
<tbody>
{children.map((child, index) => {
const { parent, 'data-id': dataID } = child.props
if (parent === 0 || parents.length === 0) {
toggles.push({
id: dataID,
toggle: false
})
parents = [dataID]
padLeft = 3
} else {
if (!parents.includes(parent)) {
toggles.push({
id: parent,
toggle: false
})
parents.push(parent)
padLeft += 1
}
}
const childsToggle = rows.length === 0 ? false : rows.filter(row => row.id === parent)[0]?.toggle || false
const rowProps = (parent === active) ? { ...child.props, style: { display: childsToggle ? 'none' : 'table-row' } } : { ...child.props }
const childs = child.props.children
? (
<>
{child.props.children.map((subChild, subIndex) => {
const modifiedProps = (parents.includes(parent) && subIndex === 0) ? { ...subChild.props, style: { paddingLeft: `${padLeft}rem` } } : { ...subChild.props }
const buttonToggle = rows.length === 0 ? false : rows.filter(row => row.id === dataID)[0]?.toggle || false
return subChild.type === ToggleTree
? React.cloneElement(subChild, {
...modifiedProps,
toggle: buttonToggle,
onClick: () => this.handleOnClick(dataID, toggles),
key: subIndex
})
: React.cloneElement(subChild, {
...modifiedProps,
key: subIndex
})
})}
</>
)
: child
return React.cloneElement(child, {
...rowProps,
children: childs,
key: index
})
})}
</tbody>
)
}
}
TableTree.propTypes = {
children: PropTypes.oneOfType([
PropTypes.array,
PropTypes.element,
PropTypes.node
]).isRequired
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment