evui 3.4.123 → 3.4.201

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "evui",
3
- "version": "3.4.123",
3
+ "version": "3.4.201",
4
4
  "description": "A EXEM Library project",
5
5
  "author": "exem <dev_client@ex-em.com>",
6
6
  "license": "MIT",
@@ -76,8 +76,14 @@ class Bar {
76
76
 
77
77
  const xArea = chartRect.chartWidth - (labelOffset.left + labelOffset.right);
78
78
  const yArea = chartRect.chartHeight - (labelOffset.top + labelOffset.bottom);
79
- const xsp = chartRect.x1 + labelOffset.left;
80
- const ysp = chartRect.y2 - labelOffset.bottom;
79
+
80
+ const xAxisPosition = chartRect.x1 + labelOffset.left;
81
+ const yAxisPosition = chartRect.y2 - labelOffset.bottom;
82
+ const xZeroPosition = Canvas.calculateX(0, minmaxX.graphMin, minmaxX.graphMax, xArea);
83
+ const yZeroPosition = Canvas.calculateY(0, minmaxY.graphMin, minmaxY.graphMax, yArea);
84
+
85
+ const xsp = isHorizontal ? xAxisPosition + xZeroPosition : xAxisPosition;
86
+ const ysp = isHorizontal ? yAxisPosition : yAxisPosition + yZeroPosition;
81
87
 
82
88
  const dArea = isHorizontal ? yArea : xArea;
83
89
  const cArea = dArea / (totalCount || 1);
@@ -135,7 +141,6 @@ class Bar {
135
141
  const item = this.data[i]; // 실제 데이터 인덱스에 해당하는 항목
136
142
  if (item) {
137
143
  // 스크롤 offset(minIndex)만큼 보정해서 그리기
138
-
139
144
  let categoryPoint;
140
145
  if (isHorizontal) {
141
146
  categoryPoint = ysp - (cArea * (screenIndex)) - cPad;
@@ -143,6 +148,7 @@ class Bar {
143
148
  categoryPoint = xsp + (cArea * (screenIndex)) + cPad;
144
149
  }
145
150
 
151
+ // 기본 위치 설정
146
152
  if (isHorizontal) {
147
153
  x = xsp;
148
154
  y = Math.round(categoryPoint - ((bArea * barSeriesX) - (h + bPad)));
@@ -151,22 +157,40 @@ class Bar {
151
157
  y = ysp;
152
158
  }
153
159
 
160
+ // 너비 / 높이 계산, 스택의 경우 위치 값 재계산
154
161
  if (isHorizontal) {
162
+ const barValue = item.b ? item.o : item.x;
163
+
164
+ w = Canvas.calculateX(
165
+ barValue, minmaxX.graphMin, minmaxX.graphMax, xArea, -xZeroPosition,
166
+ );
167
+
155
168
  if (item.b) {
156
- w = Canvas.calculateX(item.x - item.b, minmaxX.graphMin, minmaxX.graphMax, xArea);
157
- x = Canvas.calculateX(item.b, minmaxX.graphMin, minmaxX.graphMax, xArea, xsp);
158
- } else {
159
- w = Canvas.calculateX(item.x, minmaxX.graphMin, minmaxX.graphMax, xArea);
169
+ x = Canvas.calculateX(
170
+ item.b, minmaxX.graphMin, minmaxX.graphMax, xArea, xsp - xZeroPosition,
171
+ );
160
172
  }
161
- } else if (item.b) { // vertical stack bar chart
162
- h = Canvas.calculateY(item.y - item.b, minmaxY.graphMin, minmaxY.graphMax, yArea);
163
- y = Canvas.calculateY(item.b, minmaxY.graphMin, minmaxY.graphMax, yArea, ysp);
164
- } else { // vertical bar chart
165
- h = Canvas.calculateY(item.y, minmaxY.graphMin, minmaxY.graphMax, yArea);
173
+
174
+ const minimumBarWidth = barValue > 0 ? -1 : 1;
175
+ w = barValue && Math.abs(w) === 0 ? minimumBarWidth : w;
176
+ } else {
177
+ const barValue = item.b ? item.o : item.y;
178
+
179
+ h = Canvas.calculateY(
180
+ barValue, minmaxY.graphMin, minmaxY.graphMax, yArea, -yZeroPosition,
181
+ );
182
+
183
+ if (item.b) {
184
+ y = Canvas.calculateY(
185
+ item.b, minmaxY.graphMin, minmaxY.graphMax, yArea, ysp - yZeroPosition,
186
+ );
187
+ }
188
+
189
+ const minimumBarHeight = barValue > 0 ? -1 : 1;
190
+ h = barValue && Math.abs(h) === 0 ? minimumBarHeight : h;
166
191
  }
167
192
 
168
193
  const barColor = item.dataColor || this.color;
169
-
170
194
  const legendHitInfo = param?.legendHitInfo;
171
195
  const selectLabelOption = param?.selectLabel?.option;
172
196
  const selectItemOption = param?.selectItem?.option;
@@ -408,7 +432,7 @@ class Bar {
408
432
  drawValueLabels({ context, data, positions, isHighlight, textColor, index }) {
409
433
  const isHorizontal = this.isHorizontal;
410
434
  const { fontSize, textColor: seriesTextColor, align, formatter, decimalPoint } = this.showValue;
411
- const { x, y, w, h } = positions;
435
+ const { x: barX, y: barY, w: barWidth, h: barHeight } = positions;
412
436
  const ctx = context;
413
437
 
414
438
  ctx.save();
@@ -440,36 +464,40 @@ class Bar {
440
464
  formattedTxt = Util.labelSignFormat(value, decimalPoint) ?? '';
441
465
  }
442
466
 
467
+ const isNegativeValue = value < 0;
443
468
  const textWidth = Math.round(ctx.measureText(formattedTxt).width);
444
- const textHeight = fontSize + 4;
445
- const minXPos = x + 10;
446
- const minYPos = y - 10;
447
- const widthFreeSpaceToDraw = w - 10;
448
- const heightFreeSpaceToDraw = Math.abs(h + 10);
449
- const centerX = x + (w / 2) <= minXPos ? minXPos : x + (w / 2);
450
- const centerY = y + (h / 2) >= minYPos ? minYPos : y + (h / 2);
451
- const centerYHorizontal = isHighlight ? y + (h / 2) : y - (h / 2);
469
+ const textHeight = fontSize; // fontSize와 textHeight는 같을 수 없지만, 정확히 구할 필요 없음
470
+
471
+ const GAP = 10;
472
+ const minXPos = isNegativeValue ? barX - GAP : barX + GAP;
473
+ const minYPos = isNegativeValue ? barY + GAP : barY - GAP;
474
+
475
+ const centerXOnBar = barX + (barWidth / 2);
476
+ const centerYOnBar = isHighlight ? barY + (barHeight / 2) : barY - (barHeight / 2);
477
+
478
+ const drawableBarWidth = Math.abs(barWidth) - GAP;
479
+ const drawableBarHeight = Math.abs(barHeight) - GAP;
452
480
 
453
481
  switch (align) {
454
482
  case 'start': {
455
- if (isHorizontal) {
456
- if (textWidth < widthFreeSpaceToDraw) {
457
- ctx.fillText(formattedTxt, minXPos, centerYHorizontal);
458
- }
459
- } else if (textHeight < heightFreeSpaceToDraw) {
460
- ctx.fillText(formattedTxt, centerX, minYPos);
483
+ if (isHorizontal && textWidth < drawableBarWidth) {
484
+ const xPos = isNegativeValue ? minXPos - textWidth : minXPos;
485
+ ctx.fillText(formattedTxt, xPos, centerYOnBar);
486
+ } else if (!isHorizontal && textHeight < drawableBarHeight) {
487
+ const yPos = isNegativeValue
488
+ ? barY + GAP
489
+ : barY - GAP;
490
+ ctx.fillText(formattedTxt, centerXOnBar, yPos);
461
491
  }
462
492
 
463
493
  break;
464
494
  }
465
495
 
466
496
  case 'center': {
467
- if (isHorizontal) {
468
- if (textWidth < widthFreeSpaceToDraw) {
469
- ctx.fillText(formattedTxt, centerX, centerYHorizontal);
470
- }
471
- } else if (textHeight < heightFreeSpaceToDraw) {
472
- ctx.fillText(formattedTxt, centerX, centerY);
497
+ if (isHorizontal && textWidth < drawableBarWidth) {
498
+ ctx.fillText(formattedTxt, centerXOnBar, centerYOnBar);
499
+ } else if (!isHorizontal && textHeight < drawableBarHeight) {
500
+ ctx.fillText(formattedTxt, centerXOnBar, barY + (barHeight / 2));
473
501
  }
474
502
 
475
503
  break;
@@ -482,9 +510,25 @@ class Bar {
482
510
  }
483
511
 
484
512
  if (isHorizontal) {
485
- ctx.fillText(formattedTxt, minXPos + w, centerYHorizontal);
513
+ const minXOnChart = this.chartRect.x1 + this.labelOffset.left;
514
+ const maxXOnChart = this.chartRect.x2 - this.labelOffset.right;
515
+
516
+ if (isNegativeValue) {
517
+ const xPos = barX - GAP + barWidth - textWidth;
518
+ if (xPos > minXOnChart) {
519
+ ctx.fillText(formattedTxt, xPos, centerYOnBar);
520
+ }
521
+ } else {
522
+ const xPos = barX + GAP + barWidth;
523
+ if (xPos + textWidth < maxXOnChart) {
524
+ ctx.fillText(formattedTxt, xPos, centerYOnBar);
525
+ }
526
+ }
486
527
  } else {
487
- ctx.fillText(formattedTxt, centerX, y + h - (textHeight / 2));
528
+ const yPos = isNegativeValue
529
+ ? barY + barHeight + GAP
530
+ : barY + barHeight - GAP;
531
+ ctx.fillText(formattedTxt, centerXOnBar, yPos);
488
532
  }
489
533
 
490
534
  break;
@@ -492,14 +536,21 @@ class Bar {
492
536
 
493
537
  default:
494
538
  case 'end': {
495
- if (isHorizontal) {
496
- if (textWidth < widthFreeSpaceToDraw) {
497
- const xPos = x + w - (textWidth * 2);
498
- ctx.fillText(formattedTxt, xPos <= minXPos ? minXPos : xPos, centerYHorizontal);
539
+ if (isHorizontal && textWidth < drawableBarWidth) {
540
+ const xPos = isNegativeValue
541
+ ? barX + barWidth + GAP
542
+ : barX + barWidth - textWidth - GAP;
543
+ ctx.fillText(formattedTxt, xPos, centerYOnBar);
544
+ } else if (!isHorizontal) {
545
+ if (isNegativeValue) {
546
+ const yPos = barY + barHeight - GAP;
547
+ if (yPos > minYPos) {
548
+ ctx.fillText(formattedTxt, centerXOnBar, yPos);
549
+ }
550
+ } else if (textHeight < drawableBarHeight) {
551
+ const yPos = barY + barHeight + GAP;
552
+ ctx.fillText(formattedTxt, centerXOnBar, yPos);
499
553
  }
500
- } else if (textHeight < heightFreeSpaceToDraw) {
501
- const yPos = y + h + textHeight;
502
- ctx.fillText(formattedTxt, centerX, yPos >= minYPos ? minYPos : yPos);
503
554
  }
504
555
 
505
556
  break;
@@ -556,29 +607,48 @@ class Bar {
556
607
 
557
608
  ctx.beginPath();
558
609
  ctx.moveTo(x, y);
610
+ if (Math.abs(w) < r * 2) {
611
+ r = Math.abs(w) / 2;
612
+ }
613
+
614
+ if (Math.abs(h) < r * 2) {
615
+ r = Math.abs(h) / 2;
616
+ }
559
617
 
560
618
  if (isHorizontal) {
561
- if (h < r * 2) {
562
- r = h / 2;
619
+ const isNegativeValue = w < 0;
620
+ if (isNegativeValue) {
621
+ w += r;
622
+ ctx.lineTo(x + w, y);
623
+ ctx.arcTo(x + w - r, y, x + w - r, y - r, r);
624
+ ctx.arcTo(x + w - r, y - h, x + w, y - h, r);
625
+ ctx.lineTo(x, y - h);
626
+ ctx.lineTo(x, y);
627
+ } else {
628
+ w -= r;
629
+ ctx.lineTo(x + w, y);
630
+ ctx.arcTo(x + w + r, y, x + w + r, y - r, r);
631
+ ctx.arcTo(x + w + r, y - h, x + w, y - h, r);
632
+ ctx.lineTo(x, y - h);
633
+ ctx.lineTo(x, y);
563
634
  }
564
-
565
- w -= r;
566
- ctx.lineTo(x + w, y);
567
- ctx.arcTo(x + w + r, y, x + w + r, y - r, r);
568
- ctx.arcTo(x + w + r, y - h, x + w, y - h, r);
569
- ctx.lineTo(x, y - h);
570
- ctx.lineTo(x, y);
571
635
  } else {
572
- if (w < r * 2) {
573
- r = w / 2;
636
+ const isNegativeValue = h > 0;
637
+ if (isNegativeValue) {
638
+ h -= r;
639
+ ctx.lineTo(x + w, y);
640
+ ctx.lineTo(x + w, y + h);
641
+ ctx.arcTo(x + w, y + h + r, x - w + r, y + h + r, r);
642
+ ctx.arcTo(x, y + h + r, x, y + h, r);
643
+ ctx.lineTo(x, y);
644
+ } else {
645
+ h += r;
646
+ ctx.lineTo(x + w, y);
647
+ ctx.lineTo(x + w, y + h);
648
+ ctx.arcTo(x + w, y + h - r, x + w - r, y + h - r, r);
649
+ ctx.arcTo(x, y + h - r, x, y + h, r);
650
+ ctx.lineTo(x, y);
574
651
  }
575
-
576
- h += r;
577
- ctx.lineTo(x + w, y);
578
- ctx.lineTo(x + w, y + h);
579
- ctx.arcTo(x + w, y + h - r, x + w - r, y + h - r, r);
580
- ctx.arcTo(x, y + h - r, x, y + h, r);
581
- ctx.lineTo(x, y);
582
652
  }
583
653
 
584
654
  ctx.fill();
@@ -104,8 +104,6 @@ class Line {
104
104
  ctx.setLineDash(this.segments);
105
105
  }
106
106
 
107
- const endPoint = chartRect.y2 - labelOffset.bottom;
108
-
109
107
  const isLinearInterpolation = this.useLinearInterpolation();
110
108
 
111
109
  let barAreaByCombo = 0;
@@ -127,6 +125,8 @@ class Line {
127
125
 
128
126
  const getXPos = val => Canvas.calculateX(val, minmaxX.graphMin, minmaxX.graphMax, xArea, xsp);
129
127
  const getYPos = val => Canvas.calculateY(val, minmaxY.graphMin, minmaxY.graphMax, yArea, ysp);
128
+ const includeNegativeValue = this.data.some(data => data.o < 0);
129
+ const endPoint = includeNegativeValue ? getYPos(0) : chartRect.y2 - labelOffset.bottom;
130
130
 
131
131
  // draw line
132
132
  let prevValid;
@@ -183,9 +183,10 @@ class Line {
183
183
  }
184
184
  });
185
185
  const gradient = ctx.createLinearGradient(0, chartRect.y2, 0, maxValueYPos);
186
- gradient.addColorStop(0, fillColor);
186
+ const mainGradientColor = extent.opacity < 1 ? fillColor : mainColor;
187
+ gradient.addColorStop(0, includeNegativeValue ? mainGradientColor : fillColor);
187
188
  gradient.addColorStop(0.5, fillColor);
188
- gradient.addColorStop(1, (extent.opacity < 1 ? fillColor : mainColor));
189
+ gradient.addColorStop(1, mainGradientColor);
189
190
 
190
191
  ctx.fillStyle = gradient;
191
192
  } else {
@@ -242,7 +243,7 @@ class Line {
242
243
  for (let jx = endIndex; jx >= startIndex; jx--) {
243
244
  const nextData = this.data[jx];
244
245
  const xp = getXPos(nextData.x);
245
- const bp = getYPos(nextData.b) ?? endPoint;
246
+ const bp = getYPos(nextData.b) ?? getYPos(0);
246
247
  ctx.lineTo(xp, bp);
247
248
  }
248
249
 
@@ -27,8 +27,9 @@ const modules = {
27
27
  }
28
28
 
29
29
  if (labelTipOpt.use && labelTipOpt.showTip) {
30
- const isHeatMap = opt.type === 'heatMap';
31
- isExistSelectedLabel = isHeatMap ? this.drawLabelTipForHeatMap() : this.drawLabelTip();
30
+ isExistSelectedLabel = opt.type === 'heatMap'
31
+ ? this.drawLabelTipForHeatMap()
32
+ : this.drawTipForSelectedLabel();
32
33
  }
33
34
 
34
35
  const executeDrawIndicator = (tipOpt) => {
@@ -283,9 +284,10 @@ const modules = {
283
284
 
284
285
  /**
285
286
  * Draw Selected Label Tip
287
+ * none Text
286
288
  * @returns {boolean} Whether drew at least one tip
287
289
  */
288
- drawLabelTip() {
290
+ drawTipForSelectedLabel() {
289
291
  const opt = this.options;
290
292
  const isHorizontal = !!opt.horizontal;
291
293
  const labelTipOpt = opt.selectLabel;
@@ -305,7 +307,7 @@ const modules = {
305
307
  };
306
308
  const labelAxes = isHorizontal ? this.axesY[0] : this.axesX[0];
307
309
  const valueAxes = isHorizontal ? this.axesX[0] : this.axesY[0];
308
- const valueAxesRange = isHorizontal ? this.axesRange.x[0] : this.axesRange.y[0];
310
+ const valueAxesSteps = isHorizontal ? this.axesSteps.x[0] : this.axesSteps.y[0];
309
311
  const valuePositionCalcFunction = isHorizontal ? Canvas.calculateX : Canvas.calculateY;
310
312
  const labelPositionCalcFunction = isHorizontal ? Canvas.calculateY : Canvas.calculateX;
311
313
  const scrollbarOpt = isHorizontal ? this.scrollbar.y : this.scrollbar.x;
@@ -323,8 +325,8 @@ const modules = {
323
325
  .some(sId => this.seriesList[sId].isExistGrp && !this.seriesList[sId].isOverlapping);
324
326
  const groups = this.data.groups?.[0] ?? [];
325
327
 
326
- let gp;
327
- let dp;
328
+ let labelPos;
329
+ let dataPos;
328
330
  let value;
329
331
  let labelStartPoint;
330
332
  let labelEndPoint;
@@ -357,21 +359,38 @@ const modules = {
357
359
  }
358
360
 
359
361
  data.forEach((selectedData, i) => {
360
- if (labelTipOpt.fixedPosTop) {
361
- value = valueAxesRange.max;
362
- } else if (isExistGrp) {
363
- const sumValue = visibleSeries.reduce((ac, sId) => (
364
- groups.includes(sId) ? ac + (selectedData[sId]?.value ?? selectedData[sId]) : ac), 0);
365
- const nonGroupValues = visibleSeries
366
- .filter(sId => !groups.includes(sId))
367
- .map(sId => selectedData[sId]?.value ?? selectedData[sId]);
368
- value = Math.max(...nonGroupValues, sumValue);
369
- } else if (visibleSeries.length) {
370
- const visibleValue = visibleSeries
371
- .map(sId => selectedData[sId]?.value ?? selectedData[sId]);
372
- value = Math.max(...visibleValue);
373
- } else {
374
- value = valueAxesRange.max;
362
+ value = valueAxesSteps.graphMax;
363
+
364
+ if (!labelTipOpt.fixedPosTop) {
365
+ if (isExistGrp) {
366
+ const positiveSum = visibleSeries?.reduce((ac, sId) => (
367
+ groups.includes(sId) && (selectedData[sId]?.value ?? selectedData[sId]) > 0
368
+ ? ac + (selectedData[sId]?.value ?? selectedData[sId])
369
+ : ac), 0);
370
+
371
+ const nonGroupValues = visibleSeries
372
+ ?.filter(sId => !groups.includes(sId))
373
+ ?.map(sId => selectedData[sId]?.value ?? selectedData[sId]) ?? [];
374
+
375
+ const maxNonGroupValue = nonGroupValues?.length > 0
376
+ ? nonGroupValues.reduce((max, val) => Math.max(max, val ?? -Infinity), -Infinity)
377
+ : -Infinity;
378
+
379
+ value = positiveSum > 0
380
+ ? Math.max(maxNonGroupValue, positiveSum)
381
+ : Math.max(maxNonGroupValue, 0);
382
+ } else if (visibleSeries.length) {
383
+ const visibleValue = visibleSeries
384
+ .map(sId => selectedData[sId]?.value ?? selectedData[sId]);
385
+
386
+ const maxValue = visibleValue.length > 0
387
+ ? visibleValue.reduce((max, val) => Math.max(max, val ?? -Infinity), -Infinity)
388
+ : -Infinity;
389
+
390
+ value = maxValue > 0 || this.options.type !== 'bar'
391
+ ? maxValue
392
+ : 0;
393
+ }
375
394
  }
376
395
 
377
396
  if (labelAxes.labels) {
@@ -381,9 +400,9 @@ const modules = {
381
400
 
382
401
  const labelIndex = dataIndex[i] - startIndex;
383
402
  const labelCenter = Math.round(labelStartPoint + (labelGap * labelIndex));
384
- dp = labelCenter + (labelGap / 2);
403
+ labelPos = labelCenter + (labelGap / 2);
385
404
  } else {
386
- dp = labelPositionCalcFunction(
405
+ labelPos = labelPositionCalcFunction(
387
406
  label[i],
388
407
  graphX.graphMin,
389
408
  graphX.graphMax,
@@ -391,18 +410,19 @@ const modules = {
391
410
  aPos.x1 + (sizeObj.comboOffset / 2),
392
411
  );
393
412
  }
394
- gp = valuePositionCalcFunction(
413
+
414
+ dataPos = valuePositionCalcFunction(
395
415
  value,
396
- valueAxesRange.min,
397
- valueAxesRange.max,
416
+ valueAxesSteps.graphMin,
417
+ valueAxesSteps.graphMax,
398
418
  valueSpace,
399
- valueStartPoint);
400
- gp += offset;
419
+ valueStartPoint,
420
+ ) + offset;
401
421
 
402
422
  this.showTip({
403
423
  context: this.bufferCtx,
404
- x: isHorizontal ? gp : dp,
405
- y: isHorizontal ? dp : gp,
424
+ x: isHorizontal ? dataPos : labelPos,
425
+ y: isHorizontal ? labelPos : dataPos,
406
426
  opt: labelTipOpt,
407
427
  isSamePos: false,
408
428
  });
@@ -504,10 +524,11 @@ const modules = {
504
524
  }
505
525
  } else if (isHorizontal) {
506
526
  gp = Canvas.calculateX(value, graphX.graphMin, graphX.graphMax, xArea, xsp);
507
- gp += offset;
527
+ gp = value < 0 ? gp - offset : gp + offset;
508
528
  } else {
509
- gp = Canvas.calculateY(value, graphY.graphMin, graphY.graphMax, yArea, ysp);
510
- gp -= offset;
529
+ const adjustedValue = type === 'bar' && value < 0 ? 0 : value;
530
+ gp = Canvas.calculateY(adjustedValue, graphY.graphMin, graphY.graphMax, yArea, ysp);
531
+ gp = adjustedValue < 0 ? gp + offset : gp - offset;
511
532
  }
512
533
 
513
534
  let maxTipType = 'center';
@@ -541,6 +562,7 @@ const modules = {
541
562
  borderRadius,
542
563
  text,
543
564
  textStyle,
565
+ isNegative: value < 0,
544
566
  });
545
567
  }
546
568
 
@@ -563,15 +585,24 @@ const modules = {
563
585
  */
564
586
  showTextTip(param) {
565
587
  const isHorizontal = !!this.options.horizontal;
566
- const { type, width, height, x, y, arrowSize, borderRadius, text, opt, textStyle } = param;
588
+ const {
589
+ type, width, height, x, y, arrowSize, borderRadius, text, opt, textStyle, isNegative,
590
+ } = param;
567
591
 
568
592
  const ctx = param.context;
569
593
 
570
- const sx = x - (width / 2);
571
- const ex = x + (width / 2);
594
+ let sx = x - (width / 2);
595
+ let ex = x + (width / 2);
572
596
  const sy = y - height;
573
597
  const ey = y;
574
598
 
599
+ if (isNegative) {
600
+ if (isHorizontal) {
601
+ sx = x - (width / 2) - width;
602
+ ex = x - (width / 2);
603
+ }
604
+ }
605
+
575
606
  ctx.save();
576
607
  ctx.font = textStyle;
577
608
 
@@ -581,30 +612,8 @@ const modules = {
581
612
  ctx.beginPath();
582
613
  ctx.moveTo(sx + borderRadius, sy);
583
614
  ctx.quadraticCurveTo(sx, sy, sx, sy + borderRadius);
584
-
585
- if (isHorizontal) {
586
- ctx.lineTo(sx, sy + borderRadius + (arrowSize / 2));
587
- ctx.lineTo(sx - arrowSize, ey - (height / 2));
588
- ctx.lineTo(sx, ey - borderRadius - (arrowSize / 2));
589
- }
590
-
591
615
  ctx.lineTo(sx, ey - borderRadius);
592
616
  ctx.quadraticCurveTo(sx, ey, sx + borderRadius, ey);
593
-
594
- if (!isHorizontal) {
595
- if (type === 'left') {
596
- ctx.lineTo(sx + borderRadius + arrowSize, ey + arrowSize);
597
- ctx.lineTo(sx + borderRadius + (arrowSize * 2), ey);
598
- } else if (type === 'right') {
599
- ctx.lineTo(ex - (arrowSize * 2) - borderRadius, ey);
600
- ctx.lineTo(ex - arrowSize - borderRadius, ey + arrowSize);
601
- } else {
602
- ctx.lineTo(x - arrowSize, ey);
603
- ctx.lineTo(x, ey + arrowSize);
604
- ctx.lineTo(x + arrowSize, ey);
605
- }
606
- }
607
-
608
617
  ctx.lineTo(ex - borderRadius, ey);
609
618
  ctx.quadraticCurveTo(ex, ey, ex, ey - borderRadius);
610
619
  ctx.lineTo(ex, sy + borderRadius);
@@ -612,13 +621,64 @@ const modules = {
612
621
  ctx.lineTo(sx + borderRadius, sy);
613
622
  ctx.closePath();
614
623
  ctx.fill();
624
+
625
+ // draw arrow
626
+ ctx.beginPath();
627
+ if (isHorizontal) {
628
+ if (isNegative) {
629
+ ctx.moveTo(ex, ey);
630
+ ctx.lineTo(ex, sy + borderRadius + (arrowSize / 2));
631
+ ctx.lineTo(ex + arrowSize, ey - (height / 2));
632
+ ctx.lineTo(ex, ey - borderRadius - (arrowSize / 2));
633
+ } else {
634
+ ctx.moveTo(sx, sy);
635
+ ctx.lineTo(sx, sy + borderRadius + (arrowSize / 2));
636
+ ctx.lineTo(sx - arrowSize, ey - (height / 2));
637
+ ctx.lineTo(sx, ey - borderRadius - (arrowSize / 2));
638
+ }
639
+
640
+ ctx.closePath();
641
+ ctx.fill();
642
+ } else {
643
+ if (isNegative) {
644
+ if (type === 'left') {
645
+ ctx.lineTo(sx + borderRadius + arrowSize, ey + arrowSize);
646
+ ctx.lineTo(sx + borderRadius + (arrowSize * 2), ey);
647
+ } else if (type === 'right') {
648
+ ctx.lineTo(ex - (arrowSize * 2) - borderRadius, ey);
649
+ ctx.lineTo(ex - arrowSize - borderRadius, ey + arrowSize);
650
+ } else {
651
+ ctx.lineTo(x - arrowSize, ey);
652
+ ctx.lineTo(x, ey + arrowSize);
653
+ ctx.lineTo(x + arrowSize, ey);
654
+ }
655
+ } else if (!isNegative) {
656
+ if (type === 'left') {
657
+ ctx.moveTo(sx, sy);
658
+ ctx.lineTo(sx + borderRadius + arrowSize, ey + arrowSize);
659
+ ctx.lineTo(sx + borderRadius + (arrowSize * 2), ey);
660
+ } else if (type === 'right') {
661
+ ctx.moveTo(ex, sy);
662
+ ctx.lineTo(ex - (arrowSize * 2) - borderRadius, ey);
663
+ ctx.lineTo(ex - arrowSize - borderRadius, ey + arrowSize);
664
+ } else {
665
+ ctx.lineTo(x - arrowSize, ey);
666
+ ctx.lineTo(x, ey + arrowSize);
667
+ ctx.lineTo(x + arrowSize, ey);
668
+ }
669
+ }
670
+
671
+ ctx.closePath();
672
+ ctx.fill();
673
+ }
674
+
615
675
  ctx.restore();
616
676
  ctx.save();
617
677
  ctx.font = textStyle;
618
678
  ctx.fillStyle = opt.tipTextColor ?? opt.tipStyle.textColor;
619
679
  ctx.textBaseline = 'middle';
620
680
  ctx.textAlign = 'center';
621
- ctx.fillText(`${text}`, x, sy + (height / 2));
681
+ ctx.fillText(`${text}`, sx + (width / 2), sy + (height / 2));
622
682
  ctx.restore();
623
683
  },
624
684
 
@@ -640,7 +700,7 @@ const modules = {
640
700
  ctx.beginPath();
641
701
  ctx.moveTo(x, cy);
642
702
  if (isHorizontal) {
643
- ctx.lineTo(x + 6, cy - 6);
703
+ ctx.lineTo(x + 6, cy - 6);
644
704
  ctx.lineTo(x + 6, cy + 6);
645
705
  } else {
646
706
  ctx.lineTo(x + 6, cy - 6);
@@ -160,25 +160,29 @@ export default {
160
160
  return value;
161
161
  }
162
162
 
163
+ const isNegative = value < 0;
164
+ const absValue = Math.abs(value);
165
+
163
166
  const assignLabelWith = (v, target, lb) => {
164
- if (v % target === 0) {
165
- return `${(v / target).toFixed(decimalPoint)}${lb}`;
166
- }
167
- return `${(v / target).toFixed(1)}${lb}`;
167
+ const result = v % target === 0
168
+ ? `${(v / target).toFixed(decimalPoint)}${lb}`
169
+ : `${(v / target).toFixed(1)}${lb}`;
170
+
171
+ return isNegative ? `-${result}` : result;
168
172
  };
169
173
 
170
- if (value >= quad) {
171
- label = assignLabelWith(value, quad, 'P');
172
- } else if (value >= trill) {
173
- label = assignLabelWith(value, trill, 'T');
174
- } else if (value >= billi) {
175
- label = assignLabelWith(value, billi, 'G');
176
- } else if (value >= milli) {
177
- label = assignLabelWith(value, milli, 'M');
178
- } else if (value >= killo) {
179
- label = assignLabelWith(value, 1000, 'K');
174
+ if (absValue >= quad) {
175
+ label = assignLabelWith(absValue, quad, 'P');
176
+ } else if (absValue >= trill) {
177
+ label = assignLabelWith(absValue, trill, 'T');
178
+ } else if (absValue >= billi) {
179
+ label = assignLabelWith(absValue, billi, 'G');
180
+ } else if (absValue >= milli) {
181
+ label = assignLabelWith(absValue, milli, 'M');
182
+ } else if (absValue >= killo) {
183
+ label = assignLabelWith(absValue, 1000, 'K');
180
184
  } else {
181
- label = value.toFixed(decimalPoint);
185
+ label = isNegative ? `-${absValue.toFixed(decimalPoint)}` : value.toFixed(decimalPoint);
182
186
  }
183
187
 
184
188
  return label;