@pond-ts/charts 0.46.0 → 0.48.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/CHANGELOG.md +130 -3
- package/dist/ChartContainer.d.ts +56 -7
- package/dist/ChartContainer.js +32 -9
- package/dist/Layers.js +120 -18
- package/dist/XAxis.d.ts +18 -1
- package/dist/XAxis.js +140 -76
- package/dist/context.d.ts +8 -0
- package/dist/format.d.ts +20 -0
- package/dist/grid.d.ts +32 -5
- package/dist/grid.js +81 -13
- package/dist/index.d.ts +2 -2
- package/dist/theme.d.ts +13 -0
- package/dist/theme.js +5 -0
- package/dist/tickLadder.d.ts +156 -16
- package/dist/tickLadder.js +634 -25
- package/dist/tradingTimeScale.d.ts +104 -10
- package/dist/tradingTimeScale.js +116 -4
- package/package.json +4 -4
package/dist/tickLadder.d.ts
CHANGED
|
@@ -13,10 +13,27 @@ 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';
|
|
27
|
+
/**
|
|
28
|
+
* The **coarse calendar unit** of a resolved grain — the public grain a
|
|
29
|
+
* `cursorFormat` callback branches on, with the internal 1/5/15/30 strides
|
|
30
|
+
* collapsed to their unit (`hour1|3|6|12` → `hour`). Stable across zoom steps
|
|
31
|
+
* within a unit, so a consumer's `grain === 'hour'` check doesn't churn as the
|
|
32
|
+
* stride changes.
|
|
33
|
+
*/
|
|
34
|
+
export type TimeGrain = 'year' | 'quarter' | 'month' | 'week' | 'day' | 'hour' | 'minute' | 'second';
|
|
35
|
+
/** Collapse a {@link TickGranularity} to its coarse {@link TimeGrain} unit. */
|
|
36
|
+
export declare function coarseUnitOf(g: TickGranularity): TimeGrain;
|
|
20
37
|
/**
|
|
21
38
|
* The local-time bucket key for `t` at grain `g` — two instants in the same
|
|
22
39
|
* day / week / month / quarter / year share a key. Local time (not UTC) so it
|
|
@@ -28,32 +45,86 @@ export type TickGranularity = 'second1' | 'second5' | 'second15' | 'second30' |
|
|
|
28
45
|
*/
|
|
29
46
|
export declare function bucketKey(t: number, g: TickGranularity): number;
|
|
30
47
|
/**
|
|
31
|
-
* Thin an ascending run of **session opens** down to about `count` axis ticks
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
* every-nth so the axis never crowds. Exported so the container can draw session
|
|
37
|
-
* dividers at the same instants the axis labels.
|
|
48
|
+
* Thin an ascending run of **session opens** down to about `count` axis ticks.
|
|
49
|
+
* Picks the finest rung: every session → **per-month uniform session stride**
|
|
50
|
+
* (still day grain, month starts pinned) → month → quarter → year; beyond
|
|
51
|
+
* yearly it decimates every-nth so the axis never crowds. Exported so the
|
|
52
|
+
* container can draw session dividers at the same instants the axis labels.
|
|
38
53
|
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
54
|
+
* The day band thins each month to a **uniform session stride** — the month's
|
|
55
|
+
* first session, then every `k`-th session, truncated so the gap to the next
|
|
56
|
+
* month start stays ≥ `k` (slack at the month end, never a cramped tick
|
|
57
|
+
* before the month label). The decoded-and-validated TradingView algorithm:
|
|
58
|
+
* with a `provider` the stride runs in **session-index space**
|
|
59
|
+
* ({@link subdivideMonthsBySession}) — marks an equal number of bars apart,
|
|
60
|
+
* evenly spaced pixels on a collapsed axis, no weekend snapping; without one
|
|
61
|
+
* it falls back to day-of-month space ({@link subdivideMonthsByDay}).
|
|
62
|
+
* Zooming steps the stride through the integers (…4 → 3 → 2 → 1), a ~one-bar
|
|
63
|
+
* density change that re-labels some interior marks; month / year starts stay
|
|
64
|
+
* pinned at every zoom, and pans never reshuffle anything (the stride derives
|
|
65
|
+
* from the span, the indices from the calendar). Schemes tried and rejected
|
|
66
|
+
* on the way here: a global even day-stride (can't pin month starts — the
|
|
67
|
+
* `Feb` label drifted with zoom), `round(i·L/div)` division (beats against
|
|
68
|
+
* the month length), and dyadic midpoint halving (perfect zoom-nesting, but
|
|
69
|
+
* 2× density jumps and ±1-day wobble inside non-power months; the owner's
|
|
70
|
+
* TradingView captures showed uniform strides re-labelling on zoom reads
|
|
71
|
+
* calmer than either wobble). There is deliberately no week rung: a
|
|
72
|
+
* Monday-anchored week can't pin month starts either, so the day band owns
|
|
73
|
+
* everything between every-session and month grain.
|
|
74
|
+
*
|
|
75
|
+
* `count` is a **cap**, not a target: coarser grains jump by 3–4× (month →
|
|
76
|
+
* quarter → year), so a small fixed count over-coarsens long spans. Callers
|
|
77
|
+
* size the cap to the room the labels have — the container derives it from plot
|
|
78
|
+
* width — rather than passing a small constant. `spanDays` is the domain's
|
|
79
|
+
* calendar-day span from the caller (stable at a fixed zoom); absent (a direct
|
|
80
|
+
* call), it falls back to the opens' own span.
|
|
44
81
|
*
|
|
45
82
|
* This is the day-and-coarser half of the ladder; {@link buildTicks} adds the
|
|
46
83
|
* sub-day rungs.
|
|
47
84
|
*/
|
|
48
|
-
export declare function coarsenCalendar(opens: readonly number[], count: number): {
|
|
85
|
+
export declare function coarsenCalendar(opens: readonly number[], count: number, spanDays?: number, provider?: DiscontinuityProvider): {
|
|
49
86
|
ticks: number[];
|
|
50
87
|
granularity: TickGranularity;
|
|
51
88
|
};
|
|
89
|
+
/**
|
|
90
|
+
* The **grid populations** behind {@link buildTicks}' labels: every ladder rung
|
|
91
|
+
* that fits `cap` lines, each carrying its FULL anchor population — every
|
|
92
|
+
* aligned clock instant, every session open, every month / quarter / year
|
|
93
|
+
* start — finest rung first. The axis *labels* are a thinned subset of one
|
|
94
|
+
* rung; the grid is the calendar structure itself, so the container draws
|
|
95
|
+
* every anchor of each returned level and fades a level's lines by their pixel
|
|
96
|
+
* spacing (a crowding level dissolves while the coarser ones persist — the
|
|
97
|
+
* map-style hierarchical grid). Levels **nest** (a month start is a session
|
|
98
|
+
* open; an aligned hour sits inside its session; there is no week rung), so a
|
|
99
|
+
* consumer de-duplicates shared anchors coarsest-first and each line draws
|
|
100
|
+
* once, at its coarsest membership's (widest-spaced, so strongest) alpha.
|
|
101
|
+
*
|
|
102
|
+
* `cap` is the max lines per level — the caller derives it from plot width ÷
|
|
103
|
+
* the fade-out spacing, so a level too dense to be visible at all is simply
|
|
104
|
+
* absent rather than enumerated and thrown away. Sub-day rungs are gated on
|
|
105
|
+
* the live-span estimate first (like {@link buildTicks}) and skipped when they
|
|
106
|
+
* add no anchor beyond the session opens themselves (that is the day level).
|
|
107
|
+
*/
|
|
108
|
+
export declare function buildGridLevels(provider: DiscontinuityProvider, opens: readonly number[], domainEnd: number, cap: number): Array<{
|
|
109
|
+
granularity: TickGranularity;
|
|
110
|
+
values: number[];
|
|
111
|
+
}>;
|
|
112
|
+
/**
|
|
113
|
+
* The **nominal wall-clock step** of grain `g` in ms — the calendar time one
|
|
114
|
+
* grid cell of that grain covers (a day is a day whether or not its weekend
|
|
115
|
+
* neighbours are drawn; a month is ~30.44 days). This is what the grid's
|
|
116
|
+
* density fade keys off: `width × step / wallSpan` is a grain's spacing on a
|
|
117
|
+
* gap-free axis, and using it (rather than the measured on-screen gaps) makes
|
|
118
|
+
* the fade **mode-invariant** — collapsing weekends draws fewer day lines at
|
|
119
|
+
* the *same* strength, instead of wider-spaced lines that jump to full
|
|
120
|
+
* opacity at the same zoom.
|
|
121
|
+
*/
|
|
122
|
+
export declare function nominalStepMs(g: TickGranularity): number;
|
|
52
123
|
/**
|
|
53
124
|
* The full-ladder grain selection: given the provider, the domain, and the
|
|
54
125
|
* width-derived `cap`, walk the clock rungs (1s … 30s, 1m … 30m, 1h … 12h)
|
|
55
|
-
* then day
|
|
56
|
-
* first rung that fits.
|
|
126
|
+
* then day (thinned by a per-month uniform session stride) → month → quarter → year
|
|
127
|
+
* (then decimate) and return the first rung that fits.
|
|
57
128
|
* `opens` are the session-open anchors (`[domain start, ...boundaries]`) the
|
|
58
129
|
* caller already has. Sub-day rungs are only reachable when the opens
|
|
59
130
|
* themselves fit — a year of daily sessions never wastes time generating hour
|
|
@@ -78,6 +149,46 @@ export declare function majorFormatFor(g: TickGranularity): string;
|
|
|
78
149
|
* everything else. Never repeat a unit the first row already shows
|
|
79
150
|
* (`Jan 2026` under a `Jan 05` tick reads as noise). */
|
|
80
151
|
export declare function boundaryFormatFor(g: TickGranularity): string;
|
|
152
|
+
/** The **band grain** under `g`-grain ticks (the segmented stacked second row):
|
|
153
|
+
* the next coarser unit — sub-day → day, day/week → month, month/quarter →
|
|
154
|
+
* year, year → none. */
|
|
155
|
+
export declare function bandGrainFor(g: TickGranularity): TickGranularity | undefined;
|
|
156
|
+
/**
|
|
157
|
+
* d3 specifier for the **cursor / marker readout** at grain `g` — a hovered
|
|
158
|
+
* instant formatted at the axis's own granularity, never finer. A day-or-coarser
|
|
159
|
+
* axis reads a **date** (no time-of-day), so a daily bar at a foreign-tz
|
|
160
|
+
* midnight can't render as `02 AM`; a sub-day axis reads date **+** clock. This
|
|
161
|
+
* is the grain-aware default that replaces d3's multi-scale default for the
|
|
162
|
+
* readout (a `cursorFormat` override, when given, wins over it). Unambiguous by
|
|
163
|
+
* design — the readout carries the year / date the terse tick labels omit.
|
|
164
|
+
*/
|
|
165
|
+
export declare function readoutFormatFor(g: TickGranularity): string;
|
|
166
|
+
/** d3 specifier for a **band** label at band grain `g`: the date for a day band
|
|
167
|
+
* (`Jan 12`), the full month for a month band (`January`), the year for a year
|
|
168
|
+
* band (`2031`). Left-aligned in the band by the renderer. */
|
|
169
|
+
export declare function bandFormatFor(g: TickGranularity): string;
|
|
170
|
+
/**
|
|
171
|
+
* The **zebra parity** of the band starting at `t` (band grain `g`) — `true`
|
|
172
|
+
* when the band is shaded. A stable, pan/zoom-invariant flag derived from the
|
|
173
|
+
* band's own calendar identity: the year number, the months-since-epoch, or
|
|
174
|
+
* the **UTC**-day index (UTC so a DST shift never flips a band's shade). Odd
|
|
175
|
+
* index → shaded, matching the reference frames (2031 / 2033 grey).
|
|
176
|
+
*
|
|
177
|
+
* Parity is **absolute** (per calendar period), not per-visible-position — the
|
|
178
|
+
* price of pan-stability. Calendar-consecutive bands always differ, and
|
|
179
|
+
* collapsed **weekends** stay clean (Fri→Mon is a 3-index step, odd), but a
|
|
180
|
+
* lone skipped weekday — a single **holiday**, a 2-index step — can place two
|
|
181
|
+
* same-shade day-bands side by side on a gappy calendar. A rare, cosmetic
|
|
182
|
+
* consequence of keeping the shade fixed to the date rather than the slot.
|
|
183
|
+
*/
|
|
184
|
+
export declare function bandShaded(t: number, g: TickGranularity): boolean;
|
|
185
|
+
/** The local-time start of the band grain `g` containing `t` (the band's left
|
|
186
|
+
* edge): local midnight, month start, or Jan 1. Through the Date ctor so DST
|
|
187
|
+
* and month/year overflow normalize correctly. */
|
|
188
|
+
export declare function bandStartOf(t: number, g: TickGranularity): number;
|
|
189
|
+
/** The start of the band grain `g` **after** the one containing `t` — the next
|
|
190
|
+
* local midnight / month start / Jan 1. */
|
|
191
|
+
export declare function bandNext(t: number, g: TickGranularity): number;
|
|
81
192
|
/**
|
|
82
193
|
* Which of `ticks` (at grain `granularity`) carry a boundary label: every tick
|
|
83
194
|
* whose boundary-grain bucket differs from the previous tick's — i.e. a
|
|
@@ -89,4 +200,33 @@ export declare function boundaryFormatFor(g: TickGranularity): string;
|
|
|
89
200
|
* boundary row (year grain).
|
|
90
201
|
*/
|
|
91
202
|
export declare function boundaryTicks(ticks: readonly number[], granularity: TickGranularity, domainStart?: number): number[];
|
|
203
|
+
/**
|
|
204
|
+
* The terse **base** (non-promoted) flat label format for grain `g` — the label
|
|
205
|
+
* a tick carries when it opens no coarser period: the clock time for a sub-day
|
|
206
|
+
* grain, a bare day-of-month for day / week (`5`, not `Jan 5` — the month rides
|
|
207
|
+
* the promoted month-start tick), the month abbrev for month / quarter, the
|
|
208
|
+
* year for year. Terser than {@link majorFormatFor} (which carries the month on
|
|
209
|
+
* every day tick) because the flat row leans on inline promotions for context.
|
|
210
|
+
*/
|
|
211
|
+
export declare function flatBaseFormatFor(g: TickGranularity): string;
|
|
212
|
+
/**
|
|
213
|
+
* The **flat** (single-row) label format specifier for each tick, parallel to
|
|
214
|
+
* `ticks` (already at grain `granularity`). Each tick shows the coarsest
|
|
215
|
+
* calendar period it *opens* — a year / month / date promotion — and its terse
|
|
216
|
+
* {@link flatBaseFormatFor} label otherwise, so the one row reads
|
|
217
|
+
* `… 30 31 Feb 2 3 …` with `Feb` where the month turns and the year where it
|
|
218
|
+
* turns. A tick "opens" level L when its L-bucket differs from the previous
|
|
219
|
+
* tick's; the coarsest changed level wins (a Jan-1 tick promotes to the year,
|
|
220
|
+
* not the month).
|
|
221
|
+
*
|
|
222
|
+
* `domainStart` seeds the walk (like {@link boundaryTicks}): the first tick is
|
|
223
|
+
* promoted only if it crosses a period relative to the instant just *before*
|
|
224
|
+
* the domain's left edge — so a window opening mid-month doesn't falsely
|
|
225
|
+
* promote its first tick (and a live sliding window doesn't flicker the
|
|
226
|
+
* leftmost label), while a domain starting *exactly* on a boundary still
|
|
227
|
+
* promotes the boundary tick (it truly is the period's first instant: a window
|
|
228
|
+
* opening at May 1 midnight reads `May 16 …`, not `1 16 …`). Without
|
|
229
|
+
* `domainStart` the first tick is never promoted.
|
|
230
|
+
*/
|
|
231
|
+
export declare function flatFormats(ticks: readonly number[], granularity: TickGranularity, domainStart?: number): string[];
|
|
92
232
|
//# sourceMappingURL=tickLadder.d.ts.map
|