@internetstiftelsen/charts 0.16.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
@@ -16,7 +16,7 @@ A framework-agnostic, composable charting library built on D3.js with TypeScript
16
16
  - **Optional Radial Animation** - Animate pie and donut segments on first render and `chart.update(...)` with `animate`
17
17
  - **Optional Gauge Animation** - Animate gauge value transitions with `gauge.animate`
18
18
  - **Stacking Control** - Bar and area stacking modes with optional reversed visual series order
19
- - **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
20
20
  - **Axis Direction Control** - Use `scales.x.reverse` / `scales.y.reverse` to flip an axis when needed
21
21
  - **Flexible Scales** - Band, linear, time, and logarithmic scales (bar value axes stay linear)
22
22
  - **Explicit or Responsive Sizing** - Set top-level `width`/`height` or let the container drive size
@@ -6,6 +6,7 @@ type TooltipDomConfig = {
6
6
  maxWidth: number;
7
7
  transition: Required<TooltipTransitionConfig>;
8
8
  };
9
+ export type TooltipStyleOverrides = Partial<Pick<ChartTheme['tooltip'], 'background' | 'border' | 'color'>>;
9
10
  export declare class TooltipDom {
10
11
  private readonly id;
11
12
  private readonly splitTooltipOwner;
@@ -13,11 +14,12 @@ export declare class TooltipDom {
13
14
  private readonly transition;
14
15
  private readonly tooltipStyleKeys;
15
16
  private readonly tooltipTransitionFrameIds;
17
+ private readonly tooltipStyles;
16
18
  private tooltipDiv;
17
- private tooltipTheme;
18
19
  constructor(config: TooltipDomConfig);
19
20
  initialize(theme: ChartTheme): void;
20
21
  getRootTooltip(): TooltipDivSelection | null;
22
+ applyRootTooltipStyles(theme: ChartTheme, styleOverrides?: TooltipStyleOverrides): void;
21
23
  setContent(content: string): void;
22
24
  getBounds(): DOMRect | null;
23
25
  showAt(left: number, top: number): void;
@@ -30,10 +32,11 @@ export declare class TooltipDom {
30
32
  renderTooltipWithConnector(tooltip: TooltipDivSelection, arrowEdge: TooltipArrowEdge, left: number, top: number, tooltipWidth: number, tooltipHeight: number, targetX: number, targetY: number, anchor: TooltipAnchor): void;
31
33
  renderTooltipWithoutConnector(tooltip: TooltipDivSelection, left: number, top: number): void;
32
34
  hideTooltipSelection(tooltip: TooltipDivSelection): void;
33
- getSplitTooltip(index: number, theme: ChartTheme): TooltipDivSelection;
35
+ getSplitTooltip(index: number, theme: ChartTheme, styleOverrides?: TooltipStyleOverrides): TooltipDivSelection;
34
36
  hideSplitTooltips(): void;
35
37
  hideUnusedSplitTooltips(visibleTooltips: TooltipDivSelection[]): void;
36
38
  private applyTooltipStylesIfNeeded;
39
+ private resolveTooltipStyle;
37
40
  private getTooltipStyleKey;
38
41
  private writeTooltipStyles;
39
42
  private showTooltipAt;
@@ -50,6 +53,7 @@ export declare class TooltipDom {
50
53
  private appendTooltipConnector;
51
54
  private appendTooltipArrow;
52
55
  private appendTooltipArrowTriangle;
56
+ private getTooltipStyle;
53
57
  private removeSplitTooltips;
54
58
  private removeRootTooltip;
55
59
  }
@@ -39,13 +39,13 @@ export class TooltipDom {
39
39
  writable: true,
40
40
  value: new WeakMap()
41
41
  });
42
- Object.defineProperty(this, "tooltipDiv", {
42
+ Object.defineProperty(this, "tooltipStyles", {
43
43
  enumerable: true,
44
44
  configurable: true,
45
45
  writable: true,
46
- value: null
46
+ value: new WeakMap()
47
47
  });
48
- Object.defineProperty(this, "tooltipTheme", {
48
+ Object.defineProperty(this, "tooltipDiv", {
49
49
  enumerable: true,
50
50
  configurable: true,
51
51
  writable: true,
@@ -72,6 +72,12 @@ export class TooltipDom {
72
72
  getRootTooltip() {
73
73
  return this.tooltipDiv;
74
74
  }
75
+ applyRootTooltipStyles(theme, styleOverrides) {
76
+ if (!this.tooltipDiv) {
77
+ return;
78
+ }
79
+ this.applyTooltipStylesIfNeeded(this.tooltipDiv, theme, styleOverrides);
80
+ }
75
81
  setContent(content) {
76
82
  if (!this.tooltipDiv) {
77
83
  return;
@@ -158,7 +164,7 @@ export class TooltipDom {
158
164
  }
159
165
  this.hideTooltipElement(node);
160
166
  }
161
- getSplitTooltip(index, theme) {
167
+ getSplitTooltip(index, theme, styleOverrides) {
162
168
  const tooltipId = `${this.splitTooltipOwner}-${index}`;
163
169
  const existingTooltip = select(`#${tooltipId}`);
164
170
  const tooltip = existingTooltip.empty()
@@ -169,7 +175,7 @@ export class TooltipDom {
169
175
  .attr('data-chart-tooltip-owner', this.splitTooltipOwner)
170
176
  .attr('data-chart-tooltip-index', String(index))
171
177
  : existingTooltip;
172
- this.applyTooltipStylesIfNeeded(tooltip, theme);
178
+ this.applyTooltipStylesIfNeeded(tooltip, theme, styleOverrides);
173
179
  return tooltip;
174
180
  }
175
181
  hideSplitTooltips() {
@@ -192,46 +198,53 @@ export class TooltipDom {
192
198
  this.hideTooltipElement(node);
193
199
  });
194
200
  }
195
- applyTooltipStylesIfNeeded(tooltip, theme) {
201
+ applyTooltipStylesIfNeeded(tooltip, theme, styleOverrides) {
196
202
  const node = tooltip.node();
197
203
  if (!node) {
198
204
  return;
199
205
  }
200
- const styleKey = this.getTooltipStyleKey(theme);
206
+ const tooltipStyle = this.resolveTooltipStyle(theme, styleOverrides);
207
+ const styleKey = this.getTooltipStyleKey(tooltipStyle);
201
208
  if (this.tooltipStyleKeys.get(node) === styleKey) {
202
- this.tooltipTheme = theme.tooltip;
209
+ this.tooltipStyles.set(node, tooltipStyle);
203
210
  return;
204
211
  }
205
212
  this.tooltipStyleKeys.set(node, styleKey);
206
- this.writeTooltipStyles(tooltip, theme);
213
+ this.tooltipStyles.set(node, tooltipStyle);
214
+ this.writeTooltipStyles(tooltip, tooltipStyle);
207
215
  }
208
- getTooltipStyleKey(theme) {
216
+ resolveTooltipStyle(theme, styleOverrides) {
217
+ return {
218
+ ...theme.tooltip,
219
+ ...styleOverrides,
220
+ };
221
+ }
222
+ getTooltipStyleKey(tooltipStyle) {
209
223
  return [
210
- theme.tooltip.background,
211
- theme.tooltip.border,
212
- theme.tooltip.color,
213
- theme.tooltip.fontFamily,
214
- theme.tooltip.fontSize,
215
- theme.tooltip.fontWeight,
224
+ tooltipStyle.background,
225
+ tooltipStyle.border,
226
+ tooltipStyle.color,
227
+ tooltipStyle.fontFamily,
228
+ tooltipStyle.fontSize,
229
+ tooltipStyle.fontWeight,
216
230
  this.maxWidth,
217
231
  this.transition.show,
218
232
  this.transition.duration,
219
233
  this.transition.easing,
220
234
  ].join('|');
221
235
  }
222
- writeTooltipStyles(tooltip, theme) {
223
- this.tooltipTheme = theme.tooltip;
236
+ writeTooltipStyles(tooltip, tooltipStyle) {
224
237
  tooltip
225
238
  .style('position', 'absolute')
226
- .style('background-color', theme.tooltip.background)
227
- .style('border', `${TOOLTIP_BORDER_WIDTH_PX}px solid ${theme.tooltip.border}`)
239
+ .style('background-color', tooltipStyle.background)
240
+ .style('border', `${TOOLTIP_BORDER_WIDTH_PX}px solid ${tooltipStyle.border}`)
228
241
  .style('border-radius', '4px')
229
242
  .style('padding', '8px')
230
243
  .style('box-shadow', '0 2px 4px rgba(0,0,0,0.1)')
231
- .style('color', theme.tooltip.color)
232
- .style('font-family', theme.tooltip.fontFamily)
233
- .style('font-size', `${theme.tooltip.fontSize}px`)
234
- .style('font-weight', theme.tooltip.fontWeight)
244
+ .style('color', tooltipStyle.color)
245
+ .style('font-family', tooltipStyle.fontFamily)
246
+ .style('font-size', `${tooltipStyle.fontSize}px`)
247
+ .style('font-weight', tooltipStyle.fontWeight)
235
248
  .style('box-sizing', 'border-box')
236
249
  .style('overflow-wrap', 'break-word')
237
250
  .style('overflow', 'visible')
@@ -351,7 +364,8 @@ export class TooltipDom {
351
364
  body.style('position', 'relative').style('z-index', String(TOOLTIP_BODY_Z_INDEX));
352
365
  }
353
366
  appendTooltipConnector(tooltip, connectorLayout) {
354
- const tooltipBorder = this.tooltipTheme?.border ?? '#dddddd';
367
+ const tooltipStyle = this.getTooltipStyle(tooltip);
368
+ const tooltipBorder = tooltipStyle?.border ?? '#dddddd';
355
369
  const connector = tooltip
356
370
  .append('svg')
357
371
  .attr('data-chart-tooltip-connector', 'true')
@@ -377,8 +391,9 @@ export class TooltipDom {
377
391
  .attr('stroke-linejoin', 'round');
378
392
  }
379
393
  appendTooltipArrow(tooltip, connectorLayout) {
380
- const tooltipBackground = this.tooltipTheme?.background ?? '#ffffff';
381
- const tooltipBorder = this.tooltipTheme?.border ?? '#dddddd';
394
+ const tooltipStyle = this.getTooltipStyle(tooltip);
395
+ const tooltipBackground = tooltipStyle?.background ?? '#ffffff';
396
+ const tooltipBorder = tooltipStyle?.border ?? '#dddddd';
382
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);
383
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);
384
399
  }
@@ -422,6 +437,10 @@ export class TooltipDom {
422
437
  .style('border-right', `${halfHeight}px solid transparent`)
423
438
  .style('border-top', `${length}px solid ${color}`);
424
439
  }
440
+ getTooltipStyle(tooltip) {
441
+ const node = tooltip.node();
442
+ return node ? this.tooltipStyles.get(node) : undefined;
443
+ }
425
444
  removeSplitTooltips() {
426
445
  document
427
446
  .querySelectorAll(`[data-chart-tooltip-owner="${this.splitTooltipOwner}"]`)
@@ -1,12 +1,13 @@
1
1
  import type { TooltipBarAnchorPosition, TooltipPosition } from '../types.js';
2
2
  import { type SplitTooltipLayout, type SplitTooltipViewportBounds, type TooltipAnchor, type TooltipArrowEdge, type TooltipConnectorLayout, type TooltipTarget, type XYTooltipSeries } from './types.js';
3
3
  export declare function getSplitTooltipViewportBounds(): SplitTooltipViewportBounds;
4
- export declare function resolveTooltipArrowEdge(position: TooltipPosition, anchor: TooltipAnchor, target: TooltipTarget, tooltipWidth: number, tooltipHeight: number): TooltipArrowEdge;
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;
5
6
  export declare function resolveSidePlacementArrowEdge(anchor: TooltipAnchor, tooltipWidth: number, bounds?: SplitTooltipViewportBounds): TooltipArrowEdge;
6
7
  export declare function resolveVerticalPlacementArrowEdge(target: TooltipTarget, tooltipHeight: number, bounds?: SplitTooltipViewportBounds): TooltipArrowEdge;
7
8
  export declare function resolveSharedTooltipTarget(anchor: TooltipAnchor): TooltipTarget;
8
9
  export declare function resolveSplitTooltipTarget(currentSeries: XYTooltipSeries, anchor: TooltipAnchor, barAnchorPosition: TooltipBarAnchorPosition): TooltipTarget;
9
- export declare function getAnchoredTooltipPosition(anchor: TooltipAnchor, target: TooltipTarget, tooltipWidth: number, tooltipHeight: number, arrowEdge: TooltipArrowEdge): {
10
+ export declare function getAnchoredTooltipPosition(anchor: TooltipAnchor, target: TooltipTarget, tooltipWidth: number, tooltipHeight: number, arrowEdge: TooltipArrowEdge, bounds?: SplitTooltipViewportBounds): {
10
11
  left: number;
11
12
  top: number;
12
13
  } | null;
@@ -15,4 +16,4 @@ export declare function resolveTooltipArrowPosition(arrowEdge: TooltipArrowEdge,
15
16
  left: number;
16
17
  top: number;
17
18
  };
18
- export declare function resolveSplitTooltipPositions(layouts: SplitTooltipLayout[], position: TooltipPosition): void;
19
+ export declare function resolveSplitTooltipPositions(layouts: SplitTooltipLayout[], position: TooltipPosition, bounds?: SplitTooltipViewportBounds, isHorizontal?: boolean): void;