@pond-ts/charts 0.57.0 → 0.58.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 +1070 -1
- package/dist/AreaChart.d.ts +12 -1
- package/dist/AreaChart.js +131 -13
- package/dist/BarChart.js +184 -30
- package/dist/BarList.d.ts +85 -5
- package/dist/BarList.js +25 -4
- package/dist/BoxList.d.ts +70 -3
- package/dist/BoxList.js +21 -7
- package/dist/BoxPlot.d.ts +2 -1
- package/dist/BoxPlot.js +101 -9
- package/dist/Candlestick.d.ts +13 -1
- package/dist/Candlestick.js +89 -3
- package/dist/ChartContainer.d.ts +36 -48
- package/dist/ChartContainer.js +465 -59
- package/dist/ChartRow.d.ts +9 -2
- package/dist/ChartRow.js +86 -12
- package/dist/HeatMap.d.ts +176 -0
- package/dist/HeatMap.js +344 -0
- package/dist/Layers.d.ts +5 -1
- package/dist/Layers.js +1014 -253
- package/dist/Legend.js +8 -4
- package/dist/LineChart.d.ts +18 -1
- package/dist/LineChart.js +165 -4
- package/dist/ListTable.d.ts +30 -3
- package/dist/ListTable.js +381 -23
- package/dist/ScatterChart.d.ts +3 -2
- package/dist/ScatterChart.js +68 -4
- package/dist/XAxis.js +40 -22
- package/dist/area.d.ts +34 -1
- package/dist/area.js +88 -1
- package/dist/bars.d.ts +57 -3
- package/dist/bars.js +237 -26
- package/dist/box.d.ts +2 -2
- package/dist/box.js +158 -40
- package/dist/brush.d.ts +142 -0
- package/dist/brush.js +179 -0
- package/dist/child-index.d.ts +27 -0
- package/dist/child-index.js +57 -0
- package/dist/context.d.ts +859 -33
- package/dist/cursors.d.ts +161 -0
- package/dist/cursors.js +503 -0
- package/dist/decimate.d.ts +78 -1
- package/dist/decimate.js +157 -0
- package/dist/heat.d.ts +163 -0
- package/dist/heat.js +659 -0
- package/dist/index.d.ts +11 -2
- package/dist/index.js +22 -0
- package/dist/line.d.ts +137 -0
- package/dist/line.js +328 -0
- package/dist/ohlc.d.ts +16 -1
- package/dist/ohlc.js +93 -4
- package/dist/scatter.d.ts +17 -9
- package/dist/scatter.js +221 -33
- package/dist/select.d.ts +13 -5
- package/dist/select.js +14 -6
- package/dist/selection-fixtures.d.ts +174 -0
- package/dist/selection-fixtures.js +569 -0
- package/dist/selection-stories.d.ts +73 -0
- package/dist/selection-stories.js +301 -0
- package/dist/selectors.d.ts +316 -0
- package/dist/selectors.js +391 -0
- package/dist/span.d.ts +122 -0
- package/dist/span.js +203 -0
- package/dist/sweep.d.ts +154 -0
- package/dist/sweep.js +282 -0
- package/dist/theme.d.ts +456 -5
- package/dist/theme.js +217 -41
- package/dist/tracker.d.ts +6 -0
- package/dist/tracker.js +6 -0
- package/dist/tradingAxis.fixture.d.ts +78 -0
- package/dist/tradingAxis.fixture.js +215 -0
- package/dist/useChartLegend.js +18 -3
- package/package.json +3 -3
package/dist/theme.js
CHANGED
|
@@ -1,78 +1,199 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The neutral default theme.
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* `
|
|
2
|
+
* The neutral default theme. The shared data hue is a cerulean (`#0284c7`)
|
|
3
|
+
* across `line` / `band` / `area` / `scatter` / `box` / candle-rising, chosen
|
|
4
|
+
* to clear the bar palette's *selection* blue (`#3F5BE0`) — the original M1
|
|
5
|
+
* royal blue (`#2563eb`) sat ~ΔE 5 from it, so a line drawn over bars read as
|
|
6
|
+
* nearly the selection colour. `primary` / `secondary` / `context` are a
|
|
7
|
+
* built-in generic role vocabulary; an unrecognised (e.g. domain-specific)
|
|
8
|
+
* identifier falls back to `default`.
|
|
9
|
+
*
|
|
10
|
+
* **Spreading this inherits every slot you don't override** — including colours
|
|
11
|
+
* for layers you haven't added yet, so a `{ ...defaultTheme, bar: … }` theme
|
|
12
|
+
* paints this blue the first time someone drops in a `<LineChart>`. That is the
|
|
13
|
+
* intended workflow, not a trap: take the defaults, change the one or two things
|
|
14
|
+
* that are yours. A design system that must own *every* colour should assert on
|
|
15
|
+
* that in its own test rather than catch it in review — walk the resolved theme
|
|
16
|
+
* for values outside your palette ([PND-THEMEBASE]).
|
|
17
|
+
*
|
|
18
|
+
* **The `bar` slot is the exception to "one blue".** Bars carry an interaction
|
|
19
|
+
* state (rest / hover / selected / dimmed), and encoding four states as four
|
|
20
|
+
* shades of one hue is unreadable — so `bar.default` runs its own
|
|
21
|
+
* **interaction-state palette**: teal at rest, blue when selected, brighter
|
|
22
|
+
* teal on hover. See the comment on that slot, and `brush` for the matching
|
|
23
|
+
* drag band.
|
|
7
24
|
*/
|
|
8
25
|
export const defaultTheme = {
|
|
9
26
|
line: {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
27
|
+
// **Trace interaction state is weight, plus a hue for a swept WINDOW
|
|
28
|
+
// only** — see `LineStyle.selectedWidth` / `.spanColor`. A whole-series
|
|
29
|
+
// selection thickens and keeps its colour, because colour is which series
|
|
30
|
+
// it is; a window inside one series can take the selection blue, because
|
|
31
|
+
// there identity is not in question. `spanColor` is `bar.highlight`, so a
|
|
32
|
+
// swept region on a trace reads as the same act as the band that made it.
|
|
33
|
+
default: {
|
|
34
|
+
color: '#0284c7',
|
|
35
|
+
width: 1.5,
|
|
36
|
+
selectedWidth: 3,
|
|
37
|
+
dimmedOpacity: 0.32,
|
|
38
|
+
spanColor: '#3F5BE0',
|
|
39
|
+
},
|
|
40
|
+
primary: {
|
|
41
|
+
color: '#0284c7',
|
|
42
|
+
width: 1.5,
|
|
43
|
+
selectedWidth: 3,
|
|
44
|
+
dimmedOpacity: 0.32,
|
|
45
|
+
spanColor: '#3F5BE0',
|
|
46
|
+
},
|
|
47
|
+
secondary: {
|
|
48
|
+
color: '#e8836b',
|
|
49
|
+
width: 1.5,
|
|
50
|
+
selectedWidth: 3,
|
|
51
|
+
dimmedOpacity: 0.32,
|
|
52
|
+
spanColor: '#3F5BE0',
|
|
53
|
+
},
|
|
54
|
+
context: {
|
|
55
|
+
color: '#5eb5a6',
|
|
56
|
+
width: 1.5,
|
|
57
|
+
selectedWidth: 3,
|
|
58
|
+
dimmedOpacity: 0.32,
|
|
59
|
+
spanColor: '#3F5BE0',
|
|
60
|
+
},
|
|
14
61
|
},
|
|
15
62
|
band: {
|
|
16
|
-
default: { fill: '#
|
|
17
|
-
outer: { fill: '#
|
|
18
|
-
inner: { fill: '#
|
|
63
|
+
default: { fill: '#0284c7', opacity: 0.15 },
|
|
64
|
+
outer: { fill: '#0284c7', opacity: 0.1 },
|
|
65
|
+
inner: { fill: '#0284c7', opacity: 0.2 },
|
|
19
66
|
},
|
|
20
67
|
area: {
|
|
21
68
|
// Outline at the line colour; graded fill from it. `in`/`out` are the
|
|
22
69
|
// above/below-axis roles (esnet traffic), composed as two layers.
|
|
70
|
+
// Trace interaction state — see `AreaStyle.selectedWidth`. `spanColor` is
|
|
71
|
+
// `bar.highlight`, so a swept region reads as the same act as the band.
|
|
23
72
|
default: {
|
|
24
|
-
color: '#
|
|
73
|
+
color: '#0284c7',
|
|
25
74
|
width: 1.5,
|
|
26
|
-
fill: '#
|
|
75
|
+
fill: '#0284c7',
|
|
27
76
|
fillOpacity: 0.3,
|
|
77
|
+
selectedWidth: 3,
|
|
78
|
+
selectedFillOpacity: 0.55,
|
|
79
|
+
dimmedOpacity: 0.32,
|
|
80
|
+
spanColor: '#3F5BE0',
|
|
28
81
|
},
|
|
29
|
-
in: { color: '#
|
|
82
|
+
in: { color: '#0284c7', width: 1.5, fill: '#0284c7', fillOpacity: 0.3 },
|
|
30
83
|
out: { color: '#e8836b', width: 1.5, fill: '#e8836b', fillOpacity: 0.3 },
|
|
31
84
|
},
|
|
32
85
|
scatter: {
|
|
33
|
-
//
|
|
34
|
-
//
|
|
35
|
-
// roles
|
|
86
|
+
// A 9px teal point with a white ring (legible on a busy plot), and the
|
|
87
|
+
// shared `states` ladder over it. `primary`/`secondary` keep the LINE
|
|
88
|
+
// roles' hues — that identity is the whole reason they exist, so a
|
|
89
|
+
// scatter overlaid on a line still reads as the same series — and take
|
|
90
|
+
// the same states with their own hue brightened for hover.
|
|
91
|
+
//
|
|
92
|
+
// The default role's rest is `#2A9D8F`, the bar's resting teal, and NOT
|
|
93
|
+
// the old cerulean `#0284c7`: blue has to mean *committed*, and a
|
|
94
|
+
// cerulean point going to selection blue is barely a change. Same rule
|
|
95
|
+
// the bar palette reached, for the third time.
|
|
36
96
|
default: {
|
|
37
|
-
color: '#
|
|
38
|
-
radius: 4,
|
|
97
|
+
color: '#2A9D8F',
|
|
98
|
+
radius: 4.5,
|
|
39
99
|
outline: '#ffffff',
|
|
40
100
|
outlineWidth: 1,
|
|
41
101
|
selectedOutline: '#1e293b',
|
|
42
102
|
selectedWidth: 2,
|
|
43
103
|
label: '#334155',
|
|
104
|
+
states: {
|
|
105
|
+
hover: '#4FD0BE',
|
|
106
|
+
hoverRadius: 5.5,
|
|
107
|
+
selected: '#3F5BE0', // the shared selection blue
|
|
108
|
+
halo: '#ffffff',
|
|
109
|
+
haloWidth: 2,
|
|
110
|
+
dimmedRadius: 2.5,
|
|
111
|
+
dimmedOpacity: 0.34,
|
|
112
|
+
},
|
|
44
113
|
},
|
|
45
114
|
primary: {
|
|
46
|
-
color: '#
|
|
47
|
-
radius: 4,
|
|
115
|
+
color: '#0284c7',
|
|
116
|
+
radius: 4.5,
|
|
48
117
|
outline: '#ffffff',
|
|
49
118
|
outlineWidth: 1,
|
|
50
119
|
selectedOutline: '#1e293b',
|
|
51
120
|
selectedWidth: 2,
|
|
52
121
|
label: '#334155',
|
|
122
|
+
states: {
|
|
123
|
+
hover: '#38bdf8', // the primary cerulean, brightened
|
|
124
|
+
hoverRadius: 5.5,
|
|
125
|
+
selected: '#3F5BE0', // the shared selection blue
|
|
126
|
+
halo: '#ffffff',
|
|
127
|
+
haloWidth: 2,
|
|
128
|
+
dimmedRadius: 2.5,
|
|
129
|
+
dimmedOpacity: 0.34,
|
|
130
|
+
},
|
|
53
131
|
},
|
|
54
132
|
secondary: {
|
|
55
133
|
color: '#e8836b',
|
|
56
|
-
radius: 4,
|
|
134
|
+
radius: 4.5,
|
|
57
135
|
outline: '#ffffff',
|
|
58
136
|
outlineWidth: 1,
|
|
59
137
|
selectedOutline: '#1e293b',
|
|
60
138
|
selectedWidth: 2,
|
|
61
139
|
label: '#334155',
|
|
140
|
+
states: {
|
|
141
|
+
hover: '#f5a991', // the secondary coral, brightened
|
|
142
|
+
hoverRadius: 5.5,
|
|
143
|
+
selected: '#3F5BE0', // the shared selection blue
|
|
144
|
+
halo: '#ffffff',
|
|
145
|
+
haloWidth: 2,
|
|
146
|
+
dimmedRadius: 2.5,
|
|
147
|
+
dimmedOpacity: 0.34,
|
|
148
|
+
},
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
heat: {
|
|
152
|
+
// A cell's colour is its value, so every state here is chrome around the
|
|
153
|
+
// cell (or one uniform transform over all of them) — see `HeatStates`.
|
|
154
|
+
default: {
|
|
155
|
+
veil: 'rgba(255,255,255,0.62)',
|
|
156
|
+
// White outside, dark teal inside: one of the pair reads wherever on
|
|
157
|
+
// the ramp the cell happens to sit.
|
|
158
|
+
hoverRing: ['#ffffff', '#12564E'],
|
|
159
|
+
ringWidth: 2,
|
|
160
|
+
// The shared selection blue — a selected region beside a selected bar
|
|
161
|
+
// reads as one act.
|
|
162
|
+
perimeter: '#3F5BE0',
|
|
163
|
+
perimeterWidth: 2,
|
|
62
164
|
},
|
|
63
165
|
},
|
|
64
166
|
box: {
|
|
65
|
-
// The
|
|
167
|
+
// The cerulean data box: a translucent fill outlined in the line colour, a
|
|
66
168
|
// bolder median, and matching whiskers.
|
|
67
169
|
default: {
|
|
68
|
-
fill: '#
|
|
170
|
+
fill: '#0284c7',
|
|
69
171
|
fillOpacity: 0.3,
|
|
70
|
-
stroke: '#
|
|
71
|
-
|
|
72
|
-
|
|
172
|
+
stroke: '#0284c7',
|
|
173
|
+
// 1px at rest, 1.5 when selected (`selectedStrokeWidth`) — the design's
|
|
174
|
+
// hairline rule. Previously a flat 1.5, which left no headroom for a
|
|
175
|
+
// weight change to mean anything.
|
|
176
|
+
strokeWidth: 1,
|
|
177
|
+
median: '#075985',
|
|
73
178
|
medianWidth: 2,
|
|
74
|
-
whisker: '#
|
|
179
|
+
whisker: '#a3cde5',
|
|
75
180
|
whiskerWidth: 1,
|
|
181
|
+
// The tint ladder. Lightness spacing is held across all three ladders,
|
|
182
|
+
// so the quantile read (outer < inner < stroke < median) never changes —
|
|
183
|
+
// only where the ladder sits does. It follows the bar palette's rule
|
|
184
|
+
// exactly: hover brightens within teal, blue means committed. Step 2 of
|
|
185
|
+
// each ladder IS the matching bar token (`bar.hover` / `bar.highlight`),
|
|
186
|
+
// so a live box beside a live bar reads as one act.
|
|
187
|
+
states: {
|
|
188
|
+
rest: ['#BFE3DE', '#7FC8BF', '#2A9D8F', '#1F7A6F'],
|
|
189
|
+
// Hover brightens *within teal* — blue stays reserved for a committed
|
|
190
|
+
// selection, the same rule `bar.hover` follows (and step 2 is exactly
|
|
191
|
+
// `bar.default.hover`, so a hovered box and a hovered bar match).
|
|
192
|
+
hover: ['#D6F1EC', '#9CDBD1', '#3FBFAE', '#2A9D8F'],
|
|
193
|
+
selected: ['#C0CAF6', '#8095EA', '#3F5BE0', '#1C2E9E'],
|
|
194
|
+
dimmedOpacity: 0.32,
|
|
195
|
+
},
|
|
196
|
+
selectedStrokeWidth: 1.5,
|
|
76
197
|
},
|
|
77
198
|
// The warm accent box — the second series of a paired distribution (an
|
|
78
199
|
// in/out traffic list), mirroring `bar.secondary` / `line.secondary`.
|
|
@@ -89,31 +210,62 @@ export const defaultTheme = {
|
|
|
89
210
|
},
|
|
90
211
|
candle: {
|
|
91
212
|
// Neutral / unbranded up-down pair — *not* market green/red (a consumer
|
|
92
|
-
// supplies that via cssVarTheme). Rising reuses the
|
|
93
|
-
// warm secondary accent — distinguishable at a glance on
|
|
213
|
+
// supplies that via cssVarTheme). Rising reuses the data cerulean; falling
|
|
214
|
+
// the warm secondary accent — distinguishable at a glance on light ground.
|
|
94
215
|
default: {
|
|
95
|
-
rising: { body: '#
|
|
216
|
+
rising: { body: '#0284c7', wick: '#075985' },
|
|
96
217
|
falling: { body: '#e8836b', wick: '#b4442a' },
|
|
97
218
|
neutral: { body: '#94a3b8', wick: '#64748b' },
|
|
98
219
|
bodyWidth: 0.7,
|
|
99
220
|
wickWidth: 1,
|
|
221
|
+
// No state colour at all — the direction hue owns that channel. A live
|
|
222
|
+
// candle simply grows (its body stroked in its own colour, lines
|
|
223
|
+
// heavier); a selection additionally recedes the field to `.32`, the
|
|
224
|
+
// same step every other mark uses.
|
|
225
|
+
dimmedOpacity: 0.32,
|
|
226
|
+
liveWickWidth: 1.5,
|
|
100
227
|
},
|
|
101
228
|
},
|
|
102
229
|
bar: {
|
|
103
|
-
//
|
|
104
|
-
//
|
|
230
|
+
// The **interaction-state palette**: state is a *hue* difference, not a
|
|
231
|
+
// shade of one colour. Rest is teal; a committed selection is blue; hover
|
|
232
|
+
// is a *brighter teal* — deliberately not blue, because blue is reserved
|
|
233
|
+
// for "committed". Out-of-selection bars recede to the same teal at 0.32.
|
|
234
|
+
// The shared drag band (`brush` below) is the selection blue at 7%, so the
|
|
235
|
+
// live region reads as the same act as the selection it is about to make.
|
|
236
|
+
// `secondary` reuses the line's warm accent for a second series.
|
|
105
237
|
default: {
|
|
106
|
-
fill: '#
|
|
107
|
-
opacity:
|
|
108
|
-
highlight: '#
|
|
238
|
+
fill: '#2A9D8F', // rest — teal, full opacity
|
|
239
|
+
opacity: 1,
|
|
240
|
+
highlight: '#3F5BE0', // selected — blue; committed state
|
|
241
|
+
hover: '#3FBFAE', // hover — brighter teal, never blue
|
|
242
|
+
dimmed: 'rgba(42,157,143,0.32)', // outside an active selection
|
|
109
243
|
gap: 1,
|
|
110
244
|
minWidth: 1,
|
|
111
245
|
outlineWidth: 1.5,
|
|
112
|
-
// The default threshold ladder: the bar's own
|
|
246
|
+
// The default threshold ladder: the bar's own teal as the in-range band,
|
|
113
247
|
// then amber, then red. Three entries serves the common two-threshold
|
|
114
248
|
// ok/warning/alarm ladder out of the box; a longer `thresholds` needs a
|
|
115
249
|
// longer ladder from the theme or `bandColors`.
|
|
116
|
-
bands: ['#
|
|
250
|
+
bands: ['#2A9D8F', '#e8a13c', '#d64545'],
|
|
251
|
+
// The **stack group ramp**, first group first (so a vertical stack reads
|
|
252
|
+
// teal at the bottom up to terracotta). Four muted hues at similar
|
|
253
|
+
// lightness, so no segment shouts over its neighbours the way a
|
|
254
|
+
// saturation ladder would — the ramp says "different group", not
|
|
255
|
+
// "more important". It starts on a teal near the resting `fill` so a
|
|
256
|
+
// two-group stack still looks like the rest of the palette.
|
|
257
|
+
groups: ['#4c9e8f', '#5379be', '#e2a54a', '#b5604e'],
|
|
258
|
+
// Each ramp entry desaturated (~×0.18) and lightened toward the ground,
|
|
259
|
+
// keeping its hue and its *relative* lightness — so a receded bin still
|
|
260
|
+
// reads as four bands rather than one grey block, and the amber stays
|
|
261
|
+
// the lightest of them as it is in the vivid ramp.
|
|
262
|
+
groupsDimmed: ['#c7cecd', '#ced1d6', '#dcd8d2', '#d3cdcc'],
|
|
263
|
+
// Each ramp entry brightened by the same move `fill` → `hover` makes:
|
|
264
|
+
// hue held, lightness +0.11, saturation left alone. So a hovered segment
|
|
265
|
+
// still says which group it is — the thing a single hover colour cannot
|
|
266
|
+
// do on a stack, and under a <MultiSelector> (block-scoped hover) it is
|
|
267
|
+
// the whole bin that would otherwise go one flat colour.
|
|
268
|
+
groupsHover: ['#6bb8a9', '#7c99cd', '#eabd7a', '#c68476'],
|
|
117
269
|
},
|
|
118
270
|
secondary: {
|
|
119
271
|
fill: '#e8836b',
|
|
@@ -140,15 +292,39 @@ export const defaultTheme = {
|
|
|
140
292
|
size: 11,
|
|
141
293
|
},
|
|
142
294
|
cursor: '#64748b',
|
|
295
|
+
// The drag band in the bar palette's selection blue at 7%, edged at 1px —
|
|
296
|
+
// the live region reads as the selection it is about to commit.
|
|
297
|
+
brush: { fill: 'rgba(63,91,224,0.07)', edge: 'rgba(63,91,224,0.45)' },
|
|
143
298
|
chip: { background: '#ffffff' },
|
|
144
299
|
gap: { connectorOpacity: 0.5 },
|
|
145
|
-
//
|
|
300
|
+
// Burnt-amber marks register — a deliberately warm outlier against every
|
|
301
|
+
// data hue, so a placed mark never reads as data (the same rule the docs
|
|
302
|
+
// brand's `vizMark` encodes). Verified against the whole palette: its
|
|
303
|
+
// nearest data neighbours are the threshold-ladder red `#d64545` (ΔE2000
|
|
304
|
+
// ≈ 18) and the warm secondary accent `#e8836b` (ΔE2000 ≈ 21); everything
|
|
305
|
+
// else — bar teal, hover teal, selection blue, the data cerulean — clears
|
|
306
|
+
// ΔE2000 40+. (The previous turquoise `#0d9488` sat ~ΔE 4 from the bar
|
|
307
|
+
// palette's resting teal `#2A9D8F` — indistinguishable at a glance.)
|
|
146
308
|
annotation: {
|
|
147
|
-
color: '#
|
|
309
|
+
color: '#b45309',
|
|
310
|
+
// EXPERIMENTAL — a lighter orange for a swept window's edge rules, which
|
|
311
|
+
// mark a candidate range rather than a committed mark. See `spanEdge`.
|
|
312
|
+
spanEdge: '#f0b26b',
|
|
148
313
|
fillOpacity: 0.1,
|
|
149
314
|
depth: [1, 0.7, 0.4],
|
|
150
315
|
},
|
|
151
316
|
// The in-chart series key: chip-white card, gridline border, axis-label text.
|
|
317
|
+
// The row-chart register — see `ChartTheme.list`. The two band tints are
|
|
318
|
+
// the only genuinely new values: the rails are the same teal/blue pair the
|
|
319
|
+
// bar's interaction-state palette already uses (`hover` / `highlight`), and
|
|
320
|
+
// the marker ink is the near-black the annotation register reads against.
|
|
321
|
+
list: {
|
|
322
|
+
hoverBand: '#F6F6F3', // warm neutral — a lift, not a hue
|
|
323
|
+
hoverRail: '#4FD0BE', // brighter teal, never blue
|
|
324
|
+
selectedBand: '#EEF1FD', // the selection blue, washed
|
|
325
|
+
selectedRail: '#3F5BE0', // = bar.default.highlight — committed state
|
|
326
|
+
markerInk: '#1C1C1A', // reserved from the selection hue (see the doc)
|
|
327
|
+
},
|
|
152
328
|
legend: {
|
|
153
329
|
background: '#ffffff',
|
|
154
330
|
border: '#e2e8f0',
|
package/dist/tracker.d.ts
CHANGED
|
@@ -48,6 +48,12 @@ export declare const DEFAULT_CURSOR_MODE: CursorMode;
|
|
|
48
48
|
* presets — `line` is line-only, `point` / `inline` / `flag` are dot-based with
|
|
49
49
|
* no line, `none` draws nothing. `flag` raises a staff from each point to a
|
|
50
50
|
* value flag stacked near the top of the row (drawn in `Layers`).
|
|
51
|
+
*
|
|
52
|
+
* **Superseded by the mounted cursor presets** (`cursors.tsx` — each mode's
|
|
53
|
+
* drawing now lives in its component's registered spec): `Layers` no longer
|
|
54
|
+
* reads this. Kept, with its tests, for the life of the `cursor` string-prop
|
|
55
|
+
* deprecation window as the pinned record of what each mode drew; delete both
|
|
56
|
+
* when the modes go.
|
|
51
57
|
*/
|
|
52
58
|
export declare function cursorParts(mode: CursorMode): {
|
|
53
59
|
readonly line: boolean;
|
package/dist/tracker.js
CHANGED
|
@@ -79,6 +79,12 @@ export const DEFAULT_CURSOR_MODE = 'line';
|
|
|
79
79
|
* presets — `line` is line-only, `point` / `inline` / `flag` are dot-based with
|
|
80
80
|
* no line, `none` draws nothing. `flag` raises a staff from each point to a
|
|
81
81
|
* value flag stacked near the top of the row (drawn in `Layers`).
|
|
82
|
+
*
|
|
83
|
+
* **Superseded by the mounted cursor presets** (`cursors.tsx` — each mode's
|
|
84
|
+
* drawing now lives in its component's registered spec): `Layers` no longer
|
|
85
|
+
* reads this. Kept, with its tests, for the life of the `cursor` string-prop
|
|
86
|
+
* deprecation window as the pinned record of what each mode drew; delete both
|
|
87
|
+
* when the modes go.
|
|
82
88
|
*/
|
|
83
89
|
export function cursorParts(mode) {
|
|
84
90
|
const base = { line: false, dots: false, chip: 'none', band: false };
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { BoundedSequence, TimeSeries } from 'pond-ts';
|
|
2
|
+
import type { SeriesSchema } from 'pond-ts';
|
|
3
|
+
import type { DiscontinuityProvider, TradingCalendarLike } from './tradingTimeScale.js';
|
|
4
|
+
/**
|
|
5
|
+
* Shared fixtures for the trading-time-axis stories (reference + interaction).
|
|
6
|
+
* A session calendar + **inline** discontinuity provider (in real use you'd pass
|
|
7
|
+
* `calendar.discontinuities()` from `@pond-ts/financial`); the axis only needs
|
|
8
|
+
* the structural provider, so charts stays decoupled.
|
|
9
|
+
*/
|
|
10
|
+
export declare const H = 3600000;
|
|
11
|
+
export declare const DAY = 86400000;
|
|
12
|
+
export declare const MIN = 60000;
|
|
13
|
+
export declare const MON: number;
|
|
14
|
+
export declare const WIDTH = 720;
|
|
15
|
+
export interface Session {
|
|
16
|
+
date: string;
|
|
17
|
+
open: number;
|
|
18
|
+
close: number;
|
|
19
|
+
}
|
|
20
|
+
/** `count` weekday sessions from the anchor Monday (09:30–16:00 UTC), skipping
|
|
21
|
+
* weekends and an optional holiday date — a stand-in for a real calendar. */
|
|
22
|
+
export declare function weekdaySessions(count: number, holiday?: string): Session[];
|
|
23
|
+
/** An early-close (half-day) variant of the last session. */
|
|
24
|
+
export declare function withHalfDay(sessions: Session[], closeHour?: number): Session[];
|
|
25
|
+
/** A proportional trading-time provider over the sessions' `[open, close)` spans. */
|
|
26
|
+
export declare function provider(sessions: Session[]): DiscontinuityProvider;
|
|
27
|
+
/** A **uniform** trading-time provider — each session is one equal-width slot
|
|
28
|
+
* regardless of duration (a half-day is as wide as a full day). */
|
|
29
|
+
export declare function uniformProvider(sessions: Session[]): DiscontinuityProvider;
|
|
30
|
+
/** A structural {@link TradingCalendarLike} — the shape `@pond-ts/financial`'s
|
|
31
|
+
* `TradingCalendar` satisfies — that the container's `spacing` prop drives. */
|
|
32
|
+
export declare function calendarOf(sessions: Session[]): TradingCalendarLike;
|
|
33
|
+
/** One interval per session — the daily-bar grid. */
|
|
34
|
+
export declare function sessionSeq(sessions: Session[]): BoundedSequence;
|
|
35
|
+
/** Intraday `period`-ms bars within each session (never crossing a boundary). */
|
|
36
|
+
export declare function barSeq(sessions: Session[], periodMs: number): BoundedSequence;
|
|
37
|
+
export declare const tickSchema: readonly [{
|
|
38
|
+
readonly name: "time";
|
|
39
|
+
readonly kind: "time";
|
|
40
|
+
}, {
|
|
41
|
+
readonly name: "price";
|
|
42
|
+
readonly kind: "number";
|
|
43
|
+
}];
|
|
44
|
+
/** Deterministic in-session price ticks (a smooth random-ish walk on sines). */
|
|
45
|
+
export declare function ticks(sessions: Session[], stepMs: number): TimeSeries<typeof tickSchema>;
|
|
46
|
+
/** Intraday ticks with an **overnight gap**: each session opens at a distinct
|
|
47
|
+
* base level (a gap up from the prior close) while the intraday wiggle stays
|
|
48
|
+
* smooth. So a session boundary is a real price jump — the connected line shows
|
|
49
|
+
* a near-vertical bridge across the collapsed gap, `sessionBreaks` a clean
|
|
50
|
+
* pen-up. (Plain {@link ticks} walks continuously across sessions, so close ≈
|
|
51
|
+
* next open and the break is invisible — this is the fixture that shows it.) */
|
|
52
|
+
export declare function gappingTicks(sessions: Session[], stepMs: number): TimeSeries<typeof tickSchema>;
|
|
53
|
+
export declare const OHLC: {
|
|
54
|
+
readonly open: {
|
|
55
|
+
readonly from: "price";
|
|
56
|
+
readonly using: "first";
|
|
57
|
+
};
|
|
58
|
+
readonly high: {
|
|
59
|
+
readonly from: "price";
|
|
60
|
+
readonly using: "max";
|
|
61
|
+
};
|
|
62
|
+
readonly low: {
|
|
63
|
+
readonly from: "price";
|
|
64
|
+
readonly using: "min";
|
|
65
|
+
};
|
|
66
|
+
readonly close: {
|
|
67
|
+
readonly from: "price";
|
|
68
|
+
readonly using: "last";
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
/** Interval-keyed OHLC candles over a bucket sequence (immune to point-key slot
|
|
72
|
+
* widths). Return type widened to the general `SeriesSchema`: the inferred
|
|
73
|
+
* aggregate schema references an internal pond-ts module and can't be named
|
|
74
|
+
* (TS2742), so an explicit annotation is required. `<Candlestick>` reads the
|
|
75
|
+
* o/h/l/c columns by runtime name, so the widening is invisible to callers. */
|
|
76
|
+
export declare function candles(sessions: Session[], seq: BoundedSequence, stepMs: number): TimeSeries<SeriesSchema>;
|
|
77
|
+
export declare function rangeOf(sessions: Session[]): [number, number];
|
|
78
|
+
//# sourceMappingURL=tradingAxis.fixture.d.ts.map
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import { BoundedSequence, Interval, TimeSeries } from 'pond-ts';
|
|
2
|
+
/**
|
|
3
|
+
* Shared fixtures for the trading-time-axis stories (reference + interaction).
|
|
4
|
+
* A session calendar + **inline** discontinuity provider (in real use you'd pass
|
|
5
|
+
* `calendar.discontinuities()` from `@pond-ts/financial`); the axis only needs
|
|
6
|
+
* the structural provider, so charts stays decoupled.
|
|
7
|
+
*/
|
|
8
|
+
export const H = 3_600_000;
|
|
9
|
+
export const DAY = 86_400_000;
|
|
10
|
+
export const MIN = 60_000;
|
|
11
|
+
export const MON = Date.UTC(2026, 0, 5); // a Monday
|
|
12
|
+
export const WIDTH = 720;
|
|
13
|
+
/** `count` weekday sessions from the anchor Monday (09:30–16:00 UTC), skipping
|
|
14
|
+
* weekends and an optional holiday date — a stand-in for a real calendar. */
|
|
15
|
+
export function weekdaySessions(count, holiday) {
|
|
16
|
+
const out = [];
|
|
17
|
+
for (let dayIdx = 0; out.length < count; dayIdx++) {
|
|
18
|
+
const dayStart = MON + dayIdx * DAY;
|
|
19
|
+
const dow = new Date(dayStart).getUTCDay();
|
|
20
|
+
if (dow === 0 || dow === 6)
|
|
21
|
+
continue; // weekend
|
|
22
|
+
const date = new Date(dayStart).toISOString().slice(0, 10);
|
|
23
|
+
if (date === holiday)
|
|
24
|
+
continue;
|
|
25
|
+
out.push({ date, open: dayStart + 9.5 * H, close: dayStart + 16 * H });
|
|
26
|
+
}
|
|
27
|
+
return out;
|
|
28
|
+
}
|
|
29
|
+
/** An early-close (half-day) variant of the last session. */
|
|
30
|
+
export function withHalfDay(sessions, closeHour = 13) {
|
|
31
|
+
const last = sessions[sessions.length - 1];
|
|
32
|
+
const dayStart = last.open - 9.5 * H;
|
|
33
|
+
return [
|
|
34
|
+
...sessions.slice(0, -1),
|
|
35
|
+
{ ...last, close: dayStart + closeHour * H },
|
|
36
|
+
];
|
|
37
|
+
}
|
|
38
|
+
/** A proportional trading-time provider over the sessions' `[open, close)` spans. */
|
|
39
|
+
export function provider(sessions) {
|
|
40
|
+
const segs = sessions.map((s) => [s.open, s.close]);
|
|
41
|
+
const cum = [0];
|
|
42
|
+
for (const [a, b] of segs)
|
|
43
|
+
cum.push(cum[cum.length - 1] + (b - a));
|
|
44
|
+
const total = cum[cum.length - 1];
|
|
45
|
+
const liveMs = (t) => {
|
|
46
|
+
if (t <= segs[0][0])
|
|
47
|
+
return 0;
|
|
48
|
+
if (t >= segs[segs.length - 1][1])
|
|
49
|
+
return total;
|
|
50
|
+
for (let i = 0; i < segs.length; i++) {
|
|
51
|
+
const [a, b] = segs[i];
|
|
52
|
+
if (t < a)
|
|
53
|
+
return cum[i];
|
|
54
|
+
if (t < b)
|
|
55
|
+
return cum[i] + (t - a);
|
|
56
|
+
}
|
|
57
|
+
return total;
|
|
58
|
+
};
|
|
59
|
+
const inst = (L) => {
|
|
60
|
+
if (L <= 0)
|
|
61
|
+
return segs[0][0];
|
|
62
|
+
if (L >= total)
|
|
63
|
+
return segs[segs.length - 1][1];
|
|
64
|
+
for (let i = 0; i < segs.length; i++) {
|
|
65
|
+
if (L < cum[i + 1])
|
|
66
|
+
return segs[i][0] + (L - cum[i]);
|
|
67
|
+
}
|
|
68
|
+
return segs[segs.length - 1][1];
|
|
69
|
+
};
|
|
70
|
+
const self = {
|
|
71
|
+
distance: (a, b) => liveMs(b) - liveMs(a),
|
|
72
|
+
offset: (v, amt) => inst(liveMs(v) + amt),
|
|
73
|
+
clampUp: (t) => t,
|
|
74
|
+
clampDown: (t) => t,
|
|
75
|
+
copy: () => self,
|
|
76
|
+
boundaries: (from, to) => {
|
|
77
|
+
const out = [];
|
|
78
|
+
for (let i = 1; i < segs.length; i++) {
|
|
79
|
+
const start = segs[i][0];
|
|
80
|
+
if (start > segs[i - 1][1] && start > from && start < to)
|
|
81
|
+
out.push(start);
|
|
82
|
+
}
|
|
83
|
+
return out;
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
return self;
|
|
87
|
+
}
|
|
88
|
+
/** A **uniform** trading-time provider — each session is one equal-width slot
|
|
89
|
+
* regardless of duration (a half-day is as wide as a full day). */
|
|
90
|
+
export function uniformProvider(sessions) {
|
|
91
|
+
const segs = sessions.map((s) => [s.open, s.close]);
|
|
92
|
+
const n = segs.length;
|
|
93
|
+
const uCoord = (t) => {
|
|
94
|
+
if (t <= segs[0][0])
|
|
95
|
+
return 0;
|
|
96
|
+
if (t >= segs[n - 1][1])
|
|
97
|
+
return n;
|
|
98
|
+
for (let i = 0; i < n; i++) {
|
|
99
|
+
const [a, b] = segs[i];
|
|
100
|
+
if (t < a)
|
|
101
|
+
return i; // in a gap before session i → the boundary
|
|
102
|
+
if (t < b)
|
|
103
|
+
return i + (t - a) / (b - a); // linear within the session
|
|
104
|
+
}
|
|
105
|
+
return n;
|
|
106
|
+
};
|
|
107
|
+
const inst = (u) => {
|
|
108
|
+
if (u <= 0)
|
|
109
|
+
return segs[0][0];
|
|
110
|
+
if (u >= n)
|
|
111
|
+
return segs[n - 1][1];
|
|
112
|
+
const i = Math.floor(u);
|
|
113
|
+
const [a, b] = segs[i];
|
|
114
|
+
return a + (u - i) * (b - a);
|
|
115
|
+
};
|
|
116
|
+
const self = {
|
|
117
|
+
distance: (x, y) => uCoord(y) - uCoord(x),
|
|
118
|
+
offset: (v, amt) => inst(uCoord(v) + amt),
|
|
119
|
+
clampUp: (t) => t,
|
|
120
|
+
clampDown: (t) => t,
|
|
121
|
+
copy: () => self,
|
|
122
|
+
boundaries: (from, to) => {
|
|
123
|
+
const out = [];
|
|
124
|
+
for (let i = 1; i < n; i++) {
|
|
125
|
+
const start = segs[i][0];
|
|
126
|
+
if (start > segs[i - 1][1] && start > from && start < to)
|
|
127
|
+
out.push(start);
|
|
128
|
+
}
|
|
129
|
+
return out;
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
return self;
|
|
133
|
+
}
|
|
134
|
+
/** A structural {@link TradingCalendarLike} — the shape `@pond-ts/financial`'s
|
|
135
|
+
* `TradingCalendar` satisfies — that the container's `spacing` prop drives. */
|
|
136
|
+
export function calendarOf(sessions) {
|
|
137
|
+
return {
|
|
138
|
+
discontinuities: (options) => options?.spacing === 'uniform'
|
|
139
|
+
? uniformProvider(sessions)
|
|
140
|
+
: provider(sessions),
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
/** One interval per session — the daily-bar grid. */
|
|
144
|
+
export function sessionSeq(sessions) {
|
|
145
|
+
return new BoundedSequence(sessions.map((s) => new Interval({ value: s.date, start: s.open, end: s.close })));
|
|
146
|
+
}
|
|
147
|
+
/** Intraday `period`-ms bars within each session (never crossing a boundary). */
|
|
148
|
+
export function barSeq(sessions, periodMs) {
|
|
149
|
+
const ivals = [];
|
|
150
|
+
for (const s of sessions) {
|
|
151
|
+
for (let t = s.open; t < s.close; t += periodMs) {
|
|
152
|
+
ivals.push(new Interval({
|
|
153
|
+
value: t,
|
|
154
|
+
start: t,
|
|
155
|
+
end: Math.min(t + periodMs, s.close),
|
|
156
|
+
}));
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return new BoundedSequence(ivals);
|
|
160
|
+
}
|
|
161
|
+
export const tickSchema = [
|
|
162
|
+
{ name: 'time', kind: 'time' },
|
|
163
|
+
{ name: 'price', kind: 'number' },
|
|
164
|
+
];
|
|
165
|
+
/** Deterministic in-session price ticks (a smooth random-ish walk on sines). */
|
|
166
|
+
export function ticks(sessions, stepMs) {
|
|
167
|
+
const rows = [];
|
|
168
|
+
let i = 0;
|
|
169
|
+
for (const s of sessions) {
|
|
170
|
+
for (let t = s.open; t < s.close; t += stepMs, i++) {
|
|
171
|
+
const price = 100 +
|
|
172
|
+
9 * Math.sin(i / 22) +
|
|
173
|
+
3 * Math.sin(i / 4.5) +
|
|
174
|
+
1.4 * Math.sin(i / 1.3);
|
|
175
|
+
rows.push([t, price]);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return new TimeSeries({ name: 'ticks', schema: tickSchema, rows });
|
|
179
|
+
}
|
|
180
|
+
/** Intraday ticks with an **overnight gap**: each session opens at a distinct
|
|
181
|
+
* base level (a gap up from the prior close) while the intraday wiggle stays
|
|
182
|
+
* smooth. So a session boundary is a real price jump — the connected line shows
|
|
183
|
+
* a near-vertical bridge across the collapsed gap, `sessionBreaks` a clean
|
|
184
|
+
* pen-up. (Plain {@link ticks} walks continuously across sessions, so close ≈
|
|
185
|
+
* next open and the break is invisible — this is the fixture that shows it.) */
|
|
186
|
+
export function gappingTicks(sessions, stepMs) {
|
|
187
|
+
const rows = [];
|
|
188
|
+
let i = 0;
|
|
189
|
+
sessions.forEach((s, si) => {
|
|
190
|
+
const base = 100 + si * 6; // each session gaps ~6 above the last
|
|
191
|
+
for (let t = s.open; t < s.close; t += stepMs, i++) {
|
|
192
|
+
const price = base + 4 * Math.sin(i / 18) + 1.5 * Math.sin(i / 3.5);
|
|
193
|
+
rows.push([t, price]);
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
return new TimeSeries({ name: 'ticks', schema: tickSchema, rows });
|
|
197
|
+
}
|
|
198
|
+
export const OHLC = {
|
|
199
|
+
open: { from: 'price', using: 'first' },
|
|
200
|
+
high: { from: 'price', using: 'max' },
|
|
201
|
+
low: { from: 'price', using: 'min' },
|
|
202
|
+
close: { from: 'price', using: 'last' },
|
|
203
|
+
};
|
|
204
|
+
/** Interval-keyed OHLC candles over a bucket sequence (immune to point-key slot
|
|
205
|
+
* widths). Return type widened to the general `SeriesSchema`: the inferred
|
|
206
|
+
* aggregate schema references an internal pond-ts module and can't be named
|
|
207
|
+
* (TS2742), so an explicit annotation is required. `<Candlestick>` reads the
|
|
208
|
+
* o/h/l/c columns by runtime name, so the widening is invisible to callers. */
|
|
209
|
+
export function candles(sessions, seq, stepMs) {
|
|
210
|
+
return ticks(sessions, stepMs).aggregate(seq, OHLC);
|
|
211
|
+
}
|
|
212
|
+
export function rangeOf(sessions) {
|
|
213
|
+
return [sessions[0].open, sessions[sessions.length - 1].close];
|
|
214
|
+
}
|
|
215
|
+
//# sourceMappingURL=tradingAxis.fixture.js.map
|