Files
MyClub/frontend/src/hooks/usePageElementConfig.ts
T
Tomas Dvorak 70ea0c3c91 dev day #69
2025-10-23 22:26:50 +02:00

200 lines
6.8 KiB
TypeScript

import { useState, useEffect } from 'react';
import { PageElementConfig, getPageElementConfigs } from '../services/pageElements';
export const usePageElementConfig = (pageType: string, elementName: string, defaultVariant: string = 'unified') => {
const [variant, setVariant] = useState<string>(defaultVariant);
const [loading, setLoading] = useState(true);
useEffect(() => {
let active = true;
const loadConfig = async () => {
try {
const configs = await getPageElementConfigs(pageType);
if (active) {
const config = configs.find(c => c.element_name === elementName);
if (config) {
setVariant(config.variant);
}
}
} catch (error) {
console.error(`Failed to load config for ${elementName}:`, error);
} finally {
if (active) {
setLoading(false);
}
}
};
loadConfig();
return () => {
active = false;
};
}, [pageType, elementName, defaultVariant]);
return { variant, loading };
};
export const useAllPageElementConfigs = (pageType: string) => {
const [configs, setConfigs] = useState<Record<string, string>>({});
const [visibility, setVisibility] = useState<Record<string, boolean>>({});
const [styles, setStyles] = useState<Record<string, Record<string, any>>>({});
const [elementOrder, setElementOrder] = useState<string[]>([]);
const [loading, setLoading] = useState(true);
const [refreshKey, setRefreshKey] = useState<number>(0);
useEffect(() => {
let active = true;
// Helper function to apply DOM order
const applyDOMOrder = (order: string[]) => {
// Check if MyUIbrix viewport wrapper is active
const viewportWrapper = document.querySelector('.myuibrix-viewport-wrapper');
const container = viewportWrapper || document.querySelector('.container');
if (!container) return;
const sections = Array.from(container.querySelectorAll('[data-element]')) as HTMLElement[];
const elementMap = new Map<string, HTMLElement>();
sections.forEach(section => {
const elementName = section.getAttribute('data-element');
if (elementName) {
elementMap.set(elementName, section);
}
});
order.forEach((elementName) => {
const element = elementMap.get(elementName);
if (element && element.parentElement === container) {
container.appendChild(element);
}
});
};
const loadConfigs = async () => {
try {
const data = await getPageElementConfigs(pageType);
if (active) {
const configMap: Record<string, string> = {};
const visMap: Record<string, boolean> = {};
// Sort by display_order to get correct element order
const sorted = [...data].sort((a, b) => (a.display_order || 0) - (b.display_order || 0));
const order = sorted.map(config => config.element_name);
sorted.forEach(config => {
configMap[config.element_name] = config.variant;
visMap[config.element_name] = config.visible !== false;
});
setConfigs(configMap);
setVisibility(visMap);
setElementOrder(order);
// Apply initial order to DOM only in editor/preview mode
const isEditingMode = (() => {
try {
if (typeof document !== 'undefined' && (document.body?.classList?.contains('myuibrix-edit-mode'))) return true;
const params = new URLSearchParams(window.location.search);
return params.get('myuibrix') === 'edit';
} catch {
return false;
}
})();
if (order.length > 0 && isEditingMode) {
requestAnimationFrame(() => {
applyDOMOrder(order);
});
}
}
} catch (error) {
console.error('Failed to load page element configs:', error);
} finally {
if (active) {
setLoading(false);
}
}
};
loadConfigs();
// Listen for live updates from MyUIbrix editor (ONLY in preview mode)
const handleMyUIbrixChange = ((event: CustomEvent) => {
const { elementName, variant, visible, previewMode, timestamp } = event.detail;
// Only apply changes if in preview mode (editing)
// This prevents production users from seeing draft changes
if (previewMode) {
console.log(`[usePageElementConfig] Variant change: ${elementName} -> ${variant}`);
setConfigs(prev => ({
...prev,
[elementName]: variant
}));
setVisibility(prev => ({
...prev,
[elementName]: visible
}));
// Force React to re-render by incrementing refresh key
setRefreshKey(prev => prev + 1);
}
}) as EventListener;
// Listen for reorder events
const handleMyUIbrixReorder = ((event: CustomEvent) => {
const { order } = event.detail;
setElementOrder(order);
try {
const inEdit = document.body?.classList?.contains('myuibrix-edit-mode') || false;
if (inEdit) {
applyDOMOrder(order);
}
} catch {
// no-op
}
}) as EventListener;
// Listen for style changes from VisualStylePanel
const handleMyUIbrixStyleChange = ((event: CustomEvent) => {
const { elementName, styles: newStyles, previewMode } = event.detail;
if (previewMode) {
// Only update state - let React apply the styles through component rendering
// This prevents conflicts with React's virtual DOM
setStyles(prev => ({
...prev,
[elementName]: newStyles
}));
}
}) as EventListener;
window.addEventListener('myuibrix-change', handleMyUIbrixChange);
window.addEventListener('myuibrix-reorder', handleMyUIbrixReorder);
window.addEventListener('myuibrix-style-change', handleMyUIbrixStyleChange);
return () => {
active = false;
window.removeEventListener('myuibrix-change', handleMyUIbrixChange);
window.removeEventListener('myuibrix-reorder', handleMyUIbrixReorder);
window.removeEventListener('myuibrix-style-change', handleMyUIbrixStyleChange);
};
}, [pageType]);
const getVariant = (elementName: string, defaultVariant: string = 'unified'): string => {
return configs[elementName] || defaultVariant;
};
const isVisible = (elementName: string, defaultVisible: boolean = true): boolean => {
return visibility[elementName] !== undefined ? visibility[elementName] : defaultVisible;
};
const getStyles = (elementName: string): Record<string, any> | undefined => {
return styles[elementName];
};
return { configs, visibility, styles, elementOrder, getVariant, isVisible, getStyles, loading, refreshKey };
};