@wicklib/layouts 0.12.0 → 0.13.1
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/README.md +45 -7
- package/dist/index.cjs +138 -46
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +41 -5
- package/dist/index.d.ts +41 -5
- package/dist/index.js +138 -46
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -44,13 +44,46 @@ Time-range and crosshair synchronization are enabled by default.
|
|
|
44
44
|
```ts
|
|
45
45
|
const layout = createChartLayout(container, {
|
|
46
46
|
columns: 2,
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
sync: {
|
|
48
|
+
timeRange: true,
|
|
49
|
+
crosshair: "time",
|
|
50
|
+
},
|
|
49
51
|
});
|
|
50
52
|
```
|
|
51
53
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
+
Crosshair modes:
|
|
55
|
+
|
|
56
|
+
- `"off"` — no crosshair sync.
|
|
57
|
+
- `"time"` — mirrors the vertical time line.
|
|
58
|
+
- `"full"` — also mirrors a horizontal line at the same relative pane height.
|
|
59
|
+
|
|
60
|
+
The hovered chart keeps its native crosshair. Other charts receive an overlay line.
|
|
61
|
+
|
|
62
|
+
Change synchronization without recreating charts:
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
layout.setSyncOptions({
|
|
66
|
+
timeRange: false,
|
|
67
|
+
crosshair: "full",
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
layout.getSyncOptions();
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Boolean crosshair values remain accepted: `true` maps to `"time"` and `false` to `"off"`.
|
|
74
|
+
|
|
75
|
+
## Sync groups
|
|
76
|
+
|
|
77
|
+
Cells only synchronize with cells in the same group. The default group is `"default"`.
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
layout.addChart({ id: "btc", syncGroup: "crypto" });
|
|
81
|
+
layout.addChart({ id: "eth", syncGroup: "crypto" });
|
|
82
|
+
layout.addChart({ id: "spx", syncGroup: "equities" });
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
`btc` and `eth` share ranges and crosshairs; `spx` stays independent. `syncNow()` also respects
|
|
86
|
+
the active chart's group.
|
|
54
87
|
|
|
55
88
|
## Persistence
|
|
56
89
|
|
|
@@ -65,15 +98,20 @@ const saved = layout.serialize();
|
|
|
65
98
|
"version": 1,
|
|
66
99
|
"columns": 2,
|
|
67
100
|
"gap": 8,
|
|
101
|
+
"sync": {
|
|
102
|
+
"timeRange": true,
|
|
103
|
+
"crosshair": "time"
|
|
104
|
+
},
|
|
68
105
|
"slots": [
|
|
69
|
-
{ "id": "primary", "rowSpan": 1, "columnSpan": 1 },
|
|
70
|
-
{ "id": "comparison", "rowSpan": 1, "columnSpan": 1 }
|
|
106
|
+
{ "id": "primary", "rowSpan": 1, "columnSpan": 1, "syncGroup": "default" },
|
|
107
|
+
{ "id": "comparison", "rowSpan": 1, "columnSpan": 1, "syncGroup": "default" }
|
|
71
108
|
]
|
|
72
109
|
}
|
|
73
110
|
```
|
|
74
111
|
|
|
75
112
|
Persist chart state, symbol metadata, datafeed identity, drawings, and layout structure in
|
|
76
|
-
the host.
|
|
113
|
+
the host. Old snapshots that omit `sync` or `syncGroup` should fall back to
|
|
114
|
+
`{ timeRange: true, crosshair: "time" }` and `"default"` respectively. Drawings can be read with `chart.serializeDrawings()`.
|
|
77
115
|
|
|
78
116
|
## Styling
|
|
79
117
|
|
package/dist/index.cjs
CHANGED
|
@@ -5,6 +5,7 @@ var core = require('@wicklib/core');
|
|
|
5
5
|
// src/index.ts
|
|
6
6
|
var DEFAULT_COLUMNS = 2;
|
|
7
7
|
var DEFAULT_GAP = 1;
|
|
8
|
+
var DEFAULT_SYNC_GROUP = "default";
|
|
8
9
|
var MAX_COLUMNS = 12;
|
|
9
10
|
var DEFAULT_CROSSHAIR_COLOR = "rgba(139, 149, 167, 0.6)";
|
|
10
11
|
function createChartLayout(container, options = {}) {
|
|
@@ -16,11 +17,10 @@ var ChartLayout = class {
|
|
|
16
17
|
activeHandlers = /* @__PURE__ */ new Set();
|
|
17
18
|
crosshairOverlays = /* @__PURE__ */ new Map();
|
|
18
19
|
chartOptions;
|
|
19
|
-
syncTimeRange;
|
|
20
|
-
syncCrosshair;
|
|
21
20
|
crosshairColor;
|
|
22
21
|
columns;
|
|
23
22
|
gap;
|
|
23
|
+
sync;
|
|
24
24
|
activeId = null;
|
|
25
25
|
counter = 0;
|
|
26
26
|
applyingRange = false;
|
|
@@ -33,8 +33,7 @@ var ChartLayout = class {
|
|
|
33
33
|
this.columns = validateColumns(options.columns ?? DEFAULT_COLUMNS);
|
|
34
34
|
this.gap = validateGap(options.gap ?? DEFAULT_GAP);
|
|
35
35
|
this.chartOptions = options.chartOptions;
|
|
36
|
-
this.
|
|
37
|
-
this.syncCrosshair = options.syncCrosshair ?? true;
|
|
36
|
+
this.sync = resolveSyncOptions(options);
|
|
38
37
|
this.crosshairColor = options.crosshairColor ?? DEFAULT_CROSSHAIR_COLOR;
|
|
39
38
|
if (options.onActiveChange) {
|
|
40
39
|
this.activeHandlers.add(options.onActiveChange);
|
|
@@ -61,6 +60,7 @@ var ChartLayout = class {
|
|
|
61
60
|
}
|
|
62
61
|
const rowSpan = validateSpan(options.rowSpan ?? 1, "rowSpan");
|
|
63
62
|
const columnSpan = validateSpan(options.columnSpan ?? 1, "columnSpan");
|
|
63
|
+
const syncGroup = validateSyncGroup(options.syncGroup ?? DEFAULT_SYNC_GROUP);
|
|
64
64
|
const element = document.createElement("div");
|
|
65
65
|
element.className = "wicklib-chart-layout__cell";
|
|
66
66
|
element.dataset.chartId = id;
|
|
@@ -88,50 +88,23 @@ var ChartLayout = class {
|
|
|
88
88
|
chart.on("visible-range", (event) => this.handleVisibleRange(id, event)),
|
|
89
89
|
chart.on("crosshair", (event) => this.handleCrosshair(id, event))
|
|
90
90
|
];
|
|
91
|
-
let overlay = null;
|
|
92
|
-
if (this.syncCrosshair) {
|
|
93
|
-
overlay = chart.addOverlay({
|
|
94
|
-
id: "wicklib-layout-crosshair",
|
|
95
|
-
draw: (context, frame) => {
|
|
96
|
-
const current = this.crosshair;
|
|
97
|
-
if (!current || current.sourceId === id) {
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
const x = frame.timeToX(current.time);
|
|
101
|
-
if (x === null || x < frame.plot.left || x > frame.plot.right) {
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
|
-
const crispX = Math.round(x) + 0.5;
|
|
105
|
-
context.save();
|
|
106
|
-
context.beginPath();
|
|
107
|
-
context.rect(frame.plot.left, frame.plot.top, frame.plot.width, frame.plot.height);
|
|
108
|
-
context.clip();
|
|
109
|
-
context.strokeStyle = this.crosshairColor;
|
|
110
|
-
context.lineWidth = 1;
|
|
111
|
-
context.setLineDash([3, 4]);
|
|
112
|
-
context.beginPath();
|
|
113
|
-
context.moveTo(crispX, frame.plot.top);
|
|
114
|
-
context.lineTo(crispX, frame.plot.bottom);
|
|
115
|
-
context.stroke();
|
|
116
|
-
context.restore();
|
|
117
|
-
}
|
|
118
|
-
});
|
|
119
|
-
this.crosshairOverlays.set(id, overlay);
|
|
120
|
-
}
|
|
121
91
|
const slot = {
|
|
122
92
|
id,
|
|
123
93
|
chart,
|
|
124
94
|
element,
|
|
125
95
|
rowSpan,
|
|
126
|
-
columnSpan
|
|
96
|
+
columnSpan,
|
|
97
|
+
syncGroup
|
|
127
98
|
};
|
|
128
|
-
|
|
99
|
+
const entry = {
|
|
129
100
|
slot,
|
|
130
101
|
unsubscribe: unsubscribes,
|
|
131
|
-
overlay,
|
|
102
|
+
overlay: null,
|
|
132
103
|
onPointerDown,
|
|
133
104
|
onFocusIn
|
|
134
|
-
}
|
|
105
|
+
};
|
|
106
|
+
this.slots.push(entry);
|
|
107
|
+
this.ensureCrosshairOverlay(entry);
|
|
135
108
|
if (this.activeId === null) {
|
|
136
109
|
this.setActiveChart(id);
|
|
137
110
|
}
|
|
@@ -176,6 +149,28 @@ var ChartLayout = class {
|
|
|
176
149
|
this.gap = validateGap(gap);
|
|
177
150
|
this.element.style.gap = `${this.gap}px`;
|
|
178
151
|
}
|
|
152
|
+
setSyncOptions(options) {
|
|
153
|
+
this.assertAlive();
|
|
154
|
+
const next = {
|
|
155
|
+
timeRange: options.timeRange ?? this.sync.timeRange,
|
|
156
|
+
crosshair: options.crosshair === void 0 ? this.sync.crosshair : normalizeCrosshair(options.crosshair)
|
|
157
|
+
};
|
|
158
|
+
if (next.timeRange === this.sync.timeRange && next.crosshair === this.sync.crosshair) {
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
this.sync = next;
|
|
162
|
+
for (const entry of this.slots) {
|
|
163
|
+
this.ensureCrosshairOverlay(entry);
|
|
164
|
+
}
|
|
165
|
+
if (next.crosshair === "off" && this.crosshair !== null) {
|
|
166
|
+
this.crosshair = null;
|
|
167
|
+
}
|
|
168
|
+
this.requestCrosshairRender();
|
|
169
|
+
}
|
|
170
|
+
getSyncOptions() {
|
|
171
|
+
this.assertAlive();
|
|
172
|
+
return { ...this.sync };
|
|
173
|
+
}
|
|
179
174
|
setActiveChart(id) {
|
|
180
175
|
this.assertAlive();
|
|
181
176
|
if (id !== null && !this.findSlot(id)) {
|
|
@@ -215,7 +210,11 @@ var ChartLayout = class {
|
|
|
215
210
|
if (!source) {
|
|
216
211
|
return;
|
|
217
212
|
}
|
|
218
|
-
this.applyRange(
|
|
213
|
+
this.applyRange(
|
|
214
|
+
source.slot.id,
|
|
215
|
+
source.slot.syncGroup,
|
|
216
|
+
source.slot.chart.timeScale().getVisibleRange()
|
|
217
|
+
);
|
|
219
218
|
}
|
|
220
219
|
serialize() {
|
|
221
220
|
this.assertAlive();
|
|
@@ -223,10 +222,12 @@ var ChartLayout = class {
|
|
|
223
222
|
version: 1,
|
|
224
223
|
columns: this.columns,
|
|
225
224
|
gap: this.gap,
|
|
225
|
+
sync: { ...this.sync },
|
|
226
226
|
slots: this.slots.map((entry) => ({
|
|
227
227
|
id: entry.slot.id,
|
|
228
228
|
rowSpan: entry.slot.rowSpan,
|
|
229
|
-
columnSpan: entry.slot.columnSpan
|
|
229
|
+
columnSpan: entry.slot.columnSpan,
|
|
230
|
+
syncGroup: entry.slot.syncGroup
|
|
230
231
|
}))
|
|
231
232
|
};
|
|
232
233
|
}
|
|
@@ -253,19 +254,23 @@ var ChartLayout = class {
|
|
|
253
254
|
return this.slots.find((entry) => entry.slot.id === id);
|
|
254
255
|
}
|
|
255
256
|
handleVisibleRange(sourceId, event) {
|
|
256
|
-
if (!this.
|
|
257
|
+
if (!this.sync.timeRange || this.applyingRange) {
|
|
257
258
|
return;
|
|
258
259
|
}
|
|
259
|
-
this.
|
|
260
|
+
const source = this.findSlot(sourceId);
|
|
261
|
+
if (!source) {
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
this.applyRange(sourceId, source.slot.syncGroup, { from: event.from, to: event.to });
|
|
260
265
|
}
|
|
261
|
-
applyRange(sourceId, range) {
|
|
266
|
+
applyRange(sourceId, syncGroup, range) {
|
|
262
267
|
if (!Number.isFinite(range.from) || !Number.isFinite(range.to) || range.to <= range.from) {
|
|
263
268
|
return;
|
|
264
269
|
}
|
|
265
270
|
this.applyingRange = true;
|
|
266
271
|
try {
|
|
267
272
|
for (const entry of this.slots) {
|
|
268
|
-
if (entry.slot.id === sourceId) {
|
|
273
|
+
if (entry.slot.id === sourceId || entry.slot.syncGroup !== syncGroup) {
|
|
269
274
|
continue;
|
|
270
275
|
}
|
|
271
276
|
try {
|
|
@@ -278,7 +283,7 @@ var ChartLayout = class {
|
|
|
278
283
|
}
|
|
279
284
|
}
|
|
280
285
|
handleCrosshair(sourceId, event) {
|
|
281
|
-
if (
|
|
286
|
+
if (this.sync.crosshair === "off") {
|
|
282
287
|
return;
|
|
283
288
|
}
|
|
284
289
|
if (event.time === null || event.point === null) {
|
|
@@ -288,7 +293,18 @@ var ChartLayout = class {
|
|
|
288
293
|
}
|
|
289
294
|
return;
|
|
290
295
|
}
|
|
291
|
-
|
|
296
|
+
const source = this.findSlot(sourceId);
|
|
297
|
+
if (!source) {
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
const height = source.slot.element.clientHeight;
|
|
301
|
+
const yRatio = height > 0 ? Math.min(1, Math.max(0, event.point.y / height)) : null;
|
|
302
|
+
this.crosshair = {
|
|
303
|
+
sourceId,
|
|
304
|
+
sourceGroup: source.slot.syncGroup,
|
|
305
|
+
time: event.time,
|
|
306
|
+
yRatio
|
|
307
|
+
};
|
|
292
308
|
this.requestCrosshairRender();
|
|
293
309
|
}
|
|
294
310
|
requestCrosshairRender() {
|
|
@@ -296,6 +312,57 @@ var ChartLayout = class {
|
|
|
296
312
|
overlay.requestRender();
|
|
297
313
|
}
|
|
298
314
|
}
|
|
315
|
+
ensureCrosshairOverlay(entry) {
|
|
316
|
+
if (this.sync.crosshair === "off") {
|
|
317
|
+
if (entry.overlay) {
|
|
318
|
+
entry.overlay.remove();
|
|
319
|
+
entry.overlay = null;
|
|
320
|
+
this.crosshairOverlays.delete(entry.slot.id);
|
|
321
|
+
}
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
if (entry.overlay) {
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
const id = entry.slot.id;
|
|
328
|
+
const group = entry.slot.syncGroup;
|
|
329
|
+
entry.overlay = entry.slot.chart.addOverlay({
|
|
330
|
+
id: "wicklib-layout-crosshair",
|
|
331
|
+
draw: (context, frame) => this.drawCrosshair(context, frame, id, group)
|
|
332
|
+
});
|
|
333
|
+
this.crosshairOverlays.set(id, entry.overlay);
|
|
334
|
+
}
|
|
335
|
+
drawCrosshair(context, frame, id, group) {
|
|
336
|
+
const current = this.crosshair;
|
|
337
|
+
if (!current || current.sourceId === id || current.sourceGroup !== group || this.sync.crosshair === "off") {
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
const x = frame.timeToX(current.time);
|
|
341
|
+
if (x === null || x < frame.plot.left || x > frame.plot.right) {
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
context.save();
|
|
345
|
+
context.beginPath();
|
|
346
|
+
context.rect(frame.plot.left, frame.plot.top, frame.plot.width, frame.plot.height);
|
|
347
|
+
context.clip();
|
|
348
|
+
context.strokeStyle = this.crosshairColor;
|
|
349
|
+
context.lineWidth = 1;
|
|
350
|
+
context.setLineDash([3, 4]);
|
|
351
|
+
const crispX = Math.round(x) + 0.5;
|
|
352
|
+
context.beginPath();
|
|
353
|
+
context.moveTo(crispX, frame.plot.top);
|
|
354
|
+
context.lineTo(crispX, frame.plot.bottom);
|
|
355
|
+
context.stroke();
|
|
356
|
+
if (this.sync.crosshair === "full" && current.yRatio !== null) {
|
|
357
|
+
const y = frame.plot.top + current.yRatio * frame.plot.height;
|
|
358
|
+
const crispY = Math.round(y) + 0.5;
|
|
359
|
+
context.beginPath();
|
|
360
|
+
context.moveTo(frame.plot.left, crispY);
|
|
361
|
+
context.lineTo(frame.plot.right, crispY);
|
|
362
|
+
context.stroke();
|
|
363
|
+
}
|
|
364
|
+
context.restore();
|
|
365
|
+
}
|
|
299
366
|
disposeSlot(entry) {
|
|
300
367
|
entry.slot.element.removeEventListener("pointerdown", entry.onPointerDown, true);
|
|
301
368
|
entry.slot.element.removeEventListener("focusin", entry.onFocusIn);
|
|
@@ -303,6 +370,7 @@ var ChartLayout = class {
|
|
|
303
370
|
unsubscribe();
|
|
304
371
|
}
|
|
305
372
|
entry.overlay?.remove();
|
|
373
|
+
entry.overlay = null;
|
|
306
374
|
this.crosshairOverlays.delete(entry.slot.id);
|
|
307
375
|
entry.slot.chart.destroy();
|
|
308
376
|
entry.slot.element.remove();
|
|
@@ -313,6 +381,24 @@ var ChartLayout = class {
|
|
|
313
381
|
}
|
|
314
382
|
}
|
|
315
383
|
};
|
|
384
|
+
function resolveSyncOptions(options) {
|
|
385
|
+
return {
|
|
386
|
+
timeRange: options.sync?.timeRange ?? options.syncTimeRange ?? true,
|
|
387
|
+
crosshair: normalizeCrosshair(options.sync?.crosshair ?? options.syncCrosshair ?? "time")
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
function normalizeCrosshair(value) {
|
|
391
|
+
if (value === void 0 || value === true || value === "time") {
|
|
392
|
+
return "time";
|
|
393
|
+
}
|
|
394
|
+
if (value === false || value === "off") {
|
|
395
|
+
return "off";
|
|
396
|
+
}
|
|
397
|
+
if (value === "full") {
|
|
398
|
+
return "full";
|
|
399
|
+
}
|
|
400
|
+
throw new core.WickError("INVALID_ARGUMENT", "crosshair must be off, time, or full");
|
|
401
|
+
}
|
|
316
402
|
function mergeChartOptions(base, override) {
|
|
317
403
|
if (!base) {
|
|
318
404
|
return override;
|
|
@@ -340,6 +426,12 @@ function validateSpan(value, name) {
|
|
|
340
426
|
}
|
|
341
427
|
return value;
|
|
342
428
|
}
|
|
429
|
+
function validateSyncGroup(value) {
|
|
430
|
+
if (typeof value !== "string" || value.trim() === "") {
|
|
431
|
+
throw new core.WickError("INVALID_ARGUMENT", "syncGroup must be a non-empty string");
|
|
432
|
+
}
|
|
433
|
+
return value;
|
|
434
|
+
}
|
|
343
435
|
|
|
344
436
|
exports.createChartLayout = createChartLayout;
|
|
345
437
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"names":["WickError","createChart","entry"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Free multi-chart layout primitives for Wicklib.\n */\n\nimport {\n WickError,\n createChart,\n type ChartApi,\n type ChartOptions,\n type ChartOverlayApi,\n type CrosshairEvent,\n type Unsubscribe,\n type VisibleRangeEvent,\n} from \"@wicklib/core\";\n\n/**\n * Options for a layout chart cell.\n */\nexport interface ChartLayoutSlotOptions {\n /** Stable slot id. Generated when omitted. */\n id?: string;\n /** Chart options for this slot. Merged over the layout defaults. */\n chartOptions?: ChartOptions;\n /** Number of grid rows covered by the cell. Defaults to 1. */\n rowSpan?: number;\n /** Number of grid columns covered by the cell. Defaults to 1. */\n columnSpan?: number;\n}\n\n/**\n * One chart cell in a layout.\n */\nexport interface ChartLayoutSlot {\n readonly id: string;\n readonly chart: ChartApi;\n readonly element: HTMLElement;\n readonly rowSpan: number;\n readonly columnSpan: number;\n}\n\n/**\n * Serializable structural layout state. Chart state is owned by the host.\n */\nexport interface ChartLayoutSnapshot {\n version: 1;\n columns: number;\n gap: number;\n slots: ReadonlyArray<{\n id: string;\n rowSpan: number;\n columnSpan: number;\n }>;\n}\n\n/**\n * Options for a multi-chart layout.\n */\nexport interface ChartLayoutOptions {\n /** Number of grid columns. Defaults to 2. */\n columns?: number;\n /** Gap between cells in CSS pixels. Defaults to 1. */\n gap?: number;\n /** Default chart options applied to every slot. */\n chartOptions?: ChartOptions;\n /** Synchronize visible time ranges. Defaults to true. */\n syncTimeRange?: boolean;\n /** Synchronize the vertical crosshair time line. Defaults to true. */\n syncCrosshair?: boolean;\n /** Crosshair line colour. */\n crosshairColor?: string;\n /** Extra class name on the layout root. */\n className?: string;\n /** Called when the active chart changes. */\n onActiveChange?: (chartId: string | null) => void;\n}\n\n/**\n * A live multi-chart layout.\n */\nexport interface ChartLayoutApi {\n /** Layout root inserted into the host container. */\n readonly element: HTMLElement;\n /** Adds a chart cell and returns its live slot. */\n addChart(options?: ChartLayoutSlotOptions): ChartLayoutSlot;\n /** Destroys and removes one chart cell. */\n removeChart(id: string): void;\n /** Returns a live chart, or null when the id is unknown. */\n getChart(id: string): ChartApi | null;\n /** Returns the current slots in DOM order. */\n getSlots(): readonly ChartLayoutSlot[];\n /** Returns slot ids in DOM order. */\n chartIds(): readonly string[];\n /** Changes the grid column count. */\n setColumns(columns: number): void;\n /** Changes the cell gap in CSS pixels. */\n setGap(gap: number): void;\n /** Marks one chart as active. Pass null to clear. */\n setActiveChart(id: string | null): void;\n /** Returns the active chart id, or null. */\n activeChartId(): string | null;\n /** Subscribes to active chart changes. */\n onActiveChange(handler: (chartId: string | null) => void): Unsubscribe;\n /** Applies the active chart's visible range to every other chart. */\n syncNow(): void;\n /** Returns structural layout state for host persistence. */\n serialize(): ChartLayoutSnapshot;\n /** Destroys every chart and removes the layout root. */\n destroy(): void;\n}\n\ninterface InternalSlot {\n readonly slot: ChartLayoutSlot;\n readonly unsubscribe: readonly Unsubscribe[];\n readonly overlay: ChartOverlayApi | null;\n readonly onPointerDown: () => void;\n readonly onFocusIn: () => void;\n}\n\nconst DEFAULT_COLUMNS = 2;\nconst DEFAULT_GAP = 1;\nconst MAX_COLUMNS = 12;\nconst DEFAULT_CROSSHAIR_COLOR = \"rgba(139, 149, 167, 0.6)\";\n\n/**\n * Creates a framework-agnostic grid of Wicklib charts.\n *\n * The layout owns chart creation, grid placement, time-range synchronization, and a\n * mirrored crosshair line. It does not own data, symbol state, accounts, or persistence.\n */\nexport function createChartLayout(\n container: HTMLElement,\n options: ChartLayoutOptions = {},\n): ChartLayoutApi {\n return new ChartLayout(container, options);\n}\n\nclass ChartLayout implements ChartLayoutApi {\n readonly element: HTMLElement;\n\n private readonly slots: InternalSlot[] = [];\n private readonly activeHandlers = new Set<(chartId: string | null) => void>();\n private readonly crosshairOverlays = new Map<string, ChartOverlayApi>();\n private readonly chartOptions: ChartOptions | undefined;\n private readonly syncTimeRange: boolean;\n private readonly syncCrosshair: boolean;\n private readonly crosshairColor: string;\n private columns: number;\n private gap: number;\n private activeId: string | null = null;\n private counter = 0;\n private applyingRange = false;\n private crosshair: { sourceId: string; time: number } | null = null;\n private destroyed = false;\n\n constructor(container: HTMLElement, options: ChartLayoutOptions) {\n if (!container || typeof container.appendChild !== \"function\") {\n throw new WickError(\"INVALID_ARGUMENT\", \"createChartLayout requires a DOM container\");\n }\n\n this.columns = validateColumns(options.columns ?? DEFAULT_COLUMNS);\n this.gap = validateGap(options.gap ?? DEFAULT_GAP);\n this.chartOptions = options.chartOptions;\n this.syncTimeRange = options.syncTimeRange ?? true;\n this.syncCrosshair = options.syncCrosshair ?? true;\n this.crosshairColor = options.crosshairColor ?? DEFAULT_CROSSHAIR_COLOR;\n if (options.onActiveChange) {\n this.activeHandlers.add(options.onActiveChange);\n }\n\n this.element = document.createElement(\"div\");\n this.element.className = [\"wicklib-chart-layout\", options.className].filter(Boolean).join(\" \");\n Object.assign(this.element.style, {\n display: \"grid\",\n width: \"100%\",\n height: \"100%\",\n minWidth: \"0\",\n minHeight: \"0\",\n gridAutoRows: \"minmax(0, 1fr)\",\n gap: `${this.gap}px`,\n });\n this.applyGrid();\n container.appendChild(this.element);\n }\n\n addChart(options: ChartLayoutSlotOptions = {}): ChartLayoutSlot {\n this.assertAlive();\n const id = options.id ?? `chart-${++this.counter}`;\n if (this.findSlot(id)) {\n throw new WickError(\"INVALID_ARGUMENT\", `Duplicate chart layout id: ${id}`);\n }\n const rowSpan = validateSpan(options.rowSpan ?? 1, \"rowSpan\");\n const columnSpan = validateSpan(options.columnSpan ?? 1, \"columnSpan\");\n\n const element = document.createElement(\"div\");\n element.className = \"wicklib-chart-layout__cell\";\n element.dataset.chartId = id;\n Object.assign(element.style, {\n position: \"relative\",\n minWidth: \"0\",\n minHeight: \"0\",\n overflow: \"hidden\",\n gridRow: `span ${rowSpan}`,\n gridColumn: `span ${columnSpan}`,\n });\n this.element.appendChild(element);\n\n let chart: ChartApi;\n try {\n chart = createChart(element, mergeChartOptions(this.chartOptions, options.chartOptions));\n } catch (error) {\n element.remove();\n throw error;\n }\n\n const onPointerDown = (): void => this.setActiveChart(id);\n const onFocusIn = (): void => this.setActiveChart(id);\n element.addEventListener(\"pointerdown\", onPointerDown, true);\n element.addEventListener(\"focusin\", onFocusIn);\n\n const unsubscribes: Unsubscribe[] = [\n chart.on(\"visible-range\", (event) => this.handleVisibleRange(id, event)),\n chart.on(\"crosshair\", (event) => this.handleCrosshair(id, event)),\n ];\n\n let overlay: ChartOverlayApi | null = null;\n if (this.syncCrosshair) {\n overlay = chart.addOverlay({\n id: \"wicklib-layout-crosshair\",\n draw: (context, frame) => {\n const current = this.crosshair;\n if (!current || current.sourceId === id) {\n return;\n }\n const x = frame.timeToX(current.time);\n if (x === null || x < frame.plot.left || x > frame.plot.right) {\n return;\n }\n const crispX = Math.round(x) + 0.5;\n context.save();\n context.beginPath();\n context.rect(frame.plot.left, frame.plot.top, frame.plot.width, frame.plot.height);\n context.clip();\n context.strokeStyle = this.crosshairColor;\n context.lineWidth = 1;\n context.setLineDash([3, 4]);\n context.beginPath();\n context.moveTo(crispX, frame.plot.top);\n context.lineTo(crispX, frame.plot.bottom);\n context.stroke();\n context.restore();\n },\n });\n this.crosshairOverlays.set(id, overlay);\n }\n\n const slot: ChartLayoutSlot = {\n id,\n chart,\n element,\n rowSpan,\n columnSpan,\n };\n this.slots.push({\n slot,\n unsubscribe: unsubscribes,\n overlay,\n onPointerDown,\n onFocusIn,\n });\n\n if (this.activeId === null) {\n this.setActiveChart(id);\n }\n\n return slot;\n }\n\n removeChart(id: string): void {\n this.assertAlive();\n const index = this.slots.findIndex((entry) => entry.slot.id === id);\n if (index < 0) {\n return;\n }\n const entry = this.slots[index]!;\n this.disposeSlot(entry);\n this.slots.splice(index, 1);\n if (this.activeId === id) {\n this.setActiveChart(null);\n }\n if (this.crosshair?.sourceId === id) {\n this.crosshair = null;\n this.requestCrosshairRender();\n }\n }\n\n getChart(id: string): ChartApi | null {\n this.assertAlive();\n return this.findSlot(id)?.slot.chart ?? null;\n }\n\n getSlots(): readonly ChartLayoutSlot[] {\n this.assertAlive();\n return this.slots.map((entry) => entry.slot);\n }\n\n chartIds(): readonly string[] {\n this.assertAlive();\n return this.slots.map((entry) => entry.slot.id);\n }\n\n setColumns(columns: number): void {\n this.assertAlive();\n this.columns = validateColumns(columns);\n this.applyGrid();\n }\n\n setGap(gap: number): void {\n this.assertAlive();\n this.gap = validateGap(gap);\n this.element.style.gap = `${this.gap}px`;\n }\n\n setActiveChart(id: string | null): void {\n this.assertAlive();\n if (id !== null && !this.findSlot(id)) {\n throw new WickError(\"INVALID_ARGUMENT\", `Unknown chart layout id: ${id}`);\n }\n if (this.activeId === id) {\n return;\n }\n const previous = this.activeId;\n this.activeId = id;\n for (const entry of this.slots) {\n if (entry.slot.id === previous) {\n delete entry.slot.element.dataset.active;\n }\n if (entry.slot.id === id) {\n entry.slot.element.dataset.active = \"true\";\n }\n }\n for (const handler of this.activeHandlers) {\n handler(id);\n }\n }\n\n activeChartId(): string | null {\n this.assertAlive();\n return this.activeId;\n }\n\n onActiveChange(handler: (chartId: string | null) => void): Unsubscribe {\n this.assertAlive();\n this.activeHandlers.add(handler);\n return () => {\n this.activeHandlers.delete(handler);\n };\n }\n\n syncNow(): void {\n this.assertAlive();\n const source = this.findSlot(this.activeId ?? this.slots[0]?.slot.id ?? \"\");\n if (!source) {\n return;\n }\n this.applyRange(source.slot.id, source.slot.chart.timeScale().getVisibleRange());\n }\n\n serialize(): ChartLayoutSnapshot {\n this.assertAlive();\n return {\n version: 1,\n columns: this.columns,\n gap: this.gap,\n slots: this.slots.map((entry) => ({\n id: entry.slot.id,\n rowSpan: entry.slot.rowSpan,\n columnSpan: entry.slot.columnSpan,\n })),\n };\n }\n\n destroy(): void {\n if (this.destroyed) {\n return;\n }\n this.destroyed = true;\n for (const entry of this.slots) {\n this.disposeSlot(entry);\n }\n this.slots.length = 0;\n this.activeHandlers.clear();\n this.crosshairOverlays.clear();\n this.element.remove();\n }\n\n private applyGrid(): void {\n this.element.style.gridTemplateColumns = `repeat(${this.columns}, minmax(0, 1fr))`;\n }\n\n private findSlot(id: string | null): InternalSlot | undefined {\n if (id === null) {\n return undefined;\n }\n return this.slots.find((entry) => entry.slot.id === id);\n }\n\n private handleVisibleRange(sourceId: string, event: VisibleRangeEvent): void {\n if (!this.syncTimeRange || this.applyingRange) {\n return;\n }\n this.applyRange(sourceId, { from: event.from, to: event.to });\n }\n\n private applyRange(sourceId: string, range: { from: number; to: number }): void {\n if (!Number.isFinite(range.from) || !Number.isFinite(range.to) || range.to <= range.from) {\n return;\n }\n this.applyingRange = true;\n try {\n for (const entry of this.slots) {\n if (entry.slot.id === sourceId) {\n continue;\n }\n try {\n entry.slot.chart.timeScale().setVisibleRange(range);\n } catch {\n // A chart with incompatible data can reject the range; leave it unchanged.\n }\n }\n } finally {\n this.applyingRange = false;\n }\n }\n\n private handleCrosshair(sourceId: string, event: CrosshairEvent): void {\n if (!this.syncCrosshair) {\n return;\n }\n if (event.time === null || event.point === null) {\n if (this.crosshair !== null) {\n this.crosshair = null;\n this.requestCrosshairRender();\n }\n return;\n }\n this.crosshair = { sourceId, time: event.time };\n this.requestCrosshairRender();\n }\n\n private requestCrosshairRender(): void {\n for (const overlay of this.crosshairOverlays.values()) {\n overlay.requestRender();\n }\n }\n\n private disposeSlot(entry: InternalSlot): void {\n entry.slot.element.removeEventListener(\"pointerdown\", entry.onPointerDown, true);\n entry.slot.element.removeEventListener(\"focusin\", entry.onFocusIn);\n for (const unsubscribe of entry.unsubscribe) {\n unsubscribe();\n }\n entry.overlay?.remove();\n this.crosshairOverlays.delete(entry.slot.id);\n entry.slot.chart.destroy();\n entry.slot.element.remove();\n }\n\n private assertAlive(): void {\n if (this.destroyed) {\n throw new WickError(\"DISPOSED\", \"Chart layout is disposed\");\n }\n }\n}\n\nfunction mergeChartOptions(\n base: ChartOptions | undefined,\n override: ChartOptions | undefined,\n): ChartOptions | undefined {\n if (!base) {\n return override;\n }\n if (!override) {\n return base;\n }\n return { ...base, ...override };\n}\n\nfunction validateColumns(columns: number): number {\n if (!Number.isInteger(columns) || columns < 1 || columns > MAX_COLUMNS) {\n throw new WickError(\"INVALID_ARGUMENT\", `columns must be an integer from 1 to ${MAX_COLUMNS}`);\n }\n return columns;\n}\n\nfunction validateGap(gap: number): number {\n if (!Number.isFinite(gap) || gap < 0) {\n throw new WickError(\"INVALID_ARGUMENT\", \"gap must be a non-negative finite number\");\n }\n return gap;\n}\n\nfunction validateSpan(value: number, name: string): number {\n if (!Number.isInteger(value) || value < 1) {\n throw new WickError(\"INVALID_ARGUMENT\", `${name} must be a positive integer`);\n }\n return value;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":["WickError","createChart","entry"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Free multi-chart layout primitives for Wicklib.\n */\n\nimport {\n WickError,\n createChart,\n type ChartApi,\n type ChartOptions,\n type ChartOverlayApi,\n type CrosshairEvent,\n type OverlayRenderFrame,\n type Unsubscribe,\n type VisibleRangeEvent,\n} from \"@wicklib/core\";\n\n/**\n * Crosshair synchronization mode.\n *\n * `time` mirrors the vertical time line. `full` also mirrors a horizontal line at the same\n * relative pane height.\n */\nexport type ChartLayoutCrosshairMode = \"off\" | \"time\" | \"full\";\n\n/**\n * Resolved synchronization behavior for a layout.\n */\nexport interface ChartLayoutSyncOptions {\n /** Synchronize visible time ranges. */\n timeRange: boolean;\n /** Crosshair synchronization mode. */\n crosshair: ChartLayoutCrosshairMode;\n}\n\n/**\n * Partial synchronization update. Boolean crosshair values map to `time` and `off`.\n */\nexport interface ChartLayoutSyncUpdate {\n /** Synchronize visible time ranges. */\n timeRange?: boolean;\n /** Crosshair synchronization mode. */\n crosshair?: boolean | ChartLayoutCrosshairMode;\n}\n\n/**\n * Options for a layout chart cell.\n */\nexport interface ChartLayoutSlotOptions {\n /** Stable slot id. Generated when omitted. */\n id?: string;\n /** Chart options for this slot. Merged over the layout defaults. */\n chartOptions?: ChartOptions;\n /** Number of grid rows covered by the cell. Defaults to 1. */\n rowSpan?: number;\n /** Number of grid columns covered by the cell. Defaults to 1. */\n columnSpan?: number;\n /** Synchronization group. Slots only sync with slots in the same group. */\n syncGroup?: string;\n}\n\n/**\n * One chart cell in a layout.\n */\nexport interface ChartLayoutSlot {\n readonly id: string;\n readonly chart: ChartApi;\n readonly element: HTMLElement;\n readonly rowSpan: number;\n readonly columnSpan: number;\n readonly syncGroup: string;\n}\n\n/**\n * Serializable structural layout state. Chart state is owned by the host.\n */\nexport interface ChartLayoutSnapshot {\n version: 1;\n columns: number;\n gap: number;\n sync: ChartLayoutSyncOptions;\n slots: ReadonlyArray<{\n id: string;\n rowSpan: number;\n columnSpan: number;\n syncGroup: string;\n }>;\n}\n\n/**\n * Options for a multi-chart layout.\n */\nexport interface ChartLayoutOptions {\n /** Number of grid columns. Defaults to 2. */\n columns?: number;\n /** Gap between cells in CSS pixels. Defaults to 1. */\n gap?: number;\n /** Default chart options applied to every slot. */\n chartOptions?: ChartOptions;\n /** Synchronization options. */\n sync?: ChartLayoutSyncUpdate;\n /** Synchronize visible time ranges. Defaults to true. */\n syncTimeRange?: boolean;\n /** Crosshair synchronization. Defaults to `time`. */\n syncCrosshair?: boolean | ChartLayoutCrosshairMode;\n /** Crosshair line colour. */\n crosshairColor?: string;\n /** Extra class name on the layout root. */\n className?: string;\n /** Called when the active chart changes. */\n onActiveChange?: (chartId: string | null) => void;\n}\n\n/**\n * A live multi-chart layout.\n */\nexport interface ChartLayoutApi {\n /** Layout root inserted into the host container. */\n readonly element: HTMLElement;\n /** Adds a chart cell and returns its live slot. */\n addChart(options?: ChartLayoutSlotOptions): ChartLayoutSlot;\n /** Destroys and removes one chart cell. */\n removeChart(id: string): void;\n /** Returns a live chart, or null when the id is unknown. */\n getChart(id: string): ChartApi | null;\n /** Returns the current slots in DOM order. */\n getSlots(): readonly ChartLayoutSlot[];\n /** Returns slot ids in DOM order. */\n chartIds(): readonly string[];\n /** Changes the grid column count. */\n setColumns(columns: number): void;\n /** Changes the cell gap in CSS pixels. */\n setGap(gap: number): void;\n /** Changes synchronization behavior without recreating charts. */\n setSyncOptions(options: ChartLayoutSyncUpdate): void;\n /** Returns the resolved synchronization behavior. */\n getSyncOptions(): Readonly<ChartLayoutSyncOptions>;\n /** Marks one chart as active. Pass null to clear. */\n setActiveChart(id: string | null): void;\n /** Returns the active chart id, or null. */\n activeChartId(): string | null;\n /** Subscribes to active chart changes. */\n onActiveChange(handler: (chartId: string | null) => void): Unsubscribe;\n /** Applies the active chart's visible range to every chart in its sync group. */\n syncNow(): void;\n /** Returns structural layout state for host persistence. */\n serialize(): ChartLayoutSnapshot;\n /** Destroys every chart and removes the layout root. */\n destroy(): void;\n}\n\ninterface InternalSlot {\n readonly slot: ChartLayoutSlot;\n readonly unsubscribe: readonly Unsubscribe[];\n overlay: ChartOverlayApi | null;\n readonly onPointerDown: () => void;\n readonly onFocusIn: () => void;\n}\n\ninterface CrosshairState {\n sourceId: string;\n sourceGroup: string;\n time: number;\n yRatio: number | null;\n}\n\nconst DEFAULT_COLUMNS = 2;\nconst DEFAULT_GAP = 1;\nconst DEFAULT_SYNC_GROUP = \"default\";\nconst MAX_COLUMNS = 12;\nconst DEFAULT_CROSSHAIR_COLOR = \"rgba(139, 149, 167, 0.6)\";\n\n/**\n * Creates a framework-agnostic grid of Wicklib charts.\n *\n * The layout owns chart creation, grid placement, time-range synchronization, and a\n * mirrored crosshair. It does not own data, symbol state, accounts, or persistence.\n */\nexport function createChartLayout(\n container: HTMLElement,\n options: ChartLayoutOptions = {},\n): ChartLayoutApi {\n return new ChartLayout(container, options);\n}\n\nclass ChartLayout implements ChartLayoutApi {\n readonly element: HTMLElement;\n\n private readonly slots: InternalSlot[] = [];\n private readonly activeHandlers = new Set<(chartId: string | null) => void>();\n private readonly crosshairOverlays = new Map<string, ChartOverlayApi>();\n private readonly chartOptions: ChartOptions | undefined;\n private readonly crosshairColor: string;\n private columns: number;\n private gap: number;\n private sync: ChartLayoutSyncOptions;\n private activeId: string | null = null;\n private counter = 0;\n private applyingRange = false;\n private crosshair: CrosshairState | null = null;\n private destroyed = false;\n\n constructor(container: HTMLElement, options: ChartLayoutOptions) {\n if (!container || typeof container.appendChild !== \"function\") {\n throw new WickError(\"INVALID_ARGUMENT\", \"createChartLayout requires a DOM container\");\n }\n\n this.columns = validateColumns(options.columns ?? DEFAULT_COLUMNS);\n this.gap = validateGap(options.gap ?? DEFAULT_GAP);\n this.chartOptions = options.chartOptions;\n this.sync = resolveSyncOptions(options);\n this.crosshairColor = options.crosshairColor ?? DEFAULT_CROSSHAIR_COLOR;\n if (options.onActiveChange) {\n this.activeHandlers.add(options.onActiveChange);\n }\n\n this.element = document.createElement(\"div\");\n this.element.className = [\"wicklib-chart-layout\", options.className].filter(Boolean).join(\" \");\n Object.assign(this.element.style, {\n display: \"grid\",\n width: \"100%\",\n height: \"100%\",\n minWidth: \"0\",\n minHeight: \"0\",\n gridAutoRows: \"minmax(0, 1fr)\",\n gap: `${this.gap}px`,\n });\n this.applyGrid();\n container.appendChild(this.element);\n }\n\n addChart(options: ChartLayoutSlotOptions = {}): ChartLayoutSlot {\n this.assertAlive();\n const id = options.id ?? `chart-${++this.counter}`;\n if (this.findSlot(id)) {\n throw new WickError(\"INVALID_ARGUMENT\", `Duplicate chart layout id: ${id}`);\n }\n const rowSpan = validateSpan(options.rowSpan ?? 1, \"rowSpan\");\n const columnSpan = validateSpan(options.columnSpan ?? 1, \"columnSpan\");\n const syncGroup = validateSyncGroup(options.syncGroup ?? DEFAULT_SYNC_GROUP);\n\n const element = document.createElement(\"div\");\n element.className = \"wicklib-chart-layout__cell\";\n element.dataset.chartId = id;\n Object.assign(element.style, {\n position: \"relative\",\n minWidth: \"0\",\n minHeight: \"0\",\n overflow: \"hidden\",\n gridRow: `span ${rowSpan}`,\n gridColumn: `span ${columnSpan}`,\n });\n this.element.appendChild(element);\n\n let chart: ChartApi;\n try {\n chart = createChart(element, mergeChartOptions(this.chartOptions, options.chartOptions));\n } catch (error) {\n element.remove();\n throw error;\n }\n\n const onPointerDown = (): void => this.setActiveChart(id);\n const onFocusIn = (): void => this.setActiveChart(id);\n element.addEventListener(\"pointerdown\", onPointerDown, true);\n element.addEventListener(\"focusin\", onFocusIn);\n\n const unsubscribes: Unsubscribe[] = [\n chart.on(\"visible-range\", (event) => this.handleVisibleRange(id, event)),\n chart.on(\"crosshair\", (event) => this.handleCrosshair(id, event)),\n ];\n\n const slot: ChartLayoutSlot = {\n id,\n chart,\n element,\n rowSpan,\n columnSpan,\n syncGroup,\n };\n const entry: InternalSlot = {\n slot,\n unsubscribe: unsubscribes,\n overlay: null,\n onPointerDown,\n onFocusIn,\n };\n this.slots.push(entry);\n this.ensureCrosshairOverlay(entry);\n\n if (this.activeId === null) {\n this.setActiveChart(id);\n }\n\n return slot;\n }\n\n removeChart(id: string): void {\n this.assertAlive();\n const index = this.slots.findIndex((entry) => entry.slot.id === id);\n if (index < 0) {\n return;\n }\n const entry = this.slots[index]!;\n this.disposeSlot(entry);\n this.slots.splice(index, 1);\n if (this.activeId === id) {\n this.setActiveChart(null);\n }\n if (this.crosshair?.sourceId === id) {\n this.crosshair = null;\n this.requestCrosshairRender();\n }\n }\n\n getChart(id: string): ChartApi | null {\n this.assertAlive();\n return this.findSlot(id)?.slot.chart ?? null;\n }\n\n getSlots(): readonly ChartLayoutSlot[] {\n this.assertAlive();\n return this.slots.map((entry) => entry.slot);\n }\n\n chartIds(): readonly string[] {\n this.assertAlive();\n return this.slots.map((entry) => entry.slot.id);\n }\n\n setColumns(columns: number): void {\n this.assertAlive();\n this.columns = validateColumns(columns);\n this.applyGrid();\n }\n\n setGap(gap: number): void {\n this.assertAlive();\n this.gap = validateGap(gap);\n this.element.style.gap = `${this.gap}px`;\n }\n\n setSyncOptions(options: ChartLayoutSyncUpdate): void {\n this.assertAlive();\n const next: ChartLayoutSyncOptions = {\n timeRange: options.timeRange ?? this.sync.timeRange,\n crosshair:\n options.crosshair === undefined\n ? this.sync.crosshair\n : normalizeCrosshair(options.crosshair),\n };\n if (next.timeRange === this.sync.timeRange && next.crosshair === this.sync.crosshair) {\n return;\n }\n this.sync = next;\n for (const entry of this.slots) {\n this.ensureCrosshairOverlay(entry);\n }\n if (next.crosshair === \"off\" && this.crosshair !== null) {\n this.crosshair = null;\n }\n this.requestCrosshairRender();\n }\n\n getSyncOptions(): Readonly<ChartLayoutSyncOptions> {\n this.assertAlive();\n return { ...this.sync };\n }\n\n setActiveChart(id: string | null): void {\n this.assertAlive();\n if (id !== null && !this.findSlot(id)) {\n throw new WickError(\"INVALID_ARGUMENT\", `Unknown chart layout id: ${id}`);\n }\n if (this.activeId === id) {\n return;\n }\n const previous = this.activeId;\n this.activeId = id;\n for (const entry of this.slots) {\n if (entry.slot.id === previous) {\n delete entry.slot.element.dataset.active;\n }\n if (entry.slot.id === id) {\n entry.slot.element.dataset.active = \"true\";\n }\n }\n for (const handler of this.activeHandlers) {\n handler(id);\n }\n }\n\n activeChartId(): string | null {\n this.assertAlive();\n return this.activeId;\n }\n\n onActiveChange(handler: (chartId: string | null) => void): Unsubscribe {\n this.assertAlive();\n this.activeHandlers.add(handler);\n return () => {\n this.activeHandlers.delete(handler);\n };\n }\n\n syncNow(): void {\n this.assertAlive();\n const source = this.findSlot(this.activeId ?? this.slots[0]?.slot.id ?? \"\");\n if (!source) {\n return;\n }\n this.applyRange(\n source.slot.id,\n source.slot.syncGroup,\n source.slot.chart.timeScale().getVisibleRange(),\n );\n }\n\n serialize(): ChartLayoutSnapshot {\n this.assertAlive();\n return {\n version: 1,\n columns: this.columns,\n gap: this.gap,\n sync: { ...this.sync },\n slots: this.slots.map((entry) => ({\n id: entry.slot.id,\n rowSpan: entry.slot.rowSpan,\n columnSpan: entry.slot.columnSpan,\n syncGroup: entry.slot.syncGroup,\n })),\n };\n }\n\n destroy(): void {\n if (this.destroyed) {\n return;\n }\n this.destroyed = true;\n for (const entry of this.slots) {\n this.disposeSlot(entry);\n }\n this.slots.length = 0;\n this.activeHandlers.clear();\n this.crosshairOverlays.clear();\n this.element.remove();\n }\n\n private applyGrid(): void {\n this.element.style.gridTemplateColumns = `repeat(${this.columns}, minmax(0, 1fr))`;\n }\n\n private findSlot(id: string | null): InternalSlot | undefined {\n if (id === null) {\n return undefined;\n }\n return this.slots.find((entry) => entry.slot.id === id);\n }\n\n private handleVisibleRange(sourceId: string, event: VisibleRangeEvent): void {\n if (!this.sync.timeRange || this.applyingRange) {\n return;\n }\n const source = this.findSlot(sourceId);\n if (!source) {\n return;\n }\n this.applyRange(sourceId, source.slot.syncGroup, { from: event.from, to: event.to });\n }\n\n private applyRange(\n sourceId: string,\n syncGroup: string,\n range: { from: number; to: number },\n ): void {\n if (!Number.isFinite(range.from) || !Number.isFinite(range.to) || range.to <= range.from) {\n return;\n }\n this.applyingRange = true;\n try {\n for (const entry of this.slots) {\n if (entry.slot.id === sourceId || entry.slot.syncGroup !== syncGroup) {\n continue;\n }\n try {\n entry.slot.chart.timeScale().setVisibleRange(range);\n } catch {\n // A chart with incompatible data can reject the range; leave it unchanged.\n }\n }\n } finally {\n this.applyingRange = false;\n }\n }\n\n private handleCrosshair(sourceId: string, event: CrosshairEvent): void {\n if (this.sync.crosshair === \"off\") {\n return;\n }\n if (event.time === null || event.point === null) {\n if (this.crosshair !== null) {\n this.crosshair = null;\n this.requestCrosshairRender();\n }\n return;\n }\n const source = this.findSlot(sourceId);\n if (!source) {\n return;\n }\n const height = source.slot.element.clientHeight;\n const yRatio = height > 0 ? Math.min(1, Math.max(0, event.point.y / height)) : null;\n this.crosshair = {\n sourceId,\n sourceGroup: source.slot.syncGroup,\n time: event.time,\n yRatio,\n };\n this.requestCrosshairRender();\n }\n\n private requestCrosshairRender(): void {\n for (const overlay of this.crosshairOverlays.values()) {\n overlay.requestRender();\n }\n }\n\n private ensureCrosshairOverlay(entry: InternalSlot): void {\n if (this.sync.crosshair === \"off\") {\n if (entry.overlay) {\n entry.overlay.remove();\n entry.overlay = null;\n this.crosshairOverlays.delete(entry.slot.id);\n }\n return;\n }\n if (entry.overlay) {\n return;\n }\n const id = entry.slot.id;\n const group = entry.slot.syncGroup;\n entry.overlay = entry.slot.chart.addOverlay({\n id: \"wicklib-layout-crosshair\",\n draw: (context, frame) => this.drawCrosshair(context, frame, id, group),\n });\n this.crosshairOverlays.set(id, entry.overlay);\n }\n\n private drawCrosshair(\n context: CanvasRenderingContext2D,\n frame: OverlayRenderFrame,\n id: string,\n group: string,\n ): void {\n const current = this.crosshair;\n if (\n !current ||\n current.sourceId === id ||\n current.sourceGroup !== group ||\n this.sync.crosshair === \"off\"\n ) {\n return;\n }\n const x = frame.timeToX(current.time);\n if (x === null || x < frame.plot.left || x > frame.plot.right) {\n return;\n }\n context.save();\n context.beginPath();\n context.rect(frame.plot.left, frame.plot.top, frame.plot.width, frame.plot.height);\n context.clip();\n context.strokeStyle = this.crosshairColor;\n context.lineWidth = 1;\n context.setLineDash([3, 4]);\n\n const crispX = Math.round(x) + 0.5;\n context.beginPath();\n context.moveTo(crispX, frame.plot.top);\n context.lineTo(crispX, frame.plot.bottom);\n context.stroke();\n\n if (this.sync.crosshair === \"full\" && current.yRatio !== null) {\n const y = frame.plot.top + current.yRatio * frame.plot.height;\n const crispY = Math.round(y) + 0.5;\n context.beginPath();\n context.moveTo(frame.plot.left, crispY);\n context.lineTo(frame.plot.right, crispY);\n context.stroke();\n }\n\n context.restore();\n }\n\n private disposeSlot(entry: InternalSlot): void {\n entry.slot.element.removeEventListener(\"pointerdown\", entry.onPointerDown, true);\n entry.slot.element.removeEventListener(\"focusin\", entry.onFocusIn);\n for (const unsubscribe of entry.unsubscribe) {\n unsubscribe();\n }\n entry.overlay?.remove();\n entry.overlay = null;\n this.crosshairOverlays.delete(entry.slot.id);\n entry.slot.chart.destroy();\n entry.slot.element.remove();\n }\n\n private assertAlive(): void {\n if (this.destroyed) {\n throw new WickError(\"DISPOSED\", \"Chart layout is disposed\");\n }\n }\n}\n\nfunction resolveSyncOptions(options: ChartLayoutOptions): ChartLayoutSyncOptions {\n return {\n timeRange: options.sync?.timeRange ?? options.syncTimeRange ?? true,\n crosshair: normalizeCrosshair(options.sync?.crosshair ?? options.syncCrosshair ?? \"time\"),\n };\n}\n\nfunction normalizeCrosshair(\n value: boolean | ChartLayoutCrosshairMode | undefined,\n): ChartLayoutCrosshairMode {\n if (value === undefined || value === true || value === \"time\") {\n return \"time\";\n }\n if (value === false || value === \"off\") {\n return \"off\";\n }\n if (value === \"full\") {\n return \"full\";\n }\n throw new WickError(\"INVALID_ARGUMENT\", \"crosshair must be off, time, or full\");\n}\n\nfunction mergeChartOptions(\n base: ChartOptions | undefined,\n override: ChartOptions | undefined,\n): ChartOptions | undefined {\n if (!base) {\n return override;\n }\n if (!override) {\n return base;\n }\n return { ...base, ...override };\n}\n\nfunction validateColumns(columns: number): number {\n if (!Number.isInteger(columns) || columns < 1 || columns > MAX_COLUMNS) {\n throw new WickError(\"INVALID_ARGUMENT\", `columns must be an integer from 1 to ${MAX_COLUMNS}`);\n }\n return columns;\n}\n\nfunction validateGap(gap: number): number {\n if (!Number.isFinite(gap) || gap < 0) {\n throw new WickError(\"INVALID_ARGUMENT\", \"gap must be a non-negative finite number\");\n }\n return gap;\n}\n\nfunction validateSpan(value: number, name: string): number {\n if (!Number.isInteger(value) || value < 1) {\n throw new WickError(\"INVALID_ARGUMENT\", `${name} must be a positive integer`);\n }\n return value;\n}\n\nfunction validateSyncGroup(value: string): string {\n if (typeof value !== \"string\" || value.trim() === \"\") {\n throw new WickError(\"INVALID_ARGUMENT\", \"syncGroup must be a non-empty string\");\n }\n return value;\n}\n"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -4,6 +4,31 @@ import { ChartOptions, ChartApi, Unsubscribe } from '@wicklib/core';
|
|
|
4
4
|
* Free multi-chart layout primitives for Wicklib.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Crosshair synchronization mode.
|
|
9
|
+
*
|
|
10
|
+
* `time` mirrors the vertical time line. `full` also mirrors a horizontal line at the same
|
|
11
|
+
* relative pane height.
|
|
12
|
+
*/
|
|
13
|
+
type ChartLayoutCrosshairMode = "off" | "time" | "full";
|
|
14
|
+
/**
|
|
15
|
+
* Resolved synchronization behavior for a layout.
|
|
16
|
+
*/
|
|
17
|
+
interface ChartLayoutSyncOptions {
|
|
18
|
+
/** Synchronize visible time ranges. */
|
|
19
|
+
timeRange: boolean;
|
|
20
|
+
/** Crosshair synchronization mode. */
|
|
21
|
+
crosshair: ChartLayoutCrosshairMode;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Partial synchronization update. Boolean crosshair values map to `time` and `off`.
|
|
25
|
+
*/
|
|
26
|
+
interface ChartLayoutSyncUpdate {
|
|
27
|
+
/** Synchronize visible time ranges. */
|
|
28
|
+
timeRange?: boolean;
|
|
29
|
+
/** Crosshair synchronization mode. */
|
|
30
|
+
crosshair?: boolean | ChartLayoutCrosshairMode;
|
|
31
|
+
}
|
|
7
32
|
/**
|
|
8
33
|
* Options for a layout chart cell.
|
|
9
34
|
*/
|
|
@@ -16,6 +41,8 @@ interface ChartLayoutSlotOptions {
|
|
|
16
41
|
rowSpan?: number;
|
|
17
42
|
/** Number of grid columns covered by the cell. Defaults to 1. */
|
|
18
43
|
columnSpan?: number;
|
|
44
|
+
/** Synchronization group. Slots only sync with slots in the same group. */
|
|
45
|
+
syncGroup?: string;
|
|
19
46
|
}
|
|
20
47
|
/**
|
|
21
48
|
* One chart cell in a layout.
|
|
@@ -26,6 +53,7 @@ interface ChartLayoutSlot {
|
|
|
26
53
|
readonly element: HTMLElement;
|
|
27
54
|
readonly rowSpan: number;
|
|
28
55
|
readonly columnSpan: number;
|
|
56
|
+
readonly syncGroup: string;
|
|
29
57
|
}
|
|
30
58
|
/**
|
|
31
59
|
* Serializable structural layout state. Chart state is owned by the host.
|
|
@@ -34,10 +62,12 @@ interface ChartLayoutSnapshot {
|
|
|
34
62
|
version: 1;
|
|
35
63
|
columns: number;
|
|
36
64
|
gap: number;
|
|
65
|
+
sync: ChartLayoutSyncOptions;
|
|
37
66
|
slots: ReadonlyArray<{
|
|
38
67
|
id: string;
|
|
39
68
|
rowSpan: number;
|
|
40
69
|
columnSpan: number;
|
|
70
|
+
syncGroup: string;
|
|
41
71
|
}>;
|
|
42
72
|
}
|
|
43
73
|
/**
|
|
@@ -50,10 +80,12 @@ interface ChartLayoutOptions {
|
|
|
50
80
|
gap?: number;
|
|
51
81
|
/** Default chart options applied to every slot. */
|
|
52
82
|
chartOptions?: ChartOptions;
|
|
83
|
+
/** Synchronization options. */
|
|
84
|
+
sync?: ChartLayoutSyncUpdate;
|
|
53
85
|
/** Synchronize visible time ranges. Defaults to true. */
|
|
54
86
|
syncTimeRange?: boolean;
|
|
55
|
-
/**
|
|
56
|
-
syncCrosshair?: boolean;
|
|
87
|
+
/** Crosshair synchronization. Defaults to `time`. */
|
|
88
|
+
syncCrosshair?: boolean | ChartLayoutCrosshairMode;
|
|
57
89
|
/** Crosshair line colour. */
|
|
58
90
|
crosshairColor?: string;
|
|
59
91
|
/** Extra class name on the layout root. */
|
|
@@ -81,13 +113,17 @@ interface ChartLayoutApi {
|
|
|
81
113
|
setColumns(columns: number): void;
|
|
82
114
|
/** Changes the cell gap in CSS pixels. */
|
|
83
115
|
setGap(gap: number): void;
|
|
116
|
+
/** Changes synchronization behavior without recreating charts. */
|
|
117
|
+
setSyncOptions(options: ChartLayoutSyncUpdate): void;
|
|
118
|
+
/** Returns the resolved synchronization behavior. */
|
|
119
|
+
getSyncOptions(): Readonly<ChartLayoutSyncOptions>;
|
|
84
120
|
/** Marks one chart as active. Pass null to clear. */
|
|
85
121
|
setActiveChart(id: string | null): void;
|
|
86
122
|
/** Returns the active chart id, or null. */
|
|
87
123
|
activeChartId(): string | null;
|
|
88
124
|
/** Subscribes to active chart changes. */
|
|
89
125
|
onActiveChange(handler: (chartId: string | null) => void): Unsubscribe;
|
|
90
|
-
/** Applies the active chart's visible range to every
|
|
126
|
+
/** Applies the active chart's visible range to every chart in its sync group. */
|
|
91
127
|
syncNow(): void;
|
|
92
128
|
/** Returns structural layout state for host persistence. */
|
|
93
129
|
serialize(): ChartLayoutSnapshot;
|
|
@@ -98,9 +134,9 @@ interface ChartLayoutApi {
|
|
|
98
134
|
* Creates a framework-agnostic grid of Wicklib charts.
|
|
99
135
|
*
|
|
100
136
|
* The layout owns chart creation, grid placement, time-range synchronization, and a
|
|
101
|
-
* mirrored crosshair
|
|
137
|
+
* mirrored crosshair. It does not own data, symbol state, accounts, or persistence.
|
|
102
138
|
*/
|
|
103
139
|
declare function createChartLayout(container: HTMLElement, options?: ChartLayoutOptions): ChartLayoutApi;
|
|
104
140
|
|
|
105
141
|
export { createChartLayout };
|
|
106
|
-
export type { ChartLayoutApi, ChartLayoutOptions, ChartLayoutSlot, ChartLayoutSlotOptions, ChartLayoutSnapshot };
|
|
142
|
+
export type { ChartLayoutApi, ChartLayoutCrosshairMode, ChartLayoutOptions, ChartLayoutSlot, ChartLayoutSlotOptions, ChartLayoutSnapshot, ChartLayoutSyncOptions, ChartLayoutSyncUpdate };
|
package/dist/index.d.ts
CHANGED
|
@@ -4,6 +4,31 @@ import { ChartOptions, ChartApi, Unsubscribe } from '@wicklib/core';
|
|
|
4
4
|
* Free multi-chart layout primitives for Wicklib.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Crosshair synchronization mode.
|
|
9
|
+
*
|
|
10
|
+
* `time` mirrors the vertical time line. `full` also mirrors a horizontal line at the same
|
|
11
|
+
* relative pane height.
|
|
12
|
+
*/
|
|
13
|
+
type ChartLayoutCrosshairMode = "off" | "time" | "full";
|
|
14
|
+
/**
|
|
15
|
+
* Resolved synchronization behavior for a layout.
|
|
16
|
+
*/
|
|
17
|
+
interface ChartLayoutSyncOptions {
|
|
18
|
+
/** Synchronize visible time ranges. */
|
|
19
|
+
timeRange: boolean;
|
|
20
|
+
/** Crosshair synchronization mode. */
|
|
21
|
+
crosshair: ChartLayoutCrosshairMode;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Partial synchronization update. Boolean crosshair values map to `time` and `off`.
|
|
25
|
+
*/
|
|
26
|
+
interface ChartLayoutSyncUpdate {
|
|
27
|
+
/** Synchronize visible time ranges. */
|
|
28
|
+
timeRange?: boolean;
|
|
29
|
+
/** Crosshair synchronization mode. */
|
|
30
|
+
crosshair?: boolean | ChartLayoutCrosshairMode;
|
|
31
|
+
}
|
|
7
32
|
/**
|
|
8
33
|
* Options for a layout chart cell.
|
|
9
34
|
*/
|
|
@@ -16,6 +41,8 @@ interface ChartLayoutSlotOptions {
|
|
|
16
41
|
rowSpan?: number;
|
|
17
42
|
/** Number of grid columns covered by the cell. Defaults to 1. */
|
|
18
43
|
columnSpan?: number;
|
|
44
|
+
/** Synchronization group. Slots only sync with slots in the same group. */
|
|
45
|
+
syncGroup?: string;
|
|
19
46
|
}
|
|
20
47
|
/**
|
|
21
48
|
* One chart cell in a layout.
|
|
@@ -26,6 +53,7 @@ interface ChartLayoutSlot {
|
|
|
26
53
|
readonly element: HTMLElement;
|
|
27
54
|
readonly rowSpan: number;
|
|
28
55
|
readonly columnSpan: number;
|
|
56
|
+
readonly syncGroup: string;
|
|
29
57
|
}
|
|
30
58
|
/**
|
|
31
59
|
* Serializable structural layout state. Chart state is owned by the host.
|
|
@@ -34,10 +62,12 @@ interface ChartLayoutSnapshot {
|
|
|
34
62
|
version: 1;
|
|
35
63
|
columns: number;
|
|
36
64
|
gap: number;
|
|
65
|
+
sync: ChartLayoutSyncOptions;
|
|
37
66
|
slots: ReadonlyArray<{
|
|
38
67
|
id: string;
|
|
39
68
|
rowSpan: number;
|
|
40
69
|
columnSpan: number;
|
|
70
|
+
syncGroup: string;
|
|
41
71
|
}>;
|
|
42
72
|
}
|
|
43
73
|
/**
|
|
@@ -50,10 +80,12 @@ interface ChartLayoutOptions {
|
|
|
50
80
|
gap?: number;
|
|
51
81
|
/** Default chart options applied to every slot. */
|
|
52
82
|
chartOptions?: ChartOptions;
|
|
83
|
+
/** Synchronization options. */
|
|
84
|
+
sync?: ChartLayoutSyncUpdate;
|
|
53
85
|
/** Synchronize visible time ranges. Defaults to true. */
|
|
54
86
|
syncTimeRange?: boolean;
|
|
55
|
-
/**
|
|
56
|
-
syncCrosshair?: boolean;
|
|
87
|
+
/** Crosshair synchronization. Defaults to `time`. */
|
|
88
|
+
syncCrosshair?: boolean | ChartLayoutCrosshairMode;
|
|
57
89
|
/** Crosshair line colour. */
|
|
58
90
|
crosshairColor?: string;
|
|
59
91
|
/** Extra class name on the layout root. */
|
|
@@ -81,13 +113,17 @@ interface ChartLayoutApi {
|
|
|
81
113
|
setColumns(columns: number): void;
|
|
82
114
|
/** Changes the cell gap in CSS pixels. */
|
|
83
115
|
setGap(gap: number): void;
|
|
116
|
+
/** Changes synchronization behavior without recreating charts. */
|
|
117
|
+
setSyncOptions(options: ChartLayoutSyncUpdate): void;
|
|
118
|
+
/** Returns the resolved synchronization behavior. */
|
|
119
|
+
getSyncOptions(): Readonly<ChartLayoutSyncOptions>;
|
|
84
120
|
/** Marks one chart as active. Pass null to clear. */
|
|
85
121
|
setActiveChart(id: string | null): void;
|
|
86
122
|
/** Returns the active chart id, or null. */
|
|
87
123
|
activeChartId(): string | null;
|
|
88
124
|
/** Subscribes to active chart changes. */
|
|
89
125
|
onActiveChange(handler: (chartId: string | null) => void): Unsubscribe;
|
|
90
|
-
/** Applies the active chart's visible range to every
|
|
126
|
+
/** Applies the active chart's visible range to every chart in its sync group. */
|
|
91
127
|
syncNow(): void;
|
|
92
128
|
/** Returns structural layout state for host persistence. */
|
|
93
129
|
serialize(): ChartLayoutSnapshot;
|
|
@@ -98,9 +134,9 @@ interface ChartLayoutApi {
|
|
|
98
134
|
* Creates a framework-agnostic grid of Wicklib charts.
|
|
99
135
|
*
|
|
100
136
|
* The layout owns chart creation, grid placement, time-range synchronization, and a
|
|
101
|
-
* mirrored crosshair
|
|
137
|
+
* mirrored crosshair. It does not own data, symbol state, accounts, or persistence.
|
|
102
138
|
*/
|
|
103
139
|
declare function createChartLayout(container: HTMLElement, options?: ChartLayoutOptions): ChartLayoutApi;
|
|
104
140
|
|
|
105
141
|
export { createChartLayout };
|
|
106
|
-
export type { ChartLayoutApi, ChartLayoutOptions, ChartLayoutSlot, ChartLayoutSlotOptions, ChartLayoutSnapshot };
|
|
142
|
+
export type { ChartLayoutApi, ChartLayoutCrosshairMode, ChartLayoutOptions, ChartLayoutSlot, ChartLayoutSlotOptions, ChartLayoutSnapshot, ChartLayoutSyncOptions, ChartLayoutSyncUpdate };
|
package/dist/index.js
CHANGED
|
@@ -3,6 +3,7 @@ import { WickError, createChart } from '@wicklib/core';
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
var DEFAULT_COLUMNS = 2;
|
|
5
5
|
var DEFAULT_GAP = 1;
|
|
6
|
+
var DEFAULT_SYNC_GROUP = "default";
|
|
6
7
|
var MAX_COLUMNS = 12;
|
|
7
8
|
var DEFAULT_CROSSHAIR_COLOR = "rgba(139, 149, 167, 0.6)";
|
|
8
9
|
function createChartLayout(container, options = {}) {
|
|
@@ -14,11 +15,10 @@ var ChartLayout = class {
|
|
|
14
15
|
activeHandlers = /* @__PURE__ */ new Set();
|
|
15
16
|
crosshairOverlays = /* @__PURE__ */ new Map();
|
|
16
17
|
chartOptions;
|
|
17
|
-
syncTimeRange;
|
|
18
|
-
syncCrosshair;
|
|
19
18
|
crosshairColor;
|
|
20
19
|
columns;
|
|
21
20
|
gap;
|
|
21
|
+
sync;
|
|
22
22
|
activeId = null;
|
|
23
23
|
counter = 0;
|
|
24
24
|
applyingRange = false;
|
|
@@ -31,8 +31,7 @@ var ChartLayout = class {
|
|
|
31
31
|
this.columns = validateColumns(options.columns ?? DEFAULT_COLUMNS);
|
|
32
32
|
this.gap = validateGap(options.gap ?? DEFAULT_GAP);
|
|
33
33
|
this.chartOptions = options.chartOptions;
|
|
34
|
-
this.
|
|
35
|
-
this.syncCrosshair = options.syncCrosshair ?? true;
|
|
34
|
+
this.sync = resolveSyncOptions(options);
|
|
36
35
|
this.crosshairColor = options.crosshairColor ?? DEFAULT_CROSSHAIR_COLOR;
|
|
37
36
|
if (options.onActiveChange) {
|
|
38
37
|
this.activeHandlers.add(options.onActiveChange);
|
|
@@ -59,6 +58,7 @@ var ChartLayout = class {
|
|
|
59
58
|
}
|
|
60
59
|
const rowSpan = validateSpan(options.rowSpan ?? 1, "rowSpan");
|
|
61
60
|
const columnSpan = validateSpan(options.columnSpan ?? 1, "columnSpan");
|
|
61
|
+
const syncGroup = validateSyncGroup(options.syncGroup ?? DEFAULT_SYNC_GROUP);
|
|
62
62
|
const element = document.createElement("div");
|
|
63
63
|
element.className = "wicklib-chart-layout__cell";
|
|
64
64
|
element.dataset.chartId = id;
|
|
@@ -86,50 +86,23 @@ var ChartLayout = class {
|
|
|
86
86
|
chart.on("visible-range", (event) => this.handleVisibleRange(id, event)),
|
|
87
87
|
chart.on("crosshair", (event) => this.handleCrosshair(id, event))
|
|
88
88
|
];
|
|
89
|
-
let overlay = null;
|
|
90
|
-
if (this.syncCrosshair) {
|
|
91
|
-
overlay = chart.addOverlay({
|
|
92
|
-
id: "wicklib-layout-crosshair",
|
|
93
|
-
draw: (context, frame) => {
|
|
94
|
-
const current = this.crosshair;
|
|
95
|
-
if (!current || current.sourceId === id) {
|
|
96
|
-
return;
|
|
97
|
-
}
|
|
98
|
-
const x = frame.timeToX(current.time);
|
|
99
|
-
if (x === null || x < frame.plot.left || x > frame.plot.right) {
|
|
100
|
-
return;
|
|
101
|
-
}
|
|
102
|
-
const crispX = Math.round(x) + 0.5;
|
|
103
|
-
context.save();
|
|
104
|
-
context.beginPath();
|
|
105
|
-
context.rect(frame.plot.left, frame.plot.top, frame.plot.width, frame.plot.height);
|
|
106
|
-
context.clip();
|
|
107
|
-
context.strokeStyle = this.crosshairColor;
|
|
108
|
-
context.lineWidth = 1;
|
|
109
|
-
context.setLineDash([3, 4]);
|
|
110
|
-
context.beginPath();
|
|
111
|
-
context.moveTo(crispX, frame.plot.top);
|
|
112
|
-
context.lineTo(crispX, frame.plot.bottom);
|
|
113
|
-
context.stroke();
|
|
114
|
-
context.restore();
|
|
115
|
-
}
|
|
116
|
-
});
|
|
117
|
-
this.crosshairOverlays.set(id, overlay);
|
|
118
|
-
}
|
|
119
89
|
const slot = {
|
|
120
90
|
id,
|
|
121
91
|
chart,
|
|
122
92
|
element,
|
|
123
93
|
rowSpan,
|
|
124
|
-
columnSpan
|
|
94
|
+
columnSpan,
|
|
95
|
+
syncGroup
|
|
125
96
|
};
|
|
126
|
-
|
|
97
|
+
const entry = {
|
|
127
98
|
slot,
|
|
128
99
|
unsubscribe: unsubscribes,
|
|
129
|
-
overlay,
|
|
100
|
+
overlay: null,
|
|
130
101
|
onPointerDown,
|
|
131
102
|
onFocusIn
|
|
132
|
-
}
|
|
103
|
+
};
|
|
104
|
+
this.slots.push(entry);
|
|
105
|
+
this.ensureCrosshairOverlay(entry);
|
|
133
106
|
if (this.activeId === null) {
|
|
134
107
|
this.setActiveChart(id);
|
|
135
108
|
}
|
|
@@ -174,6 +147,28 @@ var ChartLayout = class {
|
|
|
174
147
|
this.gap = validateGap(gap);
|
|
175
148
|
this.element.style.gap = `${this.gap}px`;
|
|
176
149
|
}
|
|
150
|
+
setSyncOptions(options) {
|
|
151
|
+
this.assertAlive();
|
|
152
|
+
const next = {
|
|
153
|
+
timeRange: options.timeRange ?? this.sync.timeRange,
|
|
154
|
+
crosshair: options.crosshair === void 0 ? this.sync.crosshair : normalizeCrosshair(options.crosshair)
|
|
155
|
+
};
|
|
156
|
+
if (next.timeRange === this.sync.timeRange && next.crosshair === this.sync.crosshair) {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
this.sync = next;
|
|
160
|
+
for (const entry of this.slots) {
|
|
161
|
+
this.ensureCrosshairOverlay(entry);
|
|
162
|
+
}
|
|
163
|
+
if (next.crosshair === "off" && this.crosshair !== null) {
|
|
164
|
+
this.crosshair = null;
|
|
165
|
+
}
|
|
166
|
+
this.requestCrosshairRender();
|
|
167
|
+
}
|
|
168
|
+
getSyncOptions() {
|
|
169
|
+
this.assertAlive();
|
|
170
|
+
return { ...this.sync };
|
|
171
|
+
}
|
|
177
172
|
setActiveChart(id) {
|
|
178
173
|
this.assertAlive();
|
|
179
174
|
if (id !== null && !this.findSlot(id)) {
|
|
@@ -213,7 +208,11 @@ var ChartLayout = class {
|
|
|
213
208
|
if (!source) {
|
|
214
209
|
return;
|
|
215
210
|
}
|
|
216
|
-
this.applyRange(
|
|
211
|
+
this.applyRange(
|
|
212
|
+
source.slot.id,
|
|
213
|
+
source.slot.syncGroup,
|
|
214
|
+
source.slot.chart.timeScale().getVisibleRange()
|
|
215
|
+
);
|
|
217
216
|
}
|
|
218
217
|
serialize() {
|
|
219
218
|
this.assertAlive();
|
|
@@ -221,10 +220,12 @@ var ChartLayout = class {
|
|
|
221
220
|
version: 1,
|
|
222
221
|
columns: this.columns,
|
|
223
222
|
gap: this.gap,
|
|
223
|
+
sync: { ...this.sync },
|
|
224
224
|
slots: this.slots.map((entry) => ({
|
|
225
225
|
id: entry.slot.id,
|
|
226
226
|
rowSpan: entry.slot.rowSpan,
|
|
227
|
-
columnSpan: entry.slot.columnSpan
|
|
227
|
+
columnSpan: entry.slot.columnSpan,
|
|
228
|
+
syncGroup: entry.slot.syncGroup
|
|
228
229
|
}))
|
|
229
230
|
};
|
|
230
231
|
}
|
|
@@ -251,19 +252,23 @@ var ChartLayout = class {
|
|
|
251
252
|
return this.slots.find((entry) => entry.slot.id === id);
|
|
252
253
|
}
|
|
253
254
|
handleVisibleRange(sourceId, event) {
|
|
254
|
-
if (!this.
|
|
255
|
+
if (!this.sync.timeRange || this.applyingRange) {
|
|
255
256
|
return;
|
|
256
257
|
}
|
|
257
|
-
this.
|
|
258
|
+
const source = this.findSlot(sourceId);
|
|
259
|
+
if (!source) {
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
this.applyRange(sourceId, source.slot.syncGroup, { from: event.from, to: event.to });
|
|
258
263
|
}
|
|
259
|
-
applyRange(sourceId, range) {
|
|
264
|
+
applyRange(sourceId, syncGroup, range) {
|
|
260
265
|
if (!Number.isFinite(range.from) || !Number.isFinite(range.to) || range.to <= range.from) {
|
|
261
266
|
return;
|
|
262
267
|
}
|
|
263
268
|
this.applyingRange = true;
|
|
264
269
|
try {
|
|
265
270
|
for (const entry of this.slots) {
|
|
266
|
-
if (entry.slot.id === sourceId) {
|
|
271
|
+
if (entry.slot.id === sourceId || entry.slot.syncGroup !== syncGroup) {
|
|
267
272
|
continue;
|
|
268
273
|
}
|
|
269
274
|
try {
|
|
@@ -276,7 +281,7 @@ var ChartLayout = class {
|
|
|
276
281
|
}
|
|
277
282
|
}
|
|
278
283
|
handleCrosshair(sourceId, event) {
|
|
279
|
-
if (
|
|
284
|
+
if (this.sync.crosshair === "off") {
|
|
280
285
|
return;
|
|
281
286
|
}
|
|
282
287
|
if (event.time === null || event.point === null) {
|
|
@@ -286,7 +291,18 @@ var ChartLayout = class {
|
|
|
286
291
|
}
|
|
287
292
|
return;
|
|
288
293
|
}
|
|
289
|
-
|
|
294
|
+
const source = this.findSlot(sourceId);
|
|
295
|
+
if (!source) {
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
const height = source.slot.element.clientHeight;
|
|
299
|
+
const yRatio = height > 0 ? Math.min(1, Math.max(0, event.point.y / height)) : null;
|
|
300
|
+
this.crosshair = {
|
|
301
|
+
sourceId,
|
|
302
|
+
sourceGroup: source.slot.syncGroup,
|
|
303
|
+
time: event.time,
|
|
304
|
+
yRatio
|
|
305
|
+
};
|
|
290
306
|
this.requestCrosshairRender();
|
|
291
307
|
}
|
|
292
308
|
requestCrosshairRender() {
|
|
@@ -294,6 +310,57 @@ var ChartLayout = class {
|
|
|
294
310
|
overlay.requestRender();
|
|
295
311
|
}
|
|
296
312
|
}
|
|
313
|
+
ensureCrosshairOverlay(entry) {
|
|
314
|
+
if (this.sync.crosshair === "off") {
|
|
315
|
+
if (entry.overlay) {
|
|
316
|
+
entry.overlay.remove();
|
|
317
|
+
entry.overlay = null;
|
|
318
|
+
this.crosshairOverlays.delete(entry.slot.id);
|
|
319
|
+
}
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
if (entry.overlay) {
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
const id = entry.slot.id;
|
|
326
|
+
const group = entry.slot.syncGroup;
|
|
327
|
+
entry.overlay = entry.slot.chart.addOverlay({
|
|
328
|
+
id: "wicklib-layout-crosshair",
|
|
329
|
+
draw: (context, frame) => this.drawCrosshair(context, frame, id, group)
|
|
330
|
+
});
|
|
331
|
+
this.crosshairOverlays.set(id, entry.overlay);
|
|
332
|
+
}
|
|
333
|
+
drawCrosshair(context, frame, id, group) {
|
|
334
|
+
const current = this.crosshair;
|
|
335
|
+
if (!current || current.sourceId === id || current.sourceGroup !== group || this.sync.crosshair === "off") {
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
const x = frame.timeToX(current.time);
|
|
339
|
+
if (x === null || x < frame.plot.left || x > frame.plot.right) {
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
context.save();
|
|
343
|
+
context.beginPath();
|
|
344
|
+
context.rect(frame.plot.left, frame.plot.top, frame.plot.width, frame.plot.height);
|
|
345
|
+
context.clip();
|
|
346
|
+
context.strokeStyle = this.crosshairColor;
|
|
347
|
+
context.lineWidth = 1;
|
|
348
|
+
context.setLineDash([3, 4]);
|
|
349
|
+
const crispX = Math.round(x) + 0.5;
|
|
350
|
+
context.beginPath();
|
|
351
|
+
context.moveTo(crispX, frame.plot.top);
|
|
352
|
+
context.lineTo(crispX, frame.plot.bottom);
|
|
353
|
+
context.stroke();
|
|
354
|
+
if (this.sync.crosshair === "full" && current.yRatio !== null) {
|
|
355
|
+
const y = frame.plot.top + current.yRatio * frame.plot.height;
|
|
356
|
+
const crispY = Math.round(y) + 0.5;
|
|
357
|
+
context.beginPath();
|
|
358
|
+
context.moveTo(frame.plot.left, crispY);
|
|
359
|
+
context.lineTo(frame.plot.right, crispY);
|
|
360
|
+
context.stroke();
|
|
361
|
+
}
|
|
362
|
+
context.restore();
|
|
363
|
+
}
|
|
297
364
|
disposeSlot(entry) {
|
|
298
365
|
entry.slot.element.removeEventListener("pointerdown", entry.onPointerDown, true);
|
|
299
366
|
entry.slot.element.removeEventListener("focusin", entry.onFocusIn);
|
|
@@ -301,6 +368,7 @@ var ChartLayout = class {
|
|
|
301
368
|
unsubscribe();
|
|
302
369
|
}
|
|
303
370
|
entry.overlay?.remove();
|
|
371
|
+
entry.overlay = null;
|
|
304
372
|
this.crosshairOverlays.delete(entry.slot.id);
|
|
305
373
|
entry.slot.chart.destroy();
|
|
306
374
|
entry.slot.element.remove();
|
|
@@ -311,6 +379,24 @@ var ChartLayout = class {
|
|
|
311
379
|
}
|
|
312
380
|
}
|
|
313
381
|
};
|
|
382
|
+
function resolveSyncOptions(options) {
|
|
383
|
+
return {
|
|
384
|
+
timeRange: options.sync?.timeRange ?? options.syncTimeRange ?? true,
|
|
385
|
+
crosshair: normalizeCrosshair(options.sync?.crosshair ?? options.syncCrosshair ?? "time")
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
function normalizeCrosshair(value) {
|
|
389
|
+
if (value === void 0 || value === true || value === "time") {
|
|
390
|
+
return "time";
|
|
391
|
+
}
|
|
392
|
+
if (value === false || value === "off") {
|
|
393
|
+
return "off";
|
|
394
|
+
}
|
|
395
|
+
if (value === "full") {
|
|
396
|
+
return "full";
|
|
397
|
+
}
|
|
398
|
+
throw new WickError("INVALID_ARGUMENT", "crosshair must be off, time, or full");
|
|
399
|
+
}
|
|
314
400
|
function mergeChartOptions(base, override) {
|
|
315
401
|
if (!base) {
|
|
316
402
|
return override;
|
|
@@ -338,6 +424,12 @@ function validateSpan(value, name) {
|
|
|
338
424
|
}
|
|
339
425
|
return value;
|
|
340
426
|
}
|
|
427
|
+
function validateSyncGroup(value) {
|
|
428
|
+
if (typeof value !== "string" || value.trim() === "") {
|
|
429
|
+
throw new WickError("INVALID_ARGUMENT", "syncGroup must be a non-empty string");
|
|
430
|
+
}
|
|
431
|
+
return value;
|
|
432
|
+
}
|
|
341
433
|
|
|
342
434
|
export { createChartLayout };
|
|
343
435
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"names":["entry"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Free multi-chart layout primitives for Wicklib.\n */\n\nimport {\n WickError,\n createChart,\n type ChartApi,\n type ChartOptions,\n type ChartOverlayApi,\n type CrosshairEvent,\n type Unsubscribe,\n type VisibleRangeEvent,\n} from \"@wicklib/core\";\n\n/**\n * Options for a layout chart cell.\n */\nexport interface ChartLayoutSlotOptions {\n /** Stable slot id. Generated when omitted. */\n id?: string;\n /** Chart options for this slot. Merged over the layout defaults. */\n chartOptions?: ChartOptions;\n /** Number of grid rows covered by the cell. Defaults to 1. */\n rowSpan?: number;\n /** Number of grid columns covered by the cell. Defaults to 1. */\n columnSpan?: number;\n}\n\n/**\n * One chart cell in a layout.\n */\nexport interface ChartLayoutSlot {\n readonly id: string;\n readonly chart: ChartApi;\n readonly element: HTMLElement;\n readonly rowSpan: number;\n readonly columnSpan: number;\n}\n\n/**\n * Serializable structural layout state. Chart state is owned by the host.\n */\nexport interface ChartLayoutSnapshot {\n version: 1;\n columns: number;\n gap: number;\n slots: ReadonlyArray<{\n id: string;\n rowSpan: number;\n columnSpan: number;\n }>;\n}\n\n/**\n * Options for a multi-chart layout.\n */\nexport interface ChartLayoutOptions {\n /** Number of grid columns. Defaults to 2. */\n columns?: number;\n /** Gap between cells in CSS pixels. Defaults to 1. */\n gap?: number;\n /** Default chart options applied to every slot. */\n chartOptions?: ChartOptions;\n /** Synchronize visible time ranges. Defaults to true. */\n syncTimeRange?: boolean;\n /** Synchronize the vertical crosshair time line. Defaults to true. */\n syncCrosshair?: boolean;\n /** Crosshair line colour. */\n crosshairColor?: string;\n /** Extra class name on the layout root. */\n className?: string;\n /** Called when the active chart changes. */\n onActiveChange?: (chartId: string | null) => void;\n}\n\n/**\n * A live multi-chart layout.\n */\nexport interface ChartLayoutApi {\n /** Layout root inserted into the host container. */\n readonly element: HTMLElement;\n /** Adds a chart cell and returns its live slot. */\n addChart(options?: ChartLayoutSlotOptions): ChartLayoutSlot;\n /** Destroys and removes one chart cell. */\n removeChart(id: string): void;\n /** Returns a live chart, or null when the id is unknown. */\n getChart(id: string): ChartApi | null;\n /** Returns the current slots in DOM order. */\n getSlots(): readonly ChartLayoutSlot[];\n /** Returns slot ids in DOM order. */\n chartIds(): readonly string[];\n /** Changes the grid column count. */\n setColumns(columns: number): void;\n /** Changes the cell gap in CSS pixels. */\n setGap(gap: number): void;\n /** Marks one chart as active. Pass null to clear. */\n setActiveChart(id: string | null): void;\n /** Returns the active chart id, or null. */\n activeChartId(): string | null;\n /** Subscribes to active chart changes. */\n onActiveChange(handler: (chartId: string | null) => void): Unsubscribe;\n /** Applies the active chart's visible range to every other chart. */\n syncNow(): void;\n /** Returns structural layout state for host persistence. */\n serialize(): ChartLayoutSnapshot;\n /** Destroys every chart and removes the layout root. */\n destroy(): void;\n}\n\ninterface InternalSlot {\n readonly slot: ChartLayoutSlot;\n readonly unsubscribe: readonly Unsubscribe[];\n readonly overlay: ChartOverlayApi | null;\n readonly onPointerDown: () => void;\n readonly onFocusIn: () => void;\n}\n\nconst DEFAULT_COLUMNS = 2;\nconst DEFAULT_GAP = 1;\nconst MAX_COLUMNS = 12;\nconst DEFAULT_CROSSHAIR_COLOR = \"rgba(139, 149, 167, 0.6)\";\n\n/**\n * Creates a framework-agnostic grid of Wicklib charts.\n *\n * The layout owns chart creation, grid placement, time-range synchronization, and a\n * mirrored crosshair line. It does not own data, symbol state, accounts, or persistence.\n */\nexport function createChartLayout(\n container: HTMLElement,\n options: ChartLayoutOptions = {},\n): ChartLayoutApi {\n return new ChartLayout(container, options);\n}\n\nclass ChartLayout implements ChartLayoutApi {\n readonly element: HTMLElement;\n\n private readonly slots: InternalSlot[] = [];\n private readonly activeHandlers = new Set<(chartId: string | null) => void>();\n private readonly crosshairOverlays = new Map<string, ChartOverlayApi>();\n private readonly chartOptions: ChartOptions | undefined;\n private readonly syncTimeRange: boolean;\n private readonly syncCrosshair: boolean;\n private readonly crosshairColor: string;\n private columns: number;\n private gap: number;\n private activeId: string | null = null;\n private counter = 0;\n private applyingRange = false;\n private crosshair: { sourceId: string; time: number } | null = null;\n private destroyed = false;\n\n constructor(container: HTMLElement, options: ChartLayoutOptions) {\n if (!container || typeof container.appendChild !== \"function\") {\n throw new WickError(\"INVALID_ARGUMENT\", \"createChartLayout requires a DOM container\");\n }\n\n this.columns = validateColumns(options.columns ?? DEFAULT_COLUMNS);\n this.gap = validateGap(options.gap ?? DEFAULT_GAP);\n this.chartOptions = options.chartOptions;\n this.syncTimeRange = options.syncTimeRange ?? true;\n this.syncCrosshair = options.syncCrosshair ?? true;\n this.crosshairColor = options.crosshairColor ?? DEFAULT_CROSSHAIR_COLOR;\n if (options.onActiveChange) {\n this.activeHandlers.add(options.onActiveChange);\n }\n\n this.element = document.createElement(\"div\");\n this.element.className = [\"wicklib-chart-layout\", options.className].filter(Boolean).join(\" \");\n Object.assign(this.element.style, {\n display: \"grid\",\n width: \"100%\",\n height: \"100%\",\n minWidth: \"0\",\n minHeight: \"0\",\n gridAutoRows: \"minmax(0, 1fr)\",\n gap: `${this.gap}px`,\n });\n this.applyGrid();\n container.appendChild(this.element);\n }\n\n addChart(options: ChartLayoutSlotOptions = {}): ChartLayoutSlot {\n this.assertAlive();\n const id = options.id ?? `chart-${++this.counter}`;\n if (this.findSlot(id)) {\n throw new WickError(\"INVALID_ARGUMENT\", `Duplicate chart layout id: ${id}`);\n }\n const rowSpan = validateSpan(options.rowSpan ?? 1, \"rowSpan\");\n const columnSpan = validateSpan(options.columnSpan ?? 1, \"columnSpan\");\n\n const element = document.createElement(\"div\");\n element.className = \"wicklib-chart-layout__cell\";\n element.dataset.chartId = id;\n Object.assign(element.style, {\n position: \"relative\",\n minWidth: \"0\",\n minHeight: \"0\",\n overflow: \"hidden\",\n gridRow: `span ${rowSpan}`,\n gridColumn: `span ${columnSpan}`,\n });\n this.element.appendChild(element);\n\n let chart: ChartApi;\n try {\n chart = createChart(element, mergeChartOptions(this.chartOptions, options.chartOptions));\n } catch (error) {\n element.remove();\n throw error;\n }\n\n const onPointerDown = (): void => this.setActiveChart(id);\n const onFocusIn = (): void => this.setActiveChart(id);\n element.addEventListener(\"pointerdown\", onPointerDown, true);\n element.addEventListener(\"focusin\", onFocusIn);\n\n const unsubscribes: Unsubscribe[] = [\n chart.on(\"visible-range\", (event) => this.handleVisibleRange(id, event)),\n chart.on(\"crosshair\", (event) => this.handleCrosshair(id, event)),\n ];\n\n let overlay: ChartOverlayApi | null = null;\n if (this.syncCrosshair) {\n overlay = chart.addOverlay({\n id: \"wicklib-layout-crosshair\",\n draw: (context, frame) => {\n const current = this.crosshair;\n if (!current || current.sourceId === id) {\n return;\n }\n const x = frame.timeToX(current.time);\n if (x === null || x < frame.plot.left || x > frame.plot.right) {\n return;\n }\n const crispX = Math.round(x) + 0.5;\n context.save();\n context.beginPath();\n context.rect(frame.plot.left, frame.plot.top, frame.plot.width, frame.plot.height);\n context.clip();\n context.strokeStyle = this.crosshairColor;\n context.lineWidth = 1;\n context.setLineDash([3, 4]);\n context.beginPath();\n context.moveTo(crispX, frame.plot.top);\n context.lineTo(crispX, frame.plot.bottom);\n context.stroke();\n context.restore();\n },\n });\n this.crosshairOverlays.set(id, overlay);\n }\n\n const slot: ChartLayoutSlot = {\n id,\n chart,\n element,\n rowSpan,\n columnSpan,\n };\n this.slots.push({\n slot,\n unsubscribe: unsubscribes,\n overlay,\n onPointerDown,\n onFocusIn,\n });\n\n if (this.activeId === null) {\n this.setActiveChart(id);\n }\n\n return slot;\n }\n\n removeChart(id: string): void {\n this.assertAlive();\n const index = this.slots.findIndex((entry) => entry.slot.id === id);\n if (index < 0) {\n return;\n }\n const entry = this.slots[index]!;\n this.disposeSlot(entry);\n this.slots.splice(index, 1);\n if (this.activeId === id) {\n this.setActiveChart(null);\n }\n if (this.crosshair?.sourceId === id) {\n this.crosshair = null;\n this.requestCrosshairRender();\n }\n }\n\n getChart(id: string): ChartApi | null {\n this.assertAlive();\n return this.findSlot(id)?.slot.chart ?? null;\n }\n\n getSlots(): readonly ChartLayoutSlot[] {\n this.assertAlive();\n return this.slots.map((entry) => entry.slot);\n }\n\n chartIds(): readonly string[] {\n this.assertAlive();\n return this.slots.map((entry) => entry.slot.id);\n }\n\n setColumns(columns: number): void {\n this.assertAlive();\n this.columns = validateColumns(columns);\n this.applyGrid();\n }\n\n setGap(gap: number): void {\n this.assertAlive();\n this.gap = validateGap(gap);\n this.element.style.gap = `${this.gap}px`;\n }\n\n setActiveChart(id: string | null): void {\n this.assertAlive();\n if (id !== null && !this.findSlot(id)) {\n throw new WickError(\"INVALID_ARGUMENT\", `Unknown chart layout id: ${id}`);\n }\n if (this.activeId === id) {\n return;\n }\n const previous = this.activeId;\n this.activeId = id;\n for (const entry of this.slots) {\n if (entry.slot.id === previous) {\n delete entry.slot.element.dataset.active;\n }\n if (entry.slot.id === id) {\n entry.slot.element.dataset.active = \"true\";\n }\n }\n for (const handler of this.activeHandlers) {\n handler(id);\n }\n }\n\n activeChartId(): string | null {\n this.assertAlive();\n return this.activeId;\n }\n\n onActiveChange(handler: (chartId: string | null) => void): Unsubscribe {\n this.assertAlive();\n this.activeHandlers.add(handler);\n return () => {\n this.activeHandlers.delete(handler);\n };\n }\n\n syncNow(): void {\n this.assertAlive();\n const source = this.findSlot(this.activeId ?? this.slots[0]?.slot.id ?? \"\");\n if (!source) {\n return;\n }\n this.applyRange(source.slot.id, source.slot.chart.timeScale().getVisibleRange());\n }\n\n serialize(): ChartLayoutSnapshot {\n this.assertAlive();\n return {\n version: 1,\n columns: this.columns,\n gap: this.gap,\n slots: this.slots.map((entry) => ({\n id: entry.slot.id,\n rowSpan: entry.slot.rowSpan,\n columnSpan: entry.slot.columnSpan,\n })),\n };\n }\n\n destroy(): void {\n if (this.destroyed) {\n return;\n }\n this.destroyed = true;\n for (const entry of this.slots) {\n this.disposeSlot(entry);\n }\n this.slots.length = 0;\n this.activeHandlers.clear();\n this.crosshairOverlays.clear();\n this.element.remove();\n }\n\n private applyGrid(): void {\n this.element.style.gridTemplateColumns = `repeat(${this.columns}, minmax(0, 1fr))`;\n }\n\n private findSlot(id: string | null): InternalSlot | undefined {\n if (id === null) {\n return undefined;\n }\n return this.slots.find((entry) => entry.slot.id === id);\n }\n\n private handleVisibleRange(sourceId: string, event: VisibleRangeEvent): void {\n if (!this.syncTimeRange || this.applyingRange) {\n return;\n }\n this.applyRange(sourceId, { from: event.from, to: event.to });\n }\n\n private applyRange(sourceId: string, range: { from: number; to: number }): void {\n if (!Number.isFinite(range.from) || !Number.isFinite(range.to) || range.to <= range.from) {\n return;\n }\n this.applyingRange = true;\n try {\n for (const entry of this.slots) {\n if (entry.slot.id === sourceId) {\n continue;\n }\n try {\n entry.slot.chart.timeScale().setVisibleRange(range);\n } catch {\n // A chart with incompatible data can reject the range; leave it unchanged.\n }\n }\n } finally {\n this.applyingRange = false;\n }\n }\n\n private handleCrosshair(sourceId: string, event: CrosshairEvent): void {\n if (!this.syncCrosshair) {\n return;\n }\n if (event.time === null || event.point === null) {\n if (this.crosshair !== null) {\n this.crosshair = null;\n this.requestCrosshairRender();\n }\n return;\n }\n this.crosshair = { sourceId, time: event.time };\n this.requestCrosshairRender();\n }\n\n private requestCrosshairRender(): void {\n for (const overlay of this.crosshairOverlays.values()) {\n overlay.requestRender();\n }\n }\n\n private disposeSlot(entry: InternalSlot): void {\n entry.slot.element.removeEventListener(\"pointerdown\", entry.onPointerDown, true);\n entry.slot.element.removeEventListener(\"focusin\", entry.onFocusIn);\n for (const unsubscribe of entry.unsubscribe) {\n unsubscribe();\n }\n entry.overlay?.remove();\n this.crosshairOverlays.delete(entry.slot.id);\n entry.slot.chart.destroy();\n entry.slot.element.remove();\n }\n\n private assertAlive(): void {\n if (this.destroyed) {\n throw new WickError(\"DISPOSED\", \"Chart layout is disposed\");\n }\n }\n}\n\nfunction mergeChartOptions(\n base: ChartOptions | undefined,\n override: ChartOptions | undefined,\n): ChartOptions | undefined {\n if (!base) {\n return override;\n }\n if (!override) {\n return base;\n }\n return { ...base, ...override };\n}\n\nfunction validateColumns(columns: number): number {\n if (!Number.isInteger(columns) || columns < 1 || columns > MAX_COLUMNS) {\n throw new WickError(\"INVALID_ARGUMENT\", `columns must be an integer from 1 to ${MAX_COLUMNS}`);\n }\n return columns;\n}\n\nfunction validateGap(gap: number): number {\n if (!Number.isFinite(gap) || gap < 0) {\n throw new WickError(\"INVALID_ARGUMENT\", \"gap must be a non-negative finite number\");\n }\n return gap;\n}\n\nfunction validateSpan(value: number, name: string): number {\n if (!Number.isInteger(value) || value < 1) {\n throw new WickError(\"INVALID_ARGUMENT\", `${name} must be a positive integer`);\n }\n return value;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":["entry"],"mappings":";;;AAqKA,IAAM,eAAA,GAAkB,CAAA;AACxB,IAAM,WAAA,GAAc,CAAA;AACpB,IAAM,kBAAA,GAAqB,SAAA;AAC3B,IAAM,WAAA,GAAc,EAAA;AACpB,IAAM,uBAAA,GAA0B,0BAAA;AAQzB,SAAS,iBAAA,CACd,SAAA,EACA,OAAA,GAA8B,EAAC,EACf;AAChB,EAAA,OAAO,IAAI,WAAA,CAAY,SAAA,EAAW,OAAO,CAAA;AAC3C;AAEA,IAAM,cAAN,MAA4C;AAAA,EACjC,OAAA;AAAA,EAEQ,QAAwB,EAAC;AAAA,EACzB,cAAA,uBAAqB,GAAA,EAAsC;AAAA,EAC3D,iBAAA,uBAAwB,GAAA,EAA6B;AAAA,EACrD,YAAA;AAAA,EACA,cAAA;AAAA,EACT,OAAA;AAAA,EACA,GAAA;AAAA,EACA,IAAA;AAAA,EACA,QAAA,GAA0B,IAAA;AAAA,EAC1B,OAAA,GAAU,CAAA;AAAA,EACV,aAAA,GAAgB,KAAA;AAAA,EAChB,SAAA,GAAmC,IAAA;AAAA,EACnC,SAAA,GAAY,KAAA;AAAA,EAEpB,WAAA,CAAY,WAAwB,OAAA,EAA6B;AAC/D,IAAA,IAAI,CAAC,SAAA,IAAa,OAAO,SAAA,CAAU,gBAAgB,UAAA,EAAY;AAC7D,MAAA,MAAM,IAAI,SAAA,CAAU,kBAAA,EAAoB,4CAA4C,CAAA;AAAA,IACtF;AAEA,IAAA,IAAA,CAAK,OAAA,GAAU,eAAA,CAAgB,OAAA,CAAQ,OAAA,IAAW,eAAe,CAAA;AACjE,IAAA,IAAA,CAAK,GAAA,GAAM,WAAA,CAAY,OAAA,CAAQ,GAAA,IAAO,WAAW,CAAA;AACjD,IAAA,IAAA,CAAK,eAAe,OAAA,CAAQ,YAAA;AAC5B,IAAA,IAAA,CAAK,IAAA,GAAO,mBAAmB,OAAO,CAAA;AACtC,IAAA,IAAA,CAAK,cAAA,GAAiB,QAAQ,cAAA,IAAkB,uBAAA;AAChD,IAAA,IAAI,QAAQ,cAAA,EAAgB;AAC1B,MAAA,IAAA,CAAK,cAAA,CAAe,GAAA,CAAI,OAAA,CAAQ,cAAc,CAAA;AAAA,IAChD;AAEA,IAAA,IAAA,CAAK,OAAA,GAAU,QAAA,CAAS,aAAA,CAAc,KAAK,CAAA;AAC3C,IAAA,IAAA,CAAK,OAAA,CAAQ,SAAA,GAAY,CAAC,sBAAA,EAAwB,OAAA,CAAQ,SAAS,CAAA,CAAE,MAAA,CAAO,OAAO,CAAA,CAAE,IAAA,CAAK,GAAG,CAAA;AAC7F,IAAA,MAAA,CAAO,MAAA,CAAO,IAAA,CAAK,OAAA,CAAQ,KAAA,EAAO;AAAA,MAChC,OAAA,EAAS,MAAA;AAAA,MACT,KAAA,EAAO,MAAA;AAAA,MACP,MAAA,EAAQ,MAAA;AAAA,MACR,QAAA,EAAU,GAAA;AAAA,MACV,SAAA,EAAW,GAAA;AAAA,MACX,YAAA,EAAc,gBAAA;AAAA,MACd,GAAA,EAAK,CAAA,EAAG,IAAA,CAAK,GAAG,CAAA,EAAA;AAAA,KACjB,CAAA;AACD,IAAA,IAAA,CAAK,SAAA,EAAU;AACf,IAAA,SAAA,CAAU,WAAA,CAAY,KAAK,OAAO,CAAA;AAAA,EACpC;AAAA,EAEA,QAAA,CAAS,OAAA,GAAkC,EAAC,EAAoB;AAC9D,IAAA,IAAA,CAAK,WAAA,EAAY;AACjB,IAAA,MAAM,KAAK,OAAA,CAAQ,EAAA,IAAM,CAAA,MAAA,EAAS,EAAE,KAAK,OAAO,CAAA,CAAA;AAChD,IAAA,IAAI,IAAA,CAAK,QAAA,CAAS,EAAE,CAAA,EAAG;AACrB,MAAA,MAAM,IAAI,SAAA,CAAU,kBAAA,EAAoB,CAAA,2BAAA,EAA8B,EAAE,CAAA,CAAE,CAAA;AAAA,IAC5E;AACA,IAAA,MAAM,OAAA,GAAU,YAAA,CAAa,OAAA,CAAQ,OAAA,IAAW,GAAG,SAAS,CAAA;AAC5D,IAAA,MAAM,UAAA,GAAa,YAAA,CAAa,OAAA,CAAQ,UAAA,IAAc,GAAG,YAAY,CAAA;AACrE,IAAA,MAAM,SAAA,GAAY,iBAAA,CAAkB,OAAA,CAAQ,SAAA,IAAa,kBAAkB,CAAA;AAE3E,IAAA,MAAM,OAAA,GAAU,QAAA,CAAS,aAAA,CAAc,KAAK,CAAA;AAC5C,IAAA,OAAA,CAAQ,SAAA,GAAY,4BAAA;AACpB,IAAA,OAAA,CAAQ,QAAQ,OAAA,GAAU,EAAA;AAC1B,IAAA,MAAA,CAAO,MAAA,CAAO,QAAQ,KAAA,EAAO;AAAA,MAC3B,QAAA,EAAU,UAAA;AAAA,MACV,QAAA,EAAU,GAAA;AAAA,MACV,SAAA,EAAW,GAAA;AAAA,MACX,QAAA,EAAU,QAAA;AAAA,MACV,OAAA,EAAS,QAAQ,OAAO,CAAA,CAAA;AAAA,MACxB,UAAA,EAAY,QAAQ,UAAU,CAAA;AAAA,KAC/B,CAAA;AACD,IAAA,IAAA,CAAK,OAAA,CAAQ,YAAY,OAAO,CAAA;AAEhC,IAAA,IAAI,KAAA;AACJ,IAAA,IAAI;AACF,MAAA,KAAA,GAAQ,YAAY,OAAA,EAAS,iBAAA,CAAkB,KAAK,YAAA,EAAc,OAAA,CAAQ,YAAY,CAAC,CAAA;AAAA,IACzF,SAAS,KAAA,EAAO;AACd,MAAA,OAAA,CAAQ,MAAA,EAAO;AACf,MAAA,MAAM,KAAA;AAAA,IACR;AAEA,IAAA,MAAM,aAAA,GAAgB,MAAY,IAAA,CAAK,cAAA,CAAe,EAAE,CAAA;AACxD,IAAA,MAAM,SAAA,GAAY,MAAY,IAAA,CAAK,cAAA,CAAe,EAAE,CAAA;AACpD,IAAA,OAAA,CAAQ,gBAAA,CAAiB,aAAA,EAAe,aAAA,EAAe,IAAI,CAAA;AAC3D,IAAA,OAAA,CAAQ,gBAAA,CAAiB,WAAW,SAAS,CAAA;AAE7C,IAAA,MAAM,YAAA,GAA8B;AAAA,MAClC,KAAA,CAAM,GAAG,eAAA,EAAiB,CAAC,UAAU,IAAA,CAAK,kBAAA,CAAmB,EAAA,EAAI,KAAK,CAAC,CAAA;AAAA,MACvE,KAAA,CAAM,GAAG,WAAA,EAAa,CAAC,UAAU,IAAA,CAAK,eAAA,CAAgB,EAAA,EAAI,KAAK,CAAC;AAAA,KAClE;AAEA,IAAA,MAAM,IAAA,GAAwB;AAAA,MAC5B,EAAA;AAAA,MACA,KAAA;AAAA,MACA,OAAA;AAAA,MACA,OAAA;AAAA,MACA,UAAA;AAAA,MACA;AAAA,KACF;AACA,IAAA,MAAM,KAAA,GAAsB;AAAA,MAC1B,IAAA;AAAA,MACA,WAAA,EAAa,YAAA;AAAA,MACb,OAAA,EAAS,IAAA;AAAA,MACT,aAAA;AAAA,MACA;AAAA,KACF;AACA,IAAA,IAAA,CAAK,KAAA,CAAM,KAAK,KAAK,CAAA;AACrB,IAAA,IAAA,CAAK,uBAAuB,KAAK,CAAA;AAEjC,IAAA,IAAI,IAAA,CAAK,aAAa,IAAA,EAAM;AAC1B,MAAA,IAAA,CAAK,eAAe,EAAE,CAAA;AAAA,IACxB;AAEA,IAAA,OAAO,IAAA;AAAA,EACT;AAAA,EAEA,YAAY,EAAA,EAAkB;AAC5B,IAAA,IAAA,CAAK,WAAA,EAAY;AACjB,IAAA,MAAM,KAAA,GAAQ,KAAK,KAAA,CAAM,SAAA,CAAU,CAACA,MAAAA,KAAUA,MAAAA,CAAM,IAAA,CAAK,EAAA,KAAO,EAAE,CAAA;AAClE,IAAA,IAAI,QAAQ,CAAA,EAAG;AACb,MAAA;AAAA,IACF;AACA,IAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,KAAA,CAAM,KAAK,CAAA;AAC9B,IAAA,IAAA,CAAK,YAAY,KAAK,CAAA;AACtB,IAAA,IAAA,CAAK,KAAA,CAAM,MAAA,CAAO,KAAA,EAAO,CAAC,CAAA;AAC1B,IAAA,IAAI,IAAA,CAAK,aAAa,EAAA,EAAI;AACxB,MAAA,IAAA,CAAK,eAAe,IAAI,CAAA;AAAA,IAC1B;AACA,IAAA,IAAI,IAAA,CAAK,SAAA,EAAW,QAAA,KAAa,EAAA,EAAI;AACnC,MAAA,IAAA,CAAK,SAAA,GAAY,IAAA;AACjB,MAAA,IAAA,CAAK,sBAAA,EAAuB;AAAA,IAC9B;AAAA,EACF;AAAA,EAEA,SAAS,EAAA,EAA6B;AACpC,IAAA,IAAA,CAAK,WAAA,EAAY;AACjB,IAAA,OAAO,IAAA,CAAK,QAAA,CAAS,EAAE,CAAA,EAAG,KAAK,KAAA,IAAS,IAAA;AAAA,EAC1C;AAAA,EAEA,QAAA,GAAuC;AACrC,IAAA,IAAA,CAAK,WAAA,EAAY;AACjB,IAAA,OAAO,KAAK,KAAA,CAAM,GAAA,CAAI,CAAC,KAAA,KAAU,MAAM,IAAI,CAAA;AAAA,EAC7C;AAAA,EAEA,QAAA,GAA8B;AAC5B,IAAA,IAAA,CAAK,WAAA,EAAY;AACjB,IAAA,OAAO,KAAK,KAAA,CAAM,GAAA,CAAI,CAAC,KAAA,KAAU,KAAA,CAAM,KAAK,EAAE,CAAA;AAAA,EAChD;AAAA,EAEA,WAAW,OAAA,EAAuB;AAChC,IAAA,IAAA,CAAK,WAAA,EAAY;AACjB,IAAA,IAAA,CAAK,OAAA,GAAU,gBAAgB,OAAO,CAAA;AACtC,IAAA,IAAA,CAAK,SAAA,EAAU;AAAA,EACjB;AAAA,EAEA,OAAO,GAAA,EAAmB;AACxB,IAAA,IAAA,CAAK,WAAA,EAAY;AACjB,IAAA,IAAA,CAAK,GAAA,GAAM,YAAY,GAAG,CAAA;AAC1B,IAAA,IAAA,CAAK,OAAA,CAAQ,KAAA,CAAM,GAAA,GAAM,CAAA,EAAG,KAAK,GAAG,CAAA,EAAA,CAAA;AAAA,EACtC;AAAA,EAEA,eAAe,OAAA,EAAsC;AACnD,IAAA,IAAA,CAAK,WAAA,EAAY;AACjB,IAAA,MAAM,IAAA,GAA+B;AAAA,MACnC,SAAA,EAAW,OAAA,CAAQ,SAAA,IAAa,IAAA,CAAK,IAAA,CAAK,SAAA;AAAA,MAC1C,SAAA,EACE,QAAQ,SAAA,KAAc,MAAA,GAClB,KAAK,IAAA,CAAK,SAAA,GACV,kBAAA,CAAmB,OAAA,CAAQ,SAAS;AAAA,KAC5C;AACA,IAAA,IAAI,IAAA,CAAK,cAAc,IAAA,CAAK,IAAA,CAAK,aAAa,IAAA,CAAK,SAAA,KAAc,IAAA,CAAK,IAAA,CAAK,SAAA,EAAW;AACpF,MAAA;AAAA,IACF;AACA,IAAA,IAAA,CAAK,IAAA,GAAO,IAAA;AACZ,IAAA,KAAA,MAAW,KAAA,IAAS,KAAK,KAAA,EAAO;AAC9B,MAAA,IAAA,CAAK,uBAAuB,KAAK,CAAA;AAAA,IACnC;AACA,IAAA,IAAI,IAAA,CAAK,SAAA,KAAc,KAAA,IAAS,IAAA,CAAK,cAAc,IAAA,EAAM;AACvD,MAAA,IAAA,CAAK,SAAA,GAAY,IAAA;AAAA,IACnB;AACA,IAAA,IAAA,CAAK,sBAAA,EAAuB;AAAA,EAC9B;AAAA,EAEA,cAAA,GAAmD;AACjD,IAAA,IAAA,CAAK,WAAA,EAAY;AACjB,IAAA,OAAO,EAAE,GAAG,IAAA,CAAK,IAAA,EAAK;AAAA,EACxB;AAAA,EAEA,eAAe,EAAA,EAAyB;AACtC,IAAA,IAAA,CAAK,WAAA,EAAY;AACjB,IAAA,IAAI,OAAO,IAAA,IAAQ,CAAC,IAAA,CAAK,QAAA,CAAS,EAAE,CAAA,EAAG;AACrC,MAAA,MAAM,IAAI,SAAA,CAAU,kBAAA,EAAoB,CAAA,yBAAA,EAA4B,EAAE,CAAA,CAAE,CAAA;AAAA,IAC1E;AACA,IAAA,IAAI,IAAA,CAAK,aAAa,EAAA,EAAI;AACxB,MAAA;AAAA,IACF;AACA,IAAA,MAAM,WAAW,IAAA,CAAK,QAAA;AACtB,IAAA,IAAA,CAAK,QAAA,GAAW,EAAA;AAChB,IAAA,KAAA,MAAW,KAAA,IAAS,KAAK,KAAA,EAAO;AAC9B,MAAA,IAAI,KAAA,CAAM,IAAA,CAAK,EAAA,KAAO,QAAA,EAAU;AAC9B,QAAA,OAAO,KAAA,CAAM,IAAA,CAAK,OAAA,CAAQ,OAAA,CAAQ,MAAA;AAAA,MACpC;AACA,MAAA,IAAI,KAAA,CAAM,IAAA,CAAK,EAAA,KAAO,EAAA,EAAI;AACxB,QAAA,KAAA,CAAM,IAAA,CAAK,OAAA,CAAQ,OAAA,CAAQ,MAAA,GAAS,MAAA;AAAA,MACtC;AAAA,IACF;AACA,IAAA,KAAA,MAAW,OAAA,IAAW,KAAK,cAAA,EAAgB;AACzC,MAAA,OAAA,CAAQ,EAAE,CAAA;AAAA,IACZ;AAAA,EACF;AAAA,EAEA,aAAA,GAA+B;AAC7B,IAAA,IAAA,CAAK,WAAA,EAAY;AACjB,IAAA,OAAO,IAAA,CAAK,QAAA;AAAA,EACd;AAAA,EAEA,eAAe,OAAA,EAAwD;AACrE,IAAA,IAAA,CAAK,WAAA,EAAY;AACjB,IAAA,IAAA,CAAK,cAAA,CAAe,IAAI,OAAO,CAAA;AAC/B,IAAA,OAAO,MAAM;AACX,MAAA,IAAA,CAAK,cAAA,CAAe,OAAO,OAAO,CAAA;AAAA,IACpC,CAAA;AAAA,EACF;AAAA,EAEA,OAAA,GAAgB;AACd,IAAA,IAAA,CAAK,WAAA,EAAY;AACjB,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,QAAA,CAAS,IAAA,CAAK,QAAA,IAAY,IAAA,CAAK,KAAA,CAAM,CAAC,CAAA,EAAG,IAAA,CAAK,EAAA,IAAM,EAAE,CAAA;AAC1E,IAAA,IAAI,CAAC,MAAA,EAAQ;AACX,MAAA;AAAA,IACF;AACA,IAAA,IAAA,CAAK,UAAA;AAAA,MACH,OAAO,IAAA,CAAK,EAAA;AAAA,MACZ,OAAO,IAAA,CAAK,SAAA;AAAA,MACZ,MAAA,CAAO,IAAA,CAAK,KAAA,CAAM,SAAA,GAAY,eAAA;AAAgB,KAChD;AAAA,EACF;AAAA,EAEA,SAAA,GAAiC;AAC/B,IAAA,IAAA,CAAK,WAAA,EAAY;AACjB,IAAA,OAAO;AAAA,MACL,OAAA,EAAS,CAAA;AAAA,MACT,SAAS,IAAA,CAAK,OAAA;AAAA,MACd,KAAK,IAAA,CAAK,GAAA;AAAA,MACV,IAAA,EAAM,EAAE,GAAG,IAAA,CAAK,IAAA,EAAK;AAAA,MACrB,KAAA,EAAO,IAAA,CAAK,KAAA,CAAM,GAAA,CAAI,CAAC,KAAA,MAAW;AAAA,QAChC,EAAA,EAAI,MAAM,IAAA,CAAK,EAAA;AAAA,QACf,OAAA,EAAS,MAAM,IAAA,CAAK,OAAA;AAAA,QACpB,UAAA,EAAY,MAAM,IAAA,CAAK,UAAA;AAAA,QACvB,SAAA,EAAW,MAAM,IAAA,CAAK;AAAA,OACxB,CAAE;AAAA,KACJ;AAAA,EACF;AAAA,EAEA,OAAA,GAAgB;AACd,IAAA,IAAI,KAAK,SAAA,EAAW;AAClB,MAAA;AAAA,IACF;AACA,IAAA,IAAA,CAAK,SAAA,GAAY,IAAA;AACjB,IAAA,KAAA,MAAW,KAAA,IAAS,KAAK,KAAA,EAAO;AAC9B,MAAA,IAAA,CAAK,YAAY,KAAK,CAAA;AAAA,IACxB;AACA,IAAA,IAAA,CAAK,MAAM,MAAA,GAAS,CAAA;AACpB,IAAA,IAAA,CAAK,eAAe,KAAA,EAAM;AAC1B,IAAA,IAAA,CAAK,kBAAkB,KAAA,EAAM;AAC7B,IAAA,IAAA,CAAK,QAAQ,MAAA,EAAO;AAAA,EACtB;AAAA,EAEQ,SAAA,GAAkB;AACxB,IAAA,IAAA,CAAK,OAAA,CAAQ,KAAA,CAAM,mBAAA,GAAsB,CAAA,OAAA,EAAU,KAAK,OAAO,CAAA,iBAAA,CAAA;AAAA,EACjE;AAAA,EAEQ,SAAS,EAAA,EAA6C;AAC5D,IAAA,IAAI,OAAO,IAAA,EAAM;AACf,MAAA,OAAO,MAAA;AAAA,IACT;AACA,IAAA,OAAO,IAAA,CAAK,MAAM,IAAA,CAAK,CAAC,UAAU,KAAA,CAAM,IAAA,CAAK,OAAO,EAAE,CAAA;AAAA,EACxD;AAAA,EAEQ,kBAAA,CAAmB,UAAkB,KAAA,EAAgC;AAC3E,IAAA,IAAI,CAAC,IAAA,CAAK,IAAA,CAAK,SAAA,IAAa,KAAK,aAAA,EAAe;AAC9C,MAAA;AAAA,IACF;AACA,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,QAAA,CAAS,QAAQ,CAAA;AACrC,IAAA,IAAI,CAAC,MAAA,EAAQ;AACX,MAAA;AAAA,IACF;AACA,IAAA,IAAA,CAAK,UAAA,CAAW,QAAA,EAAU,MAAA,CAAO,IAAA,CAAK,SAAA,EAAW,EAAE,IAAA,EAAM,KAAA,CAAM,IAAA,EAAM,EAAA,EAAI,KAAA,CAAM,EAAA,EAAI,CAAA;AAAA,EACrF;AAAA,EAEQ,UAAA,CACN,QAAA,EACA,SAAA,EACA,KAAA,EACM;AACN,IAAA,IAAI,CAAC,MAAA,CAAO,QAAA,CAAS,KAAA,CAAM,IAAI,CAAA,IAAK,CAAC,MAAA,CAAO,QAAA,CAAS,MAAM,EAAE,CAAA,IAAK,KAAA,CAAM,EAAA,IAAM,MAAM,IAAA,EAAM;AACxF,MAAA;AAAA,IACF;AACA,IAAA,IAAA,CAAK,aAAA,GAAgB,IAAA;AACrB,IAAA,IAAI;AACF,MAAA,KAAA,MAAW,KAAA,IAAS,KAAK,KAAA,EAAO;AAC9B,QAAA,IAAI,MAAM,IAAA,CAAK,EAAA,KAAO,YAAY,KAAA,CAAM,IAAA,CAAK,cAAc,SAAA,EAAW;AACpE,UAAA;AAAA,QACF;AACA,QAAA,IAAI;AACF,UAAA,KAAA,CAAM,IAAA,CAAK,KAAA,CAAM,SAAA,EAAU,CAAE,gBAAgB,KAAK,CAAA;AAAA,QACpD,CAAA,CAAA,MAAQ;AAAA,QAER;AAAA,MACF;AAAA,IACF,CAAA,SAAE;AACA,MAAA,IAAA,CAAK,aAAA,GAAgB,KAAA;AAAA,IACvB;AAAA,EACF;AAAA,EAEQ,eAAA,CAAgB,UAAkB,KAAA,EAA6B;AACrE,IAAA,IAAI,IAAA,CAAK,IAAA,CAAK,SAAA,KAAc,KAAA,EAAO;AACjC,MAAA;AAAA,IACF;AACA,IAAA,IAAI,KAAA,CAAM,IAAA,KAAS,IAAA,IAAQ,KAAA,CAAM,UAAU,IAAA,EAAM;AAC/C,MAAA,IAAI,IAAA,CAAK,cAAc,IAAA,EAAM;AAC3B,QAAA,IAAA,CAAK,SAAA,GAAY,IAAA;AACjB,QAAA,IAAA,CAAK,sBAAA,EAAuB;AAAA,MAC9B;AACA,MAAA;AAAA,IACF;AACA,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,QAAA,CAAS,QAAQ,CAAA;AACrC,IAAA,IAAI,CAAC,MAAA,EAAQ;AACX,MAAA;AAAA,IACF;AACA,IAAA,MAAM,MAAA,GAAS,MAAA,CAAO,IAAA,CAAK,OAAA,CAAQ,YAAA;AACnC,IAAA,MAAM,MAAA,GAAS,MAAA,GAAS,CAAA,GAAI,IAAA,CAAK,IAAI,CAAA,EAAG,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,KAAA,CAAM,KAAA,CAAM,CAAA,GAAI,MAAM,CAAC,CAAA,GAAI,IAAA;AAC/E,IAAA,IAAA,CAAK,SAAA,GAAY;AAAA,MACf,QAAA;AAAA,MACA,WAAA,EAAa,OAAO,IAAA,CAAK,SAAA;AAAA,MACzB,MAAM,KAAA,CAAM,IAAA;AAAA,MACZ;AAAA,KACF;AACA,IAAA,IAAA,CAAK,sBAAA,EAAuB;AAAA,EAC9B;AAAA,EAEQ,sBAAA,GAA+B;AACrC,IAAA,KAAA,MAAW,OAAA,IAAW,IAAA,CAAK,iBAAA,CAAkB,MAAA,EAAO,EAAG;AACrD,MAAA,OAAA,CAAQ,aAAA,EAAc;AAAA,IACxB;AAAA,EACF;AAAA,EAEQ,uBAAuB,KAAA,EAA2B;AACxD,IAAA,IAAI,IAAA,CAAK,IAAA,CAAK,SAAA,KAAc,KAAA,EAAO;AACjC,MAAA,IAAI,MAAM,OAAA,EAAS;AACjB,QAAA,KAAA,CAAM,QAAQ,MAAA,EAAO;AACrB,QAAA,KAAA,CAAM,OAAA,GAAU,IAAA;AAChB,QAAA,IAAA,CAAK,iBAAA,CAAkB,MAAA,CAAO,KAAA,CAAM,IAAA,CAAK,EAAE,CAAA;AAAA,MAC7C;AACA,MAAA;AAAA,IACF;AACA,IAAA,IAAI,MAAM,OAAA,EAAS;AACjB,MAAA;AAAA,IACF;AACA,IAAA,MAAM,EAAA,GAAK,MAAM,IAAA,CAAK,EAAA;AACtB,IAAA,MAAM,KAAA,GAAQ,MAAM,IAAA,CAAK,SAAA;AACzB,IAAA,KAAA,CAAM,OAAA,GAAU,KAAA,CAAM,IAAA,CAAK,KAAA,CAAM,UAAA,CAAW;AAAA,MAC1C,EAAA,EAAI,0BAAA;AAAA,MACJ,IAAA,EAAM,CAAC,OAAA,EAAS,KAAA,KAAU,KAAK,aAAA,CAAc,OAAA,EAAS,KAAA,EAAO,EAAA,EAAI,KAAK;AAAA,KACvE,CAAA;AACD,IAAA,IAAA,CAAK,iBAAA,CAAkB,GAAA,CAAI,EAAA,EAAI,KAAA,CAAM,OAAO,CAAA;AAAA,EAC9C;AAAA,EAEQ,aAAA,CACN,OAAA,EACA,KAAA,EACA,EAAA,EACA,KAAA,EACM;AACN,IAAA,MAAM,UAAU,IAAA,CAAK,SAAA;AACrB,IAAA,IACE,CAAC,OAAA,IACD,OAAA,CAAQ,QAAA,KAAa,EAAA,IACrB,OAAA,CAAQ,WAAA,KAAgB,KAAA,IACxB,IAAA,CAAK,IAAA,CAAK,SAAA,KAAc,KAAA,EACxB;AACA,MAAA;AAAA,IACF;AACA,IAAA,MAAM,CAAA,GAAI,KAAA,CAAM,OAAA,CAAQ,OAAA,CAAQ,IAAI,CAAA;AACpC,IAAA,IAAI,CAAA,KAAM,QAAQ,CAAA,GAAI,KAAA,CAAM,KAAK,IAAA,IAAQ,CAAA,GAAI,KAAA,CAAM,IAAA,CAAK,KAAA,EAAO;AAC7D,MAAA;AAAA,IACF;AACA,IAAA,OAAA,CAAQ,IAAA,EAAK;AACb,IAAA,OAAA,CAAQ,SAAA,EAAU;AAClB,IAAA,OAAA,CAAQ,IAAA,CAAK,KAAA,CAAM,IAAA,CAAK,IAAA,EAAM,KAAA,CAAM,IAAA,CAAK,GAAA,EAAK,KAAA,CAAM,IAAA,CAAK,KAAA,EAAO,KAAA,CAAM,IAAA,CAAK,MAAM,CAAA;AACjF,IAAA,OAAA,CAAQ,IAAA,EAAK;AACb,IAAA,OAAA,CAAQ,cAAc,IAAA,CAAK,cAAA;AAC3B,IAAA,OAAA,CAAQ,SAAA,GAAY,CAAA;AACpB,IAAA,OAAA,CAAQ,WAAA,CAAY,CAAC,CAAA,EAAG,CAAC,CAAC,CAAA;AAE1B,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,KAAA,CAAM,CAAC,CAAA,GAAI,GAAA;AAC/B,IAAA,OAAA,CAAQ,SAAA,EAAU;AAClB,IAAA,OAAA,CAAQ,MAAA,CAAO,MAAA,EAAQ,KAAA,CAAM,IAAA,CAAK,GAAG,CAAA;AACrC,IAAA,OAAA,CAAQ,MAAA,CAAO,MAAA,EAAQ,KAAA,CAAM,IAAA,CAAK,MAAM,CAAA;AACxC,IAAA,OAAA,CAAQ,MAAA,EAAO;AAEf,IAAA,IAAI,KAAK,IAAA,CAAK,SAAA,KAAc,MAAA,IAAU,OAAA,CAAQ,WAAW,IAAA,EAAM;AAC7D,MAAA,MAAM,IAAI,KAAA,CAAM,IAAA,CAAK,MAAM,OAAA,CAAQ,MAAA,GAAS,MAAM,IAAA,CAAK,MAAA;AACvD,MAAA,MAAM,MAAA,GAAS,IAAA,CAAK,KAAA,CAAM,CAAC,CAAA,GAAI,GAAA;AAC/B,MAAA,OAAA,CAAQ,SAAA,EAAU;AAClB,MAAA,OAAA,CAAQ,MAAA,CAAO,KAAA,CAAM,IAAA,CAAK,IAAA,EAAM,MAAM,CAAA;AACtC,MAAA,OAAA,CAAQ,MAAA,CAAO,KAAA,CAAM,IAAA,CAAK,KAAA,EAAO,MAAM,CAAA;AACvC,MAAA,OAAA,CAAQ,MAAA,EAAO;AAAA,IACjB;AAEA,IAAA,OAAA,CAAQ,OAAA,EAAQ;AAAA,EAClB;AAAA,EAEQ,YAAY,KAAA,EAA2B;AAC7C,IAAA,KAAA,CAAM,KAAK,OAAA,CAAQ,mBAAA,CAAoB,aAAA,EAAe,KAAA,CAAM,eAAe,IAAI,CAAA;AAC/E,IAAA,KAAA,CAAM,IAAA,CAAK,OAAA,CAAQ,mBAAA,CAAoB,SAAA,EAAW,MAAM,SAAS,CAAA;AACjE,IAAA,KAAA,MAAW,WAAA,IAAe,MAAM,WAAA,EAAa;AAC3C,MAAA,WAAA,EAAY;AAAA,IACd;AACA,IAAA,KAAA,CAAM,SAAS,MAAA,EAAO;AACtB,IAAA,KAAA,CAAM,OAAA,GAAU,IAAA;AAChB,IAAA,IAAA,CAAK,iBAAA,CAAkB,MAAA,CAAO,KAAA,CAAM,IAAA,CAAK,EAAE,CAAA;AAC3C,IAAA,KAAA,CAAM,IAAA,CAAK,MAAM,OAAA,EAAQ;AACzB,IAAA,KAAA,CAAM,IAAA,CAAK,QAAQ,MAAA,EAAO;AAAA,EAC5B;AAAA,EAEQ,WAAA,GAAoB;AAC1B,IAAA,IAAI,KAAK,SAAA,EAAW;AAClB,MAAA,MAAM,IAAI,SAAA,CAAU,UAAA,EAAY,0BAA0B,CAAA;AAAA,IAC5D;AAAA,EACF;AACF,CAAA;AAEA,SAAS,mBAAmB,OAAA,EAAqD;AAC/E,EAAA,OAAO;AAAA,IACL,SAAA,EAAW,OAAA,CAAQ,IAAA,EAAM,SAAA,IAAa,QAAQ,aAAA,IAAiB,IAAA;AAAA,IAC/D,WAAW,kBAAA,CAAmB,OAAA,CAAQ,MAAM,SAAA,IAAa,OAAA,CAAQ,iBAAiB,MAAM;AAAA,GAC1F;AACF;AAEA,SAAS,mBACP,KAAA,EAC0B;AAC1B,EAAA,IAAI,KAAA,KAAU,MAAA,IAAa,KAAA,KAAU,IAAA,IAAQ,UAAU,MAAA,EAAQ;AAC7D,IAAA,OAAO,MAAA;AAAA,EACT;AACA,EAAA,IAAI,KAAA,KAAU,KAAA,IAAS,KAAA,KAAU,KAAA,EAAO;AACtC,IAAA,OAAO,KAAA;AAAA,EACT;AACA,EAAA,IAAI,UAAU,MAAA,EAAQ;AACpB,IAAA,OAAO,MAAA;AAAA,EACT;AACA,EAAA,MAAM,IAAI,SAAA,CAAU,kBAAA,EAAoB,sCAAsC,CAAA;AAChF;AAEA,SAAS,iBAAA,CACP,MACA,QAAA,EAC0B;AAC1B,EAAA,IAAI,CAAC,IAAA,EAAM;AACT,IAAA,OAAO,QAAA;AAAA,EACT;AACA,EAAA,IAAI,CAAC,QAAA,EAAU;AACb,IAAA,OAAO,IAAA;AAAA,EACT;AACA,EAAA,OAAO,EAAE,GAAG,IAAA,EAAM,GAAG,QAAA,EAAS;AAChC;AAEA,SAAS,gBAAgB,OAAA,EAAyB;AAChD,EAAA,IAAI,CAAC,OAAO,SAAA,CAAU,OAAO,KAAK,OAAA,GAAU,CAAA,IAAK,UAAU,WAAA,EAAa;AACtE,IAAA,MAAM,IAAI,SAAA,CAAU,kBAAA,EAAoB,CAAA,qCAAA,EAAwC,WAAW,CAAA,CAAE,CAAA;AAAA,EAC/F;AACA,EAAA,OAAO,OAAA;AACT;AAEA,SAAS,YAAY,GAAA,EAAqB;AACxC,EAAA,IAAI,CAAC,MAAA,CAAO,QAAA,CAAS,GAAG,CAAA,IAAK,MAAM,CAAA,EAAG;AACpC,IAAA,MAAM,IAAI,SAAA,CAAU,kBAAA,EAAoB,0CAA0C,CAAA;AAAA,EACpF;AACA,EAAA,OAAO,GAAA;AACT;AAEA,SAAS,YAAA,CAAa,OAAe,IAAA,EAAsB;AACzD,EAAA,IAAI,CAAC,MAAA,CAAO,SAAA,CAAU,KAAK,CAAA,IAAK,QAAQ,CAAA,EAAG;AACzC,IAAA,MAAM,IAAI,SAAA,CAAU,kBAAA,EAAoB,CAAA,EAAG,IAAI,CAAA,2BAAA,CAA6B,CAAA;AAAA,EAC9E;AACA,EAAA,OAAO,KAAA;AACT;AAEA,SAAS,kBAAkB,KAAA,EAAuB;AAChD,EAAA,IAAI,OAAO,KAAA,KAAU,QAAA,IAAY,KAAA,CAAM,IAAA,OAAW,EAAA,EAAI;AACpD,IAAA,MAAM,IAAI,SAAA,CAAU,kBAAA,EAAoB,sCAAsC,CAAA;AAAA,EAChF;AACA,EAAA,OAAO,KAAA;AACT","file":"index.js","sourcesContent":["/**\n * Free multi-chart layout primitives for Wicklib.\n */\n\nimport {\n WickError,\n createChart,\n type ChartApi,\n type ChartOptions,\n type ChartOverlayApi,\n type CrosshairEvent,\n type OverlayRenderFrame,\n type Unsubscribe,\n type VisibleRangeEvent,\n} from \"@wicklib/core\";\n\n/**\n * Crosshair synchronization mode.\n *\n * `time` mirrors the vertical time line. `full` also mirrors a horizontal line at the same\n * relative pane height.\n */\nexport type ChartLayoutCrosshairMode = \"off\" | \"time\" | \"full\";\n\n/**\n * Resolved synchronization behavior for a layout.\n */\nexport interface ChartLayoutSyncOptions {\n /** Synchronize visible time ranges. */\n timeRange: boolean;\n /** Crosshair synchronization mode. */\n crosshair: ChartLayoutCrosshairMode;\n}\n\n/**\n * Partial synchronization update. Boolean crosshair values map to `time` and `off`.\n */\nexport interface ChartLayoutSyncUpdate {\n /** Synchronize visible time ranges. */\n timeRange?: boolean;\n /** Crosshair synchronization mode. */\n crosshair?: boolean | ChartLayoutCrosshairMode;\n}\n\n/**\n * Options for a layout chart cell.\n */\nexport interface ChartLayoutSlotOptions {\n /** Stable slot id. Generated when omitted. */\n id?: string;\n /** Chart options for this slot. Merged over the layout defaults. */\n chartOptions?: ChartOptions;\n /** Number of grid rows covered by the cell. Defaults to 1. */\n rowSpan?: number;\n /** Number of grid columns covered by the cell. Defaults to 1. */\n columnSpan?: number;\n /** Synchronization group. Slots only sync with slots in the same group. */\n syncGroup?: string;\n}\n\n/**\n * One chart cell in a layout.\n */\nexport interface ChartLayoutSlot {\n readonly id: string;\n readonly chart: ChartApi;\n readonly element: HTMLElement;\n readonly rowSpan: number;\n readonly columnSpan: number;\n readonly syncGroup: string;\n}\n\n/**\n * Serializable structural layout state. Chart state is owned by the host.\n */\nexport interface ChartLayoutSnapshot {\n version: 1;\n columns: number;\n gap: number;\n sync: ChartLayoutSyncOptions;\n slots: ReadonlyArray<{\n id: string;\n rowSpan: number;\n columnSpan: number;\n syncGroup: string;\n }>;\n}\n\n/**\n * Options for a multi-chart layout.\n */\nexport interface ChartLayoutOptions {\n /** Number of grid columns. Defaults to 2. */\n columns?: number;\n /** Gap between cells in CSS pixels. Defaults to 1. */\n gap?: number;\n /** Default chart options applied to every slot. */\n chartOptions?: ChartOptions;\n /** Synchronization options. */\n sync?: ChartLayoutSyncUpdate;\n /** Synchronize visible time ranges. Defaults to true. */\n syncTimeRange?: boolean;\n /** Crosshair synchronization. Defaults to `time`. */\n syncCrosshair?: boolean | ChartLayoutCrosshairMode;\n /** Crosshair line colour. */\n crosshairColor?: string;\n /** Extra class name on the layout root. */\n className?: string;\n /** Called when the active chart changes. */\n onActiveChange?: (chartId: string | null) => void;\n}\n\n/**\n * A live multi-chart layout.\n */\nexport interface ChartLayoutApi {\n /** Layout root inserted into the host container. */\n readonly element: HTMLElement;\n /** Adds a chart cell and returns its live slot. */\n addChart(options?: ChartLayoutSlotOptions): ChartLayoutSlot;\n /** Destroys and removes one chart cell. */\n removeChart(id: string): void;\n /** Returns a live chart, or null when the id is unknown. */\n getChart(id: string): ChartApi | null;\n /** Returns the current slots in DOM order. */\n getSlots(): readonly ChartLayoutSlot[];\n /** Returns slot ids in DOM order. */\n chartIds(): readonly string[];\n /** Changes the grid column count. */\n setColumns(columns: number): void;\n /** Changes the cell gap in CSS pixels. */\n setGap(gap: number): void;\n /** Changes synchronization behavior without recreating charts. */\n setSyncOptions(options: ChartLayoutSyncUpdate): void;\n /** Returns the resolved synchronization behavior. */\n getSyncOptions(): Readonly<ChartLayoutSyncOptions>;\n /** Marks one chart as active. Pass null to clear. */\n setActiveChart(id: string | null): void;\n /** Returns the active chart id, or null. */\n activeChartId(): string | null;\n /** Subscribes to active chart changes. */\n onActiveChange(handler: (chartId: string | null) => void): Unsubscribe;\n /** Applies the active chart's visible range to every chart in its sync group. */\n syncNow(): void;\n /** Returns structural layout state for host persistence. */\n serialize(): ChartLayoutSnapshot;\n /** Destroys every chart and removes the layout root. */\n destroy(): void;\n}\n\ninterface InternalSlot {\n readonly slot: ChartLayoutSlot;\n readonly unsubscribe: readonly Unsubscribe[];\n overlay: ChartOverlayApi | null;\n readonly onPointerDown: () => void;\n readonly onFocusIn: () => void;\n}\n\ninterface CrosshairState {\n sourceId: string;\n sourceGroup: string;\n time: number;\n yRatio: number | null;\n}\n\nconst DEFAULT_COLUMNS = 2;\nconst DEFAULT_GAP = 1;\nconst DEFAULT_SYNC_GROUP = \"default\";\nconst MAX_COLUMNS = 12;\nconst DEFAULT_CROSSHAIR_COLOR = \"rgba(139, 149, 167, 0.6)\";\n\n/**\n * Creates a framework-agnostic grid of Wicklib charts.\n *\n * The layout owns chart creation, grid placement, time-range synchronization, and a\n * mirrored crosshair. It does not own data, symbol state, accounts, or persistence.\n */\nexport function createChartLayout(\n container: HTMLElement,\n options: ChartLayoutOptions = {},\n): ChartLayoutApi {\n return new ChartLayout(container, options);\n}\n\nclass ChartLayout implements ChartLayoutApi {\n readonly element: HTMLElement;\n\n private readonly slots: InternalSlot[] = [];\n private readonly activeHandlers = new Set<(chartId: string | null) => void>();\n private readonly crosshairOverlays = new Map<string, ChartOverlayApi>();\n private readonly chartOptions: ChartOptions | undefined;\n private readonly crosshairColor: string;\n private columns: number;\n private gap: number;\n private sync: ChartLayoutSyncOptions;\n private activeId: string | null = null;\n private counter = 0;\n private applyingRange = false;\n private crosshair: CrosshairState | null = null;\n private destroyed = false;\n\n constructor(container: HTMLElement, options: ChartLayoutOptions) {\n if (!container || typeof container.appendChild !== \"function\") {\n throw new WickError(\"INVALID_ARGUMENT\", \"createChartLayout requires a DOM container\");\n }\n\n this.columns = validateColumns(options.columns ?? DEFAULT_COLUMNS);\n this.gap = validateGap(options.gap ?? DEFAULT_GAP);\n this.chartOptions = options.chartOptions;\n this.sync = resolveSyncOptions(options);\n this.crosshairColor = options.crosshairColor ?? DEFAULT_CROSSHAIR_COLOR;\n if (options.onActiveChange) {\n this.activeHandlers.add(options.onActiveChange);\n }\n\n this.element = document.createElement(\"div\");\n this.element.className = [\"wicklib-chart-layout\", options.className].filter(Boolean).join(\" \");\n Object.assign(this.element.style, {\n display: \"grid\",\n width: \"100%\",\n height: \"100%\",\n minWidth: \"0\",\n minHeight: \"0\",\n gridAutoRows: \"minmax(0, 1fr)\",\n gap: `${this.gap}px`,\n });\n this.applyGrid();\n container.appendChild(this.element);\n }\n\n addChart(options: ChartLayoutSlotOptions = {}): ChartLayoutSlot {\n this.assertAlive();\n const id = options.id ?? `chart-${++this.counter}`;\n if (this.findSlot(id)) {\n throw new WickError(\"INVALID_ARGUMENT\", `Duplicate chart layout id: ${id}`);\n }\n const rowSpan = validateSpan(options.rowSpan ?? 1, \"rowSpan\");\n const columnSpan = validateSpan(options.columnSpan ?? 1, \"columnSpan\");\n const syncGroup = validateSyncGroup(options.syncGroup ?? DEFAULT_SYNC_GROUP);\n\n const element = document.createElement(\"div\");\n element.className = \"wicklib-chart-layout__cell\";\n element.dataset.chartId = id;\n Object.assign(element.style, {\n position: \"relative\",\n minWidth: \"0\",\n minHeight: \"0\",\n overflow: \"hidden\",\n gridRow: `span ${rowSpan}`,\n gridColumn: `span ${columnSpan}`,\n });\n this.element.appendChild(element);\n\n let chart: ChartApi;\n try {\n chart = createChart(element, mergeChartOptions(this.chartOptions, options.chartOptions));\n } catch (error) {\n element.remove();\n throw error;\n }\n\n const onPointerDown = (): void => this.setActiveChart(id);\n const onFocusIn = (): void => this.setActiveChart(id);\n element.addEventListener(\"pointerdown\", onPointerDown, true);\n element.addEventListener(\"focusin\", onFocusIn);\n\n const unsubscribes: Unsubscribe[] = [\n chart.on(\"visible-range\", (event) => this.handleVisibleRange(id, event)),\n chart.on(\"crosshair\", (event) => this.handleCrosshair(id, event)),\n ];\n\n const slot: ChartLayoutSlot = {\n id,\n chart,\n element,\n rowSpan,\n columnSpan,\n syncGroup,\n };\n const entry: InternalSlot = {\n slot,\n unsubscribe: unsubscribes,\n overlay: null,\n onPointerDown,\n onFocusIn,\n };\n this.slots.push(entry);\n this.ensureCrosshairOverlay(entry);\n\n if (this.activeId === null) {\n this.setActiveChart(id);\n }\n\n return slot;\n }\n\n removeChart(id: string): void {\n this.assertAlive();\n const index = this.slots.findIndex((entry) => entry.slot.id === id);\n if (index < 0) {\n return;\n }\n const entry = this.slots[index]!;\n this.disposeSlot(entry);\n this.slots.splice(index, 1);\n if (this.activeId === id) {\n this.setActiveChart(null);\n }\n if (this.crosshair?.sourceId === id) {\n this.crosshair = null;\n this.requestCrosshairRender();\n }\n }\n\n getChart(id: string): ChartApi | null {\n this.assertAlive();\n return this.findSlot(id)?.slot.chart ?? null;\n }\n\n getSlots(): readonly ChartLayoutSlot[] {\n this.assertAlive();\n return this.slots.map((entry) => entry.slot);\n }\n\n chartIds(): readonly string[] {\n this.assertAlive();\n return this.slots.map((entry) => entry.slot.id);\n }\n\n setColumns(columns: number): void {\n this.assertAlive();\n this.columns = validateColumns(columns);\n this.applyGrid();\n }\n\n setGap(gap: number): void {\n this.assertAlive();\n this.gap = validateGap(gap);\n this.element.style.gap = `${this.gap}px`;\n }\n\n setSyncOptions(options: ChartLayoutSyncUpdate): void {\n this.assertAlive();\n const next: ChartLayoutSyncOptions = {\n timeRange: options.timeRange ?? this.sync.timeRange,\n crosshair:\n options.crosshair === undefined\n ? this.sync.crosshair\n : normalizeCrosshair(options.crosshair),\n };\n if (next.timeRange === this.sync.timeRange && next.crosshair === this.sync.crosshair) {\n return;\n }\n this.sync = next;\n for (const entry of this.slots) {\n this.ensureCrosshairOverlay(entry);\n }\n if (next.crosshair === \"off\" && this.crosshair !== null) {\n this.crosshair = null;\n }\n this.requestCrosshairRender();\n }\n\n getSyncOptions(): Readonly<ChartLayoutSyncOptions> {\n this.assertAlive();\n return { ...this.sync };\n }\n\n setActiveChart(id: string | null): void {\n this.assertAlive();\n if (id !== null && !this.findSlot(id)) {\n throw new WickError(\"INVALID_ARGUMENT\", `Unknown chart layout id: ${id}`);\n }\n if (this.activeId === id) {\n return;\n }\n const previous = this.activeId;\n this.activeId = id;\n for (const entry of this.slots) {\n if (entry.slot.id === previous) {\n delete entry.slot.element.dataset.active;\n }\n if (entry.slot.id === id) {\n entry.slot.element.dataset.active = \"true\";\n }\n }\n for (const handler of this.activeHandlers) {\n handler(id);\n }\n }\n\n activeChartId(): string | null {\n this.assertAlive();\n return this.activeId;\n }\n\n onActiveChange(handler: (chartId: string | null) => void): Unsubscribe {\n this.assertAlive();\n this.activeHandlers.add(handler);\n return () => {\n this.activeHandlers.delete(handler);\n };\n }\n\n syncNow(): void {\n this.assertAlive();\n const source = this.findSlot(this.activeId ?? this.slots[0]?.slot.id ?? \"\");\n if (!source) {\n return;\n }\n this.applyRange(\n source.slot.id,\n source.slot.syncGroup,\n source.slot.chart.timeScale().getVisibleRange(),\n );\n }\n\n serialize(): ChartLayoutSnapshot {\n this.assertAlive();\n return {\n version: 1,\n columns: this.columns,\n gap: this.gap,\n sync: { ...this.sync },\n slots: this.slots.map((entry) => ({\n id: entry.slot.id,\n rowSpan: entry.slot.rowSpan,\n columnSpan: entry.slot.columnSpan,\n syncGroup: entry.slot.syncGroup,\n })),\n };\n }\n\n destroy(): void {\n if (this.destroyed) {\n return;\n }\n this.destroyed = true;\n for (const entry of this.slots) {\n this.disposeSlot(entry);\n }\n this.slots.length = 0;\n this.activeHandlers.clear();\n this.crosshairOverlays.clear();\n this.element.remove();\n }\n\n private applyGrid(): void {\n this.element.style.gridTemplateColumns = `repeat(${this.columns}, minmax(0, 1fr))`;\n }\n\n private findSlot(id: string | null): InternalSlot | undefined {\n if (id === null) {\n return undefined;\n }\n return this.slots.find((entry) => entry.slot.id === id);\n }\n\n private handleVisibleRange(sourceId: string, event: VisibleRangeEvent): void {\n if (!this.sync.timeRange || this.applyingRange) {\n return;\n }\n const source = this.findSlot(sourceId);\n if (!source) {\n return;\n }\n this.applyRange(sourceId, source.slot.syncGroup, { from: event.from, to: event.to });\n }\n\n private applyRange(\n sourceId: string,\n syncGroup: string,\n range: { from: number; to: number },\n ): void {\n if (!Number.isFinite(range.from) || !Number.isFinite(range.to) || range.to <= range.from) {\n return;\n }\n this.applyingRange = true;\n try {\n for (const entry of this.slots) {\n if (entry.slot.id === sourceId || entry.slot.syncGroup !== syncGroup) {\n continue;\n }\n try {\n entry.slot.chart.timeScale().setVisibleRange(range);\n } catch {\n // A chart with incompatible data can reject the range; leave it unchanged.\n }\n }\n } finally {\n this.applyingRange = false;\n }\n }\n\n private handleCrosshair(sourceId: string, event: CrosshairEvent): void {\n if (this.sync.crosshair === \"off\") {\n return;\n }\n if (event.time === null || event.point === null) {\n if (this.crosshair !== null) {\n this.crosshair = null;\n this.requestCrosshairRender();\n }\n return;\n }\n const source = this.findSlot(sourceId);\n if (!source) {\n return;\n }\n const height = source.slot.element.clientHeight;\n const yRatio = height > 0 ? Math.min(1, Math.max(0, event.point.y / height)) : null;\n this.crosshair = {\n sourceId,\n sourceGroup: source.slot.syncGroup,\n time: event.time,\n yRatio,\n };\n this.requestCrosshairRender();\n }\n\n private requestCrosshairRender(): void {\n for (const overlay of this.crosshairOverlays.values()) {\n overlay.requestRender();\n }\n }\n\n private ensureCrosshairOverlay(entry: InternalSlot): void {\n if (this.sync.crosshair === \"off\") {\n if (entry.overlay) {\n entry.overlay.remove();\n entry.overlay = null;\n this.crosshairOverlays.delete(entry.slot.id);\n }\n return;\n }\n if (entry.overlay) {\n return;\n }\n const id = entry.slot.id;\n const group = entry.slot.syncGroup;\n entry.overlay = entry.slot.chart.addOverlay({\n id: \"wicklib-layout-crosshair\",\n draw: (context, frame) => this.drawCrosshair(context, frame, id, group),\n });\n this.crosshairOverlays.set(id, entry.overlay);\n }\n\n private drawCrosshair(\n context: CanvasRenderingContext2D,\n frame: OverlayRenderFrame,\n id: string,\n group: string,\n ): void {\n const current = this.crosshair;\n if (\n !current ||\n current.sourceId === id ||\n current.sourceGroup !== group ||\n this.sync.crosshair === \"off\"\n ) {\n return;\n }\n const x = frame.timeToX(current.time);\n if (x === null || x < frame.plot.left || x > frame.plot.right) {\n return;\n }\n context.save();\n context.beginPath();\n context.rect(frame.plot.left, frame.plot.top, frame.plot.width, frame.plot.height);\n context.clip();\n context.strokeStyle = this.crosshairColor;\n context.lineWidth = 1;\n context.setLineDash([3, 4]);\n\n const crispX = Math.round(x) + 0.5;\n context.beginPath();\n context.moveTo(crispX, frame.plot.top);\n context.lineTo(crispX, frame.plot.bottom);\n context.stroke();\n\n if (this.sync.crosshair === \"full\" && current.yRatio !== null) {\n const y = frame.plot.top + current.yRatio * frame.plot.height;\n const crispY = Math.round(y) + 0.5;\n context.beginPath();\n context.moveTo(frame.plot.left, crispY);\n context.lineTo(frame.plot.right, crispY);\n context.stroke();\n }\n\n context.restore();\n }\n\n private disposeSlot(entry: InternalSlot): void {\n entry.slot.element.removeEventListener(\"pointerdown\", entry.onPointerDown, true);\n entry.slot.element.removeEventListener(\"focusin\", entry.onFocusIn);\n for (const unsubscribe of entry.unsubscribe) {\n unsubscribe();\n }\n entry.overlay?.remove();\n entry.overlay = null;\n this.crosshairOverlays.delete(entry.slot.id);\n entry.slot.chart.destroy();\n entry.slot.element.remove();\n }\n\n private assertAlive(): void {\n if (this.destroyed) {\n throw new WickError(\"DISPOSED\", \"Chart layout is disposed\");\n }\n }\n}\n\nfunction resolveSyncOptions(options: ChartLayoutOptions): ChartLayoutSyncOptions {\n return {\n timeRange: options.sync?.timeRange ?? options.syncTimeRange ?? true,\n crosshair: normalizeCrosshair(options.sync?.crosshair ?? options.syncCrosshair ?? \"time\"),\n };\n}\n\nfunction normalizeCrosshair(\n value: boolean | ChartLayoutCrosshairMode | undefined,\n): ChartLayoutCrosshairMode {\n if (value === undefined || value === true || value === \"time\") {\n return \"time\";\n }\n if (value === false || value === \"off\") {\n return \"off\";\n }\n if (value === \"full\") {\n return \"full\";\n }\n throw new WickError(\"INVALID_ARGUMENT\", \"crosshair must be off, time, or full\");\n}\n\nfunction mergeChartOptions(\n base: ChartOptions | undefined,\n override: ChartOptions | undefined,\n): ChartOptions | undefined {\n if (!base) {\n return override;\n }\n if (!override) {\n return base;\n }\n return { ...base, ...override };\n}\n\nfunction validateColumns(columns: number): number {\n if (!Number.isInteger(columns) || columns < 1 || columns > MAX_COLUMNS) {\n throw new WickError(\"INVALID_ARGUMENT\", `columns must be an integer from 1 to ${MAX_COLUMNS}`);\n }\n return columns;\n}\n\nfunction validateGap(gap: number): number {\n if (!Number.isFinite(gap) || gap < 0) {\n throw new WickError(\"INVALID_ARGUMENT\", \"gap must be a non-negative finite number\");\n }\n return gap;\n}\n\nfunction validateSpan(value: number, name: string): number {\n if (!Number.isInteger(value) || value < 1) {\n throw new WickError(\"INVALID_ARGUMENT\", `${name} must be a positive integer`);\n }\n return value;\n}\n\nfunction validateSyncGroup(value: string): string {\n if (typeof value !== \"string\" || value.trim() === \"\") {\n throw new WickError(\"INVALID_ARGUMENT\", \"syncGroup must be a non-empty string\");\n }\n return value;\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wicklib/layouts",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.1",
|
|
4
4
|
"description": "Framework-agnostic multi-chart layouts for Wicklib",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"directory": "packages/layouts"
|
|
38
38
|
},
|
|
39
39
|
"peerDependencies": {
|
|
40
|
-
"@wicklib/core": "^0.
|
|
40
|
+
"@wicklib/core": "^0.13.1"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@microsoft/api-extractor": "^7.59.4",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"tsup": "^9.0.0",
|
|
46
46
|
"typescript": "^5.9.3",
|
|
47
47
|
"vitest": "^5.0.5",
|
|
48
|
-
"@wicklib/core": "0.
|
|
48
|
+
"@wicklib/core": "0.13.1"
|
|
49
49
|
},
|
|
50
50
|
"scripts": {
|
|
51
51
|
"build": "tsup",
|