Chart Scale
Headless, dependency-free, d3-compatible scales with Intl tick formatting.
| Package | @vizejs/ui/chart-scale |
| Maturity | stable |
| Own the source | vize lib pull chart-scale |
| Requires | — |
| Aliases | scale, d3 scale, axis scale, linear scale, time scale, band scale |
| Covers | d3-scale, d3-array ticks, d3-time, Intl.NumberFormat, Intl.DateTimeFormat |
API
discreteKey
Identity key for discrete values; dates compare by timestamp like d3's InternMap.
function discreteKey(value: DiscreteValue): DiscreteKey
scaleOrdinal
Create an immutable ordinal scale. Unlike d3, unknown values never mutate the domain; without an unknown output they cycle through the range in the order they are first seen by this scale instance.
function scaleOrdinal<Domain extends DiscreteValue, Output>( options: OrdinalScaleOptions<Domain, Output>, ): OrdinalScale<Domain, Output>
scaleBand
Create an immutable band scale for categorical positions such as bars.
function scaleBand<Domain extends DiscreteValue>( options: BandScaleOptions<Domain> = {}, ): BandScale<Domain>
const x = scaleBand({ domain: ["a", "b", "c"], range: [0, 300], paddingInner: 0.1 });
x("b"); // band start
x.bandwidth; // band width
scalePoint
Create an immutable point scale: evenly spaced positions with zero bandwidth.
function scalePoint<Domain extends DiscreteValue>( options: BandScaleOptions<Domain> = {}, ): PointScale<Domain>
createContinuousCore
Build the forward and inverse mappings shared by every continuous scale.
function createContinuousCore(config: { readonly domain: readonly number[]; readonly range: readonly number[]; readonly clamp: boolean; readonly round: boolean; readonly unknown: number; readonly transform: Unary; readonly untransform: Unary; }): ContinuousCore
scaleLinear
Create an immutable linear scale.
function scaleLinear(options: ContinuousScaleOptions = {}): LinearScale
const y = scaleLinear({ domain: [0, 120], range: [300, 0], nice: true });
y(60); // 150
scalePow
Create an immutable power scale. Use exponent: 0.5 for area-proportional encodings such as bubble radii.
function scalePow(options: PowScaleOptions = {}): PowScale
scaleSqrt
Create a square-root scale, scalePow with exponent: 0.5.
function scaleSqrt(options: ContinuousScaleOptions = {}): PowScale
scaleLog
Create an immutable logarithmic scale. Ticks and nice domains snap to powers of the base, and tickFormat blanks minor ticks on dense axes like d3.
function scaleLog(options: LogScaleOptions = {}): LogScale
d3ScaleVectors
const d3ScaleVectors
numberTickFormat
Build a localized formatter for ticks of [start, stop]. Precision follows d3's precisionFixed(tickStep), so adjacent ticks are always distinguishable without trailing noise; with the default en-US locale the output equals d3's ",f" format except that Intl uses an ASCII hyphen-minus rather than −.
function numberTickFormat( start: number, stop: number, count: number, options: NumberTickFormatOptions = {}, ): (value: number) => string
defaultTimeTickFormats
Default Intl.DateTimeFormat options per time tick granularity.
const defaultTimeTickFormats: Readonly< Record<TimeTickGranularity, Intl.DateTimeFormatOptions> >
timeTickFormat
Build a granularity-aware time formatter. granularity decides which calendar unit a tick represents (as d3's multi-scale time format does); each unit is formatted with Intl.DateTimeFormat in timeZone, so labels are localized and identical on server and client.
function timeTickFormat( timeZone: string, granularity: (date: Date) => TimeTickGranularity, options: TimeTickFormatOptions = {}, ): (date: Date) => string
ticks
Return about count human-friendly values (multiples of 1, 2, or 5 times a power of ten) inside [start, stop], in the same direction as the input.
function ticks(start: number, stop: number, count: number): number[]
tickIncrement
Tick increment for [start, stop]. Positive values are the step; negative values are the inverse step (-10 means 0.1), which avoids float error.
function tickIncrement(start: number, stop: number, count: number): number
tickStep
Signed distance between adjacent ticks for [start, stop].
function tickStep(start: number, stop: number, count: number): number
niceExtent
Extend [start, stop] outward to tick-aligned values, iterating like d3's linear.nice until the tick increment stabilizes. Direction is preserved.
function niceExtent(start: number, stop: number, count = 10): [number, number]
decimalExponent
Decimal exponent of value, as in scientific notation (0.05 → -2).
function decimalExponent(value: number): number
precisionFixed
Fraction digits needed to distinguish ticks step apart (d3 precisionFixed).
function precisionFixed(step: number): number
bisectRight
Index of the first element greater than value in an ascending array.
function bisectRight( values: readonly number[], value: number, lo = 0, hi = values.length, ): number
createZoneClock
Create a wall-clock converter for timeZone.
function createZoneClock(timeZone: string): ZoneClock
zoneIntervals
Build (and cache) the calendar intervals for timeZone.
function zoneIntervals(timeZone: string): ZoneIntervals
timeTickInterval
Choose the tick interval d3's timeTickInterval would pick for [start, stop].
function timeTickInterval( intervals: ZoneIntervals, start: number, stop: number, count: number, tickStep: (start: number, stop: number, count: number) => number, ): TimeInterval | null
scaleTime
Create an immutable, calendar-aware time scale. Ticks, nice domains, and labels are computed in timeZone (default "UTC"), never in the host's local zone, so server rendering and hydration agree. With "UTC" the ticks equal d3's scaleUtc; with an IANA zone they follow the same calendar rules as d3's local scaleTime would in that zone.
function scaleTime(options: TimeScaleOptions = {}): TimeScale
const x = scaleTime({ domain: [start, end], range: [0, 640], timeZone: "Asia/Tokyo" });
const format = x.tickFormat({ locale: "ja-JP" });
x.ticks(6).map(format);
Behavior
Normative input -> outcome table for @vizejs/ui/chart-scale: linear, pow,
sqrt, log, time, band, point, and ordinal scales, tick generation, nice
domains, and Intl-based tick formatting. Every row is proven by the named test
in chart-scale.test.ts or chart-scale-ssr.test.ts; compile-only guarantees
live in chart-scale.types.test-d.ts.
The numeric algorithms are ports of d3-array 3, d3-scale 4, and d3-time 3 (ISC
licensed). Parity is asserted against fixed vectors in scale-d3-vectors.ts,
generated once by running the same inputs through d3 under TZ=UTC,
TZ=America/New_York, and TZ=Asia/Tokyo. There is no runtime or test
dependency on d3. Scales are immutable callable objects: nice() and with()
return copies.
| # | Input | Outcome | Proven by |
|---|---|---|---|
| K1 | ticks, tickIncrement, tickStep |
identical to d3-array, including reversed, tiny, huge, and degenerate extents | ticks, tickIncrement, and tickStep match d3-array exactly |
| K2 | linear map / invert / clamp / round | identical to d3 including piecewise domains, descending domains, and zero-width domains | linear scales map, invert, clamp, round, and tick like d3 |
| K3 | nice |
iterates to a stable tick increment like d3; copies, never mutates | nice domains iterate to a stable tick increment like d3 |
| K4 | number tickFormat |
Intl.NumberFormat with d3 precisionFixed digits; locale and percent options |
number tick formats match d3 ',f' precision through Intl |
| K5 | pow / sqrt | signed power transform and linear ticks identical to d3 | pow and sqrt scales follow d3's signed power transform |
| K6 | log | map, invert, ticks, nice, negative domains, and d3's minor-label blanking | log scales map, tick, nice, and blank minor labels like d3 |
| K7 | band / point | step, bandwidth, padding, align, round, and reversed ranges identical to d3 | band and point scales lay out categories like d3 |
| K8 | ordinal | range cycling like d3, but unknown values never mutate the domain | ordinal scales cycle the range without mutating the domain |
| K9 | UTC time ticks / nice / label granularity | identical to d3 scaleUtc from milliseconds to centuries |
UTC time ticks, nice domains, and label granularity match d3 scaleUtc |
| K10 | zoned time ticks | timeZone reproduces d3 local scaleTime under that TZ, including DST transitions |
zoned time ticks match d3 local scaleTime run under that TZ |
| K11 | time map / invert / tickFormat |
Intl.DateTimeFormat labels per granularity, in the scale's zone and locale |
time scales map dates, invert, and format ticks with Intl in their zone |
| K12 | with, options, frozen domains |
immutable copies that round-trip their options | scales are immutable and expose their options |
| K13 | SSR on hosts in different time zones | byte-identical markup, because ticks and labels never read the host zone | scale-driven markup is byte-identical across requests and host time zones |
| K14 | hydration | scale-driven markup hydrates without warnings | hydrates scale-driven markup without mismatch warnings |
Intl renders negative numbers with an ASCII hyphen-minus where d3 uses −;
that is the only intentional difference in number labels.