124 lines
3.7 KiB
TypeScript
124 lines
3.7 KiB
TypeScript
/**
|
|
* PaneLegend / ChartRightToolbar 공통 — pane 항목·좌표 계산
|
|
*/
|
|
import type { IndicatorConfig } from '../types';
|
|
import { ChartManager } from '../utils/ChartManager';
|
|
import { getIndicatorDef, getIndicatorChartTitle } from '../utils/indicatorRegistry';
|
|
import { getGlobalParamKeys, getPlotParamKeys } from '../utils/indicatorSettingsLayout';
|
|
import { formatBbLegendLabel } from '../utils/bollingerConfig';
|
|
|
|
export interface PaneItem {
|
|
id: string;
|
|
paneIndex: number;
|
|
label: string;
|
|
plotColors: string[];
|
|
hidden?: boolean;
|
|
layoutTopY?: number;
|
|
layoutHeight?: number;
|
|
}
|
|
|
|
export const MIN_SUB_PANE_HEIGHT = 12;
|
|
|
|
function makeLabel(config: IndicatorConfig): string {
|
|
if (config.type === 'BollingerBands') {
|
|
return formatBbLegendLabel(config.params ?? {});
|
|
}
|
|
const name = getIndicatorChartTitle(config.type);
|
|
const def = getIndicatorDef(config.type);
|
|
const params = (config.params ?? def?.defaultParams ?? {}) as Record<string, number | string | boolean>;
|
|
const plots = config.plots ?? def?.plots ?? [];
|
|
|
|
const orderedKeys: string[] = [];
|
|
for (let i = 0; i < plots.length; i++) {
|
|
for (const k of getPlotParamKeys(config.type, plots[i].id, i, plots, params)) {
|
|
if (!orderedKeys.includes(k)) orderedKeys.push(k);
|
|
}
|
|
}
|
|
for (const k of getGlobalParamKeys(config.type, params, plots)) {
|
|
if (!orderedKeys.includes(k) && typeof params[k] === 'number') orderedKeys.push(k);
|
|
}
|
|
|
|
const nums = orderedKeys
|
|
.map(k => params[k])
|
|
.filter((v): v is number => typeof v === 'number')
|
|
.slice(0, 3);
|
|
return nums.length ? `${name} ${nums.join(', ')}` : name;
|
|
}
|
|
|
|
function applyPaneLayoutToItem(
|
|
item: PaneItem,
|
|
layout: { topY: number; height: number },
|
|
overwrite = false,
|
|
): void {
|
|
if (!overwrite && item.layoutTopY != null) return;
|
|
item.layoutTopY = layout.topY;
|
|
item.layoutHeight = layout.height;
|
|
}
|
|
|
|
export function buildPaneItems(
|
|
manager: ChartManager,
|
|
indicators: IndicatorConfig[],
|
|
): PaneItem[] {
|
|
const wantedIds = new Set(
|
|
indicators
|
|
.filter(ind => {
|
|
const def = getIndicatorDef(ind.type);
|
|
return def && !def.overlay && !def.returnsMarkers;
|
|
})
|
|
.map(ind => ind.id),
|
|
);
|
|
|
|
const configById = new Map(indicators.map(ind => [ind.id, ind]));
|
|
|
|
const items: PaneItem[] = [];
|
|
const seenIds = new Set<string>();
|
|
for (const info of manager.getIndicatorPaneInfo()) {
|
|
if (!wantedIds.has(info.id) || seenIds.has(info.id)) continue;
|
|
seenIds.add(info.id);
|
|
const ind = configById.get(info.id) ?? info.config;
|
|
const def = getIndicatorDef(ind.type);
|
|
if (!def || def.overlay || def.returnsMarkers) continue;
|
|
|
|
let chartPane = info.paneIndex;
|
|
if (ind.mergedWith) {
|
|
const host = manager.getIndicatorPaneIndex(ind.mergedWith);
|
|
if (host >= 2) chartPane = host;
|
|
}
|
|
|
|
items.push({
|
|
id: info.id,
|
|
paneIndex: chartPane,
|
|
label: makeLabel(ind),
|
|
plotColors: info.plotColors.map(c => c.slice(0, 7)),
|
|
hidden: ind.hidden === true,
|
|
});
|
|
}
|
|
|
|
if (items.length === 0) return [];
|
|
|
|
items.sort((a, b) => a.paneIndex - b.paneIndex);
|
|
|
|
for (const item of items) {
|
|
const lay = manager.getIndicatorPaneScreenLayout(item.id);
|
|
if (lay) applyPaneLayoutToItem(item, lay, true);
|
|
}
|
|
|
|
items.sort((a, b) => {
|
|
const ay = a.layoutTopY ?? 1e9;
|
|
const by = b.layoutTopY ?? 1e9;
|
|
if (ay !== by) return ay - by;
|
|
return a.paneIndex - b.paneIndex;
|
|
});
|
|
|
|
return items.filter(
|
|
item => item.layoutTopY != null && (item.layoutHeight ?? 0) > MIN_SUB_PANE_HEIGHT,
|
|
);
|
|
}
|
|
|
|
export function resolvePaneItemLayout(
|
|
manager: ChartManager,
|
|
item: PaneItem,
|
|
): { topY: number; height: number } | null {
|
|
return manager.getIndicatorPaneScreenLayout(item.id);
|
|
}
|