meno-core 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/cli.ts +281 -0
- package/build-static.ts +298 -0
- package/bunfig.toml +39 -0
- package/entries/client-router.tsx +111 -0
- package/entries/server-router.tsx +71 -0
- package/lib/client/ClientInitializer.test.ts +9 -0
- package/lib/client/ClientInitializer.test.ts.skip +92 -0
- package/lib/client/ClientInitializer.ts +60 -0
- package/lib/client/ErrorBoundary.test.tsx +595 -0
- package/lib/client/ErrorBoundary.tsx +230 -0
- package/lib/client/componentRegistry.test.ts +165 -0
- package/lib/client/componentRegistry.ts +18 -0
- package/lib/client/contexts/ThemeContext.tsx +73 -0
- package/lib/client/core/ComponentBuilder.test.ts +677 -0
- package/lib/client/core/ComponentBuilder.ts +660 -0
- package/lib/client/core/ComponentRenderer.test.tsx +176 -0
- package/lib/client/core/ComponentRenderer.tsx +83 -0
- package/lib/client/core/cmsTemplateProcessor.ts +129 -0
- package/lib/client/elementRegistry.ts +81 -0
- package/lib/client/hmr/HMRManager.tsx +179 -0
- package/lib/client/hmr/index.ts +5 -0
- package/lib/client/hmrWebSocket.test.ts +9 -0
- package/lib/client/hmrWebSocket.ts +250 -0
- package/lib/client/hooks/useColorVariables.test.ts +166 -0
- package/lib/client/hooks/useColorVariables.ts +249 -0
- package/lib/client/hooks/usePropertyAutocomplete.test.ts +9 -0
- package/lib/client/hooks/usePropertyAutocomplete.ts +40 -0
- package/lib/client/hydration/HydrationUtils.test.ts +154 -0
- package/lib/client/hydration/HydrationUtils.ts +35 -0
- package/lib/client/i18nConfigService.test.ts +74 -0
- package/lib/client/i18nConfigService.ts +78 -0
- package/lib/client/index.ts +56 -0
- package/lib/client/navigation.test.ts +441 -0
- package/lib/client/navigation.ts +23 -0
- package/lib/client/responsiveStyleResolver.test.ts +491 -0
- package/lib/client/responsiveStyleResolver.ts +184 -0
- package/lib/client/routing/RouteLoader.test.ts +635 -0
- package/lib/client/routing/RouteLoader.ts +347 -0
- package/lib/client/routing/Router.tsx +382 -0
- package/lib/client/scripts/ScriptExecutor.test.ts +489 -0
- package/lib/client/scripts/ScriptExecutor.ts +171 -0
- package/lib/client/scripts/formHandler.ts +103 -0
- package/lib/client/styleProcessor.test.ts +126 -0
- package/lib/client/styleProcessor.ts +92 -0
- package/lib/client/styles/StyleInjector.test.ts +354 -0
- package/lib/client/styles/StyleInjector.ts +154 -0
- package/lib/client/templateEngine.test.ts +660 -0
- package/lib/client/templateEngine.ts +667 -0
- package/lib/client/theme.test.ts +173 -0
- package/lib/client/theme.ts +159 -0
- package/lib/client/utils/toast.ts +46 -0
- package/lib/server/createServer.ts +170 -0
- package/lib/server/cssGenerator.test.ts +172 -0
- package/lib/server/cssGenerator.ts +58 -0
- package/lib/server/fileWatcher.ts +134 -0
- package/lib/server/index.ts +55 -0
- package/lib/server/jsonLoader.test.ts +103 -0
- package/lib/server/jsonLoader.ts +350 -0
- package/lib/server/middleware/cors.test.ts +177 -0
- package/lib/server/middleware/cors.ts +69 -0
- package/lib/server/middleware/errorHandler.test.ts +208 -0
- package/lib/server/middleware/errorHandler.ts +63 -0
- package/lib/server/middleware/index.ts +9 -0
- package/lib/server/middleware/logger.test.ts +233 -0
- package/lib/server/middleware/logger.ts +99 -0
- package/lib/server/pageCache.test.ts +167 -0
- package/lib/server/pageCache.ts +97 -0
- package/lib/server/projectContext.ts +51 -0
- package/lib/server/providers/fileSystemCMSProvider.test.ts +292 -0
- package/lib/server/providers/fileSystemCMSProvider.ts +227 -0
- package/lib/server/providers/fileSystemPageProvider.ts +83 -0
- package/lib/server/routes/api/cms.test.ts +177 -0
- package/lib/server/routes/api/cms.ts +82 -0
- package/lib/server/routes/api/colors.ts +59 -0
- package/lib/server/routes/api/components.ts +70 -0
- package/lib/server/routes/api/config.test.ts +9 -0
- package/lib/server/routes/api/config.ts +28 -0
- package/lib/server/routes/api/core-routes.ts +182 -0
- package/lib/server/routes/api/functions.ts +170 -0
- package/lib/server/routes/api/index.ts +69 -0
- package/lib/server/routes/api/pages.ts +95 -0
- package/lib/server/routes/api/shared.test.ts +81 -0
- package/lib/server/routes/api/shared.ts +31 -0
- package/lib/server/routes/editor.test.ts +9 -0
- package/lib/server/routes/index.ts +104 -0
- package/lib/server/routes/pages.ts +161 -0
- package/lib/server/routes/static.ts +107 -0
- package/lib/server/services/ColorService.ts +193 -0
- package/lib/server/services/cmsService.test.ts +388 -0
- package/lib/server/services/cmsService.ts +296 -0
- package/lib/server/services/componentService.test.ts +276 -0
- package/lib/server/services/componentService.ts +346 -0
- package/lib/server/services/configService.ts +156 -0
- package/lib/server/services/fileWatcherService.ts +67 -0
- package/lib/server/services/index.ts +10 -0
- package/lib/server/services/pageService.test.ts +258 -0
- package/lib/server/services/pageService.ts +240 -0
- package/lib/server/ssrRenderer.test.ts +1005 -0
- package/lib/server/ssrRenderer.ts +878 -0
- package/lib/server/utilityClassGenerator.ts +11 -0
- package/lib/server/utils/index.ts +5 -0
- package/lib/server/utils/jsonLineMapper.test.ts +100 -0
- package/lib/server/utils/jsonLineMapper.ts +166 -0
- package/lib/server/validateStyleCoverage.test.ts +9 -0
- package/lib/server/validateStyleCoverage.ts +167 -0
- package/lib/server/websocketManager.test.ts +9 -0
- package/lib/server/websocketManager.ts +95 -0
- package/lib/shared/attributeNodeUtils.test.ts +152 -0
- package/lib/shared/attributeNodeUtils.ts +50 -0
- package/lib/shared/breakpoints.test.ts +166 -0
- package/lib/shared/breakpoints.ts +65 -0
- package/lib/shared/colorProperties.test.ts +111 -0
- package/lib/shared/colorProperties.ts +40 -0
- package/lib/shared/colorVariableUtils.test.ts +319 -0
- package/lib/shared/colorVariableUtils.ts +97 -0
- package/lib/shared/constants.test.ts +175 -0
- package/lib/shared/constants.ts +116 -0
- package/lib/shared/cssGeneration.ts +481 -0
- package/lib/shared/cssProperties.test.ts +252 -0
- package/lib/shared/cssProperties.ts +338 -0
- package/lib/shared/elementUtils.test.ts +245 -0
- package/lib/shared/elementUtils.ts +90 -0
- package/lib/shared/fontLoader.ts +97 -0
- package/lib/shared/i18n.test.ts +313 -0
- package/lib/shared/i18n.ts +286 -0
- package/lib/shared/index.ts +50 -0
- package/lib/shared/interfaces/contentProvider.test.ts +9 -0
- package/lib/shared/interfaces/contentProvider.ts +121 -0
- package/lib/shared/nodeUtils.test.ts +320 -0
- package/lib/shared/nodeUtils.ts +220 -0
- package/lib/shared/pathArrayUtils.test.ts +315 -0
- package/lib/shared/pathArrayUtils.ts +17 -0
- package/lib/shared/pathUtils.test.ts +260 -0
- package/lib/shared/pathUtils.ts +244 -0
- package/lib/shared/paths/Path.test.ts +74 -0
- package/lib/shared/paths/Path.ts +23 -0
- package/lib/shared/paths/PathConverter.test.ts +232 -0
- package/lib/shared/paths/PathConverter.ts +141 -0
- package/lib/shared/paths/PathUtils.ts +290 -0
- package/lib/shared/paths/PathValidator.test.ts +193 -0
- package/lib/shared/paths/PathValidator.ts +53 -0
- package/lib/shared/paths/index.ts +48 -0
- package/lib/shared/propResolver.test.ts +639 -0
- package/lib/shared/propResolver.ts +124 -0
- package/lib/shared/registry/BaseNodeTypeRegistry.test.ts +190 -0
- package/lib/shared/registry/BaseNodeTypeRegistry.ts +200 -0
- package/lib/shared/registry/ClientNodeTypeRegistry.ts +34 -0
- package/lib/shared/registry/ClientRegistry.test.ts +26 -0
- package/lib/shared/registry/ClientRegistry.ts +15 -0
- package/lib/shared/registry/ComponentRegistry.test.ts +293 -0
- package/lib/shared/registry/ComponentRegistry.ts +100 -0
- package/lib/shared/registry/NodeTypeDefinition.ts +198 -0
- package/lib/shared/registry/NodeTypeManager.ts +94 -0
- package/lib/shared/registry/RegistryManager.test.ts +58 -0
- package/lib/shared/registry/RegistryManager.ts +60 -0
- package/lib/shared/registry/SSRNodeTypeRegistry.ts +33 -0
- package/lib/shared/registry/SSRRegistry.test.ts +26 -0
- package/lib/shared/registry/SSRRegistry.ts +15 -0
- package/lib/shared/registry/createNodeType.ts +175 -0
- package/lib/shared/registry/defineNodeType.ts +73 -0
- package/lib/shared/registry/fieldPresets.ts +109 -0
- package/lib/shared/registry/index.ts +50 -0
- package/lib/shared/registry/nodeTypes/ComponentInstanceNodeType.ts +71 -0
- package/lib/shared/registry/nodeTypes/EmbedNodeType.ts +61 -0
- package/lib/shared/registry/nodeTypes/HtmlNodeType.ts +88 -0
- package/lib/shared/registry/nodeTypes/LocaleListNodeType.ts +66 -0
- package/lib/shared/registry/nodeTypes/ObjectLinkNodeType.ts +75 -0
- package/lib/shared/registry/nodeTypes/SlotMarkerType.ts +49 -0
- package/lib/shared/registry/nodeTypes/TextNodeType.ts +52 -0
- package/lib/shared/registry/nodeTypes/index.ts +75 -0
- package/lib/shared/responsiveScaling.test.ts +268 -0
- package/lib/shared/responsiveScaling.ts +194 -0
- package/lib/shared/responsiveStyleUtils.test.ts +300 -0
- package/lib/shared/responsiveStyleUtils.ts +139 -0
- package/lib/shared/slugTranslator.test.ts +325 -0
- package/lib/shared/slugTranslator.ts +177 -0
- package/lib/shared/styleNodeUtils.test.ts +132 -0
- package/lib/shared/styleNodeUtils.ts +102 -0
- package/lib/shared/styleUtils.test.ts +238 -0
- package/lib/shared/styleUtils.ts +63 -0
- package/lib/shared/themeDefaults.test.ts +113 -0
- package/lib/shared/themeDefaults.ts +103 -0
- package/lib/shared/tree/PathBuilder.ts +383 -0
- package/lib/shared/treePathUtils.test.ts +539 -0
- package/lib/shared/treePathUtils.ts +339 -0
- package/lib/shared/types/api.ts +58 -0
- package/lib/shared/types/cms.ts +95 -0
- package/lib/shared/types/colors.ts +45 -0
- package/lib/shared/types/components.ts +121 -0
- package/lib/shared/types/errors.test.ts +103 -0
- package/lib/shared/types/errors.ts +69 -0
- package/lib/shared/types/index.ts +96 -0
- package/lib/shared/types/nodes.ts +20 -0
- package/lib/shared/types/rendering.ts +61 -0
- package/lib/shared/types/styles.ts +38 -0
- package/lib/shared/types.ts +11 -0
- package/lib/shared/utilityClassConfig.ts +287 -0
- package/lib/shared/utilityClassMapper.test.ts +140 -0
- package/lib/shared/utilityClassMapper.ts +229 -0
- package/lib/shared/utils/fileUtils.test.ts +99 -0
- package/lib/shared/utils/fileUtils.ts +56 -0
- package/lib/shared/utils.test.ts +261 -0
- package/lib/shared/utils.ts +84 -0
- package/lib/shared/validation/index.ts +7 -0
- package/lib/shared/validation/propValidator.test.ts +178 -0
- package/lib/shared/validation/propValidator.ts +238 -0
- package/lib/shared/validation/schemas.test.ts +177 -0
- package/lib/shared/validation/schemas.ts +401 -0
- package/lib/shared/validation/validators.test.ts +109 -0
- package/lib/shared/validation/validators.ts +304 -0
- package/lib/test-utils/dom-setup.ts +55 -0
- package/lib/test-utils/factories/ConsoleMockFactory.ts +200 -0
- package/lib/test-utils/factories/DomMockFactory.ts +487 -0
- package/lib/test-utils/factories/EventMockFactory.ts +244 -0
- package/lib/test-utils/factories/FetchMockFactory.ts +210 -0
- package/lib/test-utils/factories/ServerMockFactory.ts +223 -0
- package/lib/test-utils/factories/StoreMockFactory.ts +370 -0
- package/lib/test-utils/factories/index.ts +11 -0
- package/lib/test-utils/fixtures.ts +134 -0
- package/lib/test-utils/helpers/asyncHelpers.test.ts +112 -0
- package/lib/test-utils/helpers/asyncHelpers.ts +196 -0
- package/lib/test-utils/helpers/index.ts +6 -0
- package/lib/test-utils/helpers.test.ts +73 -0
- package/lib/test-utils/helpers.ts +90 -0
- package/lib/test-utils/index.ts +17 -0
- package/lib/test-utils/mockFactories.ts +92 -0
- package/lib/test-utils/mocks.ts +341 -0
- package/package.json +38 -0
- package/templates/index-router.html +34 -0
- package/tsconfig.json +14 -0
- package/vite.config.ts +43 -0
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Router Component
|
|
3
|
+
*
|
|
4
|
+
* Main router component that handles routing, state management, and rendering.
|
|
5
|
+
* Uses all extracted modules for a clean, modular architecture.
|
|
6
|
+
*
|
|
7
|
+
* Responsibilities:
|
|
8
|
+
* - Route loading and navigation
|
|
9
|
+
* - Component tree state management
|
|
10
|
+
* - SSR hydration detection
|
|
11
|
+
* - HMR integration
|
|
12
|
+
* - Loading and error states
|
|
13
|
+
* - Viewport width tracking for responsive styles
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { createElement as h, useState, useEffect, useRef, useCallback, useMemo } from "react";
|
|
17
|
+
import type { ReactElement } from "react";
|
|
18
|
+
import { renderPage } from "../core/ComponentRenderer";
|
|
19
|
+
import { RouteLoader } from "./RouteLoader";
|
|
20
|
+
import { detectSSRContent, getInitialLoadingState } from "../hydration/HydrationUtils";
|
|
21
|
+
import { HMRManager } from "../hmr/HMRManager";
|
|
22
|
+
import { createNavigationHandler } from "../navigation";
|
|
23
|
+
import { initializeBreakpoints } from "../responsiveStyleResolver";
|
|
24
|
+
import { elementRegistry } from "../elementRegistry";
|
|
25
|
+
import { initializeClient, setupEventHandlers } from "../ClientInitializer";
|
|
26
|
+
import type { ComponentNode, I18nConfig } from "../../shared/types";
|
|
27
|
+
import { parseLocaleFromPath, setStoredLocale, DEFAULT_I18N_CONFIG } from "../../shared/i18n";
|
|
28
|
+
import { fetchI18nConfig, setI18nConfig as setCachedI18nConfig } from "../i18nConfigService";
|
|
29
|
+
import { IFRAME_MESSAGE_TYPES } from "../../shared/constants";
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Router component props
|
|
33
|
+
*/
|
|
34
|
+
export interface RouterProps {
|
|
35
|
+
/**
|
|
36
|
+
* Initial path (optional, defaults to window.location.pathname)
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```tsx
|
|
40
|
+
* <Router initialPath="/about" />
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
initialPath?: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Router Component
|
|
48
|
+
*
|
|
49
|
+
* Main router component that handles client-side routing, page loading, and rendering.
|
|
50
|
+
* Manages component tree state, loading states, and integrates with HMR for hot reloading.
|
|
51
|
+
*
|
|
52
|
+
* Features:
|
|
53
|
+
* - Automatic route loading on path changes
|
|
54
|
+
* - SSR hydration support
|
|
55
|
+
* - Loading state management with previous content preservation
|
|
56
|
+
* - 404 handling with available pages list
|
|
57
|
+
* - Viewport width tracking for responsive styles
|
|
58
|
+
* - HMR integration for development
|
|
59
|
+
*
|
|
60
|
+
* @param props - Router configuration props
|
|
61
|
+
* @returns ReactElement representing the router and current page
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```tsx
|
|
65
|
+
* // Basic usage
|
|
66
|
+
* <Router />
|
|
67
|
+
*
|
|
68
|
+
* // With custom initial path
|
|
69
|
+
* <Router initialPath="/custom" />
|
|
70
|
+
* ```
|
|
71
|
+
*/
|
|
72
|
+
export function Router(props: RouterProps = {}): ReactElement {
|
|
73
|
+
const { initialPath = window.location.pathname } = props;
|
|
74
|
+
const hasSSRContent = detectSSRContent();
|
|
75
|
+
|
|
76
|
+
// Initialize client services (created once on mount)
|
|
77
|
+
const services = useMemo(() => initializeClient(), []);
|
|
78
|
+
|
|
79
|
+
// Setup event handlers on mount
|
|
80
|
+
useEffect(() => {
|
|
81
|
+
const cleanup = setupEventHandlers(services);
|
|
82
|
+
|
|
83
|
+
// Cleanup on unmount
|
|
84
|
+
return cleanup;
|
|
85
|
+
}, [services]);
|
|
86
|
+
|
|
87
|
+
// State management
|
|
88
|
+
const [componentTree, setComponentTree] = useState<ComponentNode | ComponentNode[] | null>(null);
|
|
89
|
+
// Keep previous componentTree to show during transitions
|
|
90
|
+
const [previousComponentTree, setPreviousComponentTree] = useState<ComponentNode | ComponentNode[] | null>(null);
|
|
91
|
+
// Ref to track current componentTree for closure access (needed for capturing current value in async functions)
|
|
92
|
+
const componentTreeRef = useRef<ComponentNode | ComponentNode[] | null>(null);
|
|
93
|
+
const [loading, setLoading] = useState(getInitialLoadingState(hasSSRContent));
|
|
94
|
+
const [currentPath, setCurrentPath] = useState(initialPath);
|
|
95
|
+
const [availablePages, setAvailablePages] = useState<string[]>([]);
|
|
96
|
+
const [showNotFound, setShowNotFound] = useState(false);
|
|
97
|
+
const [viewportWidth, setViewportWidth] = useState<number>(
|
|
98
|
+
typeof window !== 'undefined' ? window.innerWidth : 1920
|
|
99
|
+
);
|
|
100
|
+
const [i18nConfig, setI18nConfig] = useState<I18nConfig>(DEFAULT_I18N_CONFIG);
|
|
101
|
+
const [currentLocale, setCurrentLocale] = useState<string>(DEFAULT_I18N_CONFIG.defaultLocale);
|
|
102
|
+
const [cmsContext, setCmsContext] = useState<Record<string, unknown> | null>(null);
|
|
103
|
+
const [cmsLocale, setCmsLocale] = useState<string | null>(null);
|
|
104
|
+
const [awaitingCmsContext, setAwaitingCmsContext] = useState(false);
|
|
105
|
+
|
|
106
|
+
// Create RouteLoader instance
|
|
107
|
+
const routeLoader = useRef(new RouteLoader({
|
|
108
|
+
componentRegistry: services.componentRegistry,
|
|
109
|
+
onLoadStart: () => {
|
|
110
|
+
setLoading(true);
|
|
111
|
+
setShowNotFound(false);
|
|
112
|
+
// Save current componentTree as previous before loading
|
|
113
|
+
if (componentTreeRef.current) {
|
|
114
|
+
setPreviousComponentTree(componentTreeRef.current);
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
onLoadComplete: (tree) => {
|
|
118
|
+
setComponentTree(tree);
|
|
119
|
+
setLoading(false);
|
|
120
|
+
setShowNotFound(false);
|
|
121
|
+
// Clear previous componentTree now that we have new content
|
|
122
|
+
setPreviousComponentTree(null);
|
|
123
|
+
},
|
|
124
|
+
onLoadError: (error) => {
|
|
125
|
+
setLoading(false);
|
|
126
|
+
setShowNotFound(false);
|
|
127
|
+
setPreviousComponentTree(null);
|
|
128
|
+
},
|
|
129
|
+
onNotFound: () => setShowNotFound(true),
|
|
130
|
+
onPagesLoaded: (pages) => setAvailablePages(pages),
|
|
131
|
+
onLocaleDetected: (locale) => setCurrentLocale(locale),
|
|
132
|
+
})).current;
|
|
133
|
+
|
|
134
|
+
// Keep ref in sync with state
|
|
135
|
+
useEffect(() => {
|
|
136
|
+
componentTreeRef.current = componentTree;
|
|
137
|
+
}, [componentTree]);
|
|
138
|
+
|
|
139
|
+
// Initialize breakpoints on mount
|
|
140
|
+
useEffect(() => {
|
|
141
|
+
initializeBreakpoints();
|
|
142
|
+
}, []);
|
|
143
|
+
|
|
144
|
+
// Load i18n config on mount using shared service
|
|
145
|
+
useEffect(() => {
|
|
146
|
+
fetchI18nConfig().then(config => {
|
|
147
|
+
setI18nConfig(config);
|
|
148
|
+
setCachedI18nConfig(config);
|
|
149
|
+
// Update RouteLoader with i18n config so it can extract locale from paths
|
|
150
|
+
routeLoader.setI18nConfig(config);
|
|
151
|
+
// Extract locale from current path
|
|
152
|
+
const { locale } = parseLocaleFromPath(currentPath, config);
|
|
153
|
+
setCurrentLocale(locale);
|
|
154
|
+
setStoredLocale(locale); // Persist preference
|
|
155
|
+
});
|
|
156
|
+
}, []);
|
|
157
|
+
|
|
158
|
+
// Update locale when path changes
|
|
159
|
+
useEffect(() => {
|
|
160
|
+
const { locale } = parseLocaleFromPath(currentPath, i18nConfig);
|
|
161
|
+
setCurrentLocale(locale);
|
|
162
|
+
setStoredLocale(locale); // Persist preference
|
|
163
|
+
}, [currentPath, i18nConfig]);
|
|
164
|
+
|
|
165
|
+
// Listen to viewport resize to update responsive styles
|
|
166
|
+
useEffect(() => {
|
|
167
|
+
if (typeof window === 'undefined') return;
|
|
168
|
+
|
|
169
|
+
const handleResize = () => {
|
|
170
|
+
setViewportWidth(window.innerWidth);
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
window.addEventListener('resize', handleResize);
|
|
174
|
+
return () => window.removeEventListener('resize', handleResize);
|
|
175
|
+
}, []);
|
|
176
|
+
|
|
177
|
+
// Listen for CMS context updates from parent window (editor)
|
|
178
|
+
useEffect(() => {
|
|
179
|
+
if (typeof window === 'undefined') return;
|
|
180
|
+
|
|
181
|
+
const handleMessage = (event: MessageEvent) => {
|
|
182
|
+
if (event.data?.type === IFRAME_MESSAGE_TYPES.CMS_CONTEXT_UPDATE) {
|
|
183
|
+
setCmsContext(event.data.cmsItem);
|
|
184
|
+
setCmsLocale(event.data.locale || null);
|
|
185
|
+
setAwaitingCmsContext(false); // Context received, ready to render
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
window.addEventListener('message', handleMessage);
|
|
190
|
+
return () => window.removeEventListener('message', handleMessage);
|
|
191
|
+
}, []);
|
|
192
|
+
|
|
193
|
+
// Request CMS context from parent when iframe is ready
|
|
194
|
+
useEffect(() => {
|
|
195
|
+
if (typeof window === 'undefined') return;
|
|
196
|
+
if (window.parent === window) return; // Not in iframe
|
|
197
|
+
|
|
198
|
+
setAwaitingCmsContext(true); // Mark that we're waiting for context
|
|
199
|
+
window.parent.postMessage({
|
|
200
|
+
type: IFRAME_MESSAGE_TYPES.CMS_CONTEXT_REQUEST
|
|
201
|
+
}, '*');
|
|
202
|
+
|
|
203
|
+
// Fallback timeout - if no response in 200ms, render anyway
|
|
204
|
+
// This handles cases where CMSPanel isn't mounted or page has no CMS templates
|
|
205
|
+
const timeoutId = setTimeout(() => {
|
|
206
|
+
setAwaitingCmsContext(false);
|
|
207
|
+
}, 200);
|
|
208
|
+
|
|
209
|
+
return () => clearTimeout(timeoutId);
|
|
210
|
+
}, []);
|
|
211
|
+
|
|
212
|
+
// Inject CSS and execute JavaScript after component tree is rendered
|
|
213
|
+
useEffect(() => {
|
|
214
|
+
if (componentTree) {
|
|
215
|
+
// Inject CSS immediately
|
|
216
|
+
services.styleInjector.inject();
|
|
217
|
+
|
|
218
|
+
// Wait for React to render before executing JavaScript
|
|
219
|
+
const timeoutId = setTimeout(() => {
|
|
220
|
+
services.scriptExecutor.execute();
|
|
221
|
+
}, 100);
|
|
222
|
+
|
|
223
|
+
return () => {
|
|
224
|
+
clearTimeout(timeoutId);
|
|
225
|
+
// Don't clear registry here - ref callbacks handle cleanup when elements unmount
|
|
226
|
+
// Clearing here would remove elements before React unmounts them
|
|
227
|
+
};
|
|
228
|
+
} else {
|
|
229
|
+
// Clear registry when tree is cleared (no elements to register)
|
|
230
|
+
elementRegistry.clear();
|
|
231
|
+
}
|
|
232
|
+
}, [componentTree, services]);
|
|
233
|
+
|
|
234
|
+
// Load components function using RouteLoader
|
|
235
|
+
const loadComponents = useCallback(async (path: string) => {
|
|
236
|
+
await routeLoader.loadComponents(path);
|
|
237
|
+
}, []);
|
|
238
|
+
|
|
239
|
+
// Handle navigation
|
|
240
|
+
useEffect(() => {
|
|
241
|
+
const handlePopState = () => {
|
|
242
|
+
const newPath = window.location.pathname;
|
|
243
|
+
setCurrentPath(newPath);
|
|
244
|
+
setShowNotFound(false); // Reset not found state on navigation
|
|
245
|
+
loadComponents(newPath);
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
window.addEventListener('popstate', handlePopState);
|
|
249
|
+
return () => window.removeEventListener('popstate', handlePopState);
|
|
250
|
+
}, [loadComponents]);
|
|
251
|
+
|
|
252
|
+
useEffect(() => {
|
|
253
|
+
// Initial load
|
|
254
|
+
routeLoader.loadPages();
|
|
255
|
+
routeLoader.loadGlobalComponents().then(() => {
|
|
256
|
+
loadComponents(currentPath);
|
|
257
|
+
});
|
|
258
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
259
|
+
}, []); // Only run on mount - currentPath handled by separate effect below
|
|
260
|
+
|
|
261
|
+
// Reload when path changes
|
|
262
|
+
useEffect(() => {
|
|
263
|
+
setShowNotFound(false); // Reset not found state when path changes
|
|
264
|
+
loadComponents(currentPath);
|
|
265
|
+
}, [currentPath, loadComponents]);
|
|
266
|
+
|
|
267
|
+
// Cleanup on unmount
|
|
268
|
+
useEffect(() => {
|
|
269
|
+
return () => {
|
|
270
|
+
routeLoader.cancel();
|
|
271
|
+
};
|
|
272
|
+
}, []);
|
|
273
|
+
|
|
274
|
+
// Render logic: prioritize current tree, then previous tree during loading, then loading/not found states
|
|
275
|
+
let pageContent: ReactElement | null = null;
|
|
276
|
+
|
|
277
|
+
if (componentTree && !awaitingCmsContext) {
|
|
278
|
+
// Show current tree when we have it and CMS context is ready (or not needed)
|
|
279
|
+
pageContent = renderPage({
|
|
280
|
+
tree: componentTree,
|
|
281
|
+
currentPath,
|
|
282
|
+
viewportWidth,
|
|
283
|
+
componentBuilder: services.componentBuilder,
|
|
284
|
+
locale: currentLocale,
|
|
285
|
+
i18nConfig,
|
|
286
|
+
cmsContext,
|
|
287
|
+
cmsLocale
|
|
288
|
+
});
|
|
289
|
+
} else if (componentTree && awaitingCmsContext && previousComponentTree) {
|
|
290
|
+
// While waiting for CMS context, show previous tree to avoid blink
|
|
291
|
+
pageContent = renderPage({
|
|
292
|
+
tree: previousComponentTree,
|
|
293
|
+
currentPath,
|
|
294
|
+
viewportWidth,
|
|
295
|
+
componentBuilder: services.componentBuilder,
|
|
296
|
+
locale: currentLocale,
|
|
297
|
+
i18nConfig,
|
|
298
|
+
cmsContext,
|
|
299
|
+
cmsLocale
|
|
300
|
+
});
|
|
301
|
+
} else if (loading || awaitingCmsContext) {
|
|
302
|
+
// Show previous content while loading if available
|
|
303
|
+
if (previousComponentTree) {
|
|
304
|
+
pageContent = renderPage({
|
|
305
|
+
tree: previousComponentTree,
|
|
306
|
+
currentPath,
|
|
307
|
+
viewportWidth,
|
|
308
|
+
componentBuilder: services.componentBuilder,
|
|
309
|
+
locale: currentLocale,
|
|
310
|
+
i18nConfig,
|
|
311
|
+
cmsContext,
|
|
312
|
+
cmsLocale
|
|
313
|
+
});
|
|
314
|
+
} else {
|
|
315
|
+
// Only show loading message if no previous content
|
|
316
|
+
pageContent = h('div', { style: { textAlign: 'center', padding: '40px' } },
|
|
317
|
+
h('h2', null, 'Loading page...')
|
|
318
|
+
);
|
|
319
|
+
}
|
|
320
|
+
} else if (showNotFound) {
|
|
321
|
+
pageContent = h('div', {
|
|
322
|
+
style: {
|
|
323
|
+
textAlign: 'center',
|
|
324
|
+
padding: '40px',
|
|
325
|
+
maxWidth: '800px',
|
|
326
|
+
margin: '0 auto'
|
|
327
|
+
}
|
|
328
|
+
},
|
|
329
|
+
h('h2', null, `Page not found: ${currentPath}`),
|
|
330
|
+
h('p', { style: { marginTop: '16px', marginBottom: '24px', color: '#666' } },
|
|
331
|
+
'Available pages:'
|
|
332
|
+
),
|
|
333
|
+
h('ul', {
|
|
334
|
+
style: {
|
|
335
|
+
listStyle: 'none',
|
|
336
|
+
padding: 0,
|
|
337
|
+
display: 'flex',
|
|
338
|
+
flexDirection: 'column',
|
|
339
|
+
gap: '12px',
|
|
340
|
+
alignItems: 'center'
|
|
341
|
+
}
|
|
342
|
+
},
|
|
343
|
+
availablePages.map((page, i) =>
|
|
344
|
+
h('li', { key: i },
|
|
345
|
+
h('a', {
|
|
346
|
+
href: page,
|
|
347
|
+
onClick: createNavigationHandler(page),
|
|
348
|
+
style: {
|
|
349
|
+
color: '#0070f3',
|
|
350
|
+
textDecoration: 'none',
|
|
351
|
+
fontSize: '18px',
|
|
352
|
+
fontWeight: '600'
|
|
353
|
+
}
|
|
354
|
+
}, page === '/' ? 'Home (/)' : page)
|
|
355
|
+
)
|
|
356
|
+
)
|
|
357
|
+
)
|
|
358
|
+
);
|
|
359
|
+
} else if (previousComponentTree) {
|
|
360
|
+
// Keep previous content visible during timeout (waiting to determine if not found)
|
|
361
|
+
pageContent = renderPage({
|
|
362
|
+
tree: previousComponentTree,
|
|
363
|
+
currentPath,
|
|
364
|
+
viewportWidth,
|
|
365
|
+
componentBuilder: services.componentBuilder,
|
|
366
|
+
locale: currentLocale,
|
|
367
|
+
i18nConfig,
|
|
368
|
+
cmsContext,
|
|
369
|
+
cmsLocale
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// Render HMRManager once at the top level with page content
|
|
374
|
+
return h('div', null,
|
|
375
|
+
h(HMRManager, {
|
|
376
|
+
onReload: loadComponents,
|
|
377
|
+
currentPath: currentPath,
|
|
378
|
+
}),
|
|
379
|
+
pageContent
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
|