@qfo/qfchart 0.6.5 → 0.6.7
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 +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/qfchart.min.browser.js +10 -10
- package/dist/qfchart.min.es.js +10 -10
- package/package.json +1 -1
- package/src/QFChart.ts +1434 -1431
- package/src/components/LayoutManager.ts +38 -24
- package/src/components/SeriesBuilder.ts +11 -3
- package/src/components/SeriesRendererFactory.ts +38 -36
- package/src/components/renderers/BackgroundRenderer.ts +4 -2
- package/src/components/renderers/LabelRenderer.ts +231 -0
- package/src/types.ts +206 -205
- package/src/utils/AxisUtils.ts +45 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { QFChartOptions, Indicator as IndicatorType } from '../types';
|
|
1
|
+
import { QFChartOptions, Indicator as IndicatorType, OHLCV } from '../types';
|
|
2
2
|
import { AxisUtils } from '../utils/AxisUtils';
|
|
3
3
|
|
|
4
4
|
export interface PaneConfiguration {
|
|
@@ -176,11 +176,10 @@ export class LayoutManager {
|
|
|
176
176
|
if (options.yAxisLabelFormatter) {
|
|
177
177
|
return options.yAxisLabelFormatter(value);
|
|
178
178
|
}
|
|
179
|
-
const decimals = options.yAxisDecimalPlaces !== undefined
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
return String(value);
|
|
179
|
+
const decimals = options.yAxisDecimalPlaces !== undefined
|
|
180
|
+
? options.yAxisDecimalPlaces
|
|
181
|
+
: AxisUtils.autoDetectDecimals(marketData as OHLCV[]);
|
|
182
|
+
return AxisUtils.formatValue(value, decimals);
|
|
184
183
|
},
|
|
185
184
|
},
|
|
186
185
|
splitLine: {
|
|
@@ -367,11 +366,10 @@ export class LayoutManager {
|
|
|
367
366
|
if (options.yAxisLabelFormatter) {
|
|
368
367
|
return options.yAxisLabelFormatter(value);
|
|
369
368
|
}
|
|
370
|
-
const decimals = options.yAxisDecimalPlaces !== undefined
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
return String(value);
|
|
369
|
+
const decimals = options.yAxisDecimalPlaces !== undefined
|
|
370
|
+
? options.yAxisDecimalPlaces
|
|
371
|
+
: AxisUtils.autoDetectDecimals(marketData as OHLCV[]);
|
|
372
|
+
return AxisUtils.formatValue(value, decimals);
|
|
375
373
|
},
|
|
376
374
|
},
|
|
377
375
|
axisTick: { show: !isMainCollapsed },
|
|
@@ -444,11 +442,10 @@ export class LayoutManager {
|
|
|
444
442
|
if (options.yAxisLabelFormatter) {
|
|
445
443
|
return options.yAxisLabelFormatter(value);
|
|
446
444
|
}
|
|
447
|
-
const decimals = options.yAxisDecimalPlaces !== undefined
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
return String(value);
|
|
445
|
+
const decimals = options.yAxisDecimalPlaces !== undefined
|
|
446
|
+
? options.yAxisDecimalPlaces
|
|
447
|
+
: AxisUtils.autoDetectDecimals(marketData as OHLCV[]);
|
|
448
|
+
return AxisUtils.formatValue(value, decimals);
|
|
452
449
|
},
|
|
453
450
|
},
|
|
454
451
|
});
|
|
@@ -544,12 +541,30 @@ export class LayoutManager {
|
|
|
544
541
|
// Create Y-axes for incompatible plots
|
|
545
542
|
// nextYAxisIndex already incremented in the loop above, so we know how many axes we need
|
|
546
543
|
const numOverlayAxes = overlayYAxisMap.size > 0 ? nextYAxisIndex - 1 : 0;
|
|
544
|
+
|
|
545
|
+
// Track which overlay axes are for visual-only plots (background, barcolor, etc.)
|
|
546
|
+
const visualOnlyAxes = new Set<number>();
|
|
547
|
+
overlayYAxisMap.forEach((yAxisIdx, plotKey) => {
|
|
548
|
+
// Check if this plot is visual-only by looking at the original indicator
|
|
549
|
+
indicators.forEach((indicator) => {
|
|
550
|
+
Object.entries(indicator.plots).forEach(([plotName, plot]) => {
|
|
551
|
+
const key = `${indicator.id}::${plotName}`;
|
|
552
|
+
if (key === plotKey && ['background', 'barcolor', 'char'].includes(plot.options.style)) {
|
|
553
|
+
visualOnlyAxes.add(yAxisIdx);
|
|
554
|
+
}
|
|
555
|
+
});
|
|
556
|
+
});
|
|
557
|
+
});
|
|
558
|
+
|
|
547
559
|
for (let i = 0; i < numOverlayAxes; i++) {
|
|
560
|
+
const yAxisIndex = i + 1; // Y-axis indices start at 1 for overlays
|
|
561
|
+
const isVisualOnly = visualOnlyAxes.has(yAxisIndex);
|
|
562
|
+
|
|
548
563
|
yAxis.push({
|
|
549
564
|
position: 'left',
|
|
550
|
-
scale:
|
|
551
|
-
min: AxisUtils.createMinFunction(yAxisPaddingPercent),
|
|
552
|
-
max: AxisUtils.createMaxFunction(yAxisPaddingPercent),
|
|
565
|
+
scale: !isVisualOnly, // Disable scaling for visual-only plots
|
|
566
|
+
min: isVisualOnly ? 0 : AxisUtils.createMinFunction(yAxisPaddingPercent), // Fixed range for visual plots
|
|
567
|
+
max: isVisualOnly ? 1 : AxisUtils.createMaxFunction(yAxisPaddingPercent), // Fixed range for visual plots
|
|
553
568
|
gridIndex: 0,
|
|
554
569
|
show: false, // Hide the axis visual elements
|
|
555
570
|
splitLine: { show: false },
|
|
@@ -580,11 +595,10 @@ export class LayoutManager {
|
|
|
580
595
|
if (options.yAxisLabelFormatter) {
|
|
581
596
|
return options.yAxisLabelFormatter(value);
|
|
582
597
|
}
|
|
583
|
-
const decimals = options.yAxisDecimalPlaces !== undefined
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
return String(value);
|
|
598
|
+
const decimals = options.yAxisDecimalPlaces !== undefined
|
|
599
|
+
? options.yAxisDecimalPlaces
|
|
600
|
+
: AxisUtils.autoDetectDecimals(marketData as OHLCV[]);
|
|
601
|
+
return AxisUtils.formatValue(value, decimals);
|
|
588
602
|
},
|
|
589
603
|
},
|
|
590
604
|
axisLine: { show: !pane.isCollapsed, lineStyle: { color: '#334155' } },
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { OHLCV, Indicator as IndicatorType, QFChartOptions, IndicatorPlot, IndicatorStyle } from '../types';
|
|
2
2
|
import { PaneConfiguration } from './LayoutManager';
|
|
3
3
|
import { SeriesRendererFactory } from './SeriesRendererFactory';
|
|
4
|
+
import { AxisUtils } from '../utils/AxisUtils';
|
|
4
5
|
|
|
5
6
|
export class SeriesBuilder {
|
|
6
7
|
private static readonly DEFAULT_COLOR = '#2962ff';
|
|
@@ -32,8 +33,13 @@ export class SeriesBuilder {
|
|
|
32
33
|
if (lineStyleType.startsWith('linestyle_')) {
|
|
33
34
|
lineStyleType = lineStyleType.replace('linestyle_', '') as any;
|
|
34
35
|
}
|
|
36
|
+
const decimals = options.yAxisDecimalPlaces !== undefined
|
|
37
|
+
? options.yAxisDecimalPlaces
|
|
38
|
+
: AxisUtils.autoDetectDecimals(marketData);
|
|
39
|
+
|
|
35
40
|
markLine = {
|
|
36
41
|
symbol: ['none', 'none'],
|
|
42
|
+
precision: decimals, // Ensure line position is precise enough for small values
|
|
37
43
|
data: [
|
|
38
44
|
{
|
|
39
45
|
yAxis: lastClose,
|
|
@@ -45,8 +51,7 @@ export class SeriesBuilder {
|
|
|
45
51
|
if (options.yAxisLabelFormatter) {
|
|
46
52
|
return options.yAxisLabelFormatter(params.value);
|
|
47
53
|
}
|
|
48
|
-
|
|
49
|
-
return typeof params.value === 'number' ? params.value.toFixed(decimals) : params.value;
|
|
54
|
+
return AxisUtils.formatValue(params.value, decimals);
|
|
50
55
|
},
|
|
51
56
|
color: '#fff',
|
|
52
57
|
backgroundColor: lineColor,
|
|
@@ -125,8 +130,11 @@ export class SeriesBuilder {
|
|
|
125
130
|
let yAxisIndex = 0;
|
|
126
131
|
|
|
127
132
|
// Check plot-level overlay setting (overrides indicator-level setting)
|
|
133
|
+
// IMPORTANT: If indicator is overlay (paneIndex === 0), treat all plots as overlays
|
|
134
|
+
// This allows visual-only plots (background, barcolor) to have separate Y-axes while
|
|
135
|
+
// still being on the main chart pane
|
|
128
136
|
const plotOverlay = plot.options.overlay;
|
|
129
|
-
const isPlotOverlay =
|
|
137
|
+
const isPlotOverlay = indicator.paneIndex === 0 || plotOverlay === true;
|
|
130
138
|
|
|
131
139
|
if (isPlotOverlay) {
|
|
132
140
|
// Plot should be on main chart (overlay)
|
|
@@ -1,36 +1,38 @@
|
|
|
1
|
-
import { SeriesRenderer } from './renderers/SeriesRenderer';
|
|
2
|
-
import { LineRenderer } from './renderers/LineRenderer';
|
|
3
|
-
import { StepRenderer } from './renderers/StepRenderer';
|
|
4
|
-
import { HistogramRenderer } from './renderers/HistogramRenderer';
|
|
5
|
-
import { ScatterRenderer } from './renderers/ScatterRenderer';
|
|
6
|
-
import { OHLCBarRenderer } from './renderers/OHLCBarRenderer';
|
|
7
|
-
import { ShapeRenderer } from './renderers/ShapeRenderer';
|
|
8
|
-
import { BackgroundRenderer } from './renderers/BackgroundRenderer';
|
|
9
|
-
import { FillRenderer } from './renderers/FillRenderer';
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
this.register('
|
|
17
|
-
this.register('
|
|
18
|
-
this.register('
|
|
19
|
-
this.register('
|
|
20
|
-
this.register('
|
|
21
|
-
this.register('
|
|
22
|
-
this.register('
|
|
23
|
-
this.register('
|
|
24
|
-
this.register('
|
|
25
|
-
this.register('
|
|
26
|
-
this.register('
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
1
|
+
import { SeriesRenderer } from './renderers/SeriesRenderer';
|
|
2
|
+
import { LineRenderer } from './renderers/LineRenderer';
|
|
3
|
+
import { StepRenderer } from './renderers/StepRenderer';
|
|
4
|
+
import { HistogramRenderer } from './renderers/HistogramRenderer';
|
|
5
|
+
import { ScatterRenderer } from './renderers/ScatterRenderer';
|
|
6
|
+
import { OHLCBarRenderer } from './renderers/OHLCBarRenderer';
|
|
7
|
+
import { ShapeRenderer } from './renderers/ShapeRenderer';
|
|
8
|
+
import { BackgroundRenderer } from './renderers/BackgroundRenderer';
|
|
9
|
+
import { FillRenderer } from './renderers/FillRenderer';
|
|
10
|
+
import { LabelRenderer } from './renderers/LabelRenderer';
|
|
11
|
+
|
|
12
|
+
export class SeriesRendererFactory {
|
|
13
|
+
private static renderers: Map<string, SeriesRenderer> = new Map();
|
|
14
|
+
|
|
15
|
+
static {
|
|
16
|
+
this.register('line', new LineRenderer());
|
|
17
|
+
this.register('step', new StepRenderer());
|
|
18
|
+
this.register('histogram', new HistogramRenderer());
|
|
19
|
+
this.register('columns', new HistogramRenderer());
|
|
20
|
+
this.register('circles', new ScatterRenderer());
|
|
21
|
+
this.register('cross', new ScatterRenderer());
|
|
22
|
+
this.register('char', new ScatterRenderer());
|
|
23
|
+
this.register('bar', new OHLCBarRenderer());
|
|
24
|
+
this.register('candle', new OHLCBarRenderer());
|
|
25
|
+
this.register('shape', new ShapeRenderer());
|
|
26
|
+
this.register('background', new BackgroundRenderer());
|
|
27
|
+
this.register('fill', new FillRenderer());
|
|
28
|
+
this.register('label', new LabelRenderer());
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
public static register(style: string, renderer: SeriesRenderer) {
|
|
32
|
+
this.renderers.set(style, renderer);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
public static get(style: string): SeriesRenderer {
|
|
36
|
+
return this.renderers.get(style) || this.renderers.get('line')!; // Default to line
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -14,7 +14,7 @@ export class BackgroundRenderer implements SeriesRenderer {
|
|
|
14
14
|
const xVal = api.value(0);
|
|
15
15
|
if (isNaN(xVal)) return;
|
|
16
16
|
|
|
17
|
-
const start = api.coord([xVal, 0]);
|
|
17
|
+
const start = api.coord([xVal, 0.5]); // Use 0.5 as a fixed Y-value within [0,1] range
|
|
18
18
|
const size = api.size([1, 0]);
|
|
19
19
|
const width = size[0];
|
|
20
20
|
const sys = params.coordSys;
|
|
@@ -39,7 +39,9 @@ export class BackgroundRenderer implements SeriesRenderer {
|
|
|
39
39
|
silent: true,
|
|
40
40
|
};
|
|
41
41
|
},
|
|
42
|
-
data
|
|
42
|
+
// Normalize data values to 0.5 (middle of [0,1] range) to prevent Y-axis scaling issues
|
|
43
|
+
// The actual value is only used to check if the background should render (non-null/non-NaN)
|
|
44
|
+
data: dataArray.map((val, i) => [i, val !== null && val !== undefined && !isNaN(val) ? 0.5 : null]),
|
|
43
45
|
};
|
|
44
46
|
}
|
|
45
47
|
}
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import { SeriesRenderer, RenderContext } from './SeriesRenderer';
|
|
2
|
+
import { ShapeUtils } from '../../utils/ShapeUtils';
|
|
3
|
+
|
|
4
|
+
export class LabelRenderer implements SeriesRenderer {
|
|
5
|
+
render(context: RenderContext): any {
|
|
6
|
+
const { seriesName, xAxisIndex, yAxisIndex, dataArray, candlestickData } = context;
|
|
7
|
+
|
|
8
|
+
const labelData = dataArray
|
|
9
|
+
.map((val, i) => {
|
|
10
|
+
if (val === null || val === undefined) return null;
|
|
11
|
+
|
|
12
|
+
// val is a label object: {id, x, y, text, xloc, yloc, color, style, textcolor, size, textalign, tooltip}
|
|
13
|
+
const lbl = typeof val === 'object' ? val : null;
|
|
14
|
+
if (!lbl) return null;
|
|
15
|
+
|
|
16
|
+
const text = lbl.text || '';
|
|
17
|
+
const color = lbl.color || '#2962ff';
|
|
18
|
+
const textcolor = lbl.textcolor || '#ffffff';
|
|
19
|
+
const yloc = lbl.yloc || 'price';
|
|
20
|
+
const styleRaw = lbl.style || 'style_label_down';
|
|
21
|
+
const size = lbl.size || 'normal';
|
|
22
|
+
const textalign = lbl.textalign || 'align_center';
|
|
23
|
+
const tooltip = lbl.tooltip || '';
|
|
24
|
+
|
|
25
|
+
// Map Pine style string to shape name for ShapeUtils
|
|
26
|
+
const shape = this.styleToShape(styleRaw);
|
|
27
|
+
|
|
28
|
+
// Determine Y value based on yloc
|
|
29
|
+
let yValue = lbl.y;
|
|
30
|
+
let symbolOffset: (string | number)[] = [0, 0];
|
|
31
|
+
|
|
32
|
+
if (yloc === 'abovebar') {
|
|
33
|
+
if (candlestickData && candlestickData[i]) {
|
|
34
|
+
yValue = candlestickData[i].high;
|
|
35
|
+
}
|
|
36
|
+
symbolOffset = [0, '-150%'];
|
|
37
|
+
} else if (yloc === 'belowbar') {
|
|
38
|
+
if (candlestickData && candlestickData[i]) {
|
|
39
|
+
yValue = candlestickData[i].low;
|
|
40
|
+
}
|
|
41
|
+
symbolOffset = [0, '150%'];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Get symbol from ShapeUtils
|
|
45
|
+
const symbol = ShapeUtils.getShapeSymbol(shape);
|
|
46
|
+
const symbolSize = ShapeUtils.getShapeSize(size);
|
|
47
|
+
|
|
48
|
+
// Compute font size for this label
|
|
49
|
+
const fontSize = this.getSizePx(size);
|
|
50
|
+
|
|
51
|
+
// Dynamically size the bubble to fit text content
|
|
52
|
+
let finalSize: number | number[];
|
|
53
|
+
if (shape === 'labeldown' || shape === 'labelup') {
|
|
54
|
+
// Approximate text width: chars * fontSize * avgCharWidthRatio (bold)
|
|
55
|
+
const textWidth = text.length * fontSize * 0.65;
|
|
56
|
+
const minWidth = fontSize * 2.5;
|
|
57
|
+
const bubbleWidth = Math.max(minWidth, textWidth + fontSize * 1.6);
|
|
58
|
+
const bubbleHeight = fontSize * 2.8;
|
|
59
|
+
finalSize = [bubbleWidth, bubbleHeight];
|
|
60
|
+
|
|
61
|
+
// Offset bubble so the pointer tip sits at the anchor price.
|
|
62
|
+
// The SVG path pointer is ~20% of total height.
|
|
63
|
+
if (shape === 'labeldown') {
|
|
64
|
+
symbolOffset = [symbolOffset[0], typeof symbolOffset[1] === 'string'
|
|
65
|
+
? symbolOffset[1]
|
|
66
|
+
: (symbolOffset[1] as number) - bubbleHeight * 0.35];
|
|
67
|
+
} else {
|
|
68
|
+
symbolOffset = [symbolOffset[0], typeof symbolOffset[1] === 'string'
|
|
69
|
+
? symbolOffset[1]
|
|
70
|
+
: (symbolOffset[1] as number) + bubbleHeight * 0.35];
|
|
71
|
+
}
|
|
72
|
+
} else if (shape === 'none') {
|
|
73
|
+
finalSize = 0;
|
|
74
|
+
} else {
|
|
75
|
+
if (Array.isArray(symbolSize)) {
|
|
76
|
+
finalSize = [symbolSize[0] * 1.5, symbolSize[1] * 1.5];
|
|
77
|
+
} else {
|
|
78
|
+
finalSize = symbolSize * 1.5;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Determine label position based on style direction
|
|
83
|
+
const labelPosition = this.getLabelPosition(styleRaw, yloc);
|
|
84
|
+
const isInsideLabel = labelPosition === 'inside' ||
|
|
85
|
+
labelPosition.startsWith('inside');
|
|
86
|
+
|
|
87
|
+
const item: any = {
|
|
88
|
+
value: [i, yValue],
|
|
89
|
+
symbol: symbol,
|
|
90
|
+
symbolSize: finalSize,
|
|
91
|
+
symbolOffset: symbolOffset,
|
|
92
|
+
itemStyle: {
|
|
93
|
+
color: color,
|
|
94
|
+
},
|
|
95
|
+
label: {
|
|
96
|
+
show: !!text,
|
|
97
|
+
position: labelPosition,
|
|
98
|
+
distance: isInsideLabel ? 0 : 5,
|
|
99
|
+
formatter: text,
|
|
100
|
+
color: textcolor,
|
|
101
|
+
fontSize: fontSize,
|
|
102
|
+
fontWeight: 'bold',
|
|
103
|
+
align: isInsideLabel ? 'center'
|
|
104
|
+
: textalign === 'align_left' ? 'left'
|
|
105
|
+
: textalign === 'align_right' ? 'right'
|
|
106
|
+
: 'center',
|
|
107
|
+
verticalAlign: 'middle',
|
|
108
|
+
padding: [2, 6],
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
if (tooltip) {
|
|
113
|
+
item.tooltip = { formatter: tooltip };
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return item;
|
|
117
|
+
})
|
|
118
|
+
.filter((item) => item !== null);
|
|
119
|
+
|
|
120
|
+
return {
|
|
121
|
+
name: seriesName,
|
|
122
|
+
type: 'scatter',
|
|
123
|
+
xAxisIndex: xAxisIndex,
|
|
124
|
+
yAxisIndex: yAxisIndex,
|
|
125
|
+
data: labelData,
|
|
126
|
+
z: 20,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
private styleToShape(style: string): string {
|
|
131
|
+
// Strip 'style_' prefix
|
|
132
|
+
const s = style.startsWith('style_') ? style.substring(6) : style;
|
|
133
|
+
|
|
134
|
+
switch (s) {
|
|
135
|
+
case 'label_down':
|
|
136
|
+
return 'labeldown';
|
|
137
|
+
case 'label_up':
|
|
138
|
+
return 'labelup';
|
|
139
|
+
case 'label_left':
|
|
140
|
+
return 'labeldown'; // Use labeldown shape, position text left
|
|
141
|
+
case 'label_right':
|
|
142
|
+
return 'labeldown'; // Use labeldown shape, position text right
|
|
143
|
+
case 'label_lower_left':
|
|
144
|
+
return 'labeldown';
|
|
145
|
+
case 'label_lower_right':
|
|
146
|
+
return 'labeldown';
|
|
147
|
+
case 'label_upper_left':
|
|
148
|
+
return 'labelup';
|
|
149
|
+
case 'label_upper_right':
|
|
150
|
+
return 'labelup';
|
|
151
|
+
case 'label_center':
|
|
152
|
+
return 'labeldown';
|
|
153
|
+
case 'circle':
|
|
154
|
+
return 'circle';
|
|
155
|
+
case 'square':
|
|
156
|
+
return 'square';
|
|
157
|
+
case 'diamond':
|
|
158
|
+
return 'diamond';
|
|
159
|
+
case 'flag':
|
|
160
|
+
return 'flag';
|
|
161
|
+
case 'arrowup':
|
|
162
|
+
return 'arrowup';
|
|
163
|
+
case 'arrowdown':
|
|
164
|
+
return 'arrowdown';
|
|
165
|
+
case 'cross':
|
|
166
|
+
return 'cross';
|
|
167
|
+
case 'xcross':
|
|
168
|
+
return 'xcross';
|
|
169
|
+
case 'triangleup':
|
|
170
|
+
return 'triangleup';
|
|
171
|
+
case 'triangledown':
|
|
172
|
+
return 'triangledown';
|
|
173
|
+
case 'text_outline':
|
|
174
|
+
return 'none';
|
|
175
|
+
case 'none':
|
|
176
|
+
return 'none';
|
|
177
|
+
default:
|
|
178
|
+
return 'labeldown';
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
private getLabelPosition(style: string, yloc: string): string {
|
|
183
|
+
const s = style.startsWith('style_') ? style.substring(6) : style;
|
|
184
|
+
|
|
185
|
+
switch (s) {
|
|
186
|
+
case 'label_down':
|
|
187
|
+
return 'inside';
|
|
188
|
+
case 'label_up':
|
|
189
|
+
return 'inside';
|
|
190
|
+
case 'label_left':
|
|
191
|
+
return 'left';
|
|
192
|
+
case 'label_right':
|
|
193
|
+
return 'right';
|
|
194
|
+
case 'label_lower_left':
|
|
195
|
+
return 'insideBottomLeft';
|
|
196
|
+
case 'label_lower_right':
|
|
197
|
+
return 'insideBottomRight';
|
|
198
|
+
case 'label_upper_left':
|
|
199
|
+
return 'insideTopLeft';
|
|
200
|
+
case 'label_upper_right':
|
|
201
|
+
return 'insideTopRight';
|
|
202
|
+
case 'label_center':
|
|
203
|
+
return 'inside';
|
|
204
|
+
case 'text_outline':
|
|
205
|
+
case 'none':
|
|
206
|
+
// Text only, positioned based on yloc
|
|
207
|
+
return yloc === 'abovebar' ? 'top' : yloc === 'belowbar' ? 'bottom' : 'top';
|
|
208
|
+
default:
|
|
209
|
+
// For simple shapes (circle, diamond, etc.), text goes outside
|
|
210
|
+
return yloc === 'belowbar' ? 'bottom' : 'top';
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
private getSizePx(size: string): number {
|
|
215
|
+
switch (size) {
|
|
216
|
+
case 'tiny':
|
|
217
|
+
return 8;
|
|
218
|
+
case 'small':
|
|
219
|
+
return 9;
|
|
220
|
+
case 'normal':
|
|
221
|
+
case 'auto':
|
|
222
|
+
return 10;
|
|
223
|
+
case 'large':
|
|
224
|
+
return 12;
|
|
225
|
+
case 'huge':
|
|
226
|
+
return 14;
|
|
227
|
+
default:
|
|
228
|
+
return 10;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|