@internetstiftelsen/charts 0.13.2 → 0.13.3

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/dist/tooltip.d.ts CHANGED
@@ -81,6 +81,10 @@ export declare class Tooltip implements ChartComponent<TooltipConfigBase> {
81
81
  private countPlacedLayoutsOnEdge;
82
82
  private doSplitTooltipLayoutsOverlap;
83
83
  private resolveSplitTooltipPositions;
84
+ private resolveHorizontalChartSplitTooltipPositions;
85
+ private resolveVerticalChartSplitTooltipPositions;
86
+ private getSplitTooltipViewportBounds;
87
+ private groupSplitTooltipLayoutsByEdge;
84
88
  private resolveSideSplitTooltipPositions;
85
89
  private resolveHorizontalSideSplitTooltipPositions;
86
90
  private resolveHorizontalSideSplitTooltipCollisions;
@@ -91,6 +95,11 @@ export declare class Tooltip implements ChartComponent<TooltipConfigBase> {
91
95
  private assignLayoutToHorizontalTooltipLane;
92
96
  private doHorizontalTooltipLanesOverlap;
93
97
  private doSplitTooltipLayoutsOverlapHorizontally;
94
- private resolveVerticalSplitTooltipPositions;
98
+ private resolveHorizontalChartAboveBelowSplitTooltipPositions;
99
+ private resolvePackedAboveBelowTooltipRow;
100
+ private resolveVerticalChartAboveBelowSplitTooltipPositions;
101
+ private resolveCollisionAwareAboveBelowTooltipPositions;
102
+ private resolveNonOverlappingAboveBelowTooltipLeft;
103
+ private doesSplitTooltipOverlapPlacedLayouts;
95
104
  }
96
105
  export {};
package/dist/tooltip.js CHANGED
@@ -466,10 +466,6 @@ export class Tooltip {
466
466
  if (layouts.length === 0) {
467
467
  return;
468
468
  }
469
- if (!isHorizontal) {
470
- this.resolveSplitTooltipCollisions(layouts, 'vertical', this.getOppositeVerticalArrowEdge);
471
- this.resolveSplitTooltipCollisions(layouts, 'side', this.getOppositeSideArrowEdge);
472
- }
473
469
  this.resolveSplitTooltipPositions(layouts, isHorizontal);
474
470
  layouts.forEach((layout) => {
475
471
  this.renderTooltipWithConnector(layout.div, layout.arrowEdge, layout.left, layout.top, layout.width, layout.height, layout.targetX, layout.targetY, layout.anchor);
@@ -1303,19 +1299,42 @@ export class Tooltip {
1303
1299
  a.top + a.height + SPLIT_TOOLTIP_GAP_PX > b.top);
1304
1300
  }
1305
1301
  resolveSplitTooltipPositions(layouts, isHorizontal) {
1306
- if (isHorizontal && this.position === 'side') {
1307
- this.resolveHorizontalSideSplitTooltipPositions(layouts);
1302
+ if (isHorizontal) {
1303
+ this.resolveHorizontalChartSplitTooltipPositions(layouts);
1308
1304
  return;
1309
1305
  }
1306
+ this.resolveVerticalChartSplitTooltipPositions(layouts);
1307
+ }
1308
+ resolveHorizontalChartSplitTooltipPositions(layouts) {
1310
1309
  if (this.position === 'vertical') {
1311
- this.resolveVerticalSplitTooltipPositions(layouts);
1310
+ this.resolveSplitTooltipCollisions(layouts, 'vertical', this.getOppositeVerticalArrowEdge);
1311
+ this.resolveHorizontalChartAboveBelowSplitTooltipPositions(layouts);
1312
1312
  return;
1313
1313
  }
1314
+ this.resolveHorizontalSideSplitTooltipPositions(layouts);
1315
+ }
1316
+ resolveVerticalChartSplitTooltipPositions(layouts) {
1317
+ if (this.position === 'vertical') {
1318
+ this.resolveSplitTooltipCollisions(layouts, 'vertical', this.getOppositeVerticalArrowEdge);
1319
+ this.resolveVerticalChartAboveBelowSplitTooltipPositions(layouts);
1320
+ return;
1321
+ }
1322
+ this.resolveSplitTooltipCollisions(layouts, 'side', this.getOppositeSideArrowEdge);
1314
1323
  this.resolveSideSplitTooltipPositions(layouts);
1315
1324
  }
1316
- resolveSideSplitTooltipPositions(layouts) {
1317
- const minTop = window.scrollY + TOOLTIP_VIEWPORT_PADDING_PX;
1318
- const maxBottom = window.scrollY + window.innerHeight - TOOLTIP_VIEWPORT_PADDING_PX;
1325
+ getSplitTooltipViewportBounds() {
1326
+ return {
1327
+ minLeft: window.scrollX + TOOLTIP_VIEWPORT_PADDING_PX,
1328
+ maxRight: window.scrollX +
1329
+ window.innerWidth -
1330
+ TOOLTIP_VIEWPORT_PADDING_PX,
1331
+ minTop: window.scrollY + TOOLTIP_VIEWPORT_PADDING_PX,
1332
+ maxBottom: window.scrollY +
1333
+ window.innerHeight -
1334
+ TOOLTIP_VIEWPORT_PADDING_PX,
1335
+ };
1336
+ }
1337
+ groupSplitTooltipLayoutsByEdge(layouts) {
1319
1338
  const tooltipsByEdge = {
1320
1339
  left: [],
1321
1340
  right: [],
@@ -1325,6 +1344,11 @@ export class Tooltip {
1325
1344
  layouts.forEach((layout) => {
1326
1345
  tooltipsByEdge[layout.arrowEdge].push(layout);
1327
1346
  });
1347
+ return tooltipsByEdge;
1348
+ }
1349
+ resolveSideSplitTooltipPositions(layouts) {
1350
+ const { minTop, maxBottom } = this.getSplitTooltipViewportBounds();
1351
+ const tooltipsByEdge = this.groupSplitTooltipLayoutsByEdge(layouts);
1328
1352
  Object.values(tooltipsByEdge).forEach((edgeLayouts) => {
1329
1353
  if (edgeLayouts.length === 0) {
1330
1354
  return;
@@ -1463,65 +1487,98 @@ export class Tooltip {
1463
1487
  return (a.left < b.left + b.width + SPLIT_TOOLTIP_GAP_PX &&
1464
1488
  a.left + a.width + SPLIT_TOOLTIP_GAP_PX > b.left);
1465
1489
  }
1466
- resolveVerticalSplitTooltipPositions(layouts) {
1467
- const minLeft = window.scrollX + TOOLTIP_VIEWPORT_PADDING_PX;
1468
- const maxRight = window.scrollX + window.innerWidth - TOOLTIP_VIEWPORT_PADDING_PX;
1469
- const minTop = window.scrollY + TOOLTIP_VIEWPORT_PADDING_PX;
1470
- const maxBottom = window.scrollY + window.innerHeight - TOOLTIP_VIEWPORT_PADDING_PX;
1471
- const tooltipsByEdge = {
1472
- left: [],
1473
- right: [],
1474
- top: [],
1475
- bottom: [],
1476
- };
1477
- layouts.forEach((layout) => {
1478
- tooltipsByEdge[layout.arrowEdge].push(layout);
1490
+ resolveHorizontalChartAboveBelowSplitTooltipPositions(layouts) {
1491
+ const bounds = this.getSplitTooltipViewportBounds();
1492
+ const tooltipsByEdge = this.groupSplitTooltipLayoutsByEdge(layouts);
1493
+ this.resolvePackedAboveBelowTooltipRow(tooltipsByEdge.top, bounds);
1494
+ this.resolvePackedAboveBelowTooltipRow(tooltipsByEdge.bottom, bounds);
1495
+ this.resolveSideSplitTooltipPositions([
1496
+ ...tooltipsByEdge.left,
1497
+ ...tooltipsByEdge.right,
1498
+ ]);
1499
+ }
1500
+ resolvePackedAboveBelowTooltipRow(layouts, bounds) {
1501
+ if (layouts.length === 0) {
1502
+ return;
1503
+ }
1504
+ const orderedLayouts = [...layouts].sort((a, b) => a.left - b.left || a.targetX - b.targetX);
1505
+ orderedLayouts.forEach((layout) => {
1506
+ const maxTop = Math.max(bounds.minTop, bounds.maxBottom - layout.height);
1507
+ layout.top = Math.max(bounds.minTop, Math.min(layout.top, maxTop));
1479
1508
  });
1480
- [tooltipsByEdge.top, tooltipsByEdge.bottom].forEach((edgeLayouts) => {
1481
- if (edgeLayouts.length === 0) {
1482
- return;
1483
- }
1484
- edgeLayouts.sort((a, b) => a.left - b.left);
1485
- edgeLayouts[0].left = Math.max(minLeft, edgeLayouts[0].left);
1486
- for (let i = 1; i < edgeLayouts.length; i++) {
1487
- const previousLayout = edgeLayouts[i - 1];
1488
- const currentLayout = edgeLayouts[i];
1489
- const minAllowedLeft = previousLayout.left +
1490
- previousLayout.width +
1509
+ const firstLayout = orderedLayouts[0];
1510
+ const firstMaxLeft = Math.max(bounds.minLeft, bounds.maxRight - firstLayout.width);
1511
+ firstLayout.left = Math.max(bounds.minLeft, Math.min(firstLayout.left, firstMaxLeft));
1512
+ for (let i = 1; i < orderedLayouts.length; i++) {
1513
+ const previousLayout = orderedLayouts[i - 1];
1514
+ const currentLayout = orderedLayouts[i];
1515
+ const minAllowedLeft = previousLayout.left +
1516
+ previousLayout.width +
1517
+ SPLIT_TOOLTIP_GAP_PX;
1518
+ currentLayout.left = Math.max(currentLayout.left, minAllowedLeft);
1519
+ }
1520
+ const lastLayout = orderedLayouts[orderedLayouts.length - 1];
1521
+ const overflow = lastLayout.left + lastLayout.width - bounds.maxRight;
1522
+ if (overflow > 0) {
1523
+ lastLayout.left -= overflow;
1524
+ for (let i = orderedLayouts.length - 2; i >= 0; i--) {
1525
+ const currentLayout = orderedLayouts[i];
1526
+ const nextLayout = orderedLayouts[i + 1];
1527
+ const maxAllowedLeft = nextLayout.left -
1528
+ currentLayout.width -
1491
1529
  SPLIT_TOOLTIP_GAP_PX;
1492
- currentLayout.left = Math.max(currentLayout.left, minAllowedLeft);
1530
+ currentLayout.left = Math.min(currentLayout.left, maxAllowedLeft);
1493
1531
  }
1494
- const lastLayout = edgeLayouts[edgeLayouts.length - 1];
1495
- const overflow = lastLayout.left + lastLayout.width - maxRight;
1496
- if (overflow > 0) {
1497
- lastLayout.left -= overflow;
1498
- for (let i = edgeLayouts.length - 2; i >= 0; i--) {
1499
- const currentLayout = edgeLayouts[i];
1500
- const nextLayout = edgeLayouts[i + 1];
1501
- const maxAllowedLeft = nextLayout.left -
1502
- currentLayout.width -
1503
- SPLIT_TOOLTIP_GAP_PX;
1504
- currentLayout.left = Math.min(currentLayout.left, maxAllowedLeft);
1505
- }
1506
- const underflow = minLeft - edgeLayouts[0].left;
1507
- if (underflow > 0) {
1508
- edgeLayouts.forEach((layout) => {
1509
- layout.left += underflow;
1510
- });
1511
- }
1532
+ const underflow = bounds.minLeft - orderedLayouts[0].left;
1533
+ if (underflow > 0) {
1534
+ orderedLayouts.forEach((layout) => {
1535
+ layout.left += underflow;
1536
+ });
1512
1537
  }
1513
- edgeLayouts.forEach((layout) => {
1514
- const maxLeft = maxRight - layout.width;
1515
- const maxTop = maxBottom - layout.height;
1516
- layout.left = Math.max(minLeft, Math.min(layout.left, maxLeft));
1517
- layout.top = Math.max(minTop, Math.min(layout.top, maxTop));
1518
- });
1538
+ }
1539
+ orderedLayouts.forEach((layout) => {
1540
+ const maxLeft = Math.max(bounds.minLeft, bounds.maxRight - layout.width);
1541
+ layout.left = Math.max(bounds.minLeft, Math.min(layout.left, maxLeft));
1519
1542
  });
1543
+ }
1544
+ resolveVerticalChartAboveBelowSplitTooltipPositions(layouts) {
1545
+ const bounds = this.getSplitTooltipViewportBounds();
1546
+ const tooltipsByEdge = this.groupSplitTooltipLayoutsByEdge(layouts);
1547
+ this.resolveCollisionAwareAboveBelowTooltipPositions(tooltipsByEdge.top, bounds);
1548
+ this.resolveCollisionAwareAboveBelowTooltipPositions(tooltipsByEdge.bottom, bounds);
1520
1549
  this.resolveSideSplitTooltipPositions([
1521
1550
  ...tooltipsByEdge.left,
1522
1551
  ...tooltipsByEdge.right,
1523
1552
  ]);
1524
1553
  }
1554
+ resolveCollisionAwareAboveBelowTooltipPositions(layouts, bounds) {
1555
+ const placedLayouts = [];
1556
+ const orderedLayouts = [...layouts].sort((a, b) => a.left - b.left || a.top - b.top);
1557
+ orderedLayouts.forEach((layout) => {
1558
+ const maxLeft = Math.max(bounds.minLeft, bounds.maxRight - layout.width);
1559
+ const maxTop = Math.max(bounds.minTop, bounds.maxBottom - layout.height);
1560
+ layout.top = Math.max(bounds.minTop, Math.min(layout.top, maxTop));
1561
+ layout.left = this.resolveNonOverlappingAboveBelowTooltipLeft(layout, placedLayouts, bounds.minLeft, maxLeft);
1562
+ placedLayouts.push(layout);
1563
+ });
1564
+ }
1565
+ resolveNonOverlappingAboveBelowTooltipLeft(layout, placedLayouts, minLeft, maxLeft) {
1566
+ const preferredLeft = Math.max(minLeft, Math.min(layout.left, maxLeft));
1567
+ if (!this.doesSplitTooltipOverlapPlacedLayouts({ ...layout, left: preferredLeft }, placedLayouts)) {
1568
+ return preferredLeft;
1569
+ }
1570
+ const candidates = placedLayouts.flatMap((placedLayout) => [
1571
+ placedLayout.left + placedLayout.width + SPLIT_TOOLTIP_GAP_PX,
1572
+ placedLayout.left - layout.width - SPLIT_TOOLTIP_GAP_PX,
1573
+ ]);
1574
+ return (Array.from(new Set(candidates.map((candidate) => Math.round(Math.max(minLeft, Math.min(candidate, maxLeft))))))
1575
+ .sort((a, b) => Math.abs(a - preferredLeft) -
1576
+ Math.abs(b - preferredLeft))
1577
+ .find((candidate) => !this.doesSplitTooltipOverlapPlacedLayouts({ ...layout, left: candidate }, placedLayouts)) ?? preferredLeft);
1578
+ }
1579
+ doesSplitTooltipOverlapPlacedLayouts(layout, placedLayouts) {
1580
+ return placedLayouts.some((placedLayout) => this.doSplitTooltipLayoutsOverlap(layout, placedLayout));
1581
+ }
1525
1582
  }
1526
1583
  Object.defineProperty(Tooltip, "nextTooltipId", {
1527
1584
  enumerable: true,
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.13.2",
2
+ "version": "0.13.3",
3
3
  "name": "@internetstiftelsen/charts",
4
4
  "type": "module",
5
5
  "sideEffects": false,