Last active
February 12, 2020 06:34
-
-
Save jmakeig/32f73ea3f08f01eaba96918e6ddf76ef to your computer and use it in GitHub Desktop.
Display complex tree data structures as nested HTML tables.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="utf-8" /> | |
<title>Nested Tables</title> | |
<style> | |
html { | |
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, | |
Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; | |
font-size: 14px; | |
} | |
body { | |
padding: 1em; | |
} | |
pre { | |
font-family: monospace; | |
font-size: 80%; | |
} | |
table { | |
width: 100%; | |
border-collapse: collapse; | |
} | |
th, | |
td { | |
padding: 0.5em; | |
border: solid 0.5px #ccc; | |
text-align: left; | |
vertical-align: top; | |
} | |
th { | |
position: relative; | |
padding-right: 1.75em; | |
background: #efefef; | |
} | |
th.string::after, | |
th.number::after, | |
th.date::after, | |
th.object::after { | |
position: absolute; | |
top: 1em; | |
right: 0.75em; | |
display: inline-block; | |
width: 1em; | |
padding: 0.25em; | |
border-radius: 50%; | |
color: #ccc; | |
font-size: 65%; | |
font-weight: normal; | |
line-height: 1; | |
text-align: center; | |
} | |
th.string::after { | |
content: 'S'; | |
background: #3700b3; | |
} | |
th.number::after { | |
color: #333; | |
content: 'N'; | |
background: #03dac6; | |
} | |
th.number.currency::after { | |
content: '$' !important; | |
} | |
th.date::after { | |
color: #333; | |
content: 'D'; | |
background: #ffd600; | |
} | |
th.object::after { | |
color: #333; | |
content: 'O'; | |
background: #0091ea; | |
} | |
td.number, | |
td.date { | |
text-align: right; | |
white-space: nowrap; | |
font-variant-numeric: tabular-nums; | |
} | |
th, | |
td.string { | |
overflow-wrap: break-word; | |
} | |
.heading { | |
display: flex; | |
flex-flow: row wrap; | |
} | |
.heading > * { | |
/* white-space: nowrap; */ | |
display: inline-block; | |
flex-basis: 0; | |
flex: 1; | |
} | |
.heading > .label { | |
margin-right: 0.5em; | |
order: 1; | |
flex: 4; | |
} | |
.heading > .name { | |
color: #999; | |
font-size: 65%; | |
text-align: right; | |
text-align: left; | |
order: 3; | |
flex-basis: 100%; | |
} | |
.heading > .name:before { | |
content: '('; | |
} | |
.heading > .name:after { | |
content: ')'; | |
} | |
.heading > .count { | |
text-align: right; | |
order: 2; | |
flex: 1; | |
} | |
td.error { | |
color: rgb(245, 34, 45); | |
background-color: rgb(255, 241, 240); | |
} | |
td.error:before { | |
position: relative; | |
top: -1px; | |
display: inline-block; | |
width: 6px; | |
height: 6px; | |
margin-right: 0.25em; | |
border-radius: 50%; | |
content: ''; | |
background-color: rgb(245, 34, 45); | |
} | |
table.collapsed tbody { | |
display: none; | |
} | |
table.collapsed thead { | |
display: flex column; | |
} | |
table.collapsed thead th { | |
display: block; | |
/* Hide continguous borders */ | |
margin-top: -1px; | |
} | |
</style> | |
</head> | |
<body> | |
<h2>Table</h2> | |
<table> | |
<thead> | |
<tr> | |
<th class="number"> | |
<span class="heading" | |
><span class="label">Employee Number</span | |
><span class="name">emp_no</span></span | |
> | |
</th> | |
<th class="string"> | |
<span class="heading" | |
><span class="label">First Name</span | |
><span class="name">first_name</span></span | |
> | |
</th> | |
<th class="string"> | |
<span class="heading" | |
><span class="label">Last Name</span | |
><span class="name">last_name</span></span | |
> | |
</th> | |
<th class="string"> | |
<span class="heading" | |
><span class="label">Gender</span | |
><span class="name">gender</span></span | |
> | |
</th> | |
<th class="date"> | |
<span class="heading" | |
><span class="label">Hire Date</span | |
><span class="name">hire_date</span></span | |
> | |
</th> | |
<th class="object" id="work_experience"> | |
<span class="heading" | |
><span class="label">Work Experience</span | |
><span class="name">work_experience</span></span | |
> | |
</th> | |
<th class="object" scope="col" id="salary"> | |
<span class="heading" title="Salary history"> | |
<span class="label">Salary History</span> | |
<span class="name">salary</span> | |
<span class="count">15</span> | |
</span> | |
</th> | |
</tr> | |
</thead> | |
<tbody> | |
<tr> | |
<td class="number id">281328</td> | |
<td class="string">Eishiro</td> | |
<td class="string">Trachtenberg</td> | |
<td class="string">M</td> | |
<td class="date">1988-01-21</td> | |
<td class="object" headers="work_experience"> | |
<table> | |
<thead> | |
<tr> | |
<th class="number id"> | |
<span class="heading" | |
><span class="label">Employee Number</span | |
><span class="name">emp_no</span></span | |
> | |
</th> | |
<th class="string"> | |
<span class="heading" | |
><span class="label">Department Number</span | |
><span class="name">dept_no</span></span | |
> | |
</th> | |
<th class="string"> | |
<span class="heading" | |
><span class="label">Department Name</span | |
><span class="name">dept_name</span></span | |
> | |
</th> | |
<th class="object array" id="work_experience.titles"> | |
<span | |
class="heading" | |
title="The jobs an employee has held. Ordered in reverse chronological order." | |
> | |
<span class="label">Job Titles</span> | |
<span class="name">titles</span> | |
<span class="count">2</span> | |
</span> | |
</th> | |
</tr> | |
</thead> | |
<tbody> | |
<tr> | |
<td class="number id">281328</td> | |
<td class="string">d008</td> | |
<td class="string">Research</td> | |
<td class="object array" headers="work_experience.titles"> | |
<table> | |
<thead> | |
<tr> | |
<th class="string"> | |
<span class="heading" | |
><span class="label">Job Title</span | |
><span class="name">title</span></span | |
> | |
</th> | |
<th class="date"> | |
<span class="heading" | |
><span class="label">From</span | |
><span class="name">from_date</span></span | |
> | |
</th> | |
<th class="date"> | |
<span class="heading" | |
><span class="label">To</span | |
><span class="name">to_date</span></span | |
> | |
</th> | |
</tr> | |
</thead> | |
<tbody> | |
<tr> | |
<td class="string">Staff</td> | |
<td class="date">1988-01-21</td> | |
<td class="date">1996-01-21</td> | |
</tr> | |
<tr> | |
<td class="string">Senior Staff</td> | |
<td class="date">1996-01-21</td> | |
<td class="date">9999-01-01</td> | |
</tr> | |
</tbody> | |
</table> | |
</td> | |
</tr> | |
</tbody> | |
</table> | |
</td> | |
<td class="object" headers="salary"> | |
<table> | |
<thead> | |
<tr> | |
<th class="number currency"> | |
<span class="heading" | |
><span class="label">Salary</span | |
><span class="name">salary</span></span | |
> | |
</th> | |
<th class="date"> | |
<span class="heading" | |
><span class="label">From</span | |
><span class="name">from</span></span | |
> | |
</th> | |
<th class="date"> | |
<span class="heading" | |
><span class="label">To</span | |
><span class="name">to_date</span></span | |
> | |
</th> | |
</tr> | |
</thead> | |
<tbody> | |
<tr> | |
<td class="number currency">56926</td> | |
<td class="date">1995-01-19</td> | |
<td class="date">1996-01-19</td> | |
</tr> | |
<tr> | |
<td class="number currency">51184</td> | |
<td class="date error" title="Error: Invalid date"> | |
1989-XX-XX | |
</td> | |
<td class="date">1990-01-20</td> | |
</tr> | |
<tr> | |
<td class="number currency">60014</td> | |
<td class="date">1999-01-18</td> | |
<td class="date">2000-01-18</td> | |
</tr> | |
<tr> | |
<td class="number currency">47619</td> | |
<td class="date">1988-01-21</td> | |
<td class="date">1989-01-20</td> | |
</tr> | |
<tr> | |
<td class="number currency">58153</td> | |
<td class="date">1996-01-19</td> | |
<td class="date">1997-01-18</td> | |
</tr> | |
<tr> | |
<td class="number currency">51837</td> | |
<td class="date">1990-01-20</td> | |
<td class="date">1991-01-20</td> | |
</tr> | |
<tr> | |
<td class="number currency">56264</td> | |
<td class="date">1993-01-19</td> | |
<td class="date">1994-01-19</td> | |
</tr> | |
<tr> | |
<td class="number currency">59706</td> | |
<td class="date">1998-01-18</td> | |
<td class="date">1999-01-18</td> | |
</tr> | |
<tr> | |
<td class="number currency">62624</td> | |
<td class="date">2001-01-17</td> | |
<td class="date">2002-01-17</td> | |
</tr> | |
<tr> | |
<td class="number currency">63223</td> | |
<td class="date">2002-01-17</td> | |
<td class="date">9999-01-01</td> | |
</tr> | |
<tr> | |
<td class="number currency">57000</td> | |
<td class="date">1994-01-19</td> | |
<td class="date">1995-01-19</td> | |
</tr> | |
<tr> | |
<td class="number currency">55221</td> | |
<td class="date">1991-01-20</td> | |
<td class="date">1992-01-20</td> | |
</tr> | |
<tr> | |
<td class="number currency">54926</td> | |
<td class="date">1992-01-20</td> | |
<td class="date">1993-01-19</td> | |
</tr> | |
<tr> | |
<td class="number currency">62816</td> | |
<td class="date">2000-01-18</td> | |
<td class="date">2001-01-17</td> | |
</tr> | |
<tr> | |
<td class="number currency">58096</td> | |
<td class="date">1997-01-18</td> | |
<td class="date">1998-01-18</td> | |
</tr> | |
</tbody> | |
</table> | |
</td> | |
</tr> | |
</tbody> | |
</table> | |
<h2>Data</h2> | |
<pre> | |
{ | |
"emp_no": 281328, | |
"first_name": "Eishiro", | |
"last_name": "Trachtenberg", | |
"gender": "M", | |
"hire_date": "1988-01-21", | |
"work_experience": { | |
"emp_no": 281328, | |
"dept_no": "d008", | |
"dept_name": "Research", | |
"titles": [ | |
{ | |
"title": "Staff", | |
"from_date": "1988-01-21", | |
"to_date": "1996-01-21" | |
}, | |
{ | |
"title": "Senior Staff", | |
"from_date": "1996-01-21", | |
"to_date": "9999-01-01" | |
} | |
] | |
}, | |
"salary": [ | |
{ | |
"salary": "56926", | |
"from_date": "1995-01-19", | |
"to_date": "1996-01-19" | |
}, | |
{ | |
"salary": "51184", | |
"from_date": "1989-01-20", | |
"to_date": "1990-01-20" | |
}, | |
{ | |
"salary": "60014", | |
"from_date": "1999-01-18", | |
"to_date": "2000-01-18" | |
}, | |
{ | |
"salary": "47619", | |
"from_date": "1988-01-21", | |
"to_date": "1989-01-20" | |
}, | |
{ | |
"salary": "58153", | |
"from_date": "1996-01-19", | |
"to_date": "1997-01-18" | |
}, | |
{ | |
"salary": "51837", | |
"from_date": "1990-01-20", | |
"to_date": "1991-01-20" | |
}, | |
{ | |
"salary": "56264", | |
"from_date": "1993-01-19", | |
"to_date": "1994-01-19" | |
}, | |
{ | |
"salary": "59706", | |
"from_date": "1998-01-18", | |
"to_date": "1999-01-18" | |
}, | |
{ | |
"salary": "62624", | |
"from_date": "2001-01-17", | |
"to_date": "2002-01-17" | |
}, | |
{ | |
"salary": "63223", | |
"from_date": "2002-01-17", | |
"to_date": "9999-01-01" | |
}, | |
{ | |
"salary": "57000", | |
"from_date": "1994-01-19", | |
"to_date": "1995-01-19" | |
}, | |
{ | |
"salary": "55221", | |
"from_date": "1991-01-20", | |
"to_date": "1992-01-20" | |
}, | |
{ | |
"salary": "54926", | |
"from_date": "1992-01-20", | |
"to_date": "1993-01-19" | |
}, | |
{ | |
"salary": "62816", | |
"from_date": "2000-01-18", | |
"to_date": "2001-01-17" | |
}, | |
{ | |
"salary": "58096", | |
"from_date": "1997-01-18", | |
"to_date": "1998-01-18" | |
} | |
] | |
} | |
</pre | |
> | |
</body> | |
<script type="text/javascript"> | |
const usd = new Intl.NumberFormat('en-US', { | |
style: 'currency', | |
currency: 'USD', | |
minimumFractionDigits: 0 | |
}); | |
for (const el of document.querySelectorAll('td.number.currency')) { | |
el.dataset.value = el.textContent.trim(); | |
el.textContent = usd.format(el.textContent); | |
} | |
// FIXME: This is really ugly and needs to be factored into proper components. | |
const headers = document.querySelectorAll('th.object'); | |
for (const header of headers) { | |
const id = header.id; | |
toggleFor(id, true); | |
header.addEventListener('click', event => { | |
toggleFor(id); | |
}); | |
} | |
function toggleFor(id, toggle) { | |
const table = document.querySelector(`td[headers="${id}"] > table`); | |
if (table) { | |
if (undefined === toggle) { | |
table.classList.toggle('collapsed'); | |
} else if (toggle) { | |
table.classList.add('collapsed'); | |
} else { | |
table.classList.remove('collapsed'); | |
} | |
} | |
} | |
</script> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment