Published
Edited
Mar 18, 2020
Fork of Tables
Importers
Insert cell
Insert cell
md`
<br /><br />

*Click the caret on the left to view code for any example.*
`
Insert cell
ID = () => '_' + Math.random().toString(36).substr(2, 9)
Insert cell
formatDate = {
function pad(value, width) {
var s = value + "", length = s.length;
return length < width ? new Array(width - length + 1).join(0) + s : s;
}

function isUTCMidnight(date) {
return date.getUTCMilliseconds() === 0
&& date.getUTCSeconds() === 0
&& date.getUTCMinutes() === 0
&& date.getUTCHours() === 0;
}

function formatYear(year) {
return year < 0 ? "-" + pad(-year, 6)
: year > 9999 ? "+" + pad(year, 6)
: pad(year, 4);
}

return function formatDate(date) {
return isNaN(date)
? "Invalid Date"
: isUTCMidnight(date)
? formatYear(date.getUTCFullYear()) + "-" + pad(date.getUTCMonth() + 1, 2) + "-" + pad(date.getUTCDate(), 2)
: formatYear(date.getFullYear()) + "-" + pad(date.getMonth() + 1, 2) + "-" + pad(date.getDate(), 2)
+ "T" + pad(date.getHours(), 2) + ":" + pad(date.getMinutes(), 2)
+ (date.getMilliseconds() ? ":" + pad(date.getSeconds(), 2) + "." + pad(date.getMilliseconds(), 3)
: date.getSeconds() ? ":" + pad(date.getSeconds(), 2)
: "");
}
}
Insert cell
table = (data, options) => {
options = Object.assign({}, defaultOptions, options);
let id = ID();
const { sortable, rank, paged } = options;
let sortKey = undefined;
let sortDirection = true;
let page = 0;
let pages = Math.ceil(data.length / paged);
if (sortable && rank) {
throw new Error("A table can either be ranked or sortable, but not both");
}
let columns = Object.keys(data[0]).map(key => {
const opts = options.columns[key] || {};
return {
key: key,
type: opts.type || typeof data[0][key],
options: opts
};
});

function bake() {
if (sortKey) {
data = data.slice().sort((a, b) => {
let as = a[sortKey];
let bs = b[sortKey];
// make this sort stable
if (as == bs) return JSON.stringify(a).localeCompare(JSON.stringify(b));
let res = as > bs ? 1 : as < bs ? -1 : 0;
if (sortDirection) res = -res;
return res;
});
}
let rows = data.slice(page * paged, page * paged + paged);
return html`<div><div>
<style>
.pretty-pager {
padding-top: 1rem;
}
.pretty-pager button {
cursor: pointer;
border-radius: 3px;
border: 1px solid #fff;
font-size: inherit;
}
.pretty-pager button:hover {
border: 1px solid #888;
}
.pretty-table.normal {
font-size: 15px;
}
.pretty-table.normal th,
.pretty-table.normal td {
padding: 3px 2px;
}
.pretty-table th,
.pretty-table td {
vertical-align: top;
}
.pretty-table thead th {
/* text-transform: uppercase; */
font-weight:800; /* was: 500 */
}
.pretty-table thead th.column-type-number string {
order: 1;
}
.pretty-table th.sortable {
cursor: pointer;
}
.pretty-table thead th.column-type-number,
.pretty-table tbody td.cell-type-number,
.pretty-table tbody td.cell-rank {
text-align:right;
}
.pretty-table tbody td.cell-type-number,
.pretty-table tbody td.cell-rank {
font-family: menlo,consolas,monaco,monospace;
font-size: 90%;
}
.pretty-table tbody td.cell-rank {
padding-right: 1em;
color: #666;
}

</style>
<table class='pretty-table ${options.style}'>
${
options.header === false
? ``
: html`<thead>
${rank ? html`<th></th>` : ""}
${columns.map(c => {
return th(c, sortKey, sortDirection, sortable);
})}
</thead>`
}
<tbody>
${rows.map(
(row, i) => html`<tr>
${rank ? html`<td class='cell-rank'>${i + 1}</td>` : ""}
${columns.map(c => {
let displayValue = (c.options.formatter || identity)(
row[c.key],
i,
row
);
if ( displayValue instanceof Date ) {
displayValue = formatDate(displayValue)
}
if (
displayValue instanceof window.HTMLElement &&
displayValue.tagName == "TD"
) {
return displayValue;
}
return html`<td class='cell-type-${
c.type
}'>${displayValue}</td>`;
})}
</tr>`
)}
</tbody>
</table>
${
pages && pages > 1
? html`<div class='pretty-pager'>
<button data-action="previous">Previous</button>
${Array.from({ length: pages }).map(
(_, i) => html`<button data-page="${i + 1}" style="${i == page ? "font-weight: bold" : ""}">${i + 1}</button>`
)}
<!--<input type="range" min="1" max="${pages}" value="1" class="slider" list="${id + "tickmarks"}" id="${id + "slider"}">
<datalist id="${id + "tickmarks"}">
${Array.from({ length: pages }).map((_, i) =>
html`<option value="${i + 1}"></option>`
)}
</datalist>-->
<button data-action="next">Next</button>
</div>`
: ""
}
</div></div>`;
}

let dom = bake();

function rerender() {
dom.firstChild.remove();
dom.appendChild(bake().firstChild);

}

const slider = dom.querySelector("#" + id + "slider");
if (slider) {
slider.addEventListener("input", function () {
console.log(this.value);
page = +this.value;
rerender();
});
}

dom.addEventListener("click", e => {
if (e.target.tagName === "TH" && sortable) {
if (sortKey == e.target.dataset.key) {
sortDirection = !sortDirection;
}
sortKey = e.target.dataset.key;
rerender();
} else if (e.target.tagName === "BUTTON") {
if (e.target.dataset.action) {
switch (e.target.dataset.action) {
case "next":
page++;
if (page >= pages) { page = pages - 1; }
rerender();
break;
case "previous":
page--;
if (page < 0) { page = 0; }
rerender();
break;
}
}
}
});

return dom;
}
Insert cell
table(data)
Insert cell
data = d3.csv("https://raw.githubusercontent.com/covid19-data/covid19-data/master/output/cases/cases_WHO.csv")
Insert cell
md`
### Titanic survivors

This is a table of the Titanic survivors dataset - it shows the data in natural order, first, but the columns are sortable. It shows usage of custom formatters for the Name and Fare columns. The Fare column formatter uses the rowIndex to determine the appropriate format based on the row number. (i.e. $ for the first row only).`
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more