Skip to content

Commit 26835cb

Browse files
committed
prototyping in userland
1 parent 9d1e46f commit 26835cb

File tree

1 file changed

+114
-0
lines changed

1 file changed

+114
-0
lines changed

docs/summary-table.md

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
# SQL summary table ([#23](https://github.com/observablehq/framework/issues/23))
2+
3+
```sql echo
4+
SELECT * FROM aapl;
5+
```
6+
7+
```sql echo
8+
SELECT * FROM gaia;
9+
```
10+
11+
```sql echo
12+
SELECT * FROM penguins;
13+
```
14+
15+
```js echo
16+
const sql = DuckDBClient.sql({aapl, penguins, gaia: FileAttachment("/lib/gaia-sample.parquet")});
17+
```
18+
19+
```js echo
20+
import * as _Inputs from "npm:@observablehq/inputs"
21+
import * as Arrow from "npm:apache-arrow";
22+
import * as d3 from "npm:d3";
23+
import {html} from "npm:htl";
24+
25+
width; // refresh when resized
26+
27+
const Inputs = ({..._Inputs, table})
28+
29+
function table(data, options = {}) {
30+
if (!data) return data;
31+
32+
const container = document.createElement("div");
33+
container.append(_Inputs.table(data, options));
34+
35+
// Duck typing Arrow table
36+
if (!Array.isArray(data?.schema?.fields)) return container;
37+
38+
// Get the fields as described by Arrow, in the order given (potentially) by the options.
39+
const fields = (options.columns?.map(k => data.schema.find(({name}) => name === k)) ?? data.schema.fields).map(({name, type}) => ({name: String(name), type: String(type)}));
40+
41+
const th = d3.select(container).select("thead").selectAll("th").data([{}, ...fields]);
42+
th.append("div").classed("type", true).html(({type}) => type);
43+
const summaries = th.append("div").classed("summary", true);
44+
const footer = html`<footer style="width: 100%; height: 1em;">
45+
<div style="position: absolute; left: 0;"><!-- <input type="search" placeholder="Search text fields"> --></div>
46+
<div style="position: absolute; right: 0;">${data.numRows.toLocaleString("en-US")} rows</div>
47+
</footer>`;
48+
container.appendChild(footer);
49+
50+
requestAnimationFrame(() => summaries
51+
.filter(({type}) => type)
52+
.append(function({name, type}) {
53+
return summary(data.getChild(name), type, this.getBoundingClientRect());
54+
})
55+
);
56+
return container;
57+
}
58+
59+
function summary(values, type, {width = 80, height = 33}) {
60+
let chart;
61+
if (type.startsWith("Float") || type.startsWith("Date")) {
62+
chart = Plot.plot({
63+
width,
64+
height,
65+
style: "overflow: visible;",
66+
x: {round: true},
67+
y: {axis: null},
68+
marginLeft: 2,
69+
marginRight: 2,
70+
marginTop: 0,
71+
marginBottom: 13,
72+
marks: [
73+
Plot.rectY(values, Plot.binX(undefined, {fill: "var(--theme-foreground-focus)", inset: -.3})),
74+
Plot.axisX({tickSpacing: 41, tickSize: 3, tickPadding: 2, fontSize: 8}),
75+
]
76+
});
77+
78+
// restore insets where possible
79+
const rects = chart.querySelectorAll("rect");
80+
if (rects.length < 100) {
81+
for (const rect of rects) {
82+
rect.setAttribute("x", Math.floor(rect.getAttribute("x")));
83+
rect.setAttribute("width", Math.max(1, Math.floor(rect.getAttribute("width")) - 1));
84+
}
85+
}
86+
}
87+
else if (type === "Utf8") {
88+
chart = Plot.plot({
89+
width,
90+
height,
91+
style: "overflow: visible;",
92+
x: {axis: null},
93+
y: {axis: null},
94+
marginLeft: 2,
95+
marginRight: 2,
96+
marginTop: 0,
97+
marginBottom: 13,
98+
marks: [
99+
Plot.barX(values, Plot.groupZ({x: "count"}, {z: Plot.identity, insetRight: 1, fill: "var(--theme-foreground-focus)"})),
100+
Plot.text(values, Plot.stackX(Plot.groupZ({x: "count", text: "first"}, {z: Plot.identity, fill: "var(--plot-background)"}))),
101+
]
102+
});
103+
}
104+
return chart ?? html`<span>Unknown type ${type}`;
105+
}
106+
```
107+
108+
<style>
109+
110+
table .type {font-size: smaller; font-weight: normal; height: 1.35em;}
111+
table .summary {font-size: smaller; font-weight: normal; height: 33px;}
112+
footer {font-family: var(--sans-serif); font-size: small; color: var(--theme-foreground-faint)}
113+
114+
</style>

0 commit comments

Comments
 (0)