@internetstiftelsen/charts 0.15.0 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -11,11 +11,12 @@ A framework-agnostic, composable charting library built on D3.js with TypeScript
11
11
  - **Divergent Bar Support** - Bar charts automatically render from zero and diverge around `0` for mixed positive/negative values
12
12
  - **Mirrored Bar Sides** - Horizontal bars can mirror a series to the left for population-pyramid style charts without changing source data
13
13
  - **Custom Value Labels** - XY, pie, donut, and gauge charts support configurable labels with formatters, max-width overflow behavior, and forced rendering when labels would otherwise be hidden
14
+ - **Axis Label Overflow** - X/Y tick labels and grouped X-axis labels support max-width overflow behavior
14
15
  - **Optional XY Animation** - Animate XY series on first render and `chart.update(...)` with `animate`
15
16
  - **Optional Radial Animation** - Animate pie and donut segments on first render and `chart.update(...)` with `animate`
16
17
  - **Optional Gauge Animation** - Animate gauge value transitions with `gauge.animate`
17
18
  - **Stacking Control** - Bar and area stacking modes with optional reversed visual series order
18
- - **Configurable Tooltips** - Shared or split tooltips with connectors, transitions, and default max-width wrapping
19
+ - **Configurable Tooltips** - Shared, split, or single tooltips with connectors, transitions, color-coded series styling, and default max-width wrapping
19
20
  - **Axis Direction Control** - Use `scales.x.reverse` / `scales.y.reverse` to flip an axis when needed
20
21
  - **Flexible Scales** - Band, linear, time, and logarithmic scales (bar value axes stay linear)
21
22
  - **Explicit or Responsive Sizing** - Set top-level `width`/`height` or let the container drive size
@@ -349,6 +350,9 @@ const chart = new XYChart({
349
350
  });
350
351
  ```
351
352
 
353
+ The exported `defaultResponsiveConfig` also switches tooltip components to
354
+ `mode: 'shared'` at its `sm` breakpoint for compact XY charts.
355
+
352
356
  ## Word Cloud
353
357
 
354
358
  ```javascript
package/dist/theme.js CHANGED
@@ -325,6 +325,12 @@ export const defaultResponsiveConfig = {
325
325
  tooltip: { fontSize: 11 },
326
326
  valueLabel: { fontSize: 10 },
327
327
  },
328
+ components: [
329
+ {
330
+ match: { type: 'tooltip' },
331
+ override: { mode: 'shared' },
332
+ },
333
+ ],
328
334
  },
329
335
  md: {
330
336
  minWidth: 480,
@@ -0,0 +1,60 @@
1
+ import type { ChartTheme, TooltipTransitionConfig } from '../types.js';
2
+ import { type TooltipAnchor, type TooltipArrowEdge, type TooltipDivSelection } from './types.js';
3
+ type TooltipDomConfig = {
4
+ id: string;
5
+ splitTooltipOwner: string;
6
+ maxWidth: number;
7
+ transition: Required<TooltipTransitionConfig>;
8
+ };
9
+ export type TooltipStyleOverrides = Partial<Pick<ChartTheme['tooltip'], 'background' | 'border' | 'color'>>;
10
+ export declare class TooltipDom {
11
+ private readonly id;
12
+ private readonly splitTooltipOwner;
13
+ private readonly maxWidth;
14
+ private readonly transition;
15
+ private readonly tooltipStyleKeys;
16
+ private readonly tooltipTransitionFrameIds;
17
+ private readonly tooltipStyles;
18
+ private tooltipDiv;
19
+ constructor(config: TooltipDomConfig);
20
+ initialize(theme: ChartTheme): void;
21
+ getRootTooltip(): TooltipDivSelection | null;
22
+ applyRootTooltipStyles(theme: ChartTheme, styleOverrides?: TooltipStyleOverrides): void;
23
+ setContent(content: string): void;
24
+ getBounds(): DOMRect | null;
25
+ showAt(left: number, top: number): void;
26
+ hide(): void;
27
+ cleanup(): void;
28
+ measureTooltip(tooltip: TooltipDivSelection, content: string): {
29
+ width: number;
30
+ height: number;
31
+ } | null;
32
+ renderTooltipWithConnector(tooltip: TooltipDivSelection, arrowEdge: TooltipArrowEdge, left: number, top: number, tooltipWidth: number, tooltipHeight: number, targetX: number, targetY: number, anchor: TooltipAnchor): void;
33
+ renderTooltipWithoutConnector(tooltip: TooltipDivSelection, left: number, top: number): void;
34
+ hideTooltipSelection(tooltip: TooltipDivSelection): void;
35
+ getSplitTooltip(index: number, theme: ChartTheme, styleOverrides?: TooltipStyleOverrides): TooltipDivSelection;
36
+ hideSplitTooltips(): void;
37
+ hideUnusedSplitTooltips(visibleTooltips: TooltipDivSelection[]): void;
38
+ private applyTooltipStylesIfNeeded;
39
+ private resolveTooltipStyle;
40
+ private getTooltipStyleKey;
41
+ private writeTooltipStyles;
42
+ private showTooltipAt;
43
+ private showTooltipSelection;
44
+ private hideTooltipElement;
45
+ private getTooltipTransitionStyle;
46
+ private isTooltipVisible;
47
+ private getTooltipPosition;
48
+ private hasVisibleSlideOffset;
49
+ private slideTooltipFromOffset;
50
+ private requestTooltipTransitionFrame;
51
+ private cancelTooltipTransitionFrame;
52
+ private setTooltipMarkup;
53
+ private appendTooltipConnector;
54
+ private appendTooltipArrow;
55
+ private appendTooltipArrowTriangle;
56
+ private getTooltipStyle;
57
+ private removeSplitTooltips;
58
+ private removeRootTooltip;
59
+ }
60
+ export {};
@@ -0,0 +1,457 @@
1
+ import { select } from 'd3';
2
+ import { resolveTooltipArrowPosition, resolveTooltipConnectorLayout, } from './geometry.js';
3
+ import { TOOLTIP_ARROW_BORDER_Z_INDEX, TOOLTIP_ARROW_FILL_Z_INDEX, TOOLTIP_BODY_Z_INDEX, TOOLTIP_BORDER_WIDTH_PX, TOOLTIP_BOX_ARROW_HALF_HEIGHT_PX, TOOLTIP_BOX_ARROW_LENGTH_PX, TOOLTIP_CONNECTOR_Z_INDEX, TOOLTIP_HIDDEN_TRANSFORM, TOOLTIP_ROOT_Z_INDEX, TOOLTIP_VISIBLE_TRANSFORM, } from './types.js';
4
+ export class TooltipDom {
5
+ constructor(config) {
6
+ Object.defineProperty(this, "id", {
7
+ enumerable: true,
8
+ configurable: true,
9
+ writable: true,
10
+ value: void 0
11
+ });
12
+ Object.defineProperty(this, "splitTooltipOwner", {
13
+ enumerable: true,
14
+ configurable: true,
15
+ writable: true,
16
+ value: void 0
17
+ });
18
+ Object.defineProperty(this, "maxWidth", {
19
+ enumerable: true,
20
+ configurable: true,
21
+ writable: true,
22
+ value: void 0
23
+ });
24
+ Object.defineProperty(this, "transition", {
25
+ enumerable: true,
26
+ configurable: true,
27
+ writable: true,
28
+ value: void 0
29
+ });
30
+ Object.defineProperty(this, "tooltipStyleKeys", {
31
+ enumerable: true,
32
+ configurable: true,
33
+ writable: true,
34
+ value: new WeakMap()
35
+ });
36
+ Object.defineProperty(this, "tooltipTransitionFrameIds", {
37
+ enumerable: true,
38
+ configurable: true,
39
+ writable: true,
40
+ value: new WeakMap()
41
+ });
42
+ Object.defineProperty(this, "tooltipStyles", {
43
+ enumerable: true,
44
+ configurable: true,
45
+ writable: true,
46
+ value: new WeakMap()
47
+ });
48
+ Object.defineProperty(this, "tooltipDiv", {
49
+ enumerable: true,
50
+ configurable: true,
51
+ writable: true,
52
+ value: null
53
+ });
54
+ this.id = config.id;
55
+ this.splitTooltipOwner = config.splitTooltipOwner;
56
+ this.maxWidth = config.maxWidth;
57
+ this.transition = config.transition;
58
+ }
59
+ initialize(theme) {
60
+ const existingTooltip = select(`#${this.id}`);
61
+ const tooltip = existingTooltip.empty()
62
+ ? select('body')
63
+ .append('div')
64
+ .attr('class', 'chart-tooltip')
65
+ .attr('id', this.id)
66
+ : existingTooltip;
67
+ this.removeSplitTooltips();
68
+ this.applyTooltipStylesIfNeeded(tooltip, theme);
69
+ this.tooltipDiv = tooltip;
70
+ this.hideTooltipSelection(tooltip);
71
+ }
72
+ getRootTooltip() {
73
+ return this.tooltipDiv;
74
+ }
75
+ applyRootTooltipStyles(theme, styleOverrides) {
76
+ if (!this.tooltipDiv) {
77
+ return;
78
+ }
79
+ this.applyTooltipStylesIfNeeded(this.tooltipDiv, theme, styleOverrides);
80
+ }
81
+ setContent(content) {
82
+ if (!this.tooltipDiv) {
83
+ return;
84
+ }
85
+ this.setTooltipMarkup(this.tooltipDiv, content);
86
+ }
87
+ getBounds() {
88
+ const node = this.tooltipDiv?.node();
89
+ if (!node) {
90
+ return null;
91
+ }
92
+ return node.getBoundingClientRect();
93
+ }
94
+ showAt(left, top) {
95
+ if (!this.tooltipDiv) {
96
+ return;
97
+ }
98
+ if (!Number.isFinite(left) || !Number.isFinite(top)) {
99
+ this.hide();
100
+ return;
101
+ }
102
+ this.showTooltipAt(this.tooltipDiv, left, top);
103
+ }
104
+ hide() {
105
+ const tooltip = this.tooltipDiv ?? select(`#${this.id}`);
106
+ if (!tooltip.empty()) {
107
+ this.hideTooltipSelection(tooltip);
108
+ }
109
+ this.hideSplitTooltips();
110
+ }
111
+ cleanup() {
112
+ this.removeRootTooltip();
113
+ this.removeSplitTooltips();
114
+ this.tooltipDiv = null;
115
+ }
116
+ measureTooltip(tooltip, content) {
117
+ this.setTooltipMarkup(tooltip, content);
118
+ const tooltipNode = tooltip.node();
119
+ if (!tooltipNode) {
120
+ return null;
121
+ }
122
+ if (!this.isTooltipVisible(tooltipNode)) {
123
+ tooltip.style('left', '-9999px').style('top', '-9999px');
124
+ this.hideTooltipSelection(tooltip);
125
+ }
126
+ const tooltipRect = tooltipNode.getBoundingClientRect();
127
+ if (!Number.isFinite(tooltipRect.width) ||
128
+ !Number.isFinite(tooltipRect.height)) {
129
+ return null;
130
+ }
131
+ return {
132
+ width: tooltipRect.width,
133
+ height: tooltipRect.height,
134
+ };
135
+ }
136
+ renderTooltipWithConnector(tooltip, arrowEdge, left, top, tooltipWidth, tooltipHeight, targetX, targetY, anchor) {
137
+ if (!Number.isFinite(left) ||
138
+ !Number.isFinite(top) ||
139
+ !Number.isFinite(targetX) ||
140
+ !Number.isFinite(targetY)) {
141
+ this.hideTooltipSelection(tooltip);
142
+ return;
143
+ }
144
+ const connectorLayout = resolveTooltipConnectorLayout(arrowEdge, left, top, tooltipWidth, tooltipHeight, targetX, targetY, anchor);
145
+ if (!connectorLayout) {
146
+ this.hideTooltipSelection(tooltip);
147
+ return;
148
+ }
149
+ this.appendTooltipConnector(tooltip, connectorLayout);
150
+ this.appendTooltipArrow(tooltip, connectorLayout);
151
+ this.showTooltipAt(tooltip, left, top);
152
+ }
153
+ renderTooltipWithoutConnector(tooltip, left, top) {
154
+ if (!Number.isFinite(left) || !Number.isFinite(top)) {
155
+ this.hideTooltipSelection(tooltip);
156
+ return;
157
+ }
158
+ this.showTooltipAt(tooltip, left, top);
159
+ }
160
+ hideTooltipSelection(tooltip) {
161
+ const node = tooltip.node();
162
+ if (!node) {
163
+ return;
164
+ }
165
+ this.hideTooltipElement(node);
166
+ }
167
+ getSplitTooltip(index, theme, styleOverrides) {
168
+ const tooltipId = `${this.splitTooltipOwner}-${index}`;
169
+ const existingTooltip = select(`#${tooltipId}`);
170
+ const tooltip = existingTooltip.empty()
171
+ ? select('body')
172
+ .append('div')
173
+ .attr('class', 'chart-tooltip chart-tooltip--split')
174
+ .attr('id', tooltipId)
175
+ .attr('data-chart-tooltip-owner', this.splitTooltipOwner)
176
+ .attr('data-chart-tooltip-index', String(index))
177
+ : existingTooltip;
178
+ this.applyTooltipStylesIfNeeded(tooltip, theme, styleOverrides);
179
+ return tooltip;
180
+ }
181
+ hideSplitTooltips() {
182
+ document
183
+ .querySelectorAll(`[data-chart-tooltip-owner="${this.splitTooltipOwner}"]`)
184
+ .forEach((node) => {
185
+ this.hideTooltipElement(node);
186
+ });
187
+ }
188
+ hideUnusedSplitTooltips(visibleTooltips) {
189
+ const visibleNodes = new Set(visibleTooltips
190
+ .map((tooltip) => tooltip.node())
191
+ .filter((node) => Boolean(node)));
192
+ document
193
+ .querySelectorAll(`[data-chart-tooltip-owner="${this.splitTooltipOwner}"]`)
194
+ .forEach((node) => {
195
+ if (visibleNodes.has(node)) {
196
+ return;
197
+ }
198
+ this.hideTooltipElement(node);
199
+ });
200
+ }
201
+ applyTooltipStylesIfNeeded(tooltip, theme, styleOverrides) {
202
+ const node = tooltip.node();
203
+ if (!node) {
204
+ return;
205
+ }
206
+ const tooltipStyle = this.resolveTooltipStyle(theme, styleOverrides);
207
+ const styleKey = this.getTooltipStyleKey(tooltipStyle);
208
+ if (this.tooltipStyleKeys.get(node) === styleKey) {
209
+ this.tooltipStyles.set(node, tooltipStyle);
210
+ return;
211
+ }
212
+ this.tooltipStyleKeys.set(node, styleKey);
213
+ this.tooltipStyles.set(node, tooltipStyle);
214
+ this.writeTooltipStyles(tooltip, tooltipStyle);
215
+ }
216
+ resolveTooltipStyle(theme, styleOverrides) {
217
+ return {
218
+ ...theme.tooltip,
219
+ ...styleOverrides,
220
+ };
221
+ }
222
+ getTooltipStyleKey(tooltipStyle) {
223
+ return [
224
+ tooltipStyle.background,
225
+ tooltipStyle.border,
226
+ tooltipStyle.color,
227
+ tooltipStyle.fontFamily,
228
+ tooltipStyle.fontSize,
229
+ tooltipStyle.fontWeight,
230
+ this.maxWidth,
231
+ this.transition.show,
232
+ this.transition.duration,
233
+ this.transition.easing,
234
+ ].join('|');
235
+ }
236
+ writeTooltipStyles(tooltip, tooltipStyle) {
237
+ tooltip
238
+ .style('position', 'absolute')
239
+ .style('background-color', tooltipStyle.background)
240
+ .style('border', `${TOOLTIP_BORDER_WIDTH_PX}px solid ${tooltipStyle.border}`)
241
+ .style('border-radius', '4px')
242
+ .style('padding', '8px')
243
+ .style('box-shadow', '0 2px 4px rgba(0,0,0,0.1)')
244
+ .style('color', tooltipStyle.color)
245
+ .style('font-family', tooltipStyle.fontFamily)
246
+ .style('font-size', `${tooltipStyle.fontSize}px`)
247
+ .style('font-weight', tooltipStyle.fontWeight)
248
+ .style('box-sizing', 'border-box')
249
+ .style('overflow-wrap', 'break-word')
250
+ .style('overflow', 'visible')
251
+ .style('isolation', 'isolate')
252
+ .style('pointer-events', 'none')
253
+ .style('z-index', String(TOOLTIP_ROOT_Z_INDEX));
254
+ tooltip.style('max-width', `${this.maxWidth}px`);
255
+ if (this.transition.show) {
256
+ tooltip
257
+ .style('transition', this.getTooltipTransitionStyle())
258
+ .style('will-change', 'opacity, transform');
259
+ return;
260
+ }
261
+ tooltip
262
+ .style('opacity', null)
263
+ .style('transform', null)
264
+ .style('transition', null)
265
+ .style('will-change', null);
266
+ }
267
+ showTooltipAt(tooltip, left, top) {
268
+ const tooltipNode = tooltip.node();
269
+ const previousPosition = tooltipNode && this.isTooltipVisible(tooltipNode)
270
+ ? this.getTooltipPosition(tooltipNode)
271
+ : null;
272
+ tooltip.style('left', `${left}px`).style('top', `${top}px`);
273
+ this.showTooltipSelection(tooltip, previousPosition
274
+ ? {
275
+ x: previousPosition.left - left,
276
+ y: previousPosition.top - top,
277
+ }
278
+ : null);
279
+ }
280
+ showTooltipSelection(tooltip, slideOffset = null) {
281
+ tooltip.style('visibility', 'visible');
282
+ if (!this.transition.show) {
283
+ return;
284
+ }
285
+ tooltip.style('opacity', '1');
286
+ const node = tooltip.node();
287
+ if (!node) {
288
+ return;
289
+ }
290
+ if (!slideOffset || !this.hasVisibleSlideOffset(slideOffset)) {
291
+ this.cancelTooltipTransitionFrame(node);
292
+ tooltip.style('transform', TOOLTIP_VISIBLE_TRANSFORM);
293
+ return;
294
+ }
295
+ this.slideTooltipFromOffset(node, slideOffset);
296
+ }
297
+ hideTooltipElement(node) {
298
+ this.cancelTooltipTransitionFrame(node);
299
+ if (!this.transition.show) {
300
+ node.style.visibility = 'hidden';
301
+ return;
302
+ }
303
+ node.style.visibility = 'visible';
304
+ node.style.opacity = '0';
305
+ node.style.transform = TOOLTIP_HIDDEN_TRANSFORM;
306
+ }
307
+ getTooltipTransitionStyle() {
308
+ return `opacity ${this.transition.duration}ms ${this.transition.easing}, transform ${this.transition.duration}ms ${this.transition.easing}`;
309
+ }
310
+ isTooltipVisible(node) {
311
+ return (node.style.visibility === 'visible' && node.style.opacity !== '0');
312
+ }
313
+ getTooltipPosition(node) {
314
+ const left = Number.parseFloat(node.style.left || '0');
315
+ const top = Number.parseFloat(node.style.top || '0');
316
+ if (!Number.isFinite(left) || !Number.isFinite(top)) {
317
+ return null;
318
+ }
319
+ return { left, top };
320
+ }
321
+ hasVisibleSlideOffset(offset) {
322
+ return Math.abs(offset.x) > 0.5 || Math.abs(offset.y) > 0.5;
323
+ }
324
+ slideTooltipFromOffset(node, offset) {
325
+ const transition = node.style.transition || this.getTooltipTransitionStyle();
326
+ this.cancelTooltipTransitionFrame(node);
327
+ node.style.setProperty('transition', 'none');
328
+ node.style.setProperty('transform', `translate(${offset.x}px, ${offset.y}px)`);
329
+ node.getBoundingClientRect();
330
+ node.style.setProperty('transition', transition);
331
+ const frameId = this.requestTooltipTransitionFrame(() => {
332
+ this.tooltipTransitionFrameIds.delete(node);
333
+ node.style.setProperty('transform', TOOLTIP_VISIBLE_TRANSFORM);
334
+ });
335
+ this.tooltipTransitionFrameIds.set(node, frameId);
336
+ }
337
+ requestTooltipTransitionFrame(callback) {
338
+ if (typeof window.requestAnimationFrame === 'function') {
339
+ return window.requestAnimationFrame(callback);
340
+ }
341
+ return window.setTimeout(() => {
342
+ callback(window.performance.now());
343
+ }, 16);
344
+ }
345
+ cancelTooltipTransitionFrame(node) {
346
+ const frameId = this.tooltipTransitionFrameIds.get(node);
347
+ if (frameId === undefined) {
348
+ return;
349
+ }
350
+ if (typeof window.cancelAnimationFrame === 'function') {
351
+ window.cancelAnimationFrame(frameId);
352
+ }
353
+ else {
354
+ window.clearTimeout(frameId);
355
+ }
356
+ this.tooltipTransitionFrameIds.delete(node);
357
+ }
358
+ setTooltipMarkup(tooltip, content) {
359
+ tooltip.html(`<div data-chart-tooltip-body="true">${content}</div>`);
360
+ const body = tooltip.select('[data-chart-tooltip-body]');
361
+ if (body.empty()) {
362
+ return;
363
+ }
364
+ body.style('position', 'relative').style('z-index', String(TOOLTIP_BODY_Z_INDEX));
365
+ }
366
+ appendTooltipConnector(tooltip, connectorLayout) {
367
+ const tooltipStyle = this.getTooltipStyle(tooltip);
368
+ const tooltipBorder = tooltipStyle?.border ?? '#dddddd';
369
+ const connector = tooltip
370
+ .append('svg')
371
+ .attr('data-chart-tooltip-connector', 'true')
372
+ .attr('data-chart-tooltip-arrow-edge', connectorLayout.arrowEdge)
373
+ .attr('aria-hidden', 'true')
374
+ .attr('width', connectorLayout.width)
375
+ .attr('height', connectorLayout.height)
376
+ .attr('viewBox', `0 0 ${connectorLayout.width} ${connectorLayout.height}`)
377
+ .style('position', 'absolute')
378
+ .style('left', `${connectorLayout.left}px`)
379
+ .style('top', `${connectorLayout.top}px`)
380
+ .style('pointer-events', 'none')
381
+ .style('overflow', 'visible')
382
+ .style('z-index', String(TOOLTIP_CONNECTOR_Z_INDEX));
383
+ connector
384
+ .append('path')
385
+ .attr('data-chart-tooltip-connector-path', 'true')
386
+ .attr('d', connectorLayout.path)
387
+ .attr('fill', 'none')
388
+ .attr('stroke', tooltipBorder)
389
+ .attr('stroke-width', 1.25)
390
+ .attr('stroke-linecap', 'round')
391
+ .attr('stroke-linejoin', 'round');
392
+ }
393
+ appendTooltipArrow(tooltip, connectorLayout) {
394
+ const tooltipStyle = this.getTooltipStyle(tooltip);
395
+ const tooltipBackground = tooltipStyle?.background ?? '#ffffff';
396
+ const tooltipBorder = tooltipStyle?.border ?? '#dddddd';
397
+ this.appendTooltipArrowTriangle(tooltip, connectorLayout, 'data-chart-tooltip-arrow', tooltipBorder, TOOLTIP_BOX_ARROW_LENGTH_PX, TOOLTIP_BOX_ARROW_HALF_HEIGHT_PX, TOOLTIP_ARROW_BORDER_Z_INDEX);
398
+ this.appendTooltipArrowTriangle(tooltip, connectorLayout, 'data-chart-tooltip-arrow-fill', tooltipBackground, TOOLTIP_BOX_ARROW_LENGTH_PX - 1, TOOLTIP_BOX_ARROW_HALF_HEIGHT_PX - 1, TOOLTIP_ARROW_FILL_Z_INDEX);
399
+ }
400
+ appendTooltipArrowTriangle(tooltip, connectorLayout, dataAttribute, color, length, halfHeight, zIndex) {
401
+ const position = resolveTooltipArrowPosition(connectorLayout.arrowEdge, connectorLayout.arrowX, connectorLayout.arrowY, length, halfHeight);
402
+ const arrow = tooltip
403
+ .append('div')
404
+ .attr(dataAttribute, 'true')
405
+ .attr('data-chart-tooltip-arrow-edge', connectorLayout.arrowEdge)
406
+ .attr('aria-hidden', 'true')
407
+ .style('position', 'absolute')
408
+ .style('left', `${position.left}px`)
409
+ .style('top', `${position.top}px`)
410
+ .style('width', '0')
411
+ .style('height', '0')
412
+ .style('pointer-events', 'none')
413
+ .style('z-index', String(zIndex));
414
+ if (connectorLayout.arrowEdge === 'left') {
415
+ arrow
416
+ .style('border-top', `${halfHeight}px solid transparent`)
417
+ .style('border-bottom', `${halfHeight}px solid transparent`)
418
+ .style('border-right', `${length}px solid ${color}`);
419
+ return;
420
+ }
421
+ if (connectorLayout.arrowEdge === 'right') {
422
+ arrow
423
+ .style('border-top', `${halfHeight}px solid transparent`)
424
+ .style('border-bottom', `${halfHeight}px solid transparent`)
425
+ .style('border-left', `${length}px solid ${color}`);
426
+ return;
427
+ }
428
+ if (connectorLayout.arrowEdge === 'top') {
429
+ arrow
430
+ .style('border-left', `${halfHeight}px solid transparent`)
431
+ .style('border-right', `${halfHeight}px solid transparent`)
432
+ .style('border-bottom', `${length}px solid ${color}`);
433
+ return;
434
+ }
435
+ arrow
436
+ .style('border-left', `${halfHeight}px solid transparent`)
437
+ .style('border-right', `${halfHeight}px solid transparent`)
438
+ .style('border-top', `${length}px solid ${color}`);
439
+ }
440
+ getTooltipStyle(tooltip) {
441
+ const node = tooltip.node();
442
+ return node ? this.tooltipStyles.get(node) : undefined;
443
+ }
444
+ removeSplitTooltips() {
445
+ document
446
+ .querySelectorAll(`[data-chart-tooltip-owner="${this.splitTooltipOwner}"]`)
447
+ .forEach((node) => {
448
+ node.remove();
449
+ });
450
+ }
451
+ removeRootTooltip() {
452
+ const tooltip = this.tooltipDiv ?? select(`#${this.id}`);
453
+ if (!tooltip.empty()) {
454
+ tooltip.remove();
455
+ }
456
+ }
457
+ }
@@ -0,0 +1,19 @@
1
+ import type { TooltipBarAnchorPosition, TooltipPosition } from '../types.js';
2
+ import { type SplitTooltipLayout, type SplitTooltipViewportBounds, type TooltipAnchor, type TooltipArrowEdge, type TooltipConnectorLayout, type TooltipTarget, type XYTooltipSeries } from './types.js';
3
+ export declare function getSplitTooltipViewportBounds(): SplitTooltipViewportBounds;
4
+ export declare function clipTooltipAnchorToBounds(anchor: TooltipAnchor, bounds: SplitTooltipViewportBounds): TooltipAnchor;
5
+ export declare function resolveTooltipArrowEdge(position: TooltipPosition, anchor: TooltipAnchor, target: TooltipTarget, tooltipWidth: number, tooltipHeight: number, bounds?: SplitTooltipViewportBounds): TooltipArrowEdge;
6
+ export declare function resolveSidePlacementArrowEdge(anchor: TooltipAnchor, tooltipWidth: number, bounds?: SplitTooltipViewportBounds): TooltipArrowEdge;
7
+ export declare function resolveVerticalPlacementArrowEdge(target: TooltipTarget, tooltipHeight: number, bounds?: SplitTooltipViewportBounds): TooltipArrowEdge;
8
+ export declare function resolveSharedTooltipTarget(anchor: TooltipAnchor): TooltipTarget;
9
+ export declare function resolveSplitTooltipTarget(currentSeries: XYTooltipSeries, anchor: TooltipAnchor, barAnchorPosition: TooltipBarAnchorPosition): TooltipTarget;
10
+ export declare function getAnchoredTooltipPosition(anchor: TooltipAnchor, target: TooltipTarget, tooltipWidth: number, tooltipHeight: number, arrowEdge: TooltipArrowEdge, bounds?: SplitTooltipViewportBounds): {
11
+ left: number;
12
+ top: number;
13
+ } | null;
14
+ export declare function resolveTooltipConnectorLayout(arrowEdge: TooltipArrowEdge, tooltipLeft: number, tooltipTop: number, tooltipWidth: number, tooltipHeight: number, targetX: number, targetY: number, anchor: TooltipAnchor): TooltipConnectorLayout | null;
15
+ export declare function resolveTooltipArrowPosition(arrowEdge: TooltipArrowEdge, boxX: number, boxY: number, length: number, halfHeight: number): {
16
+ left: number;
17
+ top: number;
18
+ };
19
+ export declare function resolveSplitTooltipPositions(layouts: SplitTooltipLayout[], position: TooltipPosition, bounds?: SplitTooltipViewportBounds, isHorizontal?: boolean): void;