<!DOCTYPE html>
<html>
  <head>
    <script src="https://unpkg.com/react/dist/react-with-addons.min.js"></script>
    <script src="https://unpkg.com/react-dom/dist/react-dom.min.js"></script>
    <script src="https://unpkg.com/react-virtualized@8.0.11/dist/umd/react-virtualized.js"></script>
    <script src="https://unpkg.com/react-draggable@2.2.2/dist/react-draggable.min.js"></script>
    <script src="https://unpkg.com/react-sortable-hoc@0.0.9/dist/umd/react-sortable-hoc.min.js"></script>

    <link rel="stylesheet" href="https://unpkg.com/react-virtualized/styles.css">
    <link rel="stylesheet" href="styles.css">
  </head>

  <body>
    <div id="example"></div>

    <script src="create-list.js"></script>
    <script src="demo-app.js"></script>
    <script src="script.js"></script>
  </body>

</html>
// Render your list
ReactDOM.render(
  <DemoApp />,
  document.getElementById('example')
);
html, body, #example {
  height: 100%;
}

* {
  box-sizing: border-box;
}

.Row {
  width: 100%;
  height: 100%;
  border-bottom: 1px solid black;
  display: flex;
  align-items: center;
  padding: 0 0.5rem;
  user-select: none;
  cursor: move;
  
  /* Draggable rows should be above non-draggable List */
  z-index: 2;
}

.DraggableHeader {
  position: relative;
  padding-right: 1rem;
}

.DragHandle {
  position: absolute;
  right: 0;
  top: 0;
  z-index: 3;
  cursor: col-resize;
  color: #CCC;
  padding: 0 0.5rem;
}

.DragHandle:hover,
.DragHandleActive {
  color: #F6A;
}
const FIRST_NAMES = ['Peter', 'Tera', 'Kandy', 'Lonna', 'Kristie', 'Raul', 'Yukiko', 'Velvet', 'Donette', 'Loraine', 'Shyla', 'Marhta', 'Alene', 'Holley', 'Randell', 'Wilfred', 'Naida', 'Marine', 'Glinda', 'Lupe', 'Cordelia', 'Samara', 'Era', 'Malka', 'Edward', 'Clemencia', 'Loretta', 'Dana', 'Sanda', 'Soo', 'Apolonia', 'Liliana', 'Angel', 'Yvonne', 'Jonas', 'Tran', 'Buddy', 'Rosita', 'Rosalind', 'Renae', 'Deandrea', 'Kelvin', 'Guadalupe', 'Zelma', 'Laurel', 'Edyth', 'Marylin', 'Hsiu', 'Mark', 'Winford', 'Shizuko', 'Roslyn', 'Nilsa', 'Agustin', 'Earlie', 'Libby', 'Shanna', 'Brendan', 'Windy', 'Alice', 'Eden', 'Klara', 'Sherryl', 'Gala', 'Stephani', 'Twana', 'Mauro', 'Claudie', 'Adrienne', 'Pearlene', 'Rachelle', 'Louis', 'Susann', 'Mandi', 'Ola', 'Leif', 'Tisha', 'Hector', 'Benita', 'Kaley', 'Jim', 'Freda', 'Rafaela', 'Stasia', 'Evia', 'Rocky', 'Sonja', 'Dee', 'Damon', 'Alma', 'Linsey', 'Stefan', 'Giovanna', 'Myrtis', 'Marguerita', 'Junior', 'Allene', 'Margery', 'Nelly', 'Felix']
const LAST_NAMES = ['Brimer', 'Gaona', 'Liston', 'Wrede', 'Yard', 'Host', 'Binger', 'Natera', 'Ponton', 'Grim', 'Mable', 'Sing', 'Munden', 'Pagel', 'Tolman', 'Juneau', 'Madson', 'Amison', 'Palazzo', 'Island', 'Trotta', 'Berrier', 'Stepp', 'Spradlin', 'Haner', 'Feather', 'Rasnake', 'Hasbrouck', 'Nery', 'Reiling', 'Volk', 'Cacho', 'Couchman', 'Adam', 'Curci', 'Cesar', 'Panos', 'Ells', 'Tavares', 'Keehn', 'Bester', 'Lemmon', 'Mccullar', 'Mayers', 'Stcyr', 'Everette', 'Shevlin', 'Blackwelder', 'Ferguson', 'Noggle', 'Gilchrist', 'Cress', 'Lesniak', 'Grant', 'Jester', 'Daigle', 'Maloy', 'Wilken', 'Knittel', 'Curren', 'Lumsden', 'Morfin', 'Noack', 'Munsey', 'Frew', 'Anthony', 'Matlock', 'Meisner', 'Petrarca', 'Shurtleff', 'Piro', 'Cocco', 'Mcsweeney', 'Kempker', 'Moller', 'Mcgahan', 'Wurster', 'Pinkett', 'Jemison', 'Findley', 'Torkelson', 'Okafor', 'Markert', 'Carwile', 'Kahler', 'Almon', 'Beals', 'Fomby', 'Eatman', 'Grieve', 'Bollig', 'Cloninger', 'Blind', 'Remy', 'Dostal', 'Baranowski', 'Seto', 'Caves', 'Moudy', 'Sailer']

const list = []

for (var i = 0; i < 1000; i++) {
  let firstName = FIRST_NAMES[Math.floor(Math.random() * FIRST_NAMES.length)]
  let lastName = LAST_NAMES[Math.floor(Math.random() * LAST_NAMES.length)]

  list.push({
    age: 20 + Math.round(Math.random() * 40),
    index: i,
    name: `${firstName} ${lastName}`
  })
}
// Read deps from UMD exports
const { Component } = React
const { findDOMNode } = ReactDOM
const Draggable = ReactDraggable
const { AutoSizer, defaultTableHeaderRenderer, defaultTableRowRenderer, Column, Table } = ReactVirtualized
const { SortableContainer, SortableElement, SortableHandle } = SortableHOC

// Connect react-virtualized and react-sortable-hoc
const SortableTable = SortableContainer(Table, {
  withRef: true
})
const SortableRow = SortableElement(defaultTableRowRenderer)
const DragHandle = SortableHandle(({ children }) => (
  <div>{children}</div>
))

const MIN_COLUMN_WIDTH = 50

class DemoApp extends Component {
  constructor (props, context) {
    super(props, context)

    this._draggableHeaderRenderer = this._draggableHeaderRenderer.bind(this)
    this._draggableColumn = this._draggableColumn.bind(this)
    this._rowRenderer = this._rowRenderer.bind(this)
    this._sortRow = this._sortRow.bind(this)

    this.state = {
      flexColums: [
        'index',
        'age',
        'name'
      ],
      flexColumProps: {
        age: {
          cellRenderer: this._draggableColumn,
          dataKey: 'age',
          label: 'Age',
          width: 100
        },
        index: {
          cellRenderer: this._draggableColumn,
          dataKey: 'index',
          label: '#',
          width: 50
        },
        name: {
          cellRenderer: this._draggableColumn,
          dataKey: 'name',
          label: 'Name',
          width: 200
        }
      }
    }
  }

  render () {
    const { flexColums, flexColumProps } = this.state

    return (
      <AutoSizer>
        {({ width, height }) => (
          <SortableTable
            getContainer={(wrappedInstance) => findDOMNode(wrappedInstance.Grid)}
            headerHeight={40}
            height={height}
            onSortEnd={this._sortRow}
            rowClassName='Row'
            rowHeight={50}
            rowGetter={({ index }) => list[index]}
            rowCount={list.length}
            rowRenderer={this._rowRenderer}
            useDragHandle
            width={width}
          >
            {flexColums.map((key) => (
              <Column
                className='DragHandleColumn'
                key={key}
                headerRenderer={this._draggableHeaderRenderer}
                {...flexColumProps[key]}
              />
            ))}
          </SortableTable>
        )}
      </AutoSizer>
    )
  }

  _draggableHeaderRenderer (props) {
    return (
      <div className='DraggableHeader'>
        {defaultTableHeaderRenderer(props)}
        <Draggable
          axis='x'
          defaultClassName='DragHandle'
          defaultClassNameDragging='DragHandleActive'
          onStop={(event, data) => this._resizeColumn({
            dataKey: props.dataKey,
            deltaX: data.x
          })}
          position={{
            x: 0,
            y: 0
          }}
          zIndex={999}
        >
          <div>||</div>
        </Draggable>
      </div>
    )
  }

  _draggableColumn ({ cellData, rowData, rowIndex }) {
    if (this._isRowSortable(rowIndex)) {
      return (
        <DragHandle>{cellData}</DragHandle>
      )
    } else {
      return cellData + 1
    }
  }

  _isRowSortable (index) {
    return index >= 0 // Header row should not be draggable
  }

  _resizeColumn ({ dataKey, deltaX }) {
    const { flexColumProps } = this.state

    // Once a column has been resized, lock its size
    // This prevents an awkward user experience where their resized width is overridden by flex
    const thisColumn = flexColumProps[dataKey]
    thisColumn.flexGrow = 0
    thisColumn.flexShrink = 0
    thisColumn.width = Math.max(MIN_COLUMN_WIDTH, thisColumn.width + deltaX)

    this.setState({
      flexColumProps
    })
  }

  _rowRenderer (props) {
    const { index } = props

    return this._isRowSortable(index)
      ? <SortableRow {...props} />
      : defaultTableRowRenderer(props)
  }

  _sortRow ({ newIndex, oldIndex }) {
    if (newIndex === oldIndex) {
      return
    }

    const row = list[oldIndex]

    list.splice(oldIndex, 1)
    list.splice(newIndex, 0, row)

    this.forceUpdate() // Re-render
  }
}