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
- el.addEventListener(eventName, handler as EventListener);
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, handler as EventListener)
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 => this.applyControllerBindings(child, controllerName));
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
- // Load all JS files in the page directory (for child components)
770
- // These will call ez.define() which adds to the singleton registry
771
- const pageDir = path.dirname(viewPath);
772
- const allFiles = await walkDirectory(pageDir);
773
- const jsFiles = allFiles
774
- .filter(f => f.endsWith('.js') && !f.includes('.test.'))
775
- // Sort to load controllers FIRST (they contain state that other components need)
776
- .sort((a, b) => {
777
- const aIsController = a.includes('Controller');
778
- const bIsController = b.includes('Controller');
779
- if (aIsController && !bIsController) return -1;
780
- if (!aIsController && bIsController) return 1;
781
- return 0;
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
- for (const file of jsFiles) {
786
- try {
787
- console.error(`[Ez SSR] Loading: ${path.basename(file)}`);
788
- // Invalidate in module graph to force re-execution
789
- invalidateModule(server, file);
790
- await server.ssrLoadModule(file);
791
- } catch (err: any) {
792
- console.error(`[Ez SSR] FAILED: ${path.basename(file)} - ${err.message}`);
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)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ezfw-core",
3
- "version": "1.0.51",
3
+ "version": "1.0.53",
4
4
  "description": "Ez Framework - A declarative component framework for building modern web applications",
5
5
  "type": "module",
6
6
  "main": "./core/ez.ts",