@internetstiftelsen/charts 0.19.0 → 0.19.2
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.
|
@@ -39,6 +39,10 @@ export declare abstract class RadialChartBase extends BaseChart {
|
|
|
39
39
|
protected showTooltipAtElement(target: Element, content: string): void;
|
|
40
40
|
protected hideTooltip(): void;
|
|
41
41
|
private applyTooltipPosition;
|
|
42
|
+
private getTooltipViewportLeft;
|
|
43
|
+
private getTooltipViewportRight;
|
|
44
|
+
private getTooltipViewportTop;
|
|
45
|
+
private getTooltipViewportBottom;
|
|
42
46
|
private resolveRadialLabelLayout;
|
|
43
47
|
private resolveRadialLabelOverflowContext;
|
|
44
48
|
private createVisibleRadialLabelLayout;
|
|
@@ -159,7 +159,7 @@ export class RadialChartBase extends BaseChart {
|
|
|
159
159
|
}
|
|
160
160
|
let x = event.pageX + TOOLTIP_OFFSET_PX;
|
|
161
161
|
const y = event.pageY - rect.height / 2;
|
|
162
|
-
if (x + rect.width >
|
|
162
|
+
if (x + rect.width > this.getTooltipViewportRight()) {
|
|
163
163
|
x = event.pageX - rect.width - TOOLTIP_OFFSET_PX;
|
|
164
164
|
}
|
|
165
165
|
this.applyTooltipPosition(x, y, rect.height, rect.width);
|
|
@@ -182,7 +182,7 @@ export class RadialChartBase extends BaseChart {
|
|
|
182
182
|
window.scrollY +
|
|
183
183
|
targetRect.height / 2 -
|
|
184
184
|
tooltipRect.height / 2;
|
|
185
|
-
if (x + tooltipRect.width >
|
|
185
|
+
if (x + tooltipRect.width > this.getTooltipViewportRight()) {
|
|
186
186
|
x =
|
|
187
187
|
targetRect.left +
|
|
188
188
|
window.scrollX -
|
|
@@ -195,10 +195,22 @@ export class RadialChartBase extends BaseChart {
|
|
|
195
195
|
this.tooltip?.hide();
|
|
196
196
|
}
|
|
197
197
|
applyTooltipPosition(rawX, rawY, height, width) {
|
|
198
|
-
const x = Math.max(
|
|
199
|
-
const y = Math.max(
|
|
198
|
+
const x = Math.max(this.getTooltipViewportLeft(), Math.min(rawX, this.getTooltipViewportRight() - width));
|
|
199
|
+
const y = Math.max(this.getTooltipViewportTop(), Math.min(rawY, this.getTooltipViewportBottom() - height));
|
|
200
200
|
this.tooltip?.showAt(x, y);
|
|
201
201
|
}
|
|
202
|
+
getTooltipViewportLeft() {
|
|
203
|
+
return window.scrollX + EDGE_MARGIN_PX;
|
|
204
|
+
}
|
|
205
|
+
getTooltipViewportRight() {
|
|
206
|
+
return window.scrollX + window.innerWidth - EDGE_MARGIN_PX;
|
|
207
|
+
}
|
|
208
|
+
getTooltipViewportTop() {
|
|
209
|
+
return window.scrollY + EDGE_MARGIN_PX;
|
|
210
|
+
}
|
|
211
|
+
getTooltipViewportBottom() {
|
|
212
|
+
return window.scrollY + window.innerHeight - EDGE_MARGIN_PX;
|
|
213
|
+
}
|
|
202
214
|
resolveRadialLabelLayout(text, options) {
|
|
203
215
|
const overflowContext = this.resolveRadialLabelOverflowContext(options.maxLabelWidth);
|
|
204
216
|
if (!overflowContext) {
|
package/dist/tooltip/dom.d.ts
CHANGED
|
@@ -17,6 +17,7 @@ export declare class TooltipDom {
|
|
|
17
17
|
private readonly transition;
|
|
18
18
|
private readonly tooltipStyleKeys;
|
|
19
19
|
private readonly tooltipTransitionFrameIds;
|
|
20
|
+
private readonly tooltipPositionResetTimeoutIds;
|
|
20
21
|
private readonly tooltipStyles;
|
|
21
22
|
private tooltipDiv;
|
|
22
23
|
constructor(config: TooltipDomConfig);
|
|
@@ -45,6 +46,9 @@ export declare class TooltipDom {
|
|
|
45
46
|
private showTooltipAt;
|
|
46
47
|
private showTooltipSelection;
|
|
47
48
|
private hideTooltipElement;
|
|
49
|
+
private resetTooltipPosition;
|
|
50
|
+
private scheduleTooltipPositionReset;
|
|
51
|
+
private cancelTooltipPositionReset;
|
|
48
52
|
private getTooltipTransitionStyle;
|
|
49
53
|
private isTooltipVisible;
|
|
50
54
|
private getTooltipPosition;
|
package/dist/tooltip/dom.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { select } from 'd3';
|
|
2
2
|
import { resolveTooltipArrowPosition, resolveTooltipConnectorLayout, } from './geometry.js';
|
|
3
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
|
+
const HIDDEN_TOOLTIP_POSITION_PX = '0px';
|
|
4
5
|
export class TooltipDom {
|
|
5
6
|
constructor(config) {
|
|
6
7
|
Object.defineProperty(this, "id", {
|
|
@@ -39,6 +40,12 @@ export class TooltipDom {
|
|
|
39
40
|
writable: true,
|
|
40
41
|
value: new WeakMap()
|
|
41
42
|
});
|
|
43
|
+
Object.defineProperty(this, "tooltipPositionResetTimeoutIds", {
|
|
44
|
+
enumerable: true,
|
|
45
|
+
configurable: true,
|
|
46
|
+
writable: true,
|
|
47
|
+
value: new WeakMap()
|
|
48
|
+
});
|
|
42
49
|
Object.defineProperty(this, "tooltipStyles", {
|
|
43
50
|
enumerable: true,
|
|
44
51
|
configurable: true,
|
|
@@ -284,15 +291,16 @@ export class TooltipDom {
|
|
|
284
291
|
: null);
|
|
285
292
|
}
|
|
286
293
|
showTooltipSelection(tooltip, slideOffset = null) {
|
|
294
|
+
const node = tooltip.node();
|
|
295
|
+
if (!node) {
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
this.cancelTooltipPositionReset(node);
|
|
287
299
|
tooltip.style('visibility', 'visible');
|
|
288
300
|
if (!this.transition.show) {
|
|
289
301
|
return;
|
|
290
302
|
}
|
|
291
303
|
tooltip.style('opacity', '1');
|
|
292
|
-
const node = tooltip.node();
|
|
293
|
-
if (!node) {
|
|
294
|
-
return;
|
|
295
|
-
}
|
|
296
304
|
if (!slideOffset || !this.hasVisibleSlideOffset(slideOffset)) {
|
|
297
305
|
this.cancelTooltipTransitionFrame(node);
|
|
298
306
|
tooltip.style('transform', TOOLTIP_VISIBLE_TRANSFORM);
|
|
@@ -301,14 +309,43 @@ export class TooltipDom {
|
|
|
301
309
|
this.slideTooltipFromOffset(node, slideOffset);
|
|
302
310
|
}
|
|
303
311
|
hideTooltipElement(node) {
|
|
312
|
+
const wasVisible = this.isTooltipVisible(node);
|
|
313
|
+
this.cancelTooltipPositionReset(node);
|
|
304
314
|
this.cancelTooltipTransitionFrame(node);
|
|
305
315
|
if (!this.transition.show) {
|
|
306
316
|
node.style.visibility = 'hidden';
|
|
317
|
+
this.resetTooltipPosition(node);
|
|
307
318
|
return;
|
|
308
319
|
}
|
|
309
320
|
node.style.visibility = 'visible';
|
|
310
321
|
node.style.opacity = '0';
|
|
311
322
|
node.style.transform = TOOLTIP_HIDDEN_TRANSFORM;
|
|
323
|
+
if (!wasVisible) {
|
|
324
|
+
this.resetTooltipPosition(node);
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
this.scheduleTooltipPositionReset(node);
|
|
328
|
+
}
|
|
329
|
+
resetTooltipPosition(node) {
|
|
330
|
+
node.style.left = HIDDEN_TOOLTIP_POSITION_PX;
|
|
331
|
+
node.style.top = HIDDEN_TOOLTIP_POSITION_PX;
|
|
332
|
+
}
|
|
333
|
+
scheduleTooltipPositionReset(node) {
|
|
334
|
+
const timeoutId = window.setTimeout(() => {
|
|
335
|
+
this.tooltipPositionResetTimeoutIds.delete(node);
|
|
336
|
+
if (!this.isTooltipVisible(node)) {
|
|
337
|
+
this.resetTooltipPosition(node);
|
|
338
|
+
}
|
|
339
|
+
}, this.transition.duration);
|
|
340
|
+
this.tooltipPositionResetTimeoutIds.set(node, timeoutId);
|
|
341
|
+
}
|
|
342
|
+
cancelTooltipPositionReset(node) {
|
|
343
|
+
const timeoutId = this.tooltipPositionResetTimeoutIds.get(node);
|
|
344
|
+
if (timeoutId === undefined) {
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
window.clearTimeout(timeoutId);
|
|
348
|
+
this.tooltipPositionResetTimeoutIds.delete(node);
|
|
312
349
|
}
|
|
313
350
|
getTooltipTransitionStyle() {
|
|
314
351
|
return `opacity ${this.transition.duration}ms ${this.transition.easing}, transform ${this.transition.duration}ms ${this.transition.easing}`;
|
package/dist/tooltip/geometry.js
CHANGED
|
@@ -525,8 +525,97 @@ function packHorizontalAboveBelowRow(layouts, arrowEdge, bounds) {
|
|
|
525
525
|
top: getHorizontalAboveBelowTop(layout, arrowEdge),
|
|
526
526
|
arrowEdge,
|
|
527
527
|
}));
|
|
528
|
+
if (shouldWrapHorizontalAboveBelowRow(placements, arrowEdge, bounds)) {
|
|
529
|
+
return packHorizontalAboveBelowRows(placements, arrowEdge, bounds);
|
|
530
|
+
}
|
|
528
531
|
return packHorizontalTooltipRow(placements, bounds);
|
|
529
532
|
}
|
|
533
|
+
function packHorizontalAboveBelowRows(placements, arrowEdge, bounds) {
|
|
534
|
+
const rows = getHorizontalTooltipRows(placements, bounds);
|
|
535
|
+
if (arrowEdge === 'bottom') {
|
|
536
|
+
positionHorizontalTooltipRowsAbove(rows);
|
|
537
|
+
}
|
|
538
|
+
else {
|
|
539
|
+
positionHorizontalTooltipRowsBelow(rows);
|
|
540
|
+
}
|
|
541
|
+
return rows.flatMap((row) => packHorizontalTooltipRow(row, bounds));
|
|
542
|
+
}
|
|
543
|
+
function shouldWrapHorizontalAboveBelowRow(placements, arrowEdge, bounds) {
|
|
544
|
+
if (placements.length < 2) {
|
|
545
|
+
return false;
|
|
546
|
+
}
|
|
547
|
+
const availableWidth = Math.max(0, bounds.maxRight - bounds.minLeft);
|
|
548
|
+
const totalWidth = placements.reduce((sum, placement, index) => {
|
|
549
|
+
const gap = index === 0 ? 0 : SPLIT_TOOLTIP_GAP_PX;
|
|
550
|
+
return sum + gap + placement.layout.width;
|
|
551
|
+
}, 0);
|
|
552
|
+
if (totalWidth <= availableWidth) {
|
|
553
|
+
return false;
|
|
554
|
+
}
|
|
555
|
+
return !hasRoomOnOppositeHorizontalTooltipSide(placements, arrowEdge, bounds);
|
|
556
|
+
}
|
|
557
|
+
function hasRoomOnOppositeHorizontalTooltipSide(placements, arrowEdge, bounds) {
|
|
558
|
+
const rowHeight = getHorizontalTooltipRowHeight(placements);
|
|
559
|
+
if (arrowEdge === 'top') {
|
|
560
|
+
const oppositeTop = Math.min(...placements.map((placement) => placement.layout.anchor.top)) -
|
|
561
|
+
TOOLTIP_OFFSET_PX -
|
|
562
|
+
rowHeight;
|
|
563
|
+
return oppositeTop >= bounds.minTop;
|
|
564
|
+
}
|
|
565
|
+
const oppositeBottom = Math.max(...placements.map((placement) => placement.layout.anchor.bottom)) +
|
|
566
|
+
TOOLTIP_OFFSET_PX +
|
|
567
|
+
rowHeight;
|
|
568
|
+
return oppositeBottom <= bounds.maxBottom;
|
|
569
|
+
}
|
|
570
|
+
function getHorizontalTooltipRows(placements, bounds) {
|
|
571
|
+
const availableWidth = Math.max(0, bounds.maxRight - bounds.minLeft);
|
|
572
|
+
const rows = [];
|
|
573
|
+
let currentRow = [];
|
|
574
|
+
let currentRowWidth = 0;
|
|
575
|
+
placements.forEach((placement) => {
|
|
576
|
+
const nextRowWidth = currentRow.length === 0
|
|
577
|
+
? placement.layout.width
|
|
578
|
+
: currentRowWidth +
|
|
579
|
+
SPLIT_TOOLTIP_GAP_PX +
|
|
580
|
+
placement.layout.width;
|
|
581
|
+
if (currentRow.length > 0 && nextRowWidth > availableWidth) {
|
|
582
|
+
rows.push(currentRow);
|
|
583
|
+
currentRow = [placement];
|
|
584
|
+
currentRowWidth = placement.layout.width;
|
|
585
|
+
return;
|
|
586
|
+
}
|
|
587
|
+
currentRow.push(placement);
|
|
588
|
+
currentRowWidth = nextRowWidth;
|
|
589
|
+
});
|
|
590
|
+
if (currentRow.length > 0) {
|
|
591
|
+
rows.push(currentRow);
|
|
592
|
+
}
|
|
593
|
+
return rows;
|
|
594
|
+
}
|
|
595
|
+
function positionHorizontalTooltipRowsAbove(rows) {
|
|
596
|
+
let nextRowBottom = Math.min(...rows.flatMap((row) => row.map((placement) => placement.top + placement.layout.height)));
|
|
597
|
+
rows.forEach((row) => {
|
|
598
|
+
const rowHeight = getHorizontalTooltipRowHeight(row);
|
|
599
|
+
const rowTop = nextRowBottom - rowHeight;
|
|
600
|
+
row.forEach((placement) => {
|
|
601
|
+
placement.top = rowTop;
|
|
602
|
+
});
|
|
603
|
+
nextRowBottom = rowTop - SPLIT_TOOLTIP_GAP_PX;
|
|
604
|
+
});
|
|
605
|
+
}
|
|
606
|
+
function positionHorizontalTooltipRowsBelow(rows) {
|
|
607
|
+
let nextRowTop = Math.max(...rows.flatMap((row) => row.map((placement) => placement.top)));
|
|
608
|
+
rows.forEach((row) => {
|
|
609
|
+
const rowHeight = getHorizontalTooltipRowHeight(row);
|
|
610
|
+
row.forEach((placement) => {
|
|
611
|
+
placement.top = nextRowTop;
|
|
612
|
+
});
|
|
613
|
+
nextRowTop += rowHeight + SPLIT_TOOLTIP_GAP_PX;
|
|
614
|
+
});
|
|
615
|
+
}
|
|
616
|
+
function getHorizontalTooltipRowHeight(row) {
|
|
617
|
+
return Math.max(...row.map((placement) => placement.layout.height));
|
|
618
|
+
}
|
|
530
619
|
function packHorizontalTooltipRow(placements, bounds) {
|
|
531
620
|
if (placements.length === 0) {
|
|
532
621
|
return placements;
|
|
@@ -809,7 +809,7 @@ function attachTooltipScrollListeners(svgNode, onScroll) {
|
|
|
809
809
|
};
|
|
810
810
|
}
|
|
811
811
|
function getTooltipScrollTargets(svgNode) {
|
|
812
|
-
const scrollTargets = new Set(
|
|
812
|
+
const scrollTargets = new Set();
|
|
813
813
|
let currentElement = svgNode.parentElement;
|
|
814
814
|
while (currentElement) {
|
|
815
815
|
if (isScrollableElement(currentElement)) {
|
package/package.json
CHANGED