Skip to content

Instantly share code, notes, and snippets.

@jasonphillips
Last active January 5, 2018 10:21
Show Gist options
  • Save jasonphillips/d475fdcc25557065fadc26f38fa78c10 to your computer and use it in GitHub Desktop.
Save jasonphillips/d475fdcc25557065fadc26f38fa78c10 to your computer and use it in GitHub Desktop.
React table elements with automatic pivoting to list-style view for mobile
import React from 'react';
/* context helpers from:
* https://gist.github.com/ryanflorence/1e1290571337ebcea1c5a748e8f5b37d
*/
import provideContext from './provideContext';
import withContext from './withContext';
const contextShape = React.PropTypes.shape({
headers: React.PropTypes.object
});
const TableContext = provideContext('responsiveTable', contextShape);
const withTableContext = withContext('responsiveTable', contextShape);
export class Table extends React.Component {
constructor(props) {
super(props);
this.state = { headers: {} }
}
render() {
const {headers} = this.state;
const classes = (this.props.className || '') + ' responsiveTable';
return (
<TableContext headers={headers}>
<table {...this.props} className={classes}/>
</TableContext>
);
}
}
export const Thead = (props) => (
<thead {...props}>
{React.cloneElement(props.children, {inHeader: true})}
</thead>
);
class TrInner extends React.Component {
constructor(props) {
super(props);
const {headers} = props.responsiveTable;
if (headers && props.inHeader) {
props.children.map((child, i) => {
headers[i] = child.props.children;
});
}
}
render() {
const {children} = this.props;
return (
<tr {...this.props}>
{children && React.Children.map(children, (child, i) =>
React.cloneElement(child, {key: i, columnKey: i})
)}
</tr>
);
}
}
export const Tr = withTableContext(TrInner);
export const Th = (props) => <th {...props}/>;
export const Tbody = (props) => <tbody {...props}/>;
class TdInner extends React.Component {
render() {
if (this.props.colSpan) return <td {...this.props}/>;
const {responsiveTable: {headers}, children, columnKey} = this.props;
return (
<td className="pivoted">
<div className="tdBefore">{headers[columnKey]}</div>
{(children!==undefined) ? children : <div>&nbsp;</div>}
</td>
);
}
}
export const Td = withTableContext(TdInner);
/* inspired by: https://css-tricks.com/responsive-data-tables/ */
.responsiveTable {
td .tdBefore {
display: none;
}
/* use your our own breakpoint, ideally */
@media only screen and (max-width: 768px) {
/* Force table elements to not behave like tables anymore */
table, thead, tbody, th, td, tr {
display: block;
}
/* Hide table headers (but not display: none;, for accessibility) */
thead tr {
position: absolute;
top: -9999px;
left: -9999px;
border-bottom: 2px solid #333;
}
tbody tr {
border: 1px solid #eee;
}
td.pivoted {
/* Behave like a "row" */
border: none !important;
border-bottom: 1px solid #eee;
position: relative;
padding-left: 50% !important;
text-align: left !important;
}
td .tdBefore {
/* Now like a table header */
position: absolute;
display: block;
/* Top/left values mimic padding */
left: 1rem;
width: 45%;
padding-right: 10px;
white-space: nowrap;
text-align: left !important;
font-weight: 600;
}
}
}
/* excerpted usage example */
import React from 'react';
import {Table, Thead, Tbody, Tr, Th, Td} from './responsiveTable.js';
export const myComponent = (props) => (
<Table>
<Thead>
<Tr>
<Th>Alpha</Th>
<Th>Beta</Th>
<Th>Omega</Th>
</Tr>
</Thead>
<Tbody>
<Tr>
<Td>Lorem</Td>
<Td>299310</Td>
<Td>1.1388</Td>
</Tr>
<Tr>
<Td>Ipsum</Td>
<Td>262424</Td>
<Td>1.888</Td>
</Tr>
<Tr>
<Td>Dolor</Td>
<Td>131325</Td>
<Td>1.633</Td>
</Tr>
<Tr>
<Td>Amet</Td>
<Td>9000</Td>
<Td>1.1388</Td>
</Tr>
</Tbody>
</Table>
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment