Files
Gnuboard7/resources/js/core/devtools/ui/utils.ts
T
2026-04-01 10:30:52 +09:00

228 lines
6.9 KiB
TypeScript

/**
* G7 DevTools 유틸리티 함수
*
* DevToolsPanel에서 사용되는 공통 유틸리티 함수들을 정의합니다.
*/
import { STORAGE_KEY, StoredPanelConfig } from './constants';
/**
* JSON 값의 타입에 따른 색상 클래스 반환
*
* @param value - 색상을 결정할 값
* @returns CSS 클래스 이름
*/
export const getValueColor = (value: unknown): string => {
if (value === null) return 'g7dt-text-gray';
if (value === undefined) return 'g7dt-text-gray';
if (typeof value === 'string') return 'g7dt-text-yellow';
if (typeof value === 'number') return 'g7dt-text-cyan';
if (typeof value === 'boolean') return 'g7dt-text-pink';
if (Array.isArray(value)) return 'g7dt-text-orange';
if (typeof value === 'object') return 'g7dt-text-blue';
return 'g7dt-text-white';
};
/**
* JSON 값의 타입 라벨 반환
*
* @param value - 타입 라벨을 반환할 값
* @returns 타입 라벨 문자열
*/
export const getTypeLabel = (value: unknown): string => {
if (value === null) return 'null';
if (value === undefined) return 'undefined';
if (Array.isArray(value)) return `Array(${value.length})`;
if (typeof value === 'object') return `Object(${Object.keys(value).length})`;
return typeof value;
};
/**
* 값을 표시용 문자열로 변환
*
* @param value - 변환할 값
* @returns 표시용 문자열
*/
export const formatValue = (value: unknown): string => {
if (value === null) return 'null';
if (value === undefined) return 'undefined';
if (typeof value === 'string') {
// 긴 문자열은 줄임
if (value.length > 50) {
return `"${value.substring(0, 50)}..."`;
}
return `"${value}"`;
}
if (typeof value === 'boolean') return value ? 'true' : 'false';
if (typeof value === 'number') return String(value);
return '';
};
/**
* 타임스탬프를 포맷팅된 문자열로 변환
*
* @param timestamp - 타임스탬프 (밀리초)
* @returns 포맷팅된 시간 문자열 (HH:MM:SS.mmm)
*/
export const formatTimestamp = (timestamp: number): string => {
const date = new Date(timestamp);
const hours = date.getHours().toString().padStart(2, '0');
const minutes = date.getMinutes().toString().padStart(2, '0');
const seconds = date.getSeconds().toString().padStart(2, '0');
const ms = date.getMilliseconds().toString().padStart(3, '0');
return `${hours}:${minutes}:${seconds}.${ms}`;
};
/**
* 로컬스토리지에서 패널 설정 로드
*
* @returns 저장된 패널 설정 (부분적)
*/
export const loadPanelConfig = (): Partial<StoredPanelConfig> => {
try {
const stored = localStorage.getItem(STORAGE_KEY);
if (stored) {
return JSON.parse(stored);
}
} catch (e) {
console.warn('[G7DevTools] 설정 로드 실패:', e);
}
return {};
};
/**
* 로컬스토리지에 패널 설정 저장
*
* @param config - 저장할 설정 (부분적)
*/
export const savePanelConfig = (config: Partial<StoredPanelConfig>): void => {
try {
const current = loadPanelConfig();
const merged = { ...current, ...config };
localStorage.setItem(STORAGE_KEY, JSON.stringify(merged));
} catch (e) {
console.warn('[G7DevTools] 설정 저장 실패:', e);
}
};
/**
* 바이트 크기를 읽기 쉬운 형식으로 변환
*
* @param bytes - 바이트 크기
* @returns 포맷팅된 크기 문자열
*/
export const formatBytes = (bytes: number): string => {
if (bytes === 0) return '0 B';
const k = 1024;
const sizes = ['B', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
};
/**
* 경과 시간을 읽기 쉬운 형식으로 변환
*
* @param ms - 밀리초
* @returns 포맷팅된 시간 문자열
*/
export const formatDuration = (ms: number): string => {
if (ms < 1) return `${(ms * 1000).toFixed(0)}μs`;
if (ms < 1000) return `${ms.toFixed(1)}ms`;
if (ms < 60000) return `${(ms / 1000).toFixed(2)}s`;
return `${(ms / 60000).toFixed(2)}m`;
};
/**
* 상태 뱃지 색상 클래스 반환
*
* @param status - 상태 문자열
* @returns CSS 클래스 이름
*/
export const getStatusBadgeClass = (status: string): string => {
const statusMap: Record<string, string> = {
success: 'g7dt-badge-success',
completed: 'g7dt-badge-success',
done: 'g7dt-badge-success',
error: 'g7dt-badge-error',
failed: 'g7dt-badge-error',
warning: 'g7dt-badge-warning',
pending: 'g7dt-badge-pending',
loading: 'g7dt-badge-loading',
idle: 'g7dt-badge-idle',
};
return statusMap[status.toLowerCase()] || 'g7dt-badge-default';
};
/**
* 객체를 안전하게 JSON 문자열로 변환
*
* @param obj - 변환할 객체
* @param indent - 들여쓰기 (기본: 2)
* @returns JSON 문자열
*/
export const safeStringify = (obj: unknown, indent: number = 2): string => {
try {
const seen = new WeakSet();
return JSON.stringify(
obj,
(key, value) => {
if (typeof value === 'object' && value !== null) {
if (seen.has(value)) {
return '[Circular]';
}
seen.add(value);
}
if (typeof value === 'function') {
return '[Function]';
}
if (value instanceof Error) {
return {
name: value.name,
message: value.message,
stack: value.stack,
};
}
return value;
},
indent
);
} catch (e) {
return '[Stringify Error]';
}
};
/**
* 깊은 객체에서 경로로 값 가져오기
*
* @param obj - 대상 객체
* @param path - 점으로 구분된 경로 (예: "user.profile.name")
* @param defaultValue - 기본값
* @returns 경로에 해당하는 값 또는 기본값
*/
export const getValueByPath = (obj: unknown, path: string, defaultValue: unknown = undefined): unknown => {
if (!obj || typeof obj !== 'object') return defaultValue;
const keys = path.split('.');
let current: unknown = obj;
for (const key of keys) {
if (current === null || current === undefined) return defaultValue;
if (typeof current !== 'object') return defaultValue;
current = (current as Record<string, unknown>)[key];
}
return current ?? defaultValue;
};
/**
* 문자열을 잘라서 말줄임표 추가
*
* @param str - 원본 문자열
* @param maxLength - 최대 길이
* @returns 잘린 문자열
*/
export const truncate = (str: string, maxLength: number): string => {
if (str.length <= maxLength) return str;
return str.substring(0, maxLength - 3) + '...';
};