Skip to content

Commit 7bd7370

Browse files
committed
Auto-fit for BigNumber
1 parent e0ea070 commit 7bd7370

2 files changed

Lines changed: 48 additions & 52 deletions

File tree

apps/webapp/app/components/primitives/charts/BigNumberCard.tsx

Lines changed: 34 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type {
44
BigNumberAggregationType,
55
BigNumberConfiguration,
66
} from "~/components/metrics/QueryWidget";
7+
import { AnimatedNumber } from "../AnimatedNumber";
78
import { Spinner } from "../Spinner";
89
import { Paragraph } from "../Paragraph";
910

@@ -79,42 +80,45 @@ function aggregateValues(values: number[], aggregation: BigNumberAggregationType
7980
}
8081

8182
/**
82-
* Formats a number for display as a big number with abbreviation (K/M/B suffixes).
83+
* Computes the display value and unit suffix for abbreviated display.
84+
* Returns the divided-down number (e.g. 1.5 for 1500) and the suffix (e.g. "K"),
85+
* along with the appropriate decimal places for formatting.
8386
*/
84-
function formatBigNumberAbbreviated(value: number): { formatted: string; unitSuffix?: string } {
87+
function abbreviateValue(value: number): {
88+
displayValue: number;
89+
unitSuffix?: string;
90+
decimalPlaces: number;
91+
} {
8592
if (Math.abs(value) >= 1_000_000_000) {
8693
const v = value / 1_000_000_000;
87-
return { formatted: v % 1 === 0 ? v.toFixed(0) : v.toFixed(1), unitSuffix: "B" };
94+
return { displayValue: v, unitSuffix: "B", decimalPlaces: v % 1 === 0 ? 0 : 1 };
8895
}
8996
if (Math.abs(value) >= 1_000_000) {
9097
const v = value / 1_000_000;
91-
return { formatted: v % 1 === 0 ? v.toFixed(0) : v.toFixed(1), unitSuffix: "M" };
98+
return { displayValue: v, unitSuffix: "M", decimalPlaces: v % 1 === 0 ? 0 : 1 };
9299
}
93100
if (Math.abs(value) >= 1_000) {
94101
const v = value / 1_000;
95-
return { formatted: v % 1 === 0 ? v.toFixed(0) : v.toFixed(1), unitSuffix: "K" };
102+
return { displayValue: v, unitSuffix: "K", decimalPlaces: v % 1 === 0 ? 0 : 1 };
96103
}
97-
return { formatted: formatPlainNumber(value) };
104+
return { displayValue: value, decimalPlaces: getDecimalPlaces(value) };
98105
}
99106

100107
/**
101-
* Formats a number for display without abbreviation.
108+
* Determines decimal places for plain (non-abbreviated) display.
102109
*/
103-
function formatPlainNumber(value: number): string {
104-
if (Number.isInteger(value)) {
105-
return value.toLocaleString();
106-
}
107-
if (Math.abs(value) < 0.01) {
108-
return value.toFixed(4);
109-
}
110-
if (Math.abs(value) < 1) {
111-
return value.toFixed(3);
112-
}
113-
return value.toFixed(2);
110+
function getDecimalPlaces(value: number): number {
111+
if (Number.isInteger(value)) return 0;
112+
const abs = Math.abs(value);
113+
if (abs >= 100) return 0;
114+
if (abs >= 10) return 1;
115+
if (abs >= 1) return 2;
116+
if (abs >= 0.01) return 3;
117+
return 4;
114118
}
115119

116120
export function BigNumberCard({ rows, columns, config, isLoading = false }: BigNumberCardProps) {
117-
const { column, aggregation, sortDirection, abbreviate = false, prefix, suffix } = config;
121+
const { column, aggregation, sortDirection, abbreviate = true, prefix, suffix } = config;
118122

119123
const result = useMemo(() => {
120124
if (rows.length === 0) return null;
@@ -127,38 +131,38 @@ export function BigNumberCard({ rows, columns, config, isLoading = false }: BigN
127131

128132
if (isLoading) {
129133
return (
130-
<div className="flex h-full items-center justify-center p-6">
134+
<div className="grid h-full place-items-center [container-type:size]">
131135
<Spinner className="size-6" />
132136
</div>
133137
);
134138
}
135139

136140
if (result === null) {
137141
return (
138-
<div className="flex h-full items-center justify-center p-6">
142+
<div className="grid h-full place-items-center [container-type:size]">
139143
<Paragraph variant="small" className="text-text-dimmed">
140144
No data to display
141145
</Paragraph>
142146
</div>
143147
);
144148
}
145149

146-
const { formatted, unitSuffix } = abbreviate
147-
? formatBigNumberAbbreviated(result)
148-
: { formatted: formatPlainNumber(result), unitSuffix: undefined };
150+
const { displayValue, unitSuffix, decimalPlaces } = abbreviate
151+
? abbreviateValue(result)
152+
: { displayValue: result, unitSuffix: undefined, decimalPlaces: getDecimalPlaces(result) };
149153

150154
return (
151-
<div className="flex h-full items-center justify-center p-6">
152-
<div className="text-[3.75rem] font-normal tabular-nums leading-none text-text-bright">
153-
<div className="flex items-baseline gap-1">
155+
<div className="h-full w-full [container-type:size]">
156+
<div className="grid h-full w-full place-items-center">
157+
<div className="flex items-baseline gap-[0.15em] whitespace-nowrap font-normal tabular-nums leading-none text-text-bright text-[clamp(24px,12cqw,96px)]">
154158
{prefix && <span>{prefix}</span>}
155-
{formatted}
159+
<AnimatedNumber value={displayValue} decimalPlaces={decimalPlaces} />
156160
{(unitSuffix || suffix) && (
157-
<div className="text-2xl text-text-dimmed">
161+
<span className="text-[0.4em] text-text-dimmed">
158162
{unitSuffix}
159163
{unitSuffix && suffix ? " " : ""}
160164
{suffix}
161-
</div>
165+
</span>
162166
)}
163167
</div>
164168
</div>

apps/webapp/app/components/query/QueryEditor.tsx

Lines changed: 14 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -806,17 +806,13 @@ export function QueryEditor({
806806
onRenameTitle={handleRenameTitle}
807807
accessory={
808808
mode.type === "standalone" ? (
809-
<SimpleTooltip
810-
asChild
811-
button={
812-
<Button
813-
variant="minimal/small"
814-
LeadingIcon={BookmarkIcon}
815-
onClick={() => setIsSaveDialogOpen(true)}
816-
/>
817-
}
818-
content="Save to dashboard"
819-
/>
809+
<Button
810+
variant="minimal/small"
811+
LeadingIcon={BookmarkIcon}
812+
onClick={() => setIsSaveDialogOpen(true)}
813+
>
814+
Save to dashboard
815+
</Button>
820816
) : save ? (
821817
save(saveData)
822818
) : undefined
@@ -856,17 +852,13 @@ export function QueryEditor({
856852
onRenameTitle={handleRenameTitle}
857853
accessory={
858854
mode.type === "standalone" ? (
859-
<SimpleTooltip
860-
asChild
861-
button={
862-
<Button
863-
variant="minimal/small"
864-
LeadingIcon={BookmarkIcon}
865-
onClick={() => setIsSaveDialogOpen(true)}
866-
/>
867-
}
868-
content="Save to dashboard"
869-
/>
855+
<Button
856+
variant="minimal/small"
857+
LeadingIcon={BookmarkIcon}
858+
onClick={() => setIsSaveDialogOpen(true)}
859+
>
860+
Save to dashboard
861+
</Button>
870862
) : save ? (
871863
save(saveData)
872864
) : undefined

0 commit comments

Comments
 (0)