You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

268 lines
11 KiB

// ─────────────────────────────────────────────────────────────────────────────
// TYPE GUARDS - vcen-edit/lib/guards.ts
// ─────────────────────────────────────────────────────────────────────────────
/**
* Type guards específicos para el editor de catálogos vcen-edit.
* Proporciona validación adicional para estructuras usadas en la UI del editor.
*/
import type { AttDomain, AttValue, ViewConfig, Visual, Hotspot } from '@/vcen/lib';
// ─────────────────────────────────────────────────────────────────────────────
// ATTRIBUTE VALUE GUARDS
// ─────────────────────────────────────────────────────────────────────────────
/**
* Guard para validar AttValue
*/
export function isAttValue(value: unknown): value is AttValue {
if (typeof value !== 'object' || value === null) return false;
const v = value as Record<string, unknown>;
return 'value' in v && 'domain' in v;
}
/**
* Guard para validar que un AttDomain tiene modo 'fixed'
*/
export function isAttDomainWithFixedMode(value: unknown): value is AttDomain & { mode: 'fixed'; fixed?: string | number | boolean } {
if (typeof value !== 'object' || value === null) return false;
const d = value as Record<string, unknown>;
return typeof d.kind === 'string' &&
d.mode === 'fixed' &&
(d.fixed === undefined ||
typeof d.fixed === 'string' ||
typeof d.fixed === 'number' ||
typeof d.fixed === 'boolean');
}
/**
* Guard para validar AttDomain con modo específico
*/
export function isAttDomainWithMode<K extends AttDomain['mode']>(
value: unknown,
mode: K
): value is AttDomain & { mode: K } {
if (typeof value !== 'object' || value === null) return false;
const d = value as AttDomain;
return d.mode === mode;
}
// ─────────────────────────────────────────────────────────────────────────────
// VIEW EDITOR GUARDS
// ─────────────────────────────────────────────────────────────────────────────
/**
* Guard para validar ViewConfig con estrategia 'static'
*/
export function isViewConfigStatic(value: unknown): value is ViewConfig & { strategy: 'static'; url: string } {
if (typeof value !== 'object' || value === null) return false;
const config = value as ViewConfig;
return config.strategy === 'static' && 'url' in config && typeof config.url === 'string';
}
/**
* Guard para validar ViewConfig con estrategia 'dynamic'
*/
export function isViewConfigDynamic(value: unknown): value is ViewConfig & { strategy: 'dynamic'; template: string | object } {
if (typeof value !== 'object' || value === null) return false;
const config = value as ViewConfig;
return config.strategy === 'dynamic' && 'template' in config;
}
/**
* Guard para validar ViewConfig con estrategia 'layers'
*/
export function isViewConfigLayers(value: unknown): value is ViewConfig & { strategy: 'layers'; layers: any[] } {
if (typeof value !== 'object' || value === null) return false;
const config = value as ViewConfig;
return config.strategy === 'layers' && 'layers' in config && Array.isArray(config.layers);
}
/**
* Guard para validar ViewConfig con estrategia 'api'
*/
export function isViewConfigApi(value: unknown): value is ViewConfig & { strategy: 'api'; endpoint: string | object } {
if (typeof value !== 'object' || value === null) return false;
const config = value as ViewConfig;
return config.strategy === 'api' && 'endpoint' in config;
}
/**
* Guard para validar ViewConfig con estrategia 'd3'
*/
export function isViewConfigD3(value: unknown): value is ViewConfig & { strategy: 'd3'; modelUrl: string } {
if (typeof value !== 'object' || value === null) return false;
const config = value as ViewConfig;
return config.strategy === 'd3' && 'modelUrl' in config && typeof config.modelUrl === 'string';
}
// ─────────────────────────────────────────────────────────────────────────────
// VISUAL EDITOR GUARDS
// ─────────────────────────────────────────────────────────────────────────────
/**
* Guard para validar Visual con vistas
*/
export function isVisualWithViews(value: unknown): value is Visual & { views: any[] } {
if (typeof value !== 'object' || value === null) return false;
const visual = value as Visual;
return 'views' in visual && Array.isArray(visual.views);
}
/**
* Guard para validar Visual con basePath
*/
export function isVisualWithBasePath(value: unknown): value is Visual & { basePath: string } {
if (typeof value !== 'object' || value === null) return false;
const visual = value as Visual;
return 'basePath' in visual && typeof visual.basePath === 'string';
}
/**
* Guard para validar Visual con fallback
*/
export function isVisualWithFallback(value: unknown): value is Visual & { fallback: string } {
if (typeof value !== 'object' || value === null) return false;
const visual = value as Visual;
return 'fallback' in visual && typeof visual.fallback === 'string';
}
// ─────────────────────────────────────────────────────────────────────────────
// HOTSPOT EDITOR GUARDS
// ─────────────────────────────────────────────────────────────────────────────
/**
* Guard para validar Hotspot con targetAtt
*/
export function isHotspotWithTargetAtt(value: unknown): value is Hotspot & { targetAtt: string } {
if (typeof value !== 'object' || value === null) return false;
const hotspot = value as Hotspot;
return 'targetAtt' in hotspot && typeof hotspot.targetAtt === 'string';
}
/**
* Guard para validar Hotspot con tooltip
*/
export function isHotspotWithTooltip(value: unknown): value is Hotspot & { tooltip: Record<string, string> } {
if (typeof value !== 'object' || value === null) return false;
const hotspot = value as Hotspot;
return 'tooltip' in hotspot &&
typeof hotspot.tooltip === 'object' &&
hotspot.tooltip !== null;
}
/**
* Guard para validar Hotspot con condición de visibilidad
*/
export function isHotspotWithVisibilityCondition(value: unknown): value is Hotspot & { visible: boolean | object } {
if (typeof value !== 'object' || value === null) return false;
const hotspot = value as Hotspot;
return 'visible' in hotspot &&
(typeof hotspot.visible === 'boolean' ||
(typeof hotspot.visible === 'object' && hotspot.visible !== null));
}
/**
* Guard para validar Hotspot con color
*/
export function isHotspotWithColor(value: unknown): value is Hotspot & { color: string } {
if (typeof value !== 'object' || value === null) return false;
const hotspot = value as Hotspot;
return 'color' in hotspot && typeof hotspot.color === 'string';
}
/**
* Guard para validar Hotspot con zIndex
*/
export function isHotspotWithZIndex(value: unknown): value is Hotspot & { zIndex: number } {
if (typeof value !== 'object' || value === null) return false;
const hotspot = value as Hotspot;
return 'zIndex' in hotspot && typeof hotspot.zIndex === 'number';
}
// ─────────────────────────────────────────────────────────────────────────────
// UI STATE GUARDS
// ─────────────────────────────────────────────────────────────────────────────
/**
* Guard para validar selección de vista en el editor visual
*/
export interface VisualSelection {
kind: 'general' | 'view' | 'hotspot';
viewId?: string;
hotspotId?: string;
}
export function isVisualSelection(value: unknown): value is VisualSelection {
if (typeof value !== 'object' || value === null) return false;
const sel = value as Record<string, unknown>;
return typeof sel.kind === 'string' &&
['general', 'view', 'hotspot'].includes(sel.kind);
}
/**
* Guard para validar selección de vista específica
*/
export function isViewSelection(value: unknown): value is VisualSelection & { kind: 'view'; viewId: string } {
if (!isVisualSelection(value)) return false;
return value.kind === 'view' && typeof value.viewId === 'string';
}
/**
* Guard para validar selección de hotspot específico
*/
export function isHotspotSelection(value: unknown): value is VisualSelection & { kind: 'hotspot'; viewId: string; hotspotId: string } {
if (!isVisualSelection(value)) return false;
return value.kind === 'hotspot' &&
typeof value.viewId === 'string' &&
typeof value.hotspotId === 'string';
}
/**
* Guard para validar selección general (configuración del visual)
*/
export function isGeneralSelection(value: unknown): value is VisualSelection & { kind: 'general' } {
if (!isVisualSelection(value)) return false;
return value.kind === 'general';
}
// ─────────────────────────────────────────────────────────────────────────────
// FORM STATE GUARDS
// ─────────────────────────────────────────────────────────────────────────────
/**
* Guard para validar estado de formulario con errores
*/
export interface FormErrors {
[key: string]: string | undefined;
}
export function isFormErrors(value: unknown): value is FormErrors {
if (typeof value !== 'object' || value === null) return false;
const errors = value as Record<string, unknown>;
return Object.values(errors).every(v => v === undefined || typeof v === 'string');
}
/**
* Guard para validar estado de formulario válido
*/
export function isFormValid(errors: FormErrors): boolean {
return Object.values(errors).every(v => !v);
}

Powered by TurnKey Linux.