@pond-ts/charts 0.45.0 → 0.47.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/grid.d.ts CHANGED
@@ -1,13 +1,21 @@
1
1
  /**
2
2
  * Stroke the plot's gridlines: a vertical line at each `xTicks` pixel and a
3
- * horizontal line at each `yTicks` pixel, faint and dashed. Drawn behind the
4
- * data layers from the same tick positions the axes label, so grid and labels
5
- * line up. `+0.5` aligns each 1px stroke to the device grid for a crisp line.
3
+ * horizontal line at each `yTicks` pixel, faint and dashed. On a calendar
4
+ * axis the verticals are the **full grain populations** (every day / month /
5
+ * aligned hour in view see `TradingTimeScale.gridLevels`), not just the
6
+ * labelled ticks. `+0.5` aligns each 1px stroke to the device grid for a
7
+ * crisp line.
8
+ *
9
+ * `xAlphas` (parallel to `xTicks`) fades individual verticals — the
10
+ * hierarchical density falloff: a crowding grain's lines dim toward invisible
11
+ * while coarser grains hold full strength. Full-alpha lines (and all the
12
+ * horizontals) batch into one path; only the fading remainder pays a
13
+ * per-line stroke. A near-zero alpha is skipped.
6
14
  *
7
15
  * `save`/`restore` brackets the dash + stroke state so it doesn't leak into the
8
16
  * data layers that draw next.
9
17
  */
10
- export declare function drawGrid(ctx: CanvasRenderingContext2D, xTicks: readonly number[], yTicks: readonly number[], width: number, height: number, color: string, dash: readonly number[]): void;
18
+ export declare function drawGrid(ctx: CanvasRenderingContext2D, xTicks: readonly number[], yTicks: readonly number[], width: number, height: number, color: string, dash: readonly number[], xAlphas?: readonly number[]): void;
11
19
  /**
12
20
  * Greedily thin an **ascending** list of pixel positions so no two kept lines
13
21
  * are closer than `minGap` px — keeps the axis from crowding when collapse
@@ -20,6 +28,25 @@ export declare function thinPixels(xs: readonly number[], minGap: number): numbe
20
28
  * the plot height. Drawn a touch stronger than the dashed gridlines (solid, so a
21
29
  * session/day boundary reads as structural, not just another tick) at the
22
30
  * discontinuity provider's collapse points (see `DiscontinuityProvider.boundaries`).
31
+ *
32
+ * `alphas` (parallel to `xs`) fades individual lines — used by the `'all'`
33
+ * session-divider mode so crowding lines dim smoothly toward invisible instead
34
+ * of popping in/out with a hard density cutoff. Omitted ⇒ every line at full
35
+ * opacity in a single path (the fast default). A near-zero alpha is skipped.
36
+ */
37
+ export declare function drawDividers(ctx: CanvasRenderingContext2D, xs: readonly number[], height: number, color: string, alphas?: readonly number[]): void;
38
+ /**
39
+ * Per-line opacity for {@link drawDividers} in `'all'` mode: each line keys off
40
+ * the gap to its nearest neighbour — full at `fullPx`+, **zero** at `gonePx`
41
+ * and below, a quadratic ramp between. So as a zoom-out crowds the session
42
+ * lines they dim toward a clean plot — no hard drop that pops on pan.
43
+ *
44
+ * The curve must fall **superlinearly** in the gap: the veil a reader sees is
45
+ * `alpha × density = alpha / gap`, so the earlier linear ramp (`alpha = gap/f`)
46
+ * cancelled the density growth exactly and pinned a constant gray wash over the
47
+ * whole plot no matter how far out you zoomed. Quadratic-to-a-floor makes the
48
+ * wash itself → 0 as lines converge: alpha `t²` with
49
+ * `t = (gap − gonePx) / (fullPx − gonePx)`. `xs` ascending.
23
50
  */
24
- export declare function drawDividers(ctx: CanvasRenderingContext2D, xs: readonly number[], height: number, color: string): void;
51
+ export declare function dividerAlphas(xs: readonly number[], gonePx: number, fullPx: number): number[];
25
52
  //# sourceMappingURL=grid.d.ts.map
package/dist/grid.js CHANGED
@@ -1,20 +1,30 @@
1
1
  /**
2
2
  * Stroke the plot's gridlines: a vertical line at each `xTicks` pixel and a
3
- * horizontal line at each `yTicks` pixel, faint and dashed. Drawn behind the
4
- * data layers from the same tick positions the axes label, so grid and labels
5
- * line up. `+0.5` aligns each 1px stroke to the device grid for a crisp line.
3
+ * horizontal line at each `yTicks` pixel, faint and dashed. On a calendar
4
+ * axis the verticals are the **full grain populations** (every day / month /
5
+ * aligned hour in view see `TradingTimeScale.gridLevels`), not just the
6
+ * labelled ticks. `+0.5` aligns each 1px stroke to the device grid for a
7
+ * crisp line.
8
+ *
9
+ * `xAlphas` (parallel to `xTicks`) fades individual verticals — the
10
+ * hierarchical density falloff: a crowding grain's lines dim toward invisible
11
+ * while coarser grains hold full strength. Full-alpha lines (and all the
12
+ * horizontals) batch into one path; only the fading remainder pays a
13
+ * per-line stroke. A near-zero alpha is skipped.
6
14
  *
7
15
  * `save`/`restore` brackets the dash + stroke state so it doesn't leak into the
8
16
  * data layers that draw next.
9
17
  */
10
- export function drawGrid(ctx, xTicks, yTicks, width, height, color, dash) {
18
+ export function drawGrid(ctx, xTicks, yTicks, width, height, color, dash, xAlphas) {
11
19
  ctx.save();
12
20
  ctx.strokeStyle = color;
13
21
  ctx.lineWidth = 1;
14
22
  ctx.setLineDash([...dash]);
15
23
  ctx.beginPath();
16
- for (const x of xTicks) {
17
- const px = Math.round(x) + 0.5;
24
+ for (let i = 0; i < xTicks.length; i++) {
25
+ if (xAlphas !== undefined && (xAlphas[i] ?? 1) < 1)
26
+ continue;
27
+ const px = Math.round(xTicks[i]) + 0.5;
18
28
  ctx.moveTo(px, 0);
19
29
  ctx.lineTo(px, height);
20
30
  }
@@ -24,6 +34,19 @@ export function drawGrid(ctx, xTicks, yTicks, width, height, color, dash) {
24
34
  ctx.lineTo(width, py);
25
35
  }
26
36
  ctx.stroke();
37
+ if (xAlphas !== undefined) {
38
+ for (let i = 0; i < xTicks.length; i++) {
39
+ const a = xAlphas[i] ?? 1;
40
+ if (a >= 1 || a <= 0.02)
41
+ continue;
42
+ ctx.globalAlpha = a;
43
+ const px = Math.round(xTicks[i]) + 0.5;
44
+ ctx.beginPath();
45
+ ctx.moveTo(px, 0);
46
+ ctx.lineTo(px, height);
47
+ ctx.stroke();
48
+ }
49
+ }
27
50
  ctx.restore();
28
51
  }
29
52
  /**
@@ -45,21 +68,66 @@ export function thinPixels(xs, minGap) {
45
68
  * the plot height. Drawn a touch stronger than the dashed gridlines (solid, so a
46
69
  * session/day boundary reads as structural, not just another tick) at the
47
70
  * discontinuity provider's collapse points (see `DiscontinuityProvider.boundaries`).
71
+ *
72
+ * `alphas` (parallel to `xs`) fades individual lines — used by the `'all'`
73
+ * session-divider mode so crowding lines dim smoothly toward invisible instead
74
+ * of popping in/out with a hard density cutoff. Omitted ⇒ every line at full
75
+ * opacity in a single path (the fast default). A near-zero alpha is skipped.
48
76
  */
49
- export function drawDividers(ctx, xs, height, color) {
77
+ export function drawDividers(ctx, xs, height, color, alphas) {
50
78
  if (xs.length === 0)
51
79
  return;
52
80
  ctx.save();
53
81
  ctx.strokeStyle = color;
54
82
  ctx.lineWidth = 1;
55
83
  ctx.setLineDash([]);
56
- ctx.beginPath();
57
- for (const x of xs) {
58
- const px = Math.round(x) + 0.5;
59
- ctx.moveTo(px, 0);
60
- ctx.lineTo(px, height);
84
+ if (alphas === undefined) {
85
+ ctx.beginPath();
86
+ for (const x of xs) {
87
+ const px = Math.round(x) + 0.5;
88
+ ctx.moveTo(px, 0);
89
+ ctx.lineTo(px, height);
90
+ }
91
+ ctx.stroke();
92
+ }
93
+ else {
94
+ // Per-line opacity → each line is its own path (globalAlpha can't vary
95
+ // within one stroke). Dividers draw at base opacity 1 (nothing sets it
96
+ // before this pass), so set the line alpha directly; `restore` resets it.
97
+ for (let i = 0; i < xs.length; i++) {
98
+ const a = alphas[i] ?? 1;
99
+ if (a <= 0.02)
100
+ continue;
101
+ ctx.globalAlpha = a;
102
+ const px = Math.round(xs[i]) + 0.5;
103
+ ctx.beginPath();
104
+ ctx.moveTo(px, 0);
105
+ ctx.lineTo(px, height);
106
+ ctx.stroke();
107
+ }
61
108
  }
62
- ctx.stroke();
63
109
  ctx.restore();
64
110
  }
111
+ /**
112
+ * Per-line opacity for {@link drawDividers} in `'all'` mode: each line keys off
113
+ * the gap to its nearest neighbour — full at `fullPx`+, **zero** at `gonePx`
114
+ * and below, a quadratic ramp between. So as a zoom-out crowds the session
115
+ * lines they dim toward a clean plot — no hard drop that pops on pan.
116
+ *
117
+ * The curve must fall **superlinearly** in the gap: the veil a reader sees is
118
+ * `alpha × density = alpha / gap`, so the earlier linear ramp (`alpha = gap/f`)
119
+ * cancelled the density growth exactly and pinned a constant gray wash over the
120
+ * whole plot no matter how far out you zoomed. Quadratic-to-a-floor makes the
121
+ * wash itself → 0 as lines converge: alpha `t²` with
122
+ * `t = (gap − gonePx) / (fullPx − gonePx)`. `xs` ascending.
123
+ */
124
+ export function dividerAlphas(xs, gonePx, fullPx) {
125
+ return xs.map((x, i) => {
126
+ const left = i > 0 ? x - xs[i - 1] : Infinity;
127
+ const right = i < xs.length - 1 ? xs[i + 1] - x : Infinity;
128
+ const gap = Math.min(left, right);
129
+ const t = Math.max(0, Math.min(1, (gap - gonePx) / (fullPx - gonePx)));
130
+ return t * t;
131
+ });
132
+ }
65
133
  //# sourceMappingURL=grid.js.map
package/dist/index.d.ts CHANGED
@@ -28,6 +28,7 @@ export { YAxis } from './YAxis.js';
28
28
  export type { YAxisProps } from './YAxis.js';
29
29
  export { XAxis } from './XAxis.js';
30
30
  export type { XAxisProps } from './XAxis.js';
31
+ export type { AxisTransform } from './derivedTicks.js';
31
32
  export { TimeAxis } from './TimeAxis.js';
32
33
  export { CategoryAxis } from './CategoryAxis.js';
33
34
  export type { AxisFormat } from './format.js';
package/dist/theme.d.ts CHANGED
@@ -111,6 +111,19 @@ export interface ChartTheme {
111
111
  * boundary reads as structural.
112
112
  */
113
113
  readonly sessionDivider?: string;
114
+ /**
115
+ * The stacked date style's **band** row (the segmented second row: zebra
116
+ * date/month/year cells with left-aligned labels + dividers). `fill` is the
117
+ * shaded (odd-parity) cell background — "could be a background color" per
118
+ * the design; `divider` the turn line (falls back to {@link grid}); `label`
119
+ * the band-label ink (falls back to {@link title}.color → {@link label}).
120
+ * Optional; the whole band row is stacked-only.
121
+ */
122
+ readonly band?: {
123
+ readonly fill: string;
124
+ readonly divider?: string;
125
+ readonly label?: string;
126
+ };
114
127
  /**
115
128
  * Typography for the axis **title** — the rotated y-axis unit strip and the
116
129
  * x-axis label (distinct from the per-tick `label` colour above). Omit a
package/dist/theme.js CHANGED
@@ -112,6 +112,11 @@ export const defaultTheme = {
112
112
  grid: '#e2e8f0',
113
113
  gridDash: [2, 2],
114
114
  sessionDivider: '#cbd5e1', // slate-300 — a step stronger than the gridlines
115
+ band: {
116
+ fill: '#f8fafc', // slate-50 — the zebra shade on the stacked band row
117
+ divider: '#cbd5e1', // slate-300 turn line
118
+ label: '#334155', // slate-700 ink for band labels
119
+ },
115
120
  },
116
121
  font: {
117
122
  family: 'system-ui, -apple-system, sans-serif',
@@ -13,7 +13,14 @@ import type { DiscontinuityProvider } from './tradingTimeScale.js';
13
13
  * label doesn't carry (hours → the date, days/weeks → the month, months →
14
14
  * the year). The axis renders that as a second label row, once per boundary
15
15
  * crossing, so a month row reads `Dec Jan Feb …` with `2026` appearing exactly
16
- * where the year turns.
16
+ * where the year turns — the **stacked** date style.
17
+ *
18
+ * The **flat** date style (the default, the TradingView look) drops the second
19
+ * row: each tick that *opens* a coarser calendar period is relabelled **inline**
20
+ * to that period — the year at a year turn, the month at a month turn, the date
21
+ * at a day turn under an intraday grain — while every other tick keeps a terse
22
+ * base label (bare day-of-month, month abbrev, clock time). {@link flatFormats}
23
+ * computes the per-tick format specifiers for that single row.
17
24
  */
18
25
  /** The calendar grain a run of tick anchors is bucketed to. */
19
26
  export type TickGranularity = 'second1' | 'second5' | 'second15' | 'second30' | 'minute1' | 'minute5' | 'minute15' | 'minute30' | 'hour1' | 'hour3' | 'hour6' | 'hour12' | 'day' | 'week' | 'month' | 'quarter' | 'year';
@@ -28,32 +35,86 @@ export type TickGranularity = 'second1' | 'second5' | 'second15' | 'second30' |
28
35
  */
29
36
  export declare function bucketKey(t: number, g: TickGranularity): number;
30
37
  /**
31
- * Thin an ascending run of **session opens** down to about `count` axis ticks by
32
- * **calendar grain** the trading-terminal habit of labelling week / month /
33
- * year starts rather than an arbitrary every-nth session. Picks the finest grain
34
- * on the ladder (day week month quarter year) that yields at most
35
- * `count` buckets and returns the first open in each; beyond yearly it decimates
36
- * every-nth so the axis never crowds. Exported so the container can draw session
37
- * dividers at the same instants the axis labels.
38
+ * Thin an ascending run of **session opens** down to about `count` axis ticks.
39
+ * Picks the finest rung: every session **per-month uniform session stride**
40
+ * (still day grain, month starts pinned) month quarter year; beyond
41
+ * yearly it decimates every-nth so the axis never crowds. Exported so the
42
+ * container can draw session dividers at the same instants the axis labels.
43
+ *
44
+ * The day band thins each month to a **uniform session stride** — the month's
45
+ * first session, then every `k`-th session, truncated so the gap to the next
46
+ * month start stays ≥ `k` (slack at the month end, never a cramped tick
47
+ * before the month label). The decoded-and-validated TradingView algorithm:
48
+ * with a `provider` the stride runs in **session-index space**
49
+ * ({@link subdivideMonthsBySession}) — marks an equal number of bars apart,
50
+ * evenly spaced pixels on a collapsed axis, no weekend snapping; without one
51
+ * it falls back to day-of-month space ({@link subdivideMonthsByDay}).
52
+ * Zooming steps the stride through the integers (…4 → 3 → 2 → 1), a ~one-bar
53
+ * density change that re-labels some interior marks; month / year starts stay
54
+ * pinned at every zoom, and pans never reshuffle anything (the stride derives
55
+ * from the span, the indices from the calendar). Schemes tried and rejected
56
+ * on the way here: a global even day-stride (can't pin month starts — the
57
+ * `Feb` label drifted with zoom), `round(i·L/div)` division (beats against
58
+ * the month length), and dyadic midpoint halving (perfect zoom-nesting, but
59
+ * 2× density jumps and ±1-day wobble inside non-power months; the owner's
60
+ * TradingView captures showed uniform strides re-labelling on zoom reads
61
+ * calmer than either wobble). There is deliberately no week rung: a
62
+ * Monday-anchored week can't pin month starts either, so the day band owns
63
+ * everything between every-session and month grain.
38
64
  *
39
- * `count` is a **cap**, not a target: grains jump by 4–12× up the ladder, so a
40
- * small fixed count over-coarsens long spans (a mid-year-anchored 12-month daily
41
- * run spans 6 quarter buckets capped at 5 it collapses to year grain, 2
42
- * ticks). Callers size the cap to the room the labels have — the container
43
- * derives it from plot width rather than passing a small constant.
65
+ * `count` is a **cap**, not a target: coarser grains jump by 3–(month
66
+ * quarter → year), so a small fixed count over-coarsens long spans. Callers
67
+ * size the cap to the room the labels have the container derives it from plot
68
+ * width rather than passing a small constant. `spanDays` is the domain's
69
+ * calendar-day span from the caller (stable at a fixed zoom); absent (a direct
70
+ * call), it falls back to the opens' own span.
44
71
  *
45
72
  * This is the day-and-coarser half of the ladder; {@link buildTicks} adds the
46
73
  * sub-day rungs.
47
74
  */
48
- export declare function coarsenCalendar(opens: readonly number[], count: number): {
75
+ export declare function coarsenCalendar(opens: readonly number[], count: number, spanDays?: number, provider?: DiscontinuityProvider): {
49
76
  ticks: number[];
50
77
  granularity: TickGranularity;
51
78
  };
79
+ /**
80
+ * The **grid populations** behind {@link buildTicks}' labels: every ladder rung
81
+ * that fits `cap` lines, each carrying its FULL anchor population — every
82
+ * aligned clock instant, every session open, every month / quarter / year
83
+ * start — finest rung first. The axis *labels* are a thinned subset of one
84
+ * rung; the grid is the calendar structure itself, so the container draws
85
+ * every anchor of each returned level and fades a level's lines by their pixel
86
+ * spacing (a crowding level dissolves while the coarser ones persist — the
87
+ * map-style hierarchical grid). Levels **nest** (a month start is a session
88
+ * open; an aligned hour sits inside its session; there is no week rung), so a
89
+ * consumer de-duplicates shared anchors coarsest-first and each line draws
90
+ * once, at its coarsest membership's (widest-spaced, so strongest) alpha.
91
+ *
92
+ * `cap` is the max lines per level — the caller derives it from plot width ÷
93
+ * the fade-out spacing, so a level too dense to be visible at all is simply
94
+ * absent rather than enumerated and thrown away. Sub-day rungs are gated on
95
+ * the live-span estimate first (like {@link buildTicks}) and skipped when they
96
+ * add no anchor beyond the session opens themselves (that is the day level).
97
+ */
98
+ export declare function buildGridLevels(provider: DiscontinuityProvider, opens: readonly number[], domainEnd: number, cap: number): Array<{
99
+ granularity: TickGranularity;
100
+ values: number[];
101
+ }>;
102
+ /**
103
+ * The **nominal wall-clock step** of grain `g` in ms — the calendar time one
104
+ * grid cell of that grain covers (a day is a day whether or not its weekend
105
+ * neighbours are drawn; a month is ~30.44 days). This is what the grid's
106
+ * density fade keys off: `width × step / wallSpan` is a grain's spacing on a
107
+ * gap-free axis, and using it (rather than the measured on-screen gaps) makes
108
+ * the fade **mode-invariant** — collapsing weekends draws fewer day lines at
109
+ * the *same* strength, instead of wider-spaced lines that jump to full
110
+ * opacity at the same zoom.
111
+ */
112
+ export declare function nominalStepMs(g: TickGranularity): number;
52
113
  /**
53
114
  * The full-ladder grain selection: given the provider, the domain, and the
54
115
  * width-derived `cap`, walk the clock rungs (1s … 30s, 1m … 30m, 1h … 12h)
55
- * then day week → month → quarter → year (then decimate) and return the
56
- * first rung that fits.
116
+ * then day (thinned by a per-month uniform session stride) → month → quarter → year
117
+ * (then decimate) and return the first rung that fits.
57
118
  * `opens` are the session-open anchors (`[domain start, ...boundaries]`) the
58
119
  * caller already has. Sub-day rungs are only reachable when the opens
59
120
  * themselves fit — a year of daily sessions never wastes time generating hour
@@ -78,12 +139,74 @@ export declare function majorFormatFor(g: TickGranularity): string;
78
139
  * everything else. Never repeat a unit the first row already shows
79
140
  * (`Jan 2026` under a `Jan 05` tick reads as noise). */
80
141
  export declare function boundaryFormatFor(g: TickGranularity): string;
142
+ /** The **band grain** under `g`-grain ticks (the segmented stacked second row):
143
+ * the next coarser unit — sub-day → day, day/week → month, month/quarter →
144
+ * year, year → none. */
145
+ export declare function bandGrainFor(g: TickGranularity): TickGranularity | undefined;
146
+ /** d3 specifier for a **band** label at band grain `g`: the date for a day band
147
+ * (`Jan 12`), the full month for a month band (`January`), the year for a year
148
+ * band (`2031`). Left-aligned in the band by the renderer. */
149
+ export declare function bandFormatFor(g: TickGranularity): string;
81
150
  /**
82
- * Which of `ticks` (at grain `granularity`) carry a boundary label: the first
83
- * tick always (the reader needs context immediately), then every tick whose
84
- * boundary-grain bucket differs from the previous tick's — i.e. the first tick
85
- * of each new day / year. Returns the boundary-flagged tick values;
86
- * empty when the grain has no boundary row (year grain).
151
+ * The **zebra parity** of the band starting at `t` (band grain `g`) `true`
152
+ * when the band is shaded. A stable, pan/zoom-invariant flag derived from the
153
+ * band's own calendar identity: the year number, the months-since-epoch, or
154
+ * the **UTC**-day index (UTC so a DST shift never flips a band's shade). Odd
155
+ * index shaded, matching the reference frames (2031 / 2033 grey).
156
+ *
157
+ * Parity is **absolute** (per calendar period), not per-visible-position — the
158
+ * price of pan-stability. Calendar-consecutive bands always differ, and
159
+ * collapsed **weekends** stay clean (Fri→Mon is a 3-index step, odd), but a
160
+ * lone skipped weekday — a single **holiday**, a 2-index step — can place two
161
+ * same-shade day-bands side by side on a gappy calendar. A rare, cosmetic
162
+ * consequence of keeping the shade fixed to the date rather than the slot.
163
+ */
164
+ export declare function bandShaded(t: number, g: TickGranularity): boolean;
165
+ /** The local-time start of the band grain `g` containing `t` (the band's left
166
+ * edge): local midnight, month start, or Jan 1. Through the Date ctor so DST
167
+ * and month/year overflow normalize correctly. */
168
+ export declare function bandStartOf(t: number, g: TickGranularity): number;
169
+ /** The start of the band grain `g` **after** the one containing `t` — the next
170
+ * local midnight / month start / Jan 1. */
171
+ export declare function bandNext(t: number, g: TickGranularity): number;
172
+ /**
173
+ * Which of `ticks` (at grain `granularity`) carry a boundary label: every tick
174
+ * whose boundary-grain bucket differs from the previous tick's — i.e. a
175
+ * **crossing**, the first tick of a new day / year. The first tick is *not*
176
+ * automatically flagged: the reader's left-edge context is the pinned
177
+ * {@link TradingTimeScale.boundaryContext} label (a property of the domain
178
+ * start, not of any tick — anchoring it to the first tick made it hop
179
+ * tick-to-tick on a live sliding window). Empty when the grain has no
180
+ * boundary row (year grain).
181
+ */
182
+ export declare function boundaryTicks(ticks: readonly number[], granularity: TickGranularity, domainStart?: number): number[];
183
+ /**
184
+ * The terse **base** (non-promoted) flat label format for grain `g` — the label
185
+ * a tick carries when it opens no coarser period: the clock time for a sub-day
186
+ * grain, a bare day-of-month for day / week (`5`, not `Jan 5` — the month rides
187
+ * the promoted month-start tick), the month abbrev for month / quarter, the
188
+ * year for year. Terser than {@link majorFormatFor} (which carries the month on
189
+ * every day tick) because the flat row leans on inline promotions for context.
190
+ */
191
+ export declare function flatBaseFormatFor(g: TickGranularity): string;
192
+ /**
193
+ * The **flat** (single-row) label format specifier for each tick, parallel to
194
+ * `ticks` (already at grain `granularity`). Each tick shows the coarsest
195
+ * calendar period it *opens* — a year / month / date promotion — and its terse
196
+ * {@link flatBaseFormatFor} label otherwise, so the one row reads
197
+ * `… 30 31 Feb 2 3 …` with `Feb` where the month turns and the year where it
198
+ * turns. A tick "opens" level L when its L-bucket differs from the previous
199
+ * tick's; the coarsest changed level wins (a Jan-1 tick promotes to the year,
200
+ * not the month).
201
+ *
202
+ * `domainStart` seeds the walk (like {@link boundaryTicks}): the first tick is
203
+ * promoted only if it crosses a period relative to the instant just *before*
204
+ * the domain's left edge — so a window opening mid-month doesn't falsely
205
+ * promote its first tick (and a live sliding window doesn't flicker the
206
+ * leftmost label), while a domain starting *exactly* on a boundary still
207
+ * promotes the boundary tick (it truly is the period's first instant: a window
208
+ * opening at May 1 midnight reads `May 16 …`, not `1 16 …`). Without
209
+ * `domainStart` the first tick is never promoted.
87
210
  */
88
- export declare function boundaryTicks(ticks: readonly number[], granularity: TickGranularity): number[];
211
+ export declare function flatFormats(ticks: readonly number[], granularity: TickGranularity, domainStart?: number): string[];
89
212
  //# sourceMappingURL=tickLadder.d.ts.map