105 lines
3.0 KiB
TypeScript
105 lines
3.0 KiB
TypeScript
import type { IndicatorConfig } from '../types';
|
|
import { layoutKey } from './indicatorPaneMerge';
|
|
|
|
export type IndicatorChartSyncMode =
|
|
| 'none'
|
|
| 'remove-only'
|
|
| 'add-only'
|
|
| 'params-changed'
|
|
| 'reorder-only'
|
|
| 'layout-changed'
|
|
| 'full';
|
|
|
|
export interface IndicatorChartChange {
|
|
mode: IndicatorChartSyncMode;
|
|
removedIds: string[];
|
|
added: IndicatorConfig[];
|
|
paramsChangedIds: string[];
|
|
}
|
|
|
|
export function singleIndParamKey(i: IndicatorConfig): string {
|
|
return `${JSON.stringify(i.params)}|${(i.plots ?? []).map(p => p.id).sort().join(',')}|${i.mergedWith ?? ''}`;
|
|
}
|
|
|
|
/** 차트에 로드된 지표 id 중 현재 설정에 없는(이전 탭 잔존) 항목이 있는지 */
|
|
export function chartHasStaleIndicators(
|
|
loadedIds: Set<string>,
|
|
indicators: IndicatorConfig[],
|
|
): boolean {
|
|
if (loadedIds.size === 0) return false;
|
|
const expected = new Set(
|
|
indicators.filter(i => i.hidden !== true).map(i => i.id),
|
|
);
|
|
for (const id of loadedIds) {
|
|
if (!expected.has(id)) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/** 이전·현재 지표 목록 차이를 분류해 최소 갱신 경로를 선택 */
|
|
export function classifyIndicatorChartChange(
|
|
prev: IndicatorConfig[],
|
|
curr: IndicatorConfig[],
|
|
): IndicatorChartChange {
|
|
const prevMap = new Map(prev.map(i => [i.id, i]));
|
|
const currMap = new Map(curr.map(i => [i.id, i]));
|
|
|
|
const removedIds = prev.filter(i => !currMap.has(i.id)).map(i => i.id);
|
|
const added = curr.filter(i => !prevMap.has(i.id));
|
|
|
|
const paramsChangedIds: string[] = [];
|
|
for (const c of curr) {
|
|
const p = prevMap.get(c.id);
|
|
if (!p) continue;
|
|
if (singleIndParamKey(p) !== singleIndParamKey(c)) {
|
|
paramsChangedIds.push(c.id);
|
|
}
|
|
}
|
|
|
|
const empty: IndicatorChartChange = {
|
|
mode: 'none',
|
|
removedIds,
|
|
added,
|
|
paramsChangedIds,
|
|
};
|
|
|
|
if (removedIds.length === 0 && added.length === 0 && paramsChangedIds.length === 0) {
|
|
if (layoutKey(prev) !== layoutKey(curr)) {
|
|
return { ...empty, mode: 'layout-changed' };
|
|
}
|
|
const sameSet = prev.length === curr.length
|
|
&& prev.every(p => currMap.has(p.id));
|
|
if (sameSet && prev.map(i => i.id).join(',') !== curr.map(i => i.id).join(',')) {
|
|
return { ...empty, mode: 'reorder-only' };
|
|
}
|
|
return empty;
|
|
}
|
|
|
|
/** 병합·분리 등 mergedWith 변경 — params 키에도 잡히지만 pane 재배치가 필요 */
|
|
if (
|
|
removedIds.length === 0
|
|
&& added.length === 0
|
|
&& layoutKey(prev) !== layoutKey(curr)
|
|
) {
|
|
return { ...empty, mode: 'layout-changed', paramsChangedIds };
|
|
}
|
|
|
|
if (removedIds.length > 0 && added.length === 0 && paramsChangedIds.length === 0) {
|
|
return { ...empty, mode: 'remove-only' };
|
|
}
|
|
|
|
if (added.length > 0 && removedIds.length === 0 && paramsChangedIds.length === 0) {
|
|
return { ...empty, mode: 'add-only' };
|
|
}
|
|
|
|
if (
|
|
paramsChangedIds.length > 0
|
|
&& removedIds.length === 0
|
|
&& added.length === 0
|
|
) {
|
|
return { ...empty, mode: 'params-changed' };
|
|
}
|
|
|
|
return { ...empty, mode: 'full' };
|
|
}
|