fix(stats): scroll dense mobile charts
Some checks are pending
deploy / deploy (push) Waiting to run

This commit is contained in:
Adam 2026-05-29 15:10:44 -05:00
parent 8fe6cd9932
commit 7342e94094
No known key found for this signature in database
GPG key ID: 9CB48779AF150E75
2 changed files with 48 additions and 0 deletions

View file

@ -2858,6 +2858,37 @@
height: 400px;
}
[data-page="stats"] [data-component="top-models-chart"][data-dense-labels="true"],
[data-page="stats"] [data-component="market-share"][data-dense-labels="true"] {
overflow-x: auto;
overflow-y: visible;
overscroll-behavior-x: contain;
scrollbar-width: none;
}
[data-page="stats"] [data-component="top-models-chart"][data-dense-labels="true"]::-webkit-scrollbar,
[data-page="stats"] [data-component="market-share"][data-dense-labels="true"]::-webkit-scrollbar {
display: none;
}
[data-page="stats"] [data-component="top-models-chart"][data-dense-labels="true"] {
--top-models-mobile-bar-width: 12px;
}
[data-page="stats"] [data-component="top-models-chart"][data-dense-labels="true"] [data-slot="top-models-axis"],
[data-page="stats"] [data-component="top-models-chart"][data-dense-labels="true"] [data-slot="top-models-bars"] {
min-width: calc(var(--top-models-count) * (var(--top-models-mobile-bar-width) + var(--top-models-bar-gap)));
}
[data-page="stats"] [data-component="market-share"][data-dense-labels="true"] {
--market-mobile-bar-width: 12px;
}
[data-page="stats"] [data-component="market-share"][data-dense-labels="true"] [data-slot="market-labels"],
[data-page="stats"] [data-component="market-share"][data-dense-labels="true"] [data-slot="market-bars"] {
min-width: calc(var(--market-count) * (var(--market-mobile-bar-width) + var(--market-gap)));
}
[data-page="stats"] [data-slot="top-models-axis"] > div {
position: relative;
display: flex;

View file

@ -596,18 +596,23 @@ function TopModelsChart(props: {
activeModel: string | undefined
onActiveModelChange: (model: string | undefined) => void
}) {
let chartRef: HTMLDivElement | undefined
const [activeIndex, setActiveIndex] = createSignal<number>()
const maxTotal = createMemo(() => getTopModelsMaxTotal(props.data))
const segmentOrder = createMemo(() => getTopModelsSegmentOrder(props.data))
const activePoint = createMemo(() => props.data[activeIndex() ?? -1])
createEffect(() => scrollDenseChartToEnd(chartRef, props.range, props.data.length))
return (
<div
ref={chartRef}
data-component="top-models-chart"
data-range={props.range}
data-dense-labels={isDenseColumnRange(props.range) ? "true" : undefined}
role="img"
aria-label="Stacked top model usage chart"
style={{ "--top-models-count": props.data.length } as JSX.CSSProperties}
>
<div data-slot="top-models-axis" aria-hidden="true">
<For each={props.data}>
@ -817,6 +822,13 @@ function isDenseColumnRange(range: UsageRange) {
return range === "1M" || range === "2M"
}
function scrollDenseChartToEnd(element: HTMLDivElement | undefined, range: UsageRange, count: number) {
if (!element || count <= 0 || !isDenseColumnRange(range) || typeof window === "undefined") return
window.requestAnimationFrame(() => {
element.scrollLeft = element.scrollWidth - element.clientWidth
})
}
function formatTopModelsMobileDate(label: string, range: UsageRange) {
if (range === "1M" || range === "2M") return label.split(" - ")[0] ?? label
return label
@ -1000,8 +1012,13 @@ function MarketShare(props: {
onActiveIndexChange: (index: number) => void
onActiveAuthorChange: (author: string) => void
}) {
let chartRef: HTMLDivElement | undefined
createEffect(() => scrollDenseChartToEnd(chartRef, props.range, props.data.length))
return (
<div
ref={chartRef}
data-component="market-share"
data-range={props.range}
data-dense-labels={isDenseColumnRange(props.range) ? "true" : undefined}