보조지표 합치기, 분리, 복사하기 기능 오류 수정
This commit is contained in:
@@ -1268,9 +1268,7 @@ const TradingChart: React.FC<TradingChartProps> = ({
|
|||||||
const currSK = styleKey(indicators);
|
const currSK = styleKey(indicators);
|
||||||
|
|
||||||
const needsRecalc = prevPK !== currPK;
|
const needsRecalc = prevPK !== currPK;
|
||||||
const isReorderOnly = needsRecalc
|
const chartChange = classifyIndicatorChartChange(prevList, indicators);
|
||||||
&& sortedParamKey(indicators) === sortedParamKey(prevList)
|
|
||||||
&& currSK === prevSK;
|
|
||||||
|
|
||||||
const prevMap = new Map(prevList.map(i => [i.id, i]));
|
const prevMap = new Map(prevList.map(i => [i.id, i]));
|
||||||
const removedIds = prevList
|
const removedIds = prevList
|
||||||
@@ -1283,7 +1281,17 @@ const TradingChart: React.FC<TradingChartProps> = ({
|
|||||||
return singleIndParamKey(p) !== singleIndParamKey(c);
|
return singleIndParamKey(p) !== singleIndParamKey(c);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!needsRecalc && prevSK !== currSK) {
|
if (
|
||||||
|
chartChange.mode === 'reorder-only'
|
||||||
|
|| chartChange.mode === 'layout-changed'
|
||||||
|
) {
|
||||||
|
/** 순서·병합·분리 — 설정에 맞게 sub-pane 전체 재배치 */
|
||||||
|
indicatorSyncInFlightRef.current = true;
|
||||||
|
reloadIndicatorsWithCover(mgr, indicators, () => {
|
||||||
|
syncIndicatorTrackingRefs(indicators);
|
||||||
|
indicatorSyncInFlightRef.current = false;
|
||||||
|
});
|
||||||
|
} else if (!needsRecalc && prevSK !== currSK) {
|
||||||
for (const ind of indicators) {
|
for (const ind of indicators) {
|
||||||
mgr.applyIndicatorStyle(ind);
|
mgr.applyIndicatorStyle(ind);
|
||||||
}
|
}
|
||||||
@@ -1336,7 +1344,13 @@ const TradingChart: React.FC<TradingChartProps> = ({
|
|||||||
) {
|
) {
|
||||||
const savedLR = mgr.getVisibleLogicalRange();
|
const savedLR = mgr.getVisibleLogicalRange();
|
||||||
indicatorSyncInFlightRef.current = true;
|
indicatorSyncInFlightRef.current = true;
|
||||||
void mgr.addIndicatorsBatch(addedConfigs).then(() => {
|
/** 복사 등 목록 중간 삽입 — pane 순서를 맞추려면 전체 재로드 */
|
||||||
|
const insertIdx = addedConfigs.length === 1
|
||||||
|
? indicators.findIndex(i => i.id === addedConfigs[0].id)
|
||||||
|
: -1;
|
||||||
|
const reloadForPaneOrder = insertIdx >= 0 && insertIdx < indicators.length - 1;
|
||||||
|
|
||||||
|
const finishAdd = () => {
|
||||||
afterIndicatorPaneMutation(mgr);
|
afterIndicatorPaneMutation(mgr);
|
||||||
mgr.notifyPaneLayoutChanged();
|
mgr.notifyPaneLayoutChanged();
|
||||||
restoreLogicalRange(mgr, savedLR);
|
restoreLogicalRange(mgr, savedLR);
|
||||||
@@ -1349,7 +1363,13 @@ const TradingChart: React.FC<TradingChartProps> = ({
|
|||||||
setTimeout(() => mgr.notifyPaneLayoutChanged(), ms);
|
setTimeout(() => mgr.notifyPaneLayoutChanged(), ms);
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
});
|
};
|
||||||
|
|
||||||
|
if (reloadForPaneOrder) {
|
||||||
|
reloadIndicatorsWithCover(mgr, indicators, finishAdd);
|
||||||
|
} else {
|
||||||
|
void mgr.addIndicatorsBatch(addedConfigs).then(finishAdd);
|
||||||
|
}
|
||||||
} else if (
|
} else if (
|
||||||
paramsChangedConfigs.length > 0
|
paramsChangedConfigs.length > 0
|
||||||
&& removedIds.length === 0
|
&& removedIds.length === 0
|
||||||
@@ -1373,13 +1393,7 @@ const TradingChart: React.FC<TradingChartProps> = ({
|
|||||||
setPaneLegendPaused(false);
|
setPaneLegendPaused(false);
|
||||||
flushPendingIndicatorResync(mgr, syncGen);
|
flushPendingIndicatorResync(mgr, syncGen);
|
||||||
});
|
});
|
||||||
} else if (isReorderOnly) {
|
} else if (chartChange.mode === 'full') {
|
||||||
reloadIndicatorsWithCover(mgr, indicators, () => {
|
|
||||||
syncIndicatorTrackingRefs(indicators);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
const change = classifyIndicatorChartChange(prevList, indicators);
|
|
||||||
if (change.mode === 'full') {
|
|
||||||
reloadIndicatorsWithCover(mgr, indicators, () => {
|
reloadIndicatorsWithCover(mgr, indicators, () => {
|
||||||
syncIndicatorTrackingRefs(indicators);
|
syncIndicatorTrackingRefs(indicators);
|
||||||
});
|
});
|
||||||
@@ -1387,7 +1401,6 @@ const TradingChart: React.FC<TradingChartProps> = ({
|
|||||||
syncIndicatorTrackingRefs(indicators);
|
syncIndicatorTrackingRefs(indicators);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [chartMgr, bars, barsMarket, market, chartType, theme, logScale, indicators, chartVisible, reloadIndicatorsWithCover, afterIndicatorPaneMutation, restoreLogicalRange, syncIndicatorTrackingRefs, repairMissingIndicators, flushPendingIndicatorResync]);
|
}, [chartMgr, bars, barsMarket, market, chartType, theme, logScale, indicators, chartVisible, reloadIndicatorsWithCover, afterIndicatorPaneMutation, restoreLogicalRange, syncIndicatorTrackingRefs, repairMissingIndicators, flushPendingIndicatorResync]);
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ import {
|
|||||||
normalizeChartTimeFormat,
|
normalizeChartTimeFormat,
|
||||||
} from './chartTimeFormat';
|
} from './chartTimeFormat';
|
||||||
import { DEFAULT_DISPLAY_TIMEZONE } from './timezone';
|
import { DEFAULT_DISPLAY_TIMEZONE } from './timezone';
|
||||||
import { sortIndicatorsForPaneLoad } from './indicatorPaneMerge';
|
import { getPaneHostId, sortIndicatorsForPaneLoad } from './indicatorPaneMerge';
|
||||||
import { calculateIndicator, enrichIndicatorConfig, getIndicatorDef, getHLineLabel, type PlotData, type MarkerData } from './indicatorRegistry';
|
import { calculateIndicator, enrichIndicatorConfig, getIndicatorDef, getHLineLabel, type PlotData, type MarkerData } from './indicatorRegistry';
|
||||||
import { IchimokuCloudPlugin, type IchimokuCloudPoint } from './IchimokuCloudPlugin';
|
import { IchimokuCloudPlugin, type IchimokuCloudPoint } from './IchimokuCloudPlugin';
|
||||||
|
|
||||||
@@ -808,6 +808,8 @@ export class ChartManager {
|
|||||||
private _finalizeIndicatorPaneLayout(): void {
|
private _finalizeIndicatorPaneLayout(): void {
|
||||||
this._removeOrphanSubPanes();
|
this._removeOrphanSubPanes();
|
||||||
if (this._activeIndicatorPaneIndices().size > 0) {
|
if (this._activeIndicatorPaneIndices().size > 0) {
|
||||||
|
this._resyncIndicatorPaneIndices();
|
||||||
|
this._splitCollidingIndicatorPanes();
|
||||||
this._resyncIndicatorPaneIndices();
|
this._resyncIndicatorPaneIndices();
|
||||||
this._applyAllSeriesPriceFormats();
|
this._applyAllSeriesPriceFormats();
|
||||||
} else {
|
} else {
|
||||||
@@ -929,6 +931,8 @@ export class ChartManager {
|
|||||||
this._removeOrphanSubPanes();
|
this._removeOrphanSubPanes();
|
||||||
this._trimTrailingEmptySubPanes();
|
this._trimTrailingEmptySubPanes();
|
||||||
this._resyncIndicatorPaneIndices();
|
this._resyncIndicatorPaneIndices();
|
||||||
|
this._splitCollidingIndicatorPanes();
|
||||||
|
this._resyncIndicatorPaneIndices();
|
||||||
this.resetPaneHeights(this._lastLayoutAvailableHeight);
|
this.resetPaneHeights(this._lastLayoutAvailableHeight);
|
||||||
this._notifyPaneLayout();
|
this._notifyPaneLayout();
|
||||||
}
|
}
|
||||||
@@ -1420,21 +1424,71 @@ export class ChartManager {
|
|||||||
private _collectUsedSubPaneIndices(): Set<number> {
|
private _collectUsedSubPaneIndices(): Set<number> {
|
||||||
const indices = new Set<number>();
|
const indices = new Set<number>();
|
||||||
for (const entry of this.indicators.values()) {
|
for (const entry of this.indicators.values()) {
|
||||||
let found = false;
|
let pane = -1;
|
||||||
for (const series of entry.seriesList) {
|
for (const series of entry.seriesList) {
|
||||||
const pi = this._readSeriesPaneIndex(series);
|
const pi = this._readSeriesPaneIndex(series);
|
||||||
if (pi >= 2) {
|
if (pi >= 2) pane = Math.max(pane, pi);
|
||||||
indices.add(pi);
|
|
||||||
found = true;
|
|
||||||
}
|
}
|
||||||
|
if (pane < 2 && entry.paneIndex != null && entry.paneIndex >= 2) {
|
||||||
|
pane = entry.paneIndex;
|
||||||
}
|
}
|
||||||
if (!found && entry.paneIndex != null && entry.paneIndex >= 2) {
|
if (pane >= 2) indices.add(pane);
|
||||||
indices.add(entry.paneIndex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return indices;
|
return indices;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _indicatorConfigsFromMap(): IndicatorConfig[] {
|
||||||
|
return Array.from(this.indicators.values()).map(e => e.config);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 병합(mergedWith)이 아닌 서로 다른 호스트가 같은 sub-pane에 붙어 있으면 분리.
|
||||||
|
* entry.paneIndex 와 실제 시리즈 pane 불일치·지표 제거 후 재추가 시 겹침 방지.
|
||||||
|
*/
|
||||||
|
private _splitCollidingIndicatorPanes(): void {
|
||||||
|
const configs = this._indicatorConfigsFromMap();
|
||||||
|
const hostsByPane = new Map<number, string[]>();
|
||||||
|
|
||||||
|
for (const [id, entry] of this.indicators) {
|
||||||
|
const hostId = getPaneHostId(id, configs);
|
||||||
|
if (id !== hostId) continue;
|
||||||
|
|
||||||
|
const def = getIndicatorDef(entry.type);
|
||||||
|
if (def?.overlay || def?.returnsMarkers || entry.seriesList.length === 0) continue;
|
||||||
|
|
||||||
|
let pi = -1;
|
||||||
|
for (const series of entry.seriesList) {
|
||||||
|
pi = Math.max(pi, this._readSeriesPaneIndex(series));
|
||||||
|
}
|
||||||
|
if (pi < 2 && entry.paneIndex != null && entry.paneIndex >= 2) pi = entry.paneIndex;
|
||||||
|
if (pi < 2) continue;
|
||||||
|
|
||||||
|
const list = hostsByPane.get(pi) ?? [];
|
||||||
|
if (!list.includes(hostId)) list.push(hostId);
|
||||||
|
hostsByPane.set(pi, list);
|
||||||
|
}
|
||||||
|
|
||||||
|
let changed = false;
|
||||||
|
for (const hostIds of hostsByPane.values()) {
|
||||||
|
if (hostIds.length <= 1) continue;
|
||||||
|
for (let i = 1; i < hostIds.length; i++) {
|
||||||
|
const hostId = hostIds[i];
|
||||||
|
const target = this._getNextSubPane();
|
||||||
|
for (const [mid, ment] of this.indicators) {
|
||||||
|
if (getPaneHostId(mid, configs) !== hostId) continue;
|
||||||
|
for (const series of ment.seriesList) {
|
||||||
|
try {
|
||||||
|
series.moveToPane(target);
|
||||||
|
} catch { /* ok */ }
|
||||||
|
}
|
||||||
|
ment.paneIndex = target;
|
||||||
|
}
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (changed) this._removeOrphanSubPanes();
|
||||||
|
}
|
||||||
|
|
||||||
private _getNextSubPane(): number {
|
private _getNextSubPane(): number {
|
||||||
const used = this._collectUsedSubPaneIndices();
|
const used = this._collectUsedSubPaneIndices();
|
||||||
let pane = 2;
|
let pane = 2;
|
||||||
@@ -1815,9 +1869,13 @@ export class ChartManager {
|
|||||||
this._reapplyAllPatternMarkers();
|
this._reapplyAllPatternMarkers();
|
||||||
this._removeExtraSubPanes();
|
this._removeExtraSubPanes();
|
||||||
|
|
||||||
for (const cfg of configs) {
|
const sorted = sortIndicatorsForPaneLoad(configs);
|
||||||
|
for (const cfg of sorted) {
|
||||||
if (this._indicatorLoadStale(loadGen)) break;
|
if (this._indicatorLoadStale(loadGen)) break;
|
||||||
await this.addIndicator(cfg);
|
await this.addIndicator(cfg, { skipLayout: true });
|
||||||
|
}
|
||||||
|
if (!this._indicatorLoadStale(loadGen)) {
|
||||||
|
this._finalizeIndicatorPaneLayout();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import type { IndicatorConfig } from '../types';
|
import type { IndicatorConfig } from '../types';
|
||||||
|
import { layoutKey } from './indicatorPaneMerge';
|
||||||
|
|
||||||
export type IndicatorChartSyncMode =
|
export type IndicatorChartSyncMode =
|
||||||
| 'none'
|
| 'none'
|
||||||
@@ -6,6 +7,7 @@ export type IndicatorChartSyncMode =
|
|||||||
| 'add-only'
|
| 'add-only'
|
||||||
| 'params-changed'
|
| 'params-changed'
|
||||||
| 'reorder-only'
|
| 'reorder-only'
|
||||||
|
| 'layout-changed'
|
||||||
| 'full';
|
| 'full';
|
||||||
|
|
||||||
export interface IndicatorChartChange {
|
export interface IndicatorChartChange {
|
||||||
@@ -62,6 +64,9 @@ export function classifyIndicatorChartChange(
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (removedIds.length === 0 && added.length === 0 && paramsChangedIds.length === 0) {
|
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
|
const sameSet = prev.length === curr.length
|
||||||
&& prev.every(p => currMap.has(p.id));
|
&& prev.every(p => currMap.has(p.id));
|
||||||
if (sameSet && prev.map(i => i.id).join(',') !== curr.map(i => i.id).join(',')) {
|
if (sameSet && prev.map(i => i.id).join(',') !== curr.map(i => i.id).join(',')) {
|
||||||
@@ -70,6 +75,15 @@ export function classifyIndicatorChartChange(
|
|||||||
return empty;
|
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) {
|
if (removedIds.length > 0 && added.length === 0 && paramsChangedIds.length === 0) {
|
||||||
return { ...empty, mode: 'remove-only' };
|
return { ...empty, mode: 'remove-only' };
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
-10
@@ -21,7 +21,6 @@
|
|||||||
"@capacitor/haptics": "^7.0.0",
|
"@capacitor/haptics": "^7.0.0",
|
||||||
"@capacitor/ios": "^7.0.0",
|
"@capacitor/ios": "^7.0.0",
|
||||||
"@capacitor/preferences": "^7.0.0",
|
"@capacitor/preferences": "^7.0.0",
|
||||||
"@capacitor/push-notifications": "^7.0.0",
|
|
||||||
"@capacitor/splash-screen": "^7.0.0",
|
"@capacitor/splash-screen": "^7.0.0",
|
||||||
"@capacitor/status-bar": "^7.0.0",
|
"@capacitor/status-bar": "^7.0.0",
|
||||||
"@goldenchart/shared": "*",
|
"@goldenchart/shared": "*",
|
||||||
@@ -472,15 +471,6 @@
|
|||||||
"@capacitor/core": ">=7.0.0"
|
"@capacitor/core": ">=7.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@capacitor/push-notifications": {
|
|
||||||
"version": "7.0.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/@capacitor/push-notifications/-/push-notifications-7.0.6.tgz",
|
|
||||||
"integrity": "sha512-zAhbHpdbc15ImuVGgoFwUZsKI+jjGxy/oO5mgfYKUx8Xl2OskndzhL79PYsCPjyGLbqUR9NRUZJthV9auVT3nw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"peerDependencies": {
|
|
||||||
"@capacitor/core": ">=7.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@capacitor/splash-screen": {
|
"node_modules/@capacitor/splash-screen": {
|
||||||
"version": "7.0.5",
|
"version": "7.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/@capacitor/splash-screen/-/splash-screen-7.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/@capacitor/splash-screen/-/splash-screen-7.0.5.tgz",
|
||||||
|
|||||||
Reference in New Issue
Block a user