Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 16 additions & 5 deletions apps/web/src/components/analytics/analytics-breakdown-panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -209,14 +209,19 @@ export function AnalyticsBreakdownPanel({
)

return (
<div className="rounded-md border bg-card">
// Its own container, not `/page`: the panel's width depends on both the page
// column and whether the breakdown grid is 1- or 2-up, so the panel itself
// is the only honest measure. The expand dialog portals out of this card —
// `/panel` classes are card-only (`!ranked`); the dialog keeps viewport
// queries, which are truthful there via its `max-w-[92vw]`.
<div className="@container/panel rounded-md border bg-card">
<div className="flex flex-wrap items-center justify-between gap-x-3 gap-y-2 px-3 pt-2.5 pb-2">
{tabs}
<FilterInput
value={query}
onChange={setQuery}
nounPlural={dimension.nounPlural}
className="w-40"
className="min-w-24 max-w-40 flex-1"
/>
</div>

Expand Down Expand Up @@ -432,13 +437,18 @@ function BreakdownTable({
currentKey={sortKey}
dir={sortDir}
onSort={onSort}
// In the card, Views is the ranking column for pages — Sessions is
// the number a very narrow panel can afford to drop.
hidden={!ranked && hasViews ? "hidden @min-[380px]/panel:flex" : undefined}
/>
{showShare ? (
<ColumnHead<SortKey>
label="Share"
width="w-16"
align="right"
hidden={ranked ? "max-sm:hidden" : undefined}
// Card mode sheds Share by panel width — the row's background bar
// already carries it. The dialog sheds by viewport, as before.
hidden={ranked ? "max-sm:hidden" : "hidden @min-[380px]/panel:flex"}
/>
) : null}
</DataTable.Head>
Expand All @@ -462,7 +472,7 @@ function BreakdownTable({
// selected row's background-color instead of overwriting it.
style={shareBar(row.share)}
className={cn(
"group flex w-full items-center gap-4 border-b border-border/40 px-4 py-2 text-left transition-colors last:border-0",
"group flex w-full items-center gap-3 border-b border-border/40 px-3 py-2 text-left transition-colors last:border-0 @min-[420px]/panel:gap-4 @min-[420px]/panel:px-4",
// Hover and focus lift the whole row from behind the fill, so a
// long bar and a short one light up by the same amount.
// `--foreground`, not `--muted`: muted is barely above this
Expand Down Expand Up @@ -513,6 +523,7 @@ function BreakdownTable({
"text-right font-mono text-[11px] tabular-nums",
ranked ? "w-16 sm:w-24" : hasViews ? "w-20" : "w-24",
hasViews && !ranked && "text-muted-foreground",
!ranked && hasViews && "hidden @min-[380px]/panel:inline-block",
)}
>
{formatNumber(row.count)}
Expand All @@ -521,7 +532,7 @@ function BreakdownTable({
<span
className={cn(
"w-16 text-right font-mono text-[11px] tabular-nums text-muted-foreground",
ranked && "max-sm:hidden",
ranked ? "max-sm:hidden" : "hidden @min-[380px]/panel:inline-block",
)}
>
{formatPercent(row.share)}
Expand Down
17 changes: 12 additions & 5 deletions apps/web/src/components/analytics/analytics-metric-strip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,17 @@ import {
* this grid needs the rule between its two rows at every width. The classes are
* otherwise `StatRail`'s exactly.
*
* The 4-up break is held to `lg` rather than `md`: eight tiles carrying a value,
* a delta and a sparkline truncate their labels at tablet width where four do
* not.
* The 4-up break is a *container* query, not a viewport one: with both sidebars
* open the content column can be ~512px narrower than the viewport, and a
* viewport `lg:` flipped to four columns inside a space that fits two. 880px is
* the honest budget — each tile carries `px-5`, a no-wrap value ("12m 45s" at
* 26px mono is ~110px), a `w-24` spark slot and the gaps, ~220px apiece.
*/
const GRID =
"grid grid-cols-2 divide-x divide-y divide-border overflow-hidden rounded-md border bg-card lg:grid-cols-4"
"grid grid-cols-2 divide-x divide-y divide-border overflow-hidden rounded-md border bg-card @min-[880px]/page:grid-cols-4"

/** Tighter tile chrome once the 2-col grid leaves each tile under ~260px. */
const TILE_NARROW = "@max-[560px]/page:px-4 @max-[560px]/page:py-3"

interface AnalyticsMetricStripProps {
source: AnalyticsMetricSource
Expand Down Expand Up @@ -104,6 +109,8 @@ function MetricTile({
disabled={!available}
onSelect={() => onSelect(metric.key)}
delay={delay}
className={TILE_NARROW}
valueClassName="@max-[560px]/page:text-[22px]"
/>
)
}
Expand Down Expand Up @@ -146,7 +153,7 @@ export function AnalyticsMetricStripLoading() {
return (
<div className={GRID}>
{ANALYTICS_METRICS.map((metric) => (
<div key={metric.key} className="px-5 py-4">
<div key={metric.key} className={cn("px-5 py-4", TILE_NARROW)}>
<Skeleton className="h-3 w-20" />
<div className="mt-3 flex items-end justify-between gap-3">
<Skeleton className="h-7 w-20" />
Expand Down
10 changes: 8 additions & 2 deletions apps/web/src/components/analytics/analytics-traffic-chart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
verticalGradient,
type PlotTooltipSeries,
} from "@maple/ui/components/plot"
import { useMediaQuery } from "@maple/ui/hooks/use-media-query"
import { useTheme } from "@maple/ui/hooks/use-theme"
import { linkedCursorChartProps } from "@/hooks/use-linked-cursor"

Expand Down Expand Up @@ -153,6 +154,11 @@ export function AnalyticsTrafficChart({ metric, companion, source, syncId }: Ana
[metric, companion, colors, totals],
)

// On a phone the 52px axis gutter is ~15% of the plot; the compact tick labels
// ("1.2k", "45s") fit in 36. Viewport rather than container is honest here —
// on phones the sidebars are sheets, so the two agree.
const narrow = useMediaQuery("max-sm")

const definition = useMemo(() => {
const at = (point: TrafficPoint) => point.label
// A bucket one table has and the other doesn't is a gap, not a zero —
Expand Down Expand Up @@ -217,12 +223,12 @@ export function AnalyticsTrafficChart({ metric, companion, source, syncId }: Ana
},
},
},
margin: { left: 52, right: 8, top: 4, bottom: 0 },
margin: { left: narrow ? 36 : 52, right: 8, top: 4, bottom: 0 },
focus: "group-x",
focusRing: false,
tooltip: cursorTooltip(focusStore.anchor),
})
}, [data, painted, gradientPrefix, chromeColors, metric, focusStore])
}, [data, painted, gradientPrefix, chromeColors, metric, focusStore, narrow])

// Only when there are two series to tell apart — a lone series is already
// named by the card title, and a legend restating it is one accessory too many.
Expand Down
9 changes: 8 additions & 1 deletion apps/web/src/components/infra/primitives/stat-rail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ interface StatRailItemProps {
selected?: boolean
/** A tile with nothing to show: still rendered, but not selectable. */
disabled?: boolean
/** Extra classes on the tile shell, e.g. container-query padding overrides. */
className?: string
/** Extra classes on the value, e.g. a container-query size step-down. */
valueClassName?: string
}

/**
Expand Down Expand Up @@ -78,6 +82,8 @@ export function StatRailItem({
onSelect,
selected,
disabled,
className,
valueClassName,
}: StatRailItemProps) {
const body = (
<>
Expand Down Expand Up @@ -105,6 +111,7 @@ export function StatRailItem({
className={cn(
"shrink-0 whitespace-nowrap font-mono text-[26px] font-semibold tabular-nums leading-none tracking-[-0.01em]",
VALUE_TONE[tone],
valueClassName,
)}
>
{value}
Expand All @@ -125,7 +132,7 @@ export function StatRailItem({
</>
)

const shell = cn("relative px-5 py-4 animate-in fade-in slide-in-from-bottom-1 duration-500")
const shell = cn("relative px-5 py-4 animate-in fade-in slide-in-from-bottom-1 duration-500", className)
const style = delay ? { animationDelay: `${delay}ms`, animationFillMode: "backwards" } : undefined

if (!onSelect) {
Expand Down
4 changes: 2 additions & 2 deletions apps/web/src/routes/analytics/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,7 @@ function AnalyticsContent({

{Result.builder(breakdownsResult)
.onInitial(() => (
<div className="grid gap-4 lg:grid-cols-2">
<div className="grid gap-4 @min-[880px]/page:grid-cols-2">
<Skeleton className="h-72 w-full" />
<Skeleton className="h-72 w-full" />
</div>
Expand Down Expand Up @@ -522,7 +522,7 @@ function AnalyticsContent({
]

return (
<div className="grid items-start gap-4 lg:grid-cols-2">
<div className="grid items-start gap-4 @min-[880px]/page:grid-cols-2">
{cards.map((card) => (
<AnalyticsBreakdownPanel
key={card.id}
Expand Down
Loading