전략 시간봉 오류 수정
This commit is contained in:
@@ -69,11 +69,6 @@ function getDomSubIndicatorLayouts(
|
||||
return subs;
|
||||
}
|
||||
|
||||
/** 보조지표 1개 = 1 화면 슬롯 (병합 시 여러 item 이 한 슬롯) */
|
||||
interface VisualSlot {
|
||||
items: PaneItem[];
|
||||
}
|
||||
|
||||
interface PaneCapture {
|
||||
dataUrl: string;
|
||||
cssWidth: number;
|
||||
@@ -220,80 +215,71 @@ function makeLabel(config: IndicatorConfig): string {
|
||||
return nums.length ? `${name} ${nums.join(', ')}` : name;
|
||||
}
|
||||
|
||||
function buildVisualSlots(items: PaneItem[], indicators: IndicatorConfig[]): VisualSlot[] {
|
||||
const slots: VisualSlot[] = [];
|
||||
const hostSlotIdx = new Map<string, number>();
|
||||
|
||||
for (const item of items) {
|
||||
const ind = indicators.find(i => i.id === item.id);
|
||||
if (ind?.mergedWith) {
|
||||
const hostId = getPaneHostId(item.id, indicators);
|
||||
const idx = hostSlotIdx.get(hostId);
|
||||
if (idx !== undefined) slots[idx].items.push(item);
|
||||
continue;
|
||||
}
|
||||
const idx = slots.length;
|
||||
slots.push({ items: [item] });
|
||||
hostSlotIdx.set(item.id, idx);
|
||||
hostSlotIdx.set(getPaneHostId(item.id, indicators), idx);
|
||||
}
|
||||
return slots;
|
||||
/** paneIndex ↔ LWC pane DOM 위치 (배열 순서·orphan pane 과 무관) */
|
||||
function layoutForPaneIndex(
|
||||
paneIndex: number,
|
||||
layouts: Array<{ paneIndex: number; topY: number; height: number }>,
|
||||
): { topY: number; height: number } | null {
|
||||
if (paneIndex < 2) return null;
|
||||
const lay = layouts.find(l => l.paneIndex === paneIndex && l.height > MIN_SUB_PANE_HEIGHT);
|
||||
return lay ? { topY: lay.topY, height: lay.height } : null;
|
||||
}
|
||||
|
||||
/** 각 슬롯(호스트 paneIndex) ↔ LWC pane 레이아웃 직접 매칭 — 배열 순서·orphan pane 무관 */
|
||||
function assignLayoutsByPaneIndex(
|
||||
slots: VisualSlot[],
|
||||
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;
|
||||
}
|
||||
|
||||
/** paneIndex 기준으로만 좌표 보강 (지표 배열 순서와 화면 pane 순서가 달라도 안전) */
|
||||
function fillMissingLayoutsByPaneIndex(
|
||||
items: PaneItem[],
|
||||
layouts: Array<{ paneIndex: number; topY: number; height: number }>,
|
||||
): void {
|
||||
const byPane = new Map(layouts.map(l => [l.paneIndex, l]));
|
||||
for (const slot of slots) {
|
||||
const host = slot.items[0];
|
||||
if (!host || host.paneIndex < 2) continue;
|
||||
const lay = byPane.get(host.paneIndex);
|
||||
if (!lay || lay.height < MIN_SUB_PANE_HEIGHT) continue;
|
||||
for (const item of slot.items) {
|
||||
if (item.layoutTopY != null) continue;
|
||||
item.layoutTopY = lay.topY;
|
||||
item.layoutHeight = lay.height;
|
||||
}
|
||||
for (const item of items) {
|
||||
if (item.layoutTopY != null || item.paneIndex < 2) continue;
|
||||
const lay = layoutForPaneIndex(item.paneIndex, layouts);
|
||||
if (lay) applyPaneLayoutToItem(item, lay);
|
||||
}
|
||||
}
|
||||
|
||||
/** 화면 순서(위→아래)의 활성 sub-pane ↔ 슬롯 순서 — orphan pane index 와 분리 */
|
||||
function assignLayoutsBySubPaneOrder(
|
||||
slots: VisualSlot[],
|
||||
subLayouts: Array<{ paneIndex: number; topY: number; height: number }>,
|
||||
/** DOM 행 중 paneIndex 레이아웃 topY 와 가장 가까운 행 선택 */
|
||||
function fillMissingLayoutsFromDom(
|
||||
items: PaneItem[],
|
||||
containerEl: HTMLElement,
|
||||
paneLayouts: Array<{ paneIndex: number; topY: number; height: number }>,
|
||||
): void {
|
||||
for (let i = 0; i < slots.length && i < subLayouts.length; i++) {
|
||||
const lay = subLayouts[i];
|
||||
for (const item of slots[i].items) {
|
||||
if (item.layoutTopY != null) continue;
|
||||
item.layoutTopY = lay.topY;
|
||||
item.layoutHeight = lay.height;
|
||||
}
|
||||
}
|
||||
}
|
||||
const missing = items.filter(i => i.layoutTopY == null && i.paneIndex >= 2);
|
||||
if (missing.length === 0) return;
|
||||
|
||||
/** paneIndex·orphan pane 으로 어긋난 좌표를 슬롯 순서 기준 sub-pane 위치로 보정 */
|
||||
function reconcileLayoutsWithSubPanes(
|
||||
slots: VisualSlot[],
|
||||
subLayouts: Array<{ paneIndex: number; topY: number; height: number }>,
|
||||
): void {
|
||||
const TOL = 24;
|
||||
for (let i = 0; i < slots.length && i < subLayouts.length; i++) {
|
||||
const expected = subLayouts[i];
|
||||
for (const item of slots[i].items) {
|
||||
const cur = item.layoutTopY;
|
||||
const curH = item.layoutHeight;
|
||||
if (
|
||||
cur == null
|
||||
|| Math.abs(cur - expected.topY) > TOL
|
||||
|| (curH != null && Math.abs(curH - expected.height) > TOL)
|
||||
) {
|
||||
item.layoutTopY = expected.topY;
|
||||
item.layoutHeight = expected.height;
|
||||
const domSubs = getDomSubIndicatorLayouts(containerEl, missing.length);
|
||||
if (domSubs.length === 0) return;
|
||||
|
||||
const usedDom = new Set<number>();
|
||||
const sorted = [...missing].sort((a, b) => a.paneIndex - b.paneIndex);
|
||||
|
||||
for (const item of sorted) {
|
||||
const ref = layoutForPaneIndex(item.paneIndex, paneLayouts);
|
||||
let bestIdx = -1;
|
||||
let bestDist = Infinity;
|
||||
for (let i = 0; i < domSubs.length; i++) {
|
||||
if (usedDom.has(i)) continue;
|
||||
const dist = ref
|
||||
? Math.abs(domSubs[i].topY - ref.topY)
|
||||
: domSubs[i].topY;
|
||||
if (dist < bestDist) {
|
||||
bestDist = dist;
|
||||
bestIdx = i;
|
||||
}
|
||||
}
|
||||
if (bestIdx < 0) continue;
|
||||
if (ref && bestDist > 96) continue;
|
||||
usedDom.add(bestIdx);
|
||||
applyPaneLayoutToItem(item, domSubs[bestIdx]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -333,36 +319,24 @@ function buildPaneItems(
|
||||
|
||||
for (const item of items) {
|
||||
const lay = manager.getIndicatorPaneScreenLayout(item.id);
|
||||
if (lay) {
|
||||
item.layoutTopY = lay.topY;
|
||||
item.layoutHeight = lay.height;
|
||||
}
|
||||
if (lay) applyPaneLayoutToItem(item, lay, true);
|
||||
}
|
||||
|
||||
const slots = buildVisualSlots(items, indicators);
|
||||
const subLayouts = manager.getIndicatorSubPaneLayouts();
|
||||
assignLayoutsBySubPaneOrder(slots, subLayouts);
|
||||
assignLayoutsByPaneIndex(slots, manager.getPaneLayouts());
|
||||
if (subLayouts.length > 0) {
|
||||
reconcileLayoutsWithSubPanes(slots, subLayouts);
|
||||
}
|
||||
const paneLayouts = manager.getPaneLayouts();
|
||||
fillMissingLayoutsByPaneIndex(items, paneLayouts);
|
||||
fillMissingLayoutsByPaneIndex(items, manager.getIndicatorSubPaneLayouts());
|
||||
|
||||
if (containerEl?.isConnected) {
|
||||
const missing = items.filter(i => i.layoutTopY == null);
|
||||
if (missing.length > 0) {
|
||||
const domSubs = getDomSubIndicatorLayouts(containerEl, slots.length);
|
||||
for (let i = 0; i < slots.length && i < domSubs.length; i++) {
|
||||
if (slots[i].items.every(it => it.layoutTopY != null)) continue;
|
||||
const dom = domSubs[i];
|
||||
for (const item of slots[i].items) {
|
||||
if (item.layoutTopY != null) continue;
|
||||
item.layoutTopY = dom.topY;
|
||||
item.layoutHeight = dom.height;
|
||||
}
|
||||
}
|
||||
}
|
||||
fillMissingLayoutsFromDom(items, containerEl, paneLayouts);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user