diff --git a/docs/docs/reference/project-files/dashboards.md b/docs/docs/reference/project-files/dashboards.md
index 3d9fe2ff305..3ed0690aaeb 100644
--- a/docs/docs/reference/project-files/dashboards.md
+++ b/docs/docs/reference/project-files/dashboards.md
@@ -41,12 +41,13 @@ _**`measures`**_ — numeric [aggregates](../../develop/metrics-dashboard#measur
- _**`description`**_ — a freeform text description of the dimension for your dashboard _(optional)_
- _**`ignore`**_ — hides the measure _(optional)_
- _**`valid_percent_of_total`**_ — a boolean indicating whether percent-of-total values should be rendered for this measure _(optional)_
- - _**`format_preset`**_ — one of a set of values that format dashboard measures. _(optional; default is humanize)_. Possible values include:
- - _`humanize`_ — round off numbers in an opinionated way to thousands (K), millions (M), billions (B), etc
- - _`none`_ — raw output
- - _`currency_usd`_ — output rounded to 2 decimal points prepended with a dollar sign
- - _`percentage`_ — output transformed from a rate to a percentage appended with a percentage sign
- - _`interval_ms`_ — time intervals given in milliseconds are transformed into human readable time units like hours (h), days (d), years (y), etc
+ - _**`format_d3`**_ — controls the formatting of this measure in the dashboard using a [d3-format string](https://d3js.org/d3-format). If an invalid format string is supplied, measures will be formatted with `format_preset: humanize` (described below). Measures cannot have both `format_preset` and `format_d3` entries. _(optional; if neither `format_preset` nor `format_d3` is supplied, measures will be formatted with the `humanize` preset)_
+ - _**`format_preset`**_ — controls the formatting of this measure in the dashboard according to option specified below. Measures cannot have both `format_preset` and `format_d3` entries. _(optional; if neither `format_preset` nor `format_d3` is supplied, measures will be formatted with the `humanize` preset)_
+ - _`humanize`_ — round off numbers in an opinionated way to thousands (K), millions (M), billions (B), etc
+ - _`none`_ — raw output
+ - _`currency_usd`_ — output rounded to 2 decimal points prepended with a dollar sign
+ - _`percentage`_ — output transformed from a rate to a percentage appended with a percentage sign
+ - _`interval_ms`_ — time intervals given in milliseconds are transformed into human readable time units like hours (h), days (d), years (y), etc
_**`security`**_ - define a [security policy](../../develop/security) for the dashboard _(optional)_
- _**`access`**_ - Expression indicating if the user should be granted access to the dashboard. If not defined, it will resolve to `false` and the dashboard won't be accessible to anyone. Needs to be a valid SQL expression that evaluates to a boolean. _(optional)_
diff --git a/web-common/src/components/data-graphic/guides/PointLabel.svelte b/web-common/src/components/data-graphic/guides/PointLabel.svelte
index 9df6b90cf2d..83d94fdb0b6 100644
--- a/web-common/src/components/data-graphic/guides/PointLabel.svelte
+++ b/web-common/src/components/data-graphic/guides/PointLabel.svelte
@@ -4,8 +4,8 @@
WithGraphicContexts,
WithTween,
} from "@rilldata/web-common/components/data-graphic/functional-components";
- import { formatMeasurePercentageDifference } from "@rilldata/web-common/features/dashboards/humanize-numbers";
import { justEnoughPrecision } from "@rilldata/web-common/lib/formatters";
+ import { formatMeasurePercentageDifference } from "@rilldata/web-common/lib/number-formatting/percentage-formatter";
import { cubicOut } from "svelte/easing";
import { fade } from "svelte/transition";
export let point;
diff --git a/web-common/src/components/data-types/MeasureChange.svelte b/web-common/src/components/data-types/MeasureChange.svelte
index 82384154842..199915b31bd 100644
--- a/web-common/src/components/data-types/MeasureChange.svelte
+++ b/web-common/src/components/data-types/MeasureChange.svelte
@@ -5,9 +5,6 @@
export let dark = false;
export let customStyle = "";
export let value;
-
- $: isNegative =
- (typeof value === "string" && value?.startsWith("-")) || value < 0;
-
- {!isNegative ? "+" : ""}{value}
-
+ {value}
diff --git a/web-common/src/features/dashboards/big-number/MeasureBigNumber.svelte b/web-common/src/features/dashboards/big-number/MeasureBigNumber.svelte
index a9d8739b3dd..68fc242e2ce 100644
--- a/web-common/src/features/dashboards/big-number/MeasureBigNumber.svelte
+++ b/web-common/src/features/dashboards/big-number/MeasureBigNumber.svelte
@@ -10,13 +10,11 @@
import type { TimeComparisonOption } from "@rilldata/web-common/lib/time/types";
import { crossfade, fly } from "svelte/transition";
import Spinner from "../../entity-management/Spinner.svelte";
- import {
- formatMeasurePercentageDifference,
- humanizeDataType,
- FormatPreset,
- humanizeDataTypeExpanded,
- } from "../humanize-numbers";
+
import type { MetricsViewSpecMeasureV2 } from "@rilldata/web-common/runtime-client";
+ import { createMeasureValueFormatter } from "@rilldata/web-common/lib/number-formatting/format-measure-value";
+ import { FormatPreset } from "@rilldata/web-common/lib/number-formatting/humanizer-types";
+ import { formatMeasurePercentageDifference } from "@rilldata/web-common/lib/number-formatting/percentage-formatter";
export let measure: MetricsViewSpecMeasureV2;
export let value: number;
@@ -29,26 +27,32 @@
$: description =
measure?.description || measure?.label || measure?.expression;
- $: formatPreset =
- (measure?.formatPreset as FormatPreset) || FormatPreset.HUMANIZE;
+
+ $: measureValueFormatter = createMeasureValueFormatter(measure);
+ $: measureValueFormatterUnabridged = createMeasureValueFormatter(
+ measure,
+ true
+ );
$: name = measure?.label || measure?.expression;
$: valueIsPresent = value !== undefined && value !== null;
- $: isComparisonPositive = Number.isFinite(diff) && (diff as number) >= 0;
const [send, receive] = crossfade({ fallback: fly });
- $: diff = comparisonValue ? value - comparisonValue : false;
- $: noChange = !diff;
+ $: diff =
+ valueIsPresent && comparisonValue !== undefined
+ ? value - comparisonValue
+ : 0;
+ $: noChange = !comparisonValue;
+ $: isComparisonPositive = diff >= 0;
- $: formattedDiff = `${isComparisonPositive ? "+" : ""}${humanizeDataType(
- diff,
- formatPreset
+ $: formattedDiff = `${isComparisonPositive ? "+" : ""}${measureValueFormatter(
+ diff
)}`;
/** when the measure is a percentage, we don't show a percentage change. */
- $: measureIsPercentage = formatPreset === FormatPreset.PERCENTAGE;
+ $: measureIsPercentage = measure?.formatPreset === FormatPreset.PERCENTAGE;