ml-time-graph 1.0.0 → 1.0.5
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/API_DESIGN.md +1211 -0
- package/README.de.md +12 -0
- package/README.md +12 -0
- package/dist/analyze/index.d.ts +272 -46
- package/dist/analyze/index.js +11 -570
- package/dist/index.d.ts +346 -9
- package/dist/index.js +57 -3420
- package/dist/interaction/index.d.ts +1 -1
- package/dist/interaction/index.js +2 -435
- package/dist/internals.d.ts +20 -5
- package/dist/internals.js +40 -2455
- package/dist/{layout-Sc5UkC0r.d.ts → layout-BOYtrsZa.d.ts} +1 -1
- package/dist/{scale-Cbr0KpPz.d.ts → scale-BRE_QhbZ.d.ts} +38 -15
- package/package.json +65 -80
- package/dist/aggregated_subtypes-DZNZyFTX.d.ts +0 -43
- /package/{MKT_AGGREGATE.md → docs/MKT_AGGREGATE.md} +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { k as DataPoint, o as DrawCommand, ad as TimeScale, V as LinearScale } from '../scale-BRE_QhbZ.js';
|
|
2
2
|
|
|
3
3
|
/*!
|
|
4
4
|
* MLTimeGraph — Copyright (c) 2026 Michael Lechner
|
|
@@ -1,438 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
var LinearScale = class {
|
|
3
|
-
#domain;
|
|
4
|
-
#range;
|
|
5
|
-
constructor(config) {
|
|
6
|
-
this.#domain = [...config.domain];
|
|
7
|
-
this.#range = [...config.range];
|
|
8
|
-
}
|
|
9
|
-
map(value) {
|
|
10
|
-
const v = Number(value);
|
|
11
|
-
const [d0, d1] = this.#domain;
|
|
12
|
-
const [r0, r1] = this.#range;
|
|
13
|
-
if (d1 === d0) return r0;
|
|
14
|
-
return r0 + (v - d0) / (d1 - d0) * (r1 - r0);
|
|
15
|
-
}
|
|
16
|
-
invert(pixel) {
|
|
17
|
-
const [d0, d1] = this.#domain;
|
|
18
|
-
const [r0, r1] = this.#range;
|
|
19
|
-
if (r1 === r0) return d0;
|
|
20
|
-
return d0 + (pixel - r0) / (r1 - r0) * (d1 - d0);
|
|
21
|
-
}
|
|
22
|
-
domain() {
|
|
23
|
-
return [...this.#domain];
|
|
24
|
-
}
|
|
25
|
-
range() {
|
|
26
|
-
return [...this.#range];
|
|
27
|
-
}
|
|
28
|
-
};
|
|
29
|
-
var TIME_INTERVALS = [
|
|
30
|
-
{ label: "second", ms: 1e3 },
|
|
31
|
-
{ label: "2_seconds", ms: 2e3 },
|
|
32
|
-
{ label: "5_seconds", ms: 5e3 },
|
|
33
|
-
{ label: "10_seconds", ms: 1e4 },
|
|
34
|
-
{ label: "30_seconds", ms: 3e4 },
|
|
35
|
-
{ label: "minute", ms: 6e4 },
|
|
36
|
-
{ label: "5_minutes", ms: 3e5 },
|
|
37
|
-
{ label: "15_minutes", ms: 9e5 },
|
|
38
|
-
{ label: "30_minutes", ms: 18e5 },
|
|
39
|
-
{ label: "hour", ms: 36e5 },
|
|
40
|
-
{ label: "3_hours", ms: 108e5 },
|
|
41
|
-
{ label: "6_hours", ms: 216e5 },
|
|
42
|
-
{ label: "day", ms: 864e5 },
|
|
43
|
-
{ label: "week", ms: 6048e5 },
|
|
44
|
-
{ label: "month", ms: 2592e6 },
|
|
45
|
-
{ label: "3_months", ms: 7776e6 },
|
|
46
|
-
{ label: "6_months", ms: 15552e6 },
|
|
47
|
-
{ label: "year", ms: 31536e6 },
|
|
48
|
-
{ label: "2_years", ms: 63072e6 },
|
|
49
|
-
{ label: "5_years", ms: 15768e7 }
|
|
50
|
-
];
|
|
51
|
-
var TimeScale = class {
|
|
52
|
-
#linear;
|
|
53
|
-
#locale;
|
|
54
|
-
constructor(config) {
|
|
55
|
-
this.#linear = new LinearScale({
|
|
56
|
-
domain: config.domain,
|
|
57
|
-
range: config.range
|
|
58
|
-
});
|
|
59
|
-
this.#locale = config.locale || (typeof navigator !== "undefined" ? navigator.language : "en-US");
|
|
60
|
-
}
|
|
61
|
-
map(value) {
|
|
62
|
-
return this.#linear.map(Number(value));
|
|
63
|
-
}
|
|
64
|
-
invert(pixel) {
|
|
65
|
-
return this.#linear.invert(pixel);
|
|
66
|
-
}
|
|
67
|
-
domain() {
|
|
68
|
-
return this.#linear.domain();
|
|
69
|
-
}
|
|
70
|
-
range() {
|
|
71
|
-
return this.#linear.range();
|
|
72
|
-
}
|
|
73
|
-
get locale() {
|
|
74
|
-
return this.#locale;
|
|
75
|
-
}
|
|
76
|
-
/**
|
|
77
|
-
* Pick the "nicest" time interval that yields roughly `targetTicks` ticks
|
|
78
|
-
* across the visible range. Clamps to minTicks / maxTicks bounds.
|
|
79
|
-
*/
|
|
80
|
-
tickInterval(targetTicks, minTicks = 3, maxTicks = 12) {
|
|
81
|
-
const [d0, d1] = this.#linear.domain();
|
|
82
|
-
const totalMs = d1 - d0;
|
|
83
|
-
if (totalMs <= 0) return { interval: TIME_INTERVALS[0].ms };
|
|
84
|
-
const ideal = totalMs / targetTicks;
|
|
85
|
-
let picked = TIME_INTERVALS[0].ms;
|
|
86
|
-
for (const t of TIME_INTERVALS) {
|
|
87
|
-
if (t.ms >= ideal) {
|
|
88
|
-
picked = t.ms;
|
|
89
|
-
break;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
let candidate = picked;
|
|
93
|
-
let count = Math.round(totalMs / candidate);
|
|
94
|
-
while (count > maxTicks && candidate < TIME_INTERVALS[TIME_INTERVALS.length - 1].ms) {
|
|
95
|
-
const idx = TIME_INTERVALS.findIndex((t) => t.ms === candidate);
|
|
96
|
-
candidate = TIME_INTERVALS[Math.min(idx + 1, TIME_INTERVALS.length - 1)].ms;
|
|
97
|
-
count = Math.round(totalMs / candidate);
|
|
98
|
-
}
|
|
99
|
-
while (count < minTicks && candidate > TIME_INTERVALS[0].ms) {
|
|
100
|
-
const idx = TIME_INTERVALS.findIndex((t) => t.ms === candidate);
|
|
101
|
-
candidate = TIME_INTERVALS[Math.max(idx - 1, 0)].ms;
|
|
102
|
-
count = Math.round(totalMs / candidate);
|
|
103
|
-
}
|
|
104
|
-
return { interval: candidate };
|
|
105
|
-
}
|
|
106
|
-
/**
|
|
107
|
-
* Generate tick positions (timestamps) across the domain.
|
|
108
|
-
*/
|
|
109
|
-
ticks(opts) {
|
|
110
|
-
const minT = opts?.minTicks ?? 5;
|
|
111
|
-
const maxT = opts?.maxTicks ?? 12;
|
|
112
|
-
const { interval } = this.tickInterval(
|
|
113
|
-
(minT + maxT) / 2,
|
|
114
|
-
minT,
|
|
115
|
-
maxT
|
|
116
|
-
);
|
|
117
|
-
const [d0, d1] = this.#linear.domain();
|
|
118
|
-
const result = [];
|
|
119
|
-
const start = Math.ceil(d0 / interval) * interval;
|
|
120
|
-
for (let t = start; t <= d1; t += interval) {
|
|
121
|
-
result.push(t);
|
|
122
|
-
}
|
|
123
|
-
return result;
|
|
124
|
-
}
|
|
125
|
-
/** Format a timestamp using Intl.DateTimeFormat */
|
|
126
|
-
format(timestamp, formatOpts) {
|
|
127
|
-
return new Intl.DateTimeFormat(this.#locale, formatOpts).format(
|
|
128
|
-
new Date(timestamp)
|
|
129
|
-
);
|
|
130
|
-
}
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
// src/interaction/zoom.ts
|
|
134
|
-
var Zoom = class {
|
|
135
|
-
_config;
|
|
136
|
-
_state;
|
|
137
|
-
constructor(initialDomain, config) {
|
|
138
|
-
this._config = {
|
|
139
|
-
enabled: config?.enabled ?? true,
|
|
140
|
-
zoomSpeed: config?.zoomSpeed ?? 0.1,
|
|
141
|
-
minDomainMs: config?.minDomainMs ?? 0,
|
|
142
|
-
maxDomainMs: config?.maxDomainMs ?? 365 * 24 * 60 * 60 * 1e3
|
|
143
|
-
};
|
|
144
|
-
this._state = {
|
|
145
|
-
domain: [...initialDomain],
|
|
146
|
-
originalDomain: [...initialDomain],
|
|
147
|
-
scale: 1,
|
|
148
|
-
offset: 0
|
|
149
|
-
};
|
|
150
|
-
}
|
|
151
|
-
get state() {
|
|
152
|
-
return { ...this._state, domain: [...this._state.domain] };
|
|
153
|
-
}
|
|
154
|
-
applyZoom(x, delta, rangeWidth) {
|
|
155
|
-
if (!this._config.enabled) return;
|
|
156
|
-
const factor = 1 + Math.sign(delta) * this._config.zoomSpeed;
|
|
157
|
-
if (factor <= 0) return;
|
|
158
|
-
const [d0, d1] = this._state.domain;
|
|
159
|
-
const span = d1 - d0;
|
|
160
|
-
const ratio = x / rangeWidth;
|
|
161
|
-
let newSpan = span / factor;
|
|
162
|
-
const minSpan = this._config.minDomainMs ?? 0;
|
|
163
|
-
const maxSpan = this._config.maxDomainMs ?? Infinity;
|
|
164
|
-
if (newSpan < minSpan) newSpan = minSpan;
|
|
165
|
-
if (newSpan > maxSpan) newSpan = maxSpan;
|
|
166
|
-
const focalPoint = d0 + span * ratio;
|
|
167
|
-
let newD0 = focalPoint - newSpan * ratio;
|
|
168
|
-
let newD1 = focalPoint + newSpan * (1 - ratio);
|
|
169
|
-
const [orig0, orig1] = this._state.originalDomain;
|
|
170
|
-
if (newD1 - newD0 > orig1 - orig0) {
|
|
171
|
-
newD0 = orig0;
|
|
172
|
-
newD1 = orig1;
|
|
173
|
-
newSpan = orig1 - orig0;
|
|
174
|
-
} else {
|
|
175
|
-
if (newD0 < orig0) {
|
|
176
|
-
newD1 = orig1 - (orig1 - orig0) * ((newD1 - newD0) / (orig1 - orig0));
|
|
177
|
-
newD0 = orig0;
|
|
178
|
-
}
|
|
179
|
-
if (newD1 > orig1) {
|
|
180
|
-
newD0 = orig0 + (orig1 - orig0) * ((newD0 - orig0) / (orig1 - orig0));
|
|
181
|
-
newD1 = orig1;
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
this._state.domain = [newD0, newD1];
|
|
185
|
-
this._state.scale = (d1 - d0) / newSpan;
|
|
186
|
-
}
|
|
187
|
-
applyPan(pixelDelta, rangeWidth) {
|
|
188
|
-
if (!this._config.enabled) return;
|
|
189
|
-
const [d0, d1] = this._state.domain;
|
|
190
|
-
const span = d1 - d0;
|
|
191
|
-
const [orig0, orig1] = this._state.originalDomain;
|
|
192
|
-
let shift = -(pixelDelta / rangeWidth) * span;
|
|
193
|
-
let newD0 = d0 + shift;
|
|
194
|
-
let newD1 = d1 + shift;
|
|
195
|
-
if (newD0 < orig0) {
|
|
196
|
-
shift -= newD0 - orig0;
|
|
197
|
-
}
|
|
198
|
-
if (newD1 > orig1) {
|
|
199
|
-
shift -= newD1 - orig1;
|
|
200
|
-
}
|
|
201
|
-
this._state.domain = [d0 + shift, d1 + shift];
|
|
202
|
-
}
|
|
203
|
-
reset() {
|
|
204
|
-
this._state.domain = [...this._state.originalDomain];
|
|
205
|
-
this._state.scale = 1;
|
|
206
|
-
this._state.offset = 0;
|
|
207
|
-
}
|
|
208
|
-
get enabled() {
|
|
209
|
-
return this._config.enabled ?? true;
|
|
210
|
-
}
|
|
211
|
-
};
|
|
212
|
-
|
|
213
|
-
// src/theme/defaults.ts
|
|
214
|
-
var theme = {
|
|
215
|
-
// ── Tooltip ──
|
|
216
|
-
/** Tooltip box background */
|
|
217
|
-
tooltipBg: "#fff",
|
|
218
|
-
/** Tooltip border */
|
|
219
|
-
tooltipBorder: "#cbd5e1",
|
|
220
|
-
/** Tooltip text color */
|
|
221
|
-
tooltipText: "#1e293b",
|
|
222
|
-
/** Tooltip snap radius in pixels */
|
|
223
|
-
tooltipSnapRadius: 20,
|
|
224
|
-
// ── Minimap ──
|
|
225
|
-
/** Minimap overview line stroke */
|
|
226
|
-
minimapStroke: "#94a3b8",
|
|
227
|
-
/** Minimap background */
|
|
228
|
-
minimapBg: "#f8f9fa",
|
|
229
|
-
/** Minimap brush (viewport) fill */
|
|
230
|
-
minimapBrush: "#3b82f644"};
|
|
231
|
-
|
|
232
|
-
// src/interaction/minimap.ts
|
|
233
|
-
var Minimap = class {
|
|
234
|
-
_data;
|
|
235
|
-
_width;
|
|
236
|
-
_height;
|
|
237
|
-
_x;
|
|
238
|
-
_y;
|
|
239
|
-
_zoom;
|
|
240
|
-
_stroke;
|
|
241
|
-
_bgColor;
|
|
242
|
-
_brushColor;
|
|
243
|
-
constructor(config) {
|
|
244
|
-
this._data = config.data;
|
|
245
|
-
this._width = config.width;
|
|
246
|
-
this._height = config.height;
|
|
247
|
-
this._x = config.x;
|
|
248
|
-
this._y = config.y;
|
|
249
|
-
this._zoom = config.zoom;
|
|
250
|
-
this._stroke = config.stroke ?? theme.minimapStroke;
|
|
251
|
-
this._bgColor = config.bgColor ?? theme.minimapBg;
|
|
252
|
-
this._brushColor = config.brushColor ?? theme.minimapBrush;
|
|
253
|
-
}
|
|
254
|
-
/** Render minimap as draw commands */
|
|
255
|
-
render() {
|
|
256
|
-
if (this._data.length === 0) return [];
|
|
257
|
-
const commands = [];
|
|
258
|
-
const times = this._data.map((d) => d.time);
|
|
259
|
-
const values = this._data.map((d) => d.value).filter((v) => v !== null);
|
|
260
|
-
const timeDomain = [Math.min(...times), Math.max(...times)];
|
|
261
|
-
const valueDomain = [Math.min(...values), Math.max(...values)];
|
|
262
|
-
const timeScale = new TimeScale({
|
|
263
|
-
domain: timeDomain,
|
|
264
|
-
range: [0, this._width]
|
|
265
|
-
});
|
|
266
|
-
const valueScale = new LinearScale({
|
|
267
|
-
domain: valueDomain,
|
|
268
|
-
range: [this._height, 0]
|
|
269
|
-
});
|
|
270
|
-
commands.push({
|
|
271
|
-
type: "rect",
|
|
272
|
-
x: this._x,
|
|
273
|
-
y: this._y,
|
|
274
|
-
w: this._width,
|
|
275
|
-
h: this._height,
|
|
276
|
-
fill: this._bgColor
|
|
277
|
-
});
|
|
278
|
-
const points = this._data.map((d) => ({
|
|
279
|
-
x: this._x + timeScale.map(d.time),
|
|
280
|
-
y: this._y + valueScale.map(d.value)
|
|
281
|
-
}));
|
|
282
|
-
commands.push({
|
|
283
|
-
type: "path",
|
|
284
|
-
points,
|
|
285
|
-
smoothing: false,
|
|
286
|
-
stroke: this._stroke,
|
|
287
|
-
strokeWidth: 1
|
|
288
|
-
});
|
|
289
|
-
if (this._zoom) {
|
|
290
|
-
const zoomState = this._zoom.state;
|
|
291
|
-
const brushX0 = this._x + timeScale.map(zoomState.domain[0]);
|
|
292
|
-
const brushX1 = this._x + timeScale.map(zoomState.domain[1]);
|
|
293
|
-
const brushW = Math.max(2, brushX1 - brushX0);
|
|
294
|
-
commands.push({
|
|
295
|
-
type: "rect",
|
|
296
|
-
x: brushX0,
|
|
297
|
-
y: this._y,
|
|
298
|
-
w: brushW,
|
|
299
|
-
h: this._height,
|
|
300
|
-
fill: this._brushColor
|
|
301
|
-
});
|
|
302
|
-
}
|
|
303
|
-
return commands;
|
|
304
|
-
}
|
|
305
|
-
/** Convert minimap pixel X to time value (for click-to-navigate) */
|
|
306
|
-
navigateTo(pixelX) {
|
|
307
|
-
const times = this._data.map((d) => d.time);
|
|
308
|
-
const timeDomain = [Math.min(...times), Math.max(...times)];
|
|
309
|
-
const timeScale = new TimeScale({ domain: timeDomain, range: [0, this._width] });
|
|
310
|
-
const localX = pixelX - this._x;
|
|
311
|
-
return timeScale.invert(localX);
|
|
312
|
-
}
|
|
313
|
-
};
|
|
314
|
-
|
|
315
|
-
// src/interaction/tooltip.ts
|
|
316
|
-
var Tooltip = class {
|
|
317
|
-
_mouseX;
|
|
318
|
-
_mouseY;
|
|
319
|
-
_timeScale;
|
|
320
|
-
_valueScale;
|
|
321
|
-
_data;
|
|
322
|
-
_xRange;
|
|
323
|
-
_yRange;
|
|
324
|
-
_snapRadius;
|
|
325
|
-
_bgColor;
|
|
326
|
-
_borderColor;
|
|
327
|
-
_textColor;
|
|
328
|
-
constructor(config) {
|
|
329
|
-
this._mouseX = config.mouseX;
|
|
330
|
-
this._mouseY = config.mouseY;
|
|
331
|
-
this._timeScale = config.timeScale;
|
|
332
|
-
this._valueScale = config.valueScale;
|
|
333
|
-
this._data = config.data;
|
|
334
|
-
this._xRange = config.xRange;
|
|
335
|
-
this._yRange = config.yRange;
|
|
336
|
-
this._snapRadius = config.snapRadius ?? theme.tooltipSnapRadius;
|
|
337
|
-
this._bgColor = config.bgColor ?? theme.tooltipBg;
|
|
338
|
-
this._borderColor = config.borderColor ?? theme.tooltipBorder;
|
|
339
|
-
this._textColor = config.textColor ?? theme.tooltipText;
|
|
340
|
-
}
|
|
341
|
-
/** Compute tooltip visibility and render commands */
|
|
342
|
-
compute() {
|
|
343
|
-
this._timeScale.invert(this._mouseX);
|
|
344
|
-
const sorted = [...this._data].filter((d) => d.value !== null).sort((a, b) => a.time - b.time);
|
|
345
|
-
let closest;
|
|
346
|
-
let closestDist = Infinity;
|
|
347
|
-
for (const dp of sorted) {
|
|
348
|
-
const px = this._timeScale.map(dp.time);
|
|
349
|
-
const dist = Math.abs(this._mouseX - px);
|
|
350
|
-
if (dist < closestDist) {
|
|
351
|
-
closestDist = dist;
|
|
352
|
-
closest = dp;
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
if (!closest || closestDist > this._snapRadius) {
|
|
356
|
-
return { visible: false, commands: [] };
|
|
357
|
-
}
|
|
358
|
-
const snapX = this._timeScale.map(closest.time);
|
|
359
|
-
const snapY = this._valueScale.map(closest.value);
|
|
360
|
-
const commands = [];
|
|
361
|
-
commands.push({
|
|
362
|
-
type: "line",
|
|
363
|
-
x1: snapX,
|
|
364
|
-
y1: this._yRange[0],
|
|
365
|
-
x2: snapX,
|
|
366
|
-
y2: this._yRange[1],
|
|
367
|
-
stroke: "#94a3b8",
|
|
368
|
-
strokeWidth: 1
|
|
369
|
-
});
|
|
370
|
-
commands.push({
|
|
371
|
-
type: "line",
|
|
372
|
-
x1: this._xRange[0],
|
|
373
|
-
y1: snapY,
|
|
374
|
-
x2: this._xRange[1],
|
|
375
|
-
y2: snapY,
|
|
376
|
-
stroke: "#94a3b8",
|
|
377
|
-
strokeWidth: 1
|
|
378
|
-
});
|
|
379
|
-
commands.push({
|
|
380
|
-
type: "circle",
|
|
381
|
-
cx: snapX,
|
|
382
|
-
cy: snapY,
|
|
383
|
-
r: 5,
|
|
384
|
-
fill: "#3b82f6"
|
|
385
|
-
});
|
|
386
|
-
const timeLabel = new Date(closest.time).toLocaleString();
|
|
387
|
-
const valueLabel = closest.value.toFixed(2);
|
|
388
|
-
const boxW = 120;
|
|
389
|
-
const boxH = 44;
|
|
390
|
-
let boxX = snapX + 12;
|
|
391
|
-
let boxY = snapY - boxH - 4;
|
|
392
|
-
if (boxX + boxW > this._xRange[1]) {
|
|
393
|
-
boxX = snapX - boxW - 12;
|
|
394
|
-
}
|
|
395
|
-
if (boxY < this._yRange[0]) {
|
|
396
|
-
boxY = snapY + 12;
|
|
397
|
-
}
|
|
398
|
-
commands.push({
|
|
399
|
-
type: "rect",
|
|
400
|
-
x: boxX,
|
|
401
|
-
y: boxY,
|
|
402
|
-
w: boxW,
|
|
403
|
-
h: boxH,
|
|
404
|
-
fill: this._bgColor,
|
|
405
|
-
stroke: this._borderColor,
|
|
406
|
-
strokeWidth: 1
|
|
407
|
-
});
|
|
408
|
-
commands.push({
|
|
409
|
-
type: "text",
|
|
410
|
-
content: timeLabel,
|
|
411
|
-
x: boxX + 8,
|
|
412
|
-
y: boxY + 16,
|
|
413
|
-
fontSize: 10,
|
|
414
|
-
fill: this._textColor
|
|
415
|
-
});
|
|
416
|
-
commands.push({
|
|
417
|
-
type: "text",
|
|
418
|
-
content: `Value: ${valueLabel}`,
|
|
419
|
-
x: boxX + 8,
|
|
420
|
-
y: boxY + 32,
|
|
421
|
-
fontSize: 11,
|
|
422
|
-
fill: "#3b82f6"
|
|
423
|
-
});
|
|
424
|
-
return {
|
|
425
|
-
visible: true,
|
|
426
|
-
commands,
|
|
427
|
-
dataPoint: closest,
|
|
428
|
-
snapX
|
|
429
|
-
};
|
|
430
|
-
}
|
|
431
|
-
};
|
|
432
|
-
/*!
|
|
1
|
+
var f=class{#t;#e;constructor(t){this.#t=[...t.domain],this.#e=[...t.range];}map(t){let n=Number(t),[o,i]=this.#t,[a,r]=this.#e;return i===o?a:a+(n-o)/(i-o)*(r-a)}invert(t){let[n,o]=this.#t,[i,a]=this.#e;return a===i?n:n+(t-i)/(a-i)*(o-n)}domain(){return [...this.#t]}range(){return [...this.#e]}},_=[{label:"second",ms:1e3},{label:"2_seconds",ms:2e3},{label:"5_seconds",ms:5e3},{label:"10_seconds",ms:1e4},{label:"30_seconds",ms:3e4},{label:"minute",ms:6e4},{label:"5_minutes",ms:3e5},{label:"15_minutes",ms:9e5},{label:"30_minutes",ms:18e5},{label:"hour",ms:36e5},{label:"3_hours",ms:108e5},{label:"6_hours",ms:216e5},{label:"day",ms:864e5},{label:"week",ms:6048e5},{label:"month",ms:2592e6},{label:"3_months",ms:7776e6},{label:"6_months",ms:15552e6},{label:"year",ms:31536e6},{label:"2_years",ms:63072e6},{label:"5_years",ms:15768e7}],y=class{#t;#e;constructor(t){this.#t=new f({domain:t.domain,range:t.range}),this.#e=t.locale||(typeof navigator<"u"?navigator.language:"en-US");}map(t){return this.#t.map(Number(t))}invert(t){return this.#t.invert(t)}domain(){return this.#t.domain()}range(){return this.#t.range()}get locale(){return this.#e}tickInterval(t,n=3,o=12){let[i,a]=this.#t.domain(),r=a-i;if(r<=0)return {interval:_[0].ms};let s=r/t,m=_[0].ms;for(let u of _)if(u.ms>=s){m=u.ms;break}let e=m,l=Math.round(r/e);for(;l>o&&e<_[_.length-1].ms;){let u=_.findIndex(d=>d.ms===e);e=_[Math.min(u+1,_.length-1)].ms,l=Math.round(r/e);}for(;l<n&&e>_[0].ms;){let u=_.findIndex(d=>d.ms===e);e=_[Math.max(u-1,0)].ms,l=Math.round(r/e);}return {interval:e}}ticks(t){let n=t?.minTicks??5,o=t?.maxTicks??12,{interval:i}=this.tickInterval((n+o)/2,n,o),[a,r]=this.#t.domain(),s=[],m=Math.ceil(a/i)*i;for(let e=m;e<=r;e+=i)s.push(e);return s}format(t,n){return new Intl.DateTimeFormat(this.#e,n).format(new Date(t))}};var S=class{_config;_state;constructor(t,n){this._config={enabled:n?.enabled??true,zoomSpeed:n?.zoomSpeed??.1,minDomainMs:n?.minDomainMs??0,maxDomainMs:n?.maxDomainMs??365*24*60*60*1e3},this._state={domain:[...t],originalDomain:[...t],scale:1,offset:0};}get state(){return {...this._state,domain:[...this._state.domain]}}applyZoom(t,n,o){if(!this._config.enabled)return;let i=1+Math.sign(n)*this._config.zoomSpeed;if(i<=0)return;let[a,r]=this._state.domain,s=r-a,m=t/o,e=s/i,l=this._config.minDomainMs??0,u=this._config.maxDomainMs??1/0;e<l&&(e=l),e>u&&(e=u);let d=a+s*m,b=d-e*m,p=d+e*(1-m),[h,c]=this._state.originalDomain;p-b>c-h?(b=h,p=c,e=c-h):(b<h&&(p=c-(c-h)*((p-b)/(c-h)),b=h),p>c&&(b=h+(c-h)*((b-h)/(c-h)),p=c)),this._state.domain=[b,p],this._state.scale=(r-a)/e;}applyPan(t,n){if(!this._config.enabled)return;let[o,i]=this._state.domain,a=i-o,[r,s]=this._state.originalDomain,m=-(t/n)*a,e=o+m,l=i+m;e<r&&(m-=e-r),l>s&&(m-=l-s),this._state.domain=[o+m,i+m];}reset(){this._state.domain=[...this._state.originalDomain],this._state.scale=1,this._state.offset=0;}get enabled(){return this._config.enabled??true}};var g={tooltipBg:"#fff",tooltipBorder:"#cbd5e1",tooltipText:"#1e293b",tooltipSnapRadius:20,minimapStroke:"#94a3b8",minimapBg:"#f8f9fa",minimapBrush:"#3b82f644"};var C=class{_data;_width;_height;_x;_y;_zoom;_stroke;_bgColor;_brushColor;constructor(t){this._data=t.data,this._width=t.width,this._height=t.height,this._x=t.x,this._y=t.y,this._zoom=t.zoom,this._stroke=t.stroke??g.minimapStroke,this._bgColor=t.bgColor??g.minimapBg,this._brushColor=t.brushColor??g.minimapBrush;}render(){if(this._data.length===0)return [];let t=[],n=this._data.map(e=>e.time),o=this._data.map(e=>e.value).filter(e=>e!==null),i=[Math.min(...n),Math.max(...n)],a=[Math.min(...o),Math.max(...o)],r=new y({domain:i,range:[0,this._width]}),s=new f({domain:a,range:[this._height,0]});t.push({type:"rect",x:this._x,y:this._y,w:this._width,h:this._height,fill:this._bgColor});let m=this._data.map(e=>({x:this._x+r.map(e.time),y:this._y+s.map(e.value)}));if(t.push({type:"path",points:m,smoothing:false,stroke:this._stroke,strokeWidth:1}),this._zoom){let e=this._zoom.state,l=this._x+r.map(e.domain[0]),u=this._x+r.map(e.domain[1]),d=Math.max(2,u-l);t.push({type:"rect",x:l,y:this._y,w:d,h:this._height,fill:this._brushColor});}return t}navigateTo(t){let n=this._data.map(r=>r.time),o=[Math.min(...n),Math.max(...n)],i=new y({domain:o,range:[0,this._width]}),a=t-this._x;return i.invert(a)}};var v=class{_mouseX;_mouseY;_timeScale;_valueScale;_data;_xRange;_yRange;_snapRadius;_bgColor;_borderColor;_textColor;constructor(t){this._mouseX=t.mouseX,this._mouseY=t.mouseY,this._timeScale=t.timeScale,this._valueScale=t.valueScale,this._data=t.data,this._xRange=t.xRange,this._yRange=t.yRange,this._snapRadius=t.snapRadius??g.tooltipSnapRadius,this._bgColor=t.bgColor??g.tooltipBg,this._borderColor=t.borderColor??g.tooltipBorder,this._textColor=t.textColor??g.tooltipText;}compute(){this._timeScale.invert(this._mouseX);let n=[...this._data].filter(p=>p.value!==null).sort((p,h)=>p.time-h.time),o,i=1/0;for(let p of n){let h=this._timeScale.map(p.time),c=Math.abs(this._mouseX-h);c<i&&(i=c,o=p);}if(!o||i>this._snapRadius)return {visible:false,commands:[]};let a=this._timeScale.map(o.time),r=this._valueScale.map(o.value),s=[];s.push({type:"line",x1:a,y1:this._yRange[0],x2:a,y2:this._yRange[1],stroke:"#94a3b8",strokeWidth:1}),s.push({type:"line",x1:this._xRange[0],y1:r,x2:this._xRange[1],y2:r,stroke:"#94a3b8",strokeWidth:1}),s.push({type:"circle",cx:a,cy:r,r:5,fill:"#3b82f6"});let m=new Date(o.time).toLocaleString(),e=o.value.toFixed(2),l=120,u=44,d=a+12,b=r-u-4;return d+l>this._xRange[1]&&(d=a-l-12),b<this._yRange[0]&&(b=r+12),s.push({type:"rect",x:d,y:b,w:l,h:u,fill:this._bgColor,stroke:this._borderColor,strokeWidth:1}),s.push({type:"text",content:m,x:d+8,y:b+16,fontSize:10,fill:this._textColor}),s.push({type:"text",content:`Value: ${e}`,x:d+8,y:b+32,fontSize:11,fill:"#3b82f6"}),{visible:true,commands:s,dataPoint:o,snapX:a}}};/*!
|
|
433
2
|
* MLTimeGraph — Copyright (c) 2026 Michael Lechner
|
|
434
3
|
* MIT with Attribution: free use incl. commercial requires visible credit to
|
|
435
4
|
* "Michael Lechner". Commercial license (no attribution) on request. See LICENSE.
|
|
436
|
-
*/
|
|
437
|
-
|
|
438
|
-
export { Minimap, Tooltip, Zoom };
|
|
5
|
+
*/export{C as Minimap,v as Tooltip,S as Zoom};
|
package/dist/internals.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { M as Marker } from './layout-
|
|
2
|
-
export { L as Layout, a as LayoutConfig, b as LayoutResult, c as LegendConfig, d as LegendItem, e as LegendOrientation, f as Renderer, m as measureLegend, r as renderLegend, t as theme } from './layout-
|
|
3
|
-
import { A as AggregatedPoint,
|
|
4
|
-
export {
|
|
1
|
+
import { M as Marker } from './layout-BOYtrsZa.js';
|
|
2
|
+
export { L as Layout, a as LayoutConfig, b as LayoutResult, c as LegendConfig, d as LegendItem, e as LegendOrientation, f as Renderer, m as measureLegend, r as renderLegend, t as theme } from './layout-BOYtrsZa.js';
|
|
3
|
+
import { A as AggregatedPoint, ad as TimeScale, V as LinearScale, _ as PointStyleType, o as DrawCommand, k as DataPoint, aa as Threshold, I as Gap, O as HatchVariant, P as Highlight, d as Annotation, E as EnumMap, B as BandScale, Z as Point, f as AnnotationBandItem, U as LineVariant, G as FillSpec, M as GapStyle, af as TimeSeries } from './scale-BRE_QhbZ.js';
|
|
4
|
+
export { h as AxisOrientation, i as AxisPosition, j as BandScaleConfig, C as CircleParams, D as Dashstyle_t, m as DomainScaleConfig, n as DrawCircle, p as DrawGap, q as DrawGapType_t, r as DrawGradient, s as DrawGroup, t as DrawLine, u as DrawPath, v as DrawRect, w as DrawText, Q as LabelAnchor, S as LineParams, W as LinearScaleConfig, Y as PathParams, $ as RectParams, a0 as Scale, a1 as ScaleConfig, a8 as TIME_INTERVALS, a9 as TextParams, ae as TimeScaleConfig, ag as getHatch } from './scale-BRE_QhbZ.js';
|
|
5
5
|
|
|
6
6
|
/*!
|
|
7
7
|
* MLTimeGraph — Copyright (c) 2026 Michael Lechner
|
|
@@ -129,7 +129,7 @@ interface ThresholdRenderConfig {
|
|
|
129
129
|
/** Pixel X range of the chart area. */
|
|
130
130
|
xRange: [number, number];
|
|
131
131
|
}
|
|
132
|
-
/**
|
|
132
|
+
/** Flat-list variant: lines / fills / labels in one array (legacy callers). */
|
|
133
133
|
declare function renderThresholds(config: ThresholdRenderConfig): DrawCommand[];
|
|
134
134
|
|
|
135
135
|
/*!
|
|
@@ -229,6 +229,12 @@ interface AnnotationRenderConfig {
|
|
|
229
229
|
/**
|
|
230
230
|
* Render free-form annotations (arrow, line, rect, point, label) given in
|
|
231
231
|
* data coordinates by projecting each point to pixels via the chart's scales.
|
|
232
|
+
*
|
|
233
|
+
* Each annotation that has an `id` is wrapped in a `<g class="annotation
|
|
234
|
+
* annotation--{slug}">` group, so external code (toggle panels, custom
|
|
235
|
+
* legends, CSS) can target it by class. The chart container's own id
|
|
236
|
+
* scopes the selector (`#chart-3 .annotation--door-defekt`) — that way the
|
|
237
|
+
* same annotation can appear across multiple charts without id collisions.
|
|
232
238
|
*/
|
|
233
239
|
declare function renderAnnotations(config: AnnotationRenderConfig): DrawCommand[];
|
|
234
240
|
|
|
@@ -347,6 +353,15 @@ interface ValueAxisConfig {
|
|
|
347
353
|
/** Position for vertical orientation */
|
|
348
354
|
position?: "left" | "right";
|
|
349
355
|
colors?: AxisColorspec;
|
|
356
|
+
/**
|
|
357
|
+
* Pixel positions (Y for vertical, X for horizontal) where a tick LABEL
|
|
358
|
+
* should be suppressed. Used to avoid colliding with threshold gutter
|
|
359
|
+
* labels (e.g. "Min 2,0 °C") that already sit at the same Y as a
|
|
360
|
+
* tick would. The tick MARK itself is kept; only the text is dropped.
|
|
361
|
+
*/
|
|
362
|
+
suppressLabelsNear?: number[];
|
|
363
|
+
/** Pixel tolerance for `suppressLabelsNear`. Default 8. */
|
|
364
|
+
suppressTolerancePx?: number;
|
|
350
365
|
}
|
|
351
366
|
/** Tick information for value axis with computed positions and formatted labels. */
|
|
352
367
|
interface ValueTickInfo {
|