[20250721] domUtils add
This commit is contained in:
158
com.twin.app.shoptime/src/utils/domUtils.js
Normal file
158
com.twin.app.shoptime/src/utils/domUtils.js
Normal file
@@ -0,0 +1,158 @@
|
||||
// src/utils/domUtils.js
|
||||
// DOM 요소 관련 유틸리티 함수들 (크로미움68 호환)
|
||||
|
||||
/**
|
||||
* 특정 spotlight-id를 가진 요소를 재시도 메커니즘과 함께 찾습니다.
|
||||
* @param {string} spotlightId - 찾을 spotlight id
|
||||
* @param {number} maxRetries - 최대 재시도 횟수
|
||||
* @param {number} interval - 재시도 간격 (ms)
|
||||
* @returns {Promise<Element|null>} 찾은 DOM 요소 또는 null
|
||||
*/
|
||||
export const findElementWithRetry = (spotlightId, maxRetries = 10, interval = 100) => {
|
||||
return new Promise((resolve) => {
|
||||
let attempts = 0;
|
||||
|
||||
const checkElement = () => {
|
||||
const selector = `[data-spotlight-id="${spotlightId}"]`;
|
||||
const element = document.querySelector(selector);
|
||||
|
||||
if (element) {
|
||||
resolve(element);
|
||||
return;
|
||||
}
|
||||
|
||||
attempts++;
|
||||
if (attempts < maxRetries) {
|
||||
setTimeout(checkElement, interval);
|
||||
} else {
|
||||
console.warn(`[domUtils] Element not found after ${maxRetries} attempts: ${selector}`);
|
||||
resolve(null);
|
||||
}
|
||||
};
|
||||
|
||||
checkElement();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* DOM 요소의 위치와 크기 정보를 안전하게 가져옵니다.
|
||||
* @param {Element} element - 위치를 계산할 DOM 요소
|
||||
* @returns {Object|null} 위치 정보 객체 또는 null
|
||||
*/
|
||||
export const getElementPosition = (element) => {
|
||||
if (!element) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const rect = element.getBoundingClientRect();
|
||||
return {
|
||||
width: rect.width,
|
||||
height: rect.height,
|
||||
top: rect.top,
|
||||
left: rect.left,
|
||||
right: rect.right,
|
||||
bottom: rect.bottom,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('[domUtils] Error getting element position:', error);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* spotlight-id로 요소를 찾고 위치 정보를 반환합니다.
|
||||
* @param {string} spotlightId - 찾을 spotlight id
|
||||
* @param {number} maxRetries - 최대 재시도 횟수
|
||||
* @param {number} interval - 재시도 간격 (ms)
|
||||
* @returns {Promise<Object|null>} 위치 정보 객체 또는 null
|
||||
*/
|
||||
export const getElementPositionById = async (spotlightId, maxRetries = 10, interval = 100) => {
|
||||
const element = await findElementWithRetry(spotlightId, maxRetries, interval);
|
||||
return getElementPosition(element);
|
||||
};
|
||||
|
||||
/**
|
||||
* 모달 스타일 객체를 생성합니다.
|
||||
* @param {Object} position - 위치 정보 객체
|
||||
* @param {number} scale - 스케일 값 (선택적)
|
||||
* @returns {Object} 모달 스타일 객체
|
||||
*/
|
||||
export const createModalStyle = (position, scale = 1) => {
|
||||
if (!position) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return {
|
||||
width: position.width + "px",
|
||||
height: position.height + "px",
|
||||
top: position.top + "px",
|
||||
left: position.left + "px",
|
||||
position: "fixed",
|
||||
overflow: "visible",
|
||||
transform: scale !== 1 ? `scale(${scale})` : undefined,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* 배너별 기본 위치 정보를 반환합니다. (fallback용)
|
||||
* @param {string} bannerId - 배너 ID (banner0, banner1, etc.)
|
||||
* @returns {Object} 기본 위치 정보
|
||||
*/
|
||||
export const getDefaultBannerPosition = (bannerId) => {
|
||||
const defaultPositions = {
|
||||
banner0: { width: 640, height: 360, top: 100, left: 100 },
|
||||
banner1: { width: 640, height: 360, top: 100, left: 760 },
|
||||
banner2: { width: 640, height: 360, top: 500, left: 100 },
|
||||
banner3: { width: 640, height: 360, top: 500, left: 760 },
|
||||
};
|
||||
|
||||
return defaultPositions[bannerId] || defaultPositions.banner0;
|
||||
};
|
||||
|
||||
/**
|
||||
* 모든 배너의 위치 정보를 수집합니다.
|
||||
* @param {Array<string>} bannerIds - 수집할 배너 ID 배열
|
||||
* @returns {Promise<Object>} 배너별 위치 정보 맵
|
||||
*/
|
||||
export const collectBannerPositions = async (bannerIds = ['banner0', 'banner1', 'banner2', 'banner3']) => {
|
||||
const positions = {};
|
||||
|
||||
for (const bannerId of bannerIds) {
|
||||
const position = await getElementPositionById(bannerId, 5, 50); // 빠른 수집
|
||||
if (position) {
|
||||
positions[bannerId] = position;
|
||||
} else {
|
||||
// fallback 위치 사용
|
||||
positions[bannerId] = getDefaultBannerPosition(bannerId);
|
||||
console.warn(`[domUtils] Using fallback position for ${bannerId}`);
|
||||
}
|
||||
}
|
||||
|
||||
return positions;
|
||||
};
|
||||
|
||||
/**
|
||||
* 크로미움68 호환 - Optional Chaining 대신 안전한 객체 접근
|
||||
* @param {Object} obj - 접근할 객체
|
||||
* @param {string} path - 접근 경로 (예: 'a.b.c')
|
||||
* @param {*} defaultValue - 기본값
|
||||
* @returns {*} 값 또는 기본값
|
||||
*/
|
||||
export const safeGet = (obj, path, defaultValue = null) => {
|
||||
try {
|
||||
const keys = path.split('.');
|
||||
let result = obj;
|
||||
|
||||
for (const key of keys) {
|
||||
if (result === null || result === undefined) {
|
||||
return defaultValue;
|
||||
}
|
||||
result = result[key];
|
||||
}
|
||||
|
||||
return result !== undefined ? result : defaultValue;
|
||||
} catch (error) {
|
||||
return defaultValue;
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user