Skip to content

Commit 96d6d8d

Browse files
committed
polish dataset statistics charts
1 parent 3a69395 commit 96d6d8d

2 files changed

Lines changed: 153 additions & 48 deletions

File tree

src/components/sections/dataset-statistics/GrowthHistogram.svelte

Lines changed: 107 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,23 @@
1515
1616
const peakCount = monthly.reduce((m, d) => Math.max(m, d.count), 0) || 1;
1717
18+
function niceStep(peak, targetTicks = 4) {
19+
const rough = peak / targetTicks;
20+
const magnitude = Math.pow(10, Math.floor(Math.log10(rough)));
21+
const norm = rough / magnitude;
22+
let step;
23+
if (norm < 1.5) step = magnitude;
24+
else if (norm < 3) step = 2 * magnitude;
25+
else if (norm < 7) step = 5 * magnitude;
26+
else step = 10 * magnitude;
27+
return step;
28+
}
29+
30+
const yStep = niceStep(peakCount);
31+
const axisMax = Math.ceil(peakCount / yStep) * yStep;
32+
const yTicks = [];
33+
for (let v = yStep; v < axisMax; v += yStep) yTicks.push(v);
34+
1835
function monthLabel(yyyymm) {
1936
const [y, m] = yyyymm.split("-");
2037
const d = new Date(Number(y), Number(m) - 1, 1);
@@ -25,10 +42,10 @@
2542
? Array.from(
2643
new Set([
2744
0,
28-
Math.floor(monthly.length * 0.25),
29-
Math.floor(monthly.length * 0.5),
30-
Math.floor(monthly.length * 0.75),
31-
monthly.length - 1
45+
Math.floor(monthly.length * 0.2),
46+
Math.floor(monthly.length * 0.4),
47+
Math.floor(monthly.length * 0.6),
48+
Math.floor(monthly.length * 0.8)
3249
])
3350
)
3451
: [];
@@ -65,24 +82,47 @@
6582
<div class="histogram-wrap" bind:this={histoWrap}>
6683
<div class="histogram-meta">
6784
<span class="histogram-title">Tasks merged per month</span>
68-
<span class="histogram-axis">peak: {peakCount}</span>
6985
</div>
70-
<div
71-
class="histogram"
72-
role="img"
73-
aria-label="Monthly distribution of tasks"
74-
on:mouseleave={clearHover}
75-
>
76-
{#each monthly as m, i}
86+
<div class="chart-area">
87+
<div class="y-axis" aria-hidden="true">
88+
{#each yTicks as t}
89+
<span class="y-tick" style="bottom: {(t / axisMax) * 100}%">{t}</span>
90+
{/each}
91+
</div>
92+
<div class="plot">
93+
<div class="gridlines" aria-hidden="true">
94+
{#each yTicks as t}
95+
<div
96+
class="gridline"
97+
style="bottom: {(t / axisMax) * 100}%"
98+
></div>
99+
{/each}
100+
</div>
77101
<div
78-
class="bar"
79-
class:active={hovered === m}
80-
style="height: {(m.count / peakCount) * 100}%; --i: {i}"
81-
aria-label={`${monthLabel(m.month)}: ${m.count} task${m.count === 1 ? "" : "s"}`}
82-
on:mouseenter={(e) => hoverBar(m, e)}
83-
on:mousemove={(e) => hoverBar(m, e)}
84-
></div>
85-
{/each}
102+
class="histogram"
103+
role="img"
104+
aria-label="Monthly distribution of tasks"
105+
on:mouseleave={clearHover}
106+
>
107+
{#each monthly as m, i}
108+
<div
109+
class="bar"
110+
class:active={hovered === m}
111+
style="height: {(m.count / axisMax) * 100}%; --i: {i}"
112+
aria-label={`${monthLabel(m.month)}: ${m.count} task${m.count === 1 ? "" : "s"}`}
113+
on:mouseenter={(e) => hoverBar(m, e)}
114+
on:mousemove={(e) => hoverBar(m, e)}
115+
></div>
116+
{/each}
117+
</div>
118+
<div class="histogram-axis-row">
119+
{#each monthly as m, i}
120+
<span class="tick" class:show={tickIdxs.includes(i)}>
121+
{tickIdxs.includes(i) ? monthLabel(m.month) : ""}
122+
</span>
123+
{/each}
124+
</div>
125+
</div>
86126
</div>
87127

88128
{#if hovered}
@@ -98,13 +138,6 @@
98138
</div>
99139
</div>
100140
{/if}
101-
<div class="histogram-axis-row">
102-
{#each monthly as m, i}
103-
<span class="tick" class:show={tickIdxs.includes(i)}>
104-
{tickIdxs.includes(i) ? monthLabel(m.month) : ""}
105-
</span>
106-
{/each}
107-
</div>
108141
</div>
109142
</div>
110143

@@ -183,7 +216,54 @@
183216
letter-spacing: 0.05em;
184217
}
185218
219+
.chart-area {
220+
display: flex;
221+
gap: 8px;
222+
align-items: stretch;
223+
}
224+
225+
.y-axis {
226+
position: relative;
227+
width: 22px;
228+
flex-shrink: 0;
229+
height: 160px;
230+
font-family: var(--mono);
231+
font-size: 0.65rem;
232+
color: var(--text-muted);
233+
}
234+
235+
.y-tick {
236+
position: absolute;
237+
right: 0;
238+
transform: translateY(50%);
239+
line-height: 1;
240+
text-align: right;
241+
}
242+
243+
.plot {
244+
position: relative;
245+
flex: 1 1 0;
246+
min-width: 0;
247+
}
248+
249+
.gridlines {
250+
position: absolute;
251+
inset: 0 0 auto 0;
252+
height: 160px;
253+
pointer-events: none;
254+
}
255+
256+
.gridline {
257+
position: absolute;
258+
left: 0;
259+
right: 0;
260+
height: 1px;
261+
background: var(--border-primary);
262+
opacity: 0.5;
263+
}
264+
186265
.histogram {
266+
position: relative;
187267
display: flex;
188268
align-items: flex-end;
189269
gap: 2px;

src/components/sections/dataset-statistics/RepoTreemap.svelte

Lines changed: 46 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -111,26 +111,22 @@
111111
stroke-width="1"
112112
/>
113113
{#if labelVisible(c)}
114-
<text
115-
x={c.x0 + 8}
116-
y={c.y0 + 18}
117-
class="cell-label"
118-
fill={c.value / totalProblems > 0.04
119-
? "#fff"
120-
: "var(--text-primary)"}
114+
<foreignObject
115+
x={c.x0 + 6}
116+
y={c.y0 + 4}
117+
width={Math.max(0, c.x1 - c.x0 - 12)}
118+
height={Math.max(0, c.y1 - c.y0 - 8)}
121119
>
122-
{c.data.repository}
123-
</text>
124-
<text
125-
x={c.x0 + 8}
126-
y={c.y0 + 34}
127-
class="cell-count"
128-
fill={c.value / totalProblems > 0.04
129-
? "rgba(255,255,255,0.85)"
130-
: "var(--text-muted)"}
131-
>
132-
{c.data.count} task{c.data.count === 1 ? "" : "s"}
133-
</text>
120+
<div
121+
class="cell-label-box"
122+
class:dark={c.value / totalProblems > 0.04}
123+
>
124+
<span class="cell-label">{c.data.repository}</span>
125+
<span class="cell-count"
126+
>{c.data.count} task{c.data.count === 1 ? "" : "s"}</span
127+
>
128+
</div>
129+
</foreignObject>
134130
{/if}
135131
</g>
136132
{/each}
@@ -201,17 +197,46 @@
201197
opacity: 0.55;
202198
}
203199
200+
.cell-label-box {
201+
width: 100%;
202+
height: 100%;
203+
display: flex;
204+
flex-direction: column;
205+
gap: 2px;
206+
overflow: hidden;
207+
pointer-events: none;
208+
color: var(--text-primary);
209+
line-height: 1.2;
210+
}
211+
212+
.cell-label-box.dark {
213+
color: #fff;
214+
}
215+
216+
.cell-label-box.dark .cell-count {
217+
color: rgba(255, 255, 255, 0.85);
218+
}
219+
204220
.cell-label {
205221
font-family: var(--mono);
206222
font-size: 0.78rem;
207223
font-weight: 600;
208-
pointer-events: none;
224+
overflow-wrap: anywhere;
225+
word-break: break-word;
226+
overflow: hidden;
227+
display: -webkit-box;
228+
-webkit-line-clamp: 2;
229+
-webkit-box-orient: vertical;
209230
}
210231
211232
.cell-count {
212233
font-family: var(--sans);
213234
font-size: 0.7rem;
214-
pointer-events: none;
235+
color: var(--text-muted);
236+
overflow: hidden;
237+
text-overflow: ellipsis;
238+
white-space: nowrap;
239+
flex-shrink: 0;
215240
}
216241
217242
.tooltip {

0 commit comments

Comments
 (0)