ezfw-core 1.0.51 → 1.0.53
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.
|
@@ -4,7 +4,7 @@ declare const ez: {
|
|
|
4
4
|
_createChildElements(items: unknown[], controller: string | null, parent: unknown, css?: string | null, isRepaint?: boolean): Promise<Node[]>;
|
|
5
5
|
};
|
|
6
6
|
|
|
7
|
-
type DOMEventHandler<E extends Event = Event> = (e: E) => void;
|
|
7
|
+
type DOMEventHandler<E extends Event = Event> = (e: E, component: HtmlWrapper) => void;
|
|
8
8
|
|
|
9
9
|
export interface HtmlWrapperConfig extends EzBaseComponentConfig {
|
|
10
10
|
eztype: string;
|
|
@@ -138,6 +138,9 @@ export class HtmlWrapper extends EzBaseComponent {
|
|
|
138
138
|
async render(): Promise<HTMLElement> {
|
|
139
139
|
this._domListeners = [];
|
|
140
140
|
|
|
141
|
+
// Resolve controller so this.controller is available in event handlers
|
|
142
|
+
await this._resolveController();
|
|
143
|
+
|
|
141
144
|
const cfg = this.config;
|
|
142
145
|
const el = document.createElement(cfg.eztype) as HTMLElement;
|
|
143
146
|
|
|
@@ -282,9 +285,10 @@ export class HtmlWrapper extends EzBaseComponent {
|
|
|
282
285
|
for (const [configKey, eventName] of eventMap) {
|
|
283
286
|
const handler = cfg[configKey];
|
|
284
287
|
if (typeof handler === "function") {
|
|
285
|
-
|
|
288
|
+
const wrappedHandler = (e: Event) => (handler as (e: Event, component: HtmlWrapper) => void)(e, this);
|
|
289
|
+
el.addEventListener(eventName, wrappedHandler);
|
|
286
290
|
this._domListeners.push(() =>
|
|
287
|
-
el.removeEventListener(eventName,
|
|
291
|
+
el.removeEventListener(eventName, wrappedHandler)
|
|
288
292
|
);
|
|
289
293
|
}
|
|
290
294
|
}
|
|
@@ -17,6 +17,8 @@ interface TabConfig {
|
|
|
17
17
|
closable?: boolean;
|
|
18
18
|
items?: unknown[];
|
|
19
19
|
restoreData?: unknown;
|
|
20
|
+
cls?: string | string[];
|
|
21
|
+
[key: string]: unknown;
|
|
20
22
|
}
|
|
21
23
|
|
|
22
24
|
interface SavedTabState {
|
|
@@ -146,7 +148,7 @@ export class EzTabPanel extends EzComponent {
|
|
|
146
148
|
|
|
147
149
|
// Set up internal structure for EzComponent to render
|
|
148
150
|
this.config.layout = 'vbox';
|
|
149
|
-
this.config.style = { ...this.config.style, minHeight: 0 };
|
|
151
|
+
this.config.style = { ...this.config.style, minHeight: '0' };
|
|
150
152
|
this.config.items = [
|
|
151
153
|
{
|
|
152
154
|
cls: cls('tabHeaderWrapper'),
|
|
@@ -154,7 +156,7 @@ export class EzTabPanel extends EzComponent {
|
|
|
154
156
|
{ cls: cls('tabHeader') }
|
|
155
157
|
]
|
|
156
158
|
},
|
|
157
|
-
{ cls: cls('tabContent'), flex: 1, style: { minHeight: 0 } }
|
|
159
|
+
{ cls: cls('tabContent'), flex: 1, style: { minHeight: '0' } }
|
|
158
160
|
];
|
|
159
161
|
|
|
160
162
|
// Call super.render() to get element with all framework features (flex, styles, bindings)
|
package/core/renderer.ts
CHANGED
|
@@ -362,7 +362,11 @@ export class EzRenderer {
|
|
|
362
362
|
}
|
|
363
363
|
|
|
364
364
|
if (Array.isArray(config.items)) {
|
|
365
|
-
config.items.forEach(child =>
|
|
365
|
+
config.items.forEach(child => {
|
|
366
|
+
if (typeof child === 'object' && child !== null) {
|
|
367
|
+
this.applyControllerBindings(child as ComponentConfig, controllerName);
|
|
368
|
+
}
|
|
369
|
+
});
|
|
366
370
|
}
|
|
367
371
|
}
|
|
368
372
|
|
|
@@ -175,8 +175,8 @@ export class StaticHtmlRenderer {
|
|
|
175
175
|
let mockCtrl = null;
|
|
176
176
|
|
|
177
177
|
// Try to get controller from ez shim (set up by ViteIslandsPlugin)
|
|
178
|
-
const ez = globalThis.ez;
|
|
179
|
-
if (definition.controller && ez && ez.getControllerSync) {
|
|
178
|
+
const ez = (globalThis as unknown as { ez?: { getControllerSync?: (name: string) => { state: Record<string, unknown> } | null } }).ez;
|
|
179
|
+
if (definition.controller && typeof definition.controller === 'string' && ez && ez.getControllerSync) {
|
|
180
180
|
mockCtrl = ez.getControllerSync(definition.controller);
|
|
181
181
|
} else if (definition.state) {
|
|
182
182
|
// Fallback to component's own state
|
|
@@ -37,6 +37,12 @@ export interface EzIslandsOptions {
|
|
|
37
37
|
*/
|
|
38
38
|
islandsDir?: string;
|
|
39
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Directory containing shared components (loaded for SSR)
|
|
42
|
+
* @default 'app/components'
|
|
43
|
+
*/
|
|
44
|
+
componentsDir?: string;
|
|
45
|
+
|
|
40
46
|
/**
|
|
41
47
|
* Output directory for static files
|
|
42
48
|
* @default 'dist'
|
|
@@ -171,6 +177,7 @@ export function ezIslands(options: EzIslandsOptions = {}): Plugin {
|
|
|
171
177
|
const opts = {
|
|
172
178
|
routesConfig: options.routesConfig || 'app/routes.config.js',
|
|
173
179
|
islandsDir: options.islandsDir || 'app/islands',
|
|
180
|
+
componentsDir: options.componentsDir || 'app/components',
|
|
174
181
|
outDir: options.outDir || 'dist',
|
|
175
182
|
template: options.template || DEFAULT_TEMPLATE,
|
|
176
183
|
ssr: options.ssr !== false,
|
|
@@ -728,7 +735,7 @@ function invalidateModule(server: ViteDevServer, filePath: string): void {
|
|
|
728
735
|
async function renderPageDev(
|
|
729
736
|
page: PageInfo,
|
|
730
737
|
server: ViteDevServer,
|
|
731
|
-
opts: { template: string; islandsDir: string },
|
|
738
|
+
opts: { template: string; islandsDir: string; componentsDir?: string },
|
|
732
739
|
renderer: StaticHtmlRenderer,
|
|
733
740
|
islands: Map<string, IslandInfo>,
|
|
734
741
|
root: string
|
|
@@ -766,33 +773,42 @@ async function renderPageDev(
|
|
|
766
773
|
ez._clear();
|
|
767
774
|
console.error('[Ez SSR] Registry cleared');
|
|
768
775
|
|
|
769
|
-
//
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
});
|
|
783
|
-
console.error(`[Ez SSR] Found ${jsFiles.length} JS files in ${path.basename(pageDir)}/`);
|
|
776
|
+
// Helper to load all JS files from a directory
|
|
777
|
+
const loadComponentsFromDir = async (dir: string, label: string) => {
|
|
778
|
+
const allFiles = await walkDirectory(dir);
|
|
779
|
+
const jsFiles = allFiles
|
|
780
|
+
.filter(f => f.endsWith('.js') && !f.includes('.test.'))
|
|
781
|
+
// Sort to load controllers FIRST (they contain state that other components need)
|
|
782
|
+
.sort((a, b) => {
|
|
783
|
+
const aIsController = a.includes('Controller');
|
|
784
|
+
const bIsController = b.includes('Controller');
|
|
785
|
+
if (aIsController && !bIsController) return -1;
|
|
786
|
+
if (!aIsController && bIsController) return 1;
|
|
787
|
+
return 0;
|
|
788
|
+
});
|
|
789
|
+
console.error(`[Ez SSR] Found ${jsFiles.length} JS files in ${label}`);
|
|
784
790
|
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
791
|
+
for (const file of jsFiles) {
|
|
792
|
+
try {
|
|
793
|
+
console.error(`[Ez SSR] Loading: ${path.basename(file)}`);
|
|
794
|
+
invalidateModule(server, file);
|
|
795
|
+
await server.ssrLoadModule(file);
|
|
796
|
+
} catch (err: any) {
|
|
797
|
+
console.error(`[Ez SSR] FAILED: ${path.basename(file)} - ${err.message}`);
|
|
798
|
+
}
|
|
793
799
|
}
|
|
800
|
+
};
|
|
801
|
+
|
|
802
|
+
// Load shared components first (if componentsDir is configured)
|
|
803
|
+
const componentsDir = opts.componentsDir ? path.resolve(root, opts.componentsDir) : path.resolve(root, 'app/components');
|
|
804
|
+
if (fs.existsSync(componentsDir)) {
|
|
805
|
+
await loadComponentsFromDir(componentsDir, 'app/components/');
|
|
794
806
|
}
|
|
795
807
|
|
|
808
|
+
// Then load page-specific components (may override shared ones)
|
|
809
|
+
const pageDir = path.dirname(viewPath);
|
|
810
|
+
await loadComponentsFromDir(pageDir, `${path.basename(pageDir)}/`);
|
|
811
|
+
|
|
796
812
|
console.error(`[Ez SSR] Registry now has: ${Object.keys(ez._registry).join(', ') || 'EMPTY'}`);
|
|
797
813
|
|
|
798
814
|
// Get the page definition from SSR registry (already loaded above)
|