Skip to content

Instantly share code, notes, and snippets.

@MidnightLightning
Last active May 2, 2018 20:39
Show Gist options
  • Save MidnightLightning/44e2be2cbe4a4344f54fabc027671ed9 to your computer and use it in GitHub Desktop.
Save MidnightLightning/44e2be2cbe4a4344f54fabc027671ed9 to your computer and use it in GitHub Desktop.
React component for frozen-paned table
<html>
<head>
<meta charset="UTF-8" />
<title>Table scrolling</title>
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
<style>
/* Global styles that apply to all Datatables */
.datatable-container.scrolled-vertically .datatable-body-container {
box-shadow: inset 0 8px 8px -4px rgba(0,0,0, 0.3);
}
.datatable-container.scrolled-horizontally .datatable-body-container {
box-shadow: inset 8px 0 8px -4px rgba(0,0,0, 0.3);
}
.datatable-container.scrolled-vertically.scrolled-horizontally .datatable-body-container {
box-shadow: inset 0 8px 8px -4px rgba(0,0,0, 0.3), inset 8px 0 8px -4px rgba(0,0,0, 0.3);
}
/* Styles that apply just to the "Word Table" */
#word-table .datatable-container {
border: solid 1px red;
/* Need to set the columns and rows on each table */
grid-template-columns: 100px auto;
grid-template-rows: 100px auto;
}
#word-table .datatable-container > .datatable-header-container {
border: solid 1px green;
}
#word-table .datatable-container > .datatable-left-container {
border: solid 1px green;
}
#word-table .datatable-container > .datatable-body-container {
border: solid 1px blue;
}
/* Styles that apply just to the "Cells Table" */
#cells-table .datatable-container {
transition: background-color 0.3s, box-shadow 0.5s;
/* Need to set the columns and rows on each table */
grid-template-columns: 75px auto;
grid-template-rows: 30px auto;
}
#cells-table .datatable-container.scrolled-vertically .datatable-corner-container,
#cells-table .datatable-container.scrolled-horizontally .datatable-corner-container,
#cells-table .datatable-container.scrolled-vertically .datatable-header-container,
#cells-table .datatable-container.scrolled-horizontally .datatable-header-container,
#cells-table .datatable-container.scrolled-vertically .datatable-left-container,
#cells-table .datatable-container.scrolled-horizontally .datatable-left-container {
background-color: #DDD;
}
#cells-table .datatable-corner-container {
transition: inherit;
display: grid;
grid-template-columns: 70px; /* Column width minus left padding */
grid-auto-rows: 30px;
padding-left: 5px; /* Grid gap before first column */
align-items: end;
font-weight: bold; /* Example header style */
}
#cells-table .datatable-header-container {
transition: inherit;
display: grid;
grid-template-columns: 200px 300px 200px 200px 200px 200px 200px;
grid-auto-rows: 30px;
grid-gap: 0 5px;
padding-left: 5px; /* Grid gap before first column */
align-items: end;
font-weight: bold; /* Example header style */
}
#cells-table .datatable-left-container {
transition: inherit;
display: grid;
grid-template-columns: 70px; /* Column width minus left padding */
grid-auto-rows: 30px;
padding-left: 5px; /* Grid gap before first column */
align-items: center;
}
#cells-table .datatable-body-container {
transition: inherit;
display: grid;
grid-template-columns: 200px 300px 200px 200px 200px 200px 200px;
grid-auto-rows: 30px;
grid-gap: 0 5px;
padding-left: 5px; /* Grid gap before first column */
align-items: center;
}
</style>
</head>
<body>
<h1>Table scrolling test</h1>
<div id="word-table"></div>
<div id="cells-table"></div>
<script type="text/babel">
class DataTableCorner extends React.Component {
render() {
return (
<div style={{ gridArea: 'corner', overflow: 'hidden' }} className="datatable-corner-container">
{this.props.children}
</div>
);
}
}
class DataTableHeader extends React.Component {
render() {
return (
<div ref={this.props.containerRef} style={{ gridArea: 'header', overflow: 'hidden' }} className="datatable-header-container">
{this.props.children}
</div>
);
}
}
class DataTableSidebar extends React.Component {
render() {
return (
<div ref={this.props.containerRef} style={{ gridArea: 'left', overflow: 'hidden' }} className="datatable-left-container">
{this.props.children}
</div>
);
}
}
class DataTableBody extends React.Component {
render() {
return (
<div ref={this.props.containerRef} style={{ gridArea: 'body', overflow: 'hidden' }} className="datatable-body-container">
{this.props.children}
</div>
);
}
}
class DataTable extends React.Component {
constructor(props) {
super(props);
this.state = {
scrollX: 0,
scrollY: 0
};
this.headerContainer = false;
this.leftContainer = false;
this.bodyContainer = false;
this.updateOffsets = this.updateOffsets.bind(this);
this.handleResize = this.handleResize.bind(this);
this.handleWheel = this.handleWheel.bind(this);
}
componentDidMount() {
window.addEventListener('resize', this.handleResize);
}
componentWillUnmount() {
window.removeEventListener('resize', this.handleResize);
}
normalizeOffset(x, y, body) {
if (x < 0) x = 0;
if (y < 0) y = 0;
const maxX = body.scrollWidth - body.clientWidth;
const maxY = body.scrollHeight - body.clientHeight;
if (x > maxX) x = maxX;
if (y > maxY) y = maxY;
return { x, y };
}
updateOffsets() {
// All the scroll offsetting is done here in one function, to avoid any jagginess from having each
// sub-component handle its own scrolling and waiting for events to propagate.
if (this.headerContainer !== false) {
this.headerContainer.scrollLeft = this.state.scrollX;
}
if (this.leftContainer !== false) {
this.leftContainer.scrollTop = this.state.scrollY;
}
if (this.bodyContainer !== false) {
this.bodyContainer.scrollLeft = this.state.scrollX;
this.bodyContainer.scrollTop = this.state.scrollY;
}
}
handleResize(e) {
this.setState(curState => {
const { x, y } = this.normalizeOffset(
curState.scrollX,
curState.scrollY,
this.bodyContainer
);
curState.scrollX = x;
curState.scrollY = y;
return curState;
}, () => {
this.updateOffsets();
});
}
handleWheel(e) {
e.preventDefault();
const deltaX = e.deltaX;
const deltaY = e.deltaY;
this.setState(curState => {
const { x, y } = this.normalizeOffset(
curState.scrollX + deltaX,
curState.scrollY + deltaY,
this.bodyContainer
);
curState.scrollX = x;
curState.scrollY = y;
return curState;
}, () => {
this.updateOffsets();
});
}
render() {
// Find child elements, and put them in proper order
let cornerChild = <DataTableCorner />;
let headerChild = <DataTableHeader />;
let sidebarChild = <DataTableSidebar />;
let bodyChild = <DataTableBody />;
React.Children.forEach(this.props.children, child => {
if (child.type.name == 'DataTableCorner') {
// This piece doesn't scroll, so don't need the reference hook
cornerChild = child;
} else if (child.type.name == 'DataTableHeader') {
headerChild = React.cloneElement(child, {
containerRef: (node) => {
this.headerContainer = node;
}
});
} else if (child.type.name == 'DataTableSidebar') {
sidebarChild = React.cloneElement(child, {
containerRef: (node) => {
this.leftContainer = node;
}
});
} else if (child.type.name == 'DataTableBody') {
bodyChild = React.cloneElement(child, {
containerRef: (node) => {
this.bodyContainer = node;
}
});
}
});
const children = React.Children.toArray(this.props.children);
let containerStyle = Object.assign({}, this.props.style, {
display: 'grid',
gridTemplateAreas: "'corner header' 'left body'"
});
let classes = 'datatable-container';
if (this.state.scrollX > 0) {
classes += ' scrolled-horizontally';
}
if (this.state.scrollY > 0) {
classes += ' scrolled-vertically';
}
return (
<div className={classes} style={containerStyle} onWheel={this.handleWheel}>
{cornerChild}
{headerChild}
{sidebarChild}
{bodyChild}
</div>
);
}
}
ReactDOM.render(
<DataTable style={{ height: '400px' }}>
<DataTableCorner>Corner</DataTableCorner>
<DataTableHeader>
<p style={{ margin: '0', padding: '0', whiteSpace: 'nowrap' }}>Header Section Donut jelly-o powder candy canes marshmallow. Sweet roll icing candy pastry brownie chocolate bar. Soufflé bear claw candy canes jujubes lollipop gingerbread muffin cake lollipop. Gummies croissant brownie bear claw croissant caramels oat cake sugar plum. Danish carrot cake dragée chocolate bar tart cake gingerbread gummies gummi bears. Liquorice caramels chocolate chocolate bar croissant danish. Liquorice powder topping liquorice chocolate cake chocolate cake chocolate bar brownie. Jelly beans biscuit cookie toffee halvah chocolate cake liquorice halvah apple pie. Cookie croissant ice cream liquorice liquorice oat cake sugar plum. Gummi bears caramels candy canes icing macaroon gummies caramels. Pudding lollipop dragée marzipan. Cookie powder sesame snaps. Sugar plum dragée dessert. Soufflé jelly beans chocolate candy soufflé gummies muffin tart.</p>
</DataTableHeader>
<DataTableSidebar>Left Donut jelly-o powder candy canes marshmallow. Sweet roll icing candy pastry brownie chocolate bar. Soufflé bear claw candy canes jujubes lollipop gingerbread muffin cake lollipop. Gummies croissant brownie bear claw croissant caramels oat cake sugar plum. Danish carrot cake dragée chocolate bar tart cake gingerbread gummies gummi bears. Liquorice caramels chocolate chocolate bar croissant danish. Liquorice powder topping liquorice chocolate cake chocolate cake chocolate bar brownie. Jelly beans biscuit cookie toffee halvah chocolate cake liquorice halvah apple pie. Cookie croissant ice cream liquorice liquorice oat cake sugar plum. Gummi bears caramels candy canes icing macaroon gummies caramels. Pudding lollipop dragée marzipan. Cookie powder sesame snaps. Sugar plum dragée dessert. Soufflé jelly beans chocolate candy soufflé gummies muffin tart.</DataTableSidebar>
<DataTableBody>
<div style={{ width: '3000px' }}>
<p>Donut sesame snaps icing chocolate. Topping bear claw sweet candy canes donut soufflé apple pie tootsie roll fruitcake. Chupa chups candy pie sweet cookie danish. Cake tart dragée chupa chups oat cake. Dessert brownie sweet roll toffee gummi bears cheesecake sweet roll. Bear claw cookie liquorice lemon drops. Sugar plum gummies jelly-o chocolate. Jujubes bear claw gummi bears pastry icing toffee. Donut dessert chocolate bar cheesecake. Carrot cake dessert brownie pudding oat cake marzipan caramels jujubes cupcake. Lemon drops jelly beans pastry. Gummies gummi bears croissant macaroon sesame snaps.</p>
<p>Sweet ice cream toffee apple pie lemon drops danish cake. Muffin cookie candy jelly-o halvah fruitcake danish icing tiramisu. Pastry bear claw chocolate bar jelly bear claw. Liquorice oat cake fruitcake sugar plum. Cookie sweet roll tootsie roll cheesecake ice cream tart bonbon. Ice cream muffin caramels. Soufflé macaroon jelly-o. Dragée carrot cake jelly-o. Tiramisu croissant soufflé candy canes ice cream apple pie. Chocolate bar chupa chups donut jelly-o pastry. Tart tart tootsie roll brownie cheesecake pudding. Cheesecake gummies sesame snaps marshmallow candy canes fruitcake icing halvah marzipan. Tootsie roll topping marzipan. Muffin lollipop bonbon apple pie cake marshmallow.</p>
<p>Bonbon cotton candy tart jelly-o ice cream dragée halvah. Pudding pastry macaroon lollipop cheesecake. Brownie candy cake jujubes cotton candy jelly soufflé biscuit biscuit. Icing lemon drops dessert chocolate cake candy powder marzipan. Chocolate bar chocolate bar apple pie pastry bonbon. Halvah tootsie roll macaroon cake brownie sugar plum gummi bears chocolate cake cookie. Chupa chups bonbon liquorice toffee fruitcake wafer cake macaroon halvah. Croissant sweet fruitcake toffee candy canes oat cake. Sweet powder danish jelly beans. Caramels marshmallow gummies. Cheesecake lemon drops jelly. Candy biscuit sesame snaps pudding. Marshmallow lemon drops toffee sweet danish. Gummi bears oat cake tart.</p>
<p>Donut sesame snaps icing chocolate. Topping bear claw sweet candy canes donut soufflé apple pie tootsie roll fruitcake. Chupa chups candy pie sweet cookie danish. Cake tart dragée chupa chups oat cake. Dessert brownie sweet roll toffee gummi bears cheesecake sweet roll. Bear claw cookie liquorice lemon drops. Sugar plum gummies jelly-o chocolate. Jujubes bear claw gummi bears pastry icing toffee. Donut dessert chocolate bar cheesecake. Carrot cake dessert brownie pudding oat cake marzipan caramels jujubes cupcake. Lemon drops jelly beans pastry. Gummies gummi bears croissant macaroon sesame snaps.</p>
<p>Sweet ice cream toffee apple pie lemon drops danish cake. Muffin cookie candy jelly-o halvah fruitcake danish icing tiramisu. Pastry bear claw chocolate bar jelly bear claw. Liquorice oat cake fruitcake sugar plum. Cookie sweet roll tootsie roll cheesecake ice cream tart bonbon. Ice cream muffin caramels. Soufflé macaroon jelly-o. Dragée carrot cake jelly-o. Tiramisu croissant soufflé candy canes ice cream apple pie. Chocolate bar chupa chups donut jelly-o pastry. Tart tart tootsie roll brownie cheesecake pudding. Cheesecake gummies sesame snaps marshmallow candy canes fruitcake icing halvah marzipan. Tootsie roll topping marzipan. Muffin lollipop bonbon apple pie cake marshmallow.</p>
<p>Bonbon cotton candy tart jelly-o ice cream dragée halvah. Pudding pastry macaroon lollipop cheesecake. Brownie candy cake jujubes cotton candy jelly soufflé biscuit biscuit. Icing lemon drops dessert chocolate cake candy powder marzipan. Chocolate bar chocolate bar apple pie pastry bonbon. Halvah tootsie roll macaroon cake brownie sugar plum gummi bears chocolate cake cookie. Chupa chups bonbon liquorice toffee fruitcake wafer cake macaroon halvah. Croissant sweet fruitcake toffee candy canes oat cake. Sweet powder danish jelly beans. Caramels marshmallow gummies. Cheesecake lemon drops jelly. Candy biscuit sesame snaps pudding. Marshmallow lemon drops toffee sweet danish. Gummi bears oat cake tart.</p>
<p>Donut sesame snaps icing chocolate. Topping bear claw sweet candy canes donut soufflé apple pie tootsie roll fruitcake. Chupa chups candy pie sweet cookie danish. Cake tart dragée chupa chups oat cake. Dessert brownie sweet roll toffee gummi bears cheesecake sweet roll. Bear claw cookie liquorice lemon drops. Sugar plum gummies jelly-o chocolate. Jujubes bear claw gummi bears pastry icing toffee. Donut dessert chocolate bar cheesecake. Carrot cake dessert brownie pudding oat cake marzipan caramels jujubes cupcake. Lemon drops jelly beans pastry. Gummies gummi bears croissant macaroon sesame snaps.</p>
<p>Sweet ice cream toffee apple pie lemon drops danish cake. Muffin cookie candy jelly-o halvah fruitcake danish icing tiramisu. Pastry bear claw chocolate bar jelly bear claw. Liquorice oat cake fruitcake sugar plum. Cookie sweet roll tootsie roll cheesecake ice cream tart bonbon. Ice cream muffin caramels. Soufflé macaroon jelly-o. Dragée carrot cake jelly-o. Tiramisu croissant soufflé candy canes ice cream apple pie. Chocolate bar chupa chups donut jelly-o pastry. Tart tart tootsie roll brownie cheesecake pudding. Cheesecake gummies sesame snaps marshmallow candy canes fruitcake icing halvah marzipan. Tootsie roll topping marzipan. Muffin lollipop bonbon apple pie cake marshmallow.</p>
<p>Bonbon cotton candy tart jelly-o ice cream dragée halvah. Pudding pastry macaroon lollipop cheesecake. Brownie candy cake jujubes cotton candy jelly soufflé biscuit biscuit. Icing lemon drops dessert chocolate cake candy powder marzipan. Chocolate bar chocolate bar apple pie pastry bonbon. Halvah tootsie roll macaroon cake brownie sugar plum gummi bears chocolate cake cookie. Chupa chups bonbon liquorice toffee fruitcake wafer cake macaroon halvah. Croissant sweet fruitcake toffee candy canes oat cake. Sweet powder danish jelly beans. Caramels marshmallow gummies. Cheesecake lemon drops jelly. Candy biscuit sesame snaps pudding. Marshmallow lemon drops toffee sweet danish. Gummi bears oat cake tart.</p>
</div>
</DataTableBody>
</DataTable>,
document.getElementById('word-table')
);
ReactDOM.render(
<DataTable style={{ height: '200px', marginTop: '2em' }}>
<DataTableCorner>
<div>ID</div>
</DataTableCorner>
<DataTableHeader>
<div>Name</div>
<div>Description</div>
<div>Date</div>
<div>Created Date</div>
<div>Created User</div>
<div>Modified Date</div>
<div>Modified User</div>
</DataTableHeader>
<DataTableSidebar>
<div>123</div>
<div>234</div>
<div>345</div>
<div>456</div>
<div>567</div>
<div>678</div>
<div>789</div>
<div>890</div>
<div>123</div>
<div>234</div>
<div>345</div>
<div>456</div>
<div>567</div>
<div>678</div>
<div>789</div>
<div>890</div>
<div>123</div>
<div>234</div>
<div>345</div>
<div>456</div>
<div>567</div>
<div>678</div>
<div>789</div>
<div>890</div>
</DataTableSidebar>
<DataTableBody>
<div>Carmel</div>
<div>Sweet gooey thingy marzipan</div>
<div>Jan 1, 2000</div>
<div>04/10/2000</div>
<div>john.doe</div>
<div>04/10/2000</div>
<div>john.doe</div>
<div>Macaroon</div>
<div>Sweet gooey thingy marzipan</div>
<div>Jan 1, 2000</div>
<div>04/10/2000</div>
<div>john.doe</div>
<div>04/10/2000</div>
<div>john.doe</div>
<div>Cake</div>
<div>Sweet gooey thingy marzipan</div>
<div>Jan 1, 2000</div>
<div>04/10/2000</div>
<div>john.doe</div>
<div>04/10/2000</div>
<div>john.doe</div>
<div>Bonbon</div>
<div>Sweet gooey thingy marzipan</div>
<div>Jan 1, 2000</div>
<div>04/10/2000</div>
<div>john.doe</div>
<div>04/10/2000</div>
<div>john.doe</div>
<div>Cotton Candy</div>
<div>Sweet gooey thingy marzipan</div>
<div>Jan 1, 2000</div>
<div>04/10/2000</div>
<div>john.doe</div>
<div>04/10/2000</div>
<div>john.doe</div>
<div>Cheesecake</div>
<div>Sweet gooey thingy marzipan</div>
<div>Jan 1, 2000</div>
<div>04/10/2000</div>
<div>john.doe</div>
<div>04/10/2000</div>
<div>john.doe</div>
<div>Lemon Drops</div>
<div>Sweet gooey thingy marzipan</div>
<div>Jan 1, 2000</div>
<div>04/10/2000</div>
<div>john.doe</div>
<div>04/10/2000</div>
<div>john.doe</div>
<div>Muffin</div>
<div>Sweet gooey thingy marzipan</div>
<div>Jan 1, 2000</div>
<div>04/10/2000</div>
<div>john.doe</div>
<div>04/10/2000</div>
<div>john.doe</div>
<div>Cookie</div>
<div>Sweet gooey thingy marzipan</div>
<div>Jan 1, 2000</div>
<div>04/10/2000</div>
<div>john.doe</div>
<div>04/10/2000</div>
<div>john.doe</div>
<div>Candy</div>
<div>Sweet gooey thingy marzipan</div>
<div>Jan 1, 2000</div>
<div>04/10/2000</div>
<div>john.doe</div>
<div>04/10/2000</div>
<div>john.doe</div>
<div>Carmel</div>
<div>Sweet gooey thingy marzipan</div>
<div>Jan 1, 2000</div>
<div>04/10/2000</div>
<div>john.doe</div>
<div>04/10/2000</div>
<div>john.doe</div>
<div>Carmel</div>
<div>Sweet gooey thingy marzipan</div>
<div>Jan 1, 2000</div>
<div>04/10/2000</div>
<div>john.doe</div>
<div>04/10/2000</div>
<div>john.doe</div>
<div>Carmel</div>
<div>Sweet gooey thingy marzipan</div>
<div>Jan 1, 2000</div>
<div>04/10/2000</div>
<div>john.doe</div>
<div>04/10/2000</div>
<div>john.doe</div>
<div>Carmel</div>
<div>Sweet gooey thingy marzipan</div>
<div>Jan 1, 2000</div>
<div>04/10/2000</div>
<div>john.doe</div>
<div>04/10/2000</div>
<div>john.doe</div>
<div>Carmel</div>
<div>Sweet gooey thingy marzipan</div>
<div>Jan 1, 2000</div>
<div>04/10/2000</div>
<div>john.doe</div>
<div>04/10/2000</div>
<div>john.doe</div>
</DataTableBody>
</DataTable>,
document.getElementById('cells-table')
);
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment