JSFiddle - React, Tailwind, and code Playground

by Jihad Dzikri Waspada

HTML

<div id="app"></div>

SCSS

.wrapper {
  overflow: auto;
  width: 500px;
  height: 250px;
}
.table {
  width: 100%;
  overflow: visible;
  border-collapse: collapse;
  transform-style: preserve-3d; // this is the key,
}

th {
  text-align: left;
}

th, td {
  padding: 5px;
  background: white;
}

Babel + JSX

const CELL_FIXED_CLASSNAME = 'fixed'
let ticking = false;
class FixableTable extends React.Component {
  el
  thead
  tds

  componentDidMount() {
    this.thead = this.el.querySelector('thead')
    this.tds = Array.from(this.el.querySelectorAll(`.${CELL_FIXED_CLASSNAME}`))
  }

  handleWrapperScroll = (e) => {
    const x = e.target.scrollLeft
    const y = e.target.scrollTop
    
    this.makeFixed(x, y)
		
    // Use Animation Frame
    /* if (!ticking) {
      requestAnimationFrame(() => {
        this.makeFixed(x, y)
        ticking = false
      })
    }

    ticking = true */
  }

  makeFixed = (x, y) => {
    // const wrapper: HTMLDivElement = e.target

    // Fixed headers
    const scrollTop = `translate3d(0, ${y}px, 1px)`
    if (this.thead) {
      this.thead.style.transform = scrollTop
    }

    // Fixed Column(s)
    const scrollLeft = `translateX(${x}px)`
    this.tds.forEach(col => {
      col.style.transform = scrollLeft
    })
  }

  renderHeader() {
    const { headers = [], columnsFixed = 0 } = this.props;

    return (
      <thead>
        <tr>
          {headers.map((header, i) => <th
            className={i + 1 <= columnsFixed ? CELL_FIXED_CLASSNAME : ''}
          >{header}</th>)}
        </tr>
      </thead>
    )
  }

  renderData() {
    const { data = [], columnsFixed = 0 } = this.props;

    return (
      <tbody>
        {data.map((rows, i) =>
          <tr>{rows.map((row, i) =>
            <td
              className={i + 1 <= columnsFixed ? CELL_FIXED_CLASSNAME : ''}
            >{row}</td>)}
          </tr>
        )}
      </tbody>
    )
  }

  render() {
    const { classes, children } = this.props

    return (
      <div ref={el => (this.el = el)} className={classes.wrapper} onScroll={this.handleWrapperScroll}>
        <table className={classes.table}>
          {this.renderHeader()}
          {this.renderData()}
        </table>
      </div>
    )
  }
}

React.render(<FixableTable
          headers={['Name', 'Age', 'City',...