JSFiddle - React, Tailwind, and code Playground

by Tonio Loewald

HTML

<link rel="stylesheet" href="https://api.mapbox.com/mapbox-gl-js/v1.4.1/mapbox-gl.css">
<script src="https://api.mapbox.com/mapbox-gl-js/v1.4.1/mapbox-gl.js"></script>
<div id="layout">
  <div id="map">
  </div>
  <div id="data">
  </div>
</div>
<img...

CSS

* {
  box-sizing: border-box;
  font-family: Sans-serif;
}

th, td {
  padding: 4px 8px;
}

#layout {
  display: flex;
  height: 400px;
  width: 100%;
  flex-direction: row;
  position: relative;
}

#data {
  position: relative;
  overflow: scroll;
  overflow: overlay;
}

#layout > * {
  flex: 0 0 50%;
  height: 400px;
}

table {
  border-collapse: collapse;
}

tr:not(.header):hover {
  height: var(--row-height);
  background: #eee;
}

.picked {
  background: #ddd;
}

.header {
  background: #222;
  color: white;
}

/*
  fixed header over scrolling table content
*/
.header:nth-child(1) {
  position: fixed;
}

/*
  duplicate header row for column metrics, but hidden
*/
.header:nth-child(2) {
  visibility: hidden;
}

JavaScript

(async () => {
	const [long, lat, zoom] = [38.2641789,-120.5922877,2]
  mapboxgl.accessToken = 'pk.eyJ1IjoicG9kcGVyc29uIiwiYSI6ImNqc2JlbWU0bjA1ZmY0YW5ycHZod3VhbWcifQ.arvqfpOqMgFYkKgQ35UScA'
  const map = new mapboxgl.Map({
    container: document.getElementById('map'),
    style: 'mapbox://styles/mapbox/outdoors-v10',
    zoom,
    center: [lat, long],
  })
  
	const flameMarker = document.querySelector('img')
  flameMarker.remove()
  flameMarker.style.display = ''
  
  const response = await fetch('http://api.511.org/transit/VehicleMonitoring?api_key=0b552e16-e64a-4ac4-99b3-141fc3e99c74&agency=SF')
  const vehicles = reponse.json().Siri.ServiceDelivery.VehicleMonitoringDelivery.VehicleActivity
  const header = Object.keys(vehicles[0])
  
  const rowMarkerMap = []
  
  const tr = (data, cols, elementType='td', className) => {
  	const tr = document.createElement('tr')
    if (className) tr.classList.add(className)
    filtered = cols ? cols.map(i => data[i]) : data
    filtered.forEach(datum => {
    	const elt = document.createElement(elementType)
      elt.textContent = datum
    	tr.append(elt)
    })
    return tr
  }
  
  const cols = [0, 1, 8]
  const table = document.createElement('table')
  table.append(tr(header, cols, 'th', 'header')) 
  table.append(tr(header, cols, 'th', 'header'))
  rows.forEach((row, i) => {
  	const rowElt = tr(row, cols)
  	table.append(rowElt)
    // const red = Math.floor(parseFloat(row[8]) * 2.55)
    const element = flameMarker.cloneNode(true)
    element.style.opacity = parseFloat(row[8] * 0.01)
    const marker = new mapboxgl.Marker({
    	// color: `rgb(${red},0,0)`,
      element
    })
      .setLngLat([row[1], row[0]])
      .addTo(map);
    rowMarkerMap.push({
    	rowElt,
      markerElt: marker._element
    })
  })
  document.getElementById('data').append(table)
  
  map.on('click', (e) => {
		const elt = e.originalEvent.target.closest('.mapboxgl-marker')
    const rowElt = (rowMarkerMap.find(x => x.markerElt === elt)...