ezfw-core 1.0.39 → 1.0.40
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/components/EzBaseComponent.ts +26 -4
- package/components/EzComponent.ts +4 -2
- package/components/EzIcon.ts +2 -1
- package/components/HtmlWrapper.ts +3 -2
- package/components/drawer/EzDrawer.module.scss +2 -2
- package/components/tabs/EzTabPanel.ts +2 -2
- package/components/tree/EzTree.ts +9 -3
- package/core/ez.ts +39 -3
- package/core/loader.ts +143 -23
- package/core/renderer.ts +133 -3
- package/core/router.ts +10 -2
- package/package.json +1 -1
- package/components/EzBaseComponent.js +0 -845
- package/components/EzComponent.js +0 -52
- package/components/EzIcon.js +0 -64
- package/components/EzLabel.js +0 -51
- package/components/EzOutlet.js +0 -121
- package/components/HtmlWrapper.js +0 -163
- package/components/avatar/EzAvatar.js +0 -163
- package/components/badge/EzBadge.js +0 -57
- package/components/button/EzButton.js +0 -141
- package/components/button/EzButtonGroup.js +0 -25
- package/components/card/EzCard.js +0 -84
- package/components/chart/EzBarChart.js +0 -24
- package/components/chart/EzChart.js +0 -155
- package/components/chart/EzDoughnutChart.js +0 -32
- package/components/chart/EzLineChart.js +0 -27
- package/components/checkbox/EzCheckbox.js +0 -96
- package/components/components/EzBaseComponent.d.ts +0 -177
- package/components/components/EzBaseComponent.js +0 -841
- package/components/core/styleShortcuts.d.ts +0 -64
- package/components/core/styleShortcuts.js +0 -316
- package/components/dataview/EzDataView.js +0 -311
- package/components/dataview/modes/EzDataViewCards.js +0 -549
- package/components/dataview/modes/EzDataViewGrid.js +0 -41
- package/components/datepicker/EzDatePicker.js +0 -435
- package/components/dialog/EzDialog.js +0 -144
- package/components/drawer/EzDrawer.js +0 -52
- package/components/dropdown/EzDropdown.js +0 -197
- package/components/feed/EzActivityFeed.js +0 -42
- package/components/form/EzForm.js +0 -327
- package/components/form/EzValidators.js +0 -171
- package/components/grid/EzGrid.js +0 -1097
- package/components/grid/EzGridContainer.js +0 -58
- package/components/grid/body/EzGridBody.js +0 -421
- package/components/grid/body/EzGridCell.js +0 -154
- package/components/grid/body/EzGridRow.js +0 -122
- package/components/grid/filter/EzGridFilters.js +0 -205
- package/components/grid/footer/EzGridFooter.js +0 -419
- package/components/grid/header/EzGridHeader.js +0 -323
- package/components/grid/query/EzGridQuery.js +0 -51
- package/components/grid/state/EzGridColumns.js +0 -106
- package/components/grid/state/EzGridController.js +0 -327
- package/components/grid/state/EzGridLifecycle.js +0 -103
- package/components/grid/state/EzGridNormalizers.js +0 -122
- package/components/grid/state/EzGridParts.js +0 -146
- package/components/grid/state/EzGridPersistence.js +0 -90
- package/components/grid/state/EzGridRemote.js +0 -219
- package/components/grid/state/EzGridSelection.js +0 -178
- package/components/grid/state/EzGridSort.js +0 -209
- package/components/grid/title/EzGridActionBar.js +0 -56
- package/components/grid/title/EzGridTitle.js +0 -68
- package/components/grid/title/EzGridTitleBar.js +0 -53
- package/components/grid/types.js +0 -7
- package/components/input/EzInput.js +0 -149
- package/components/kanban/EzKanban.js +0 -152
- package/components/kanban/EzKanbanTypes.js +0 -1
- package/components/kanban/board/EzKanbanBoard.js +0 -83
- package/components/kanban/card/EzKanbanCard.js +0 -220
- package/components/kanban/card/EzKanbanCardEditor.js +0 -176
- package/components/kanban/column/EzKanbanColumn.js +0 -195
- package/components/kanban/state/EzKanbanController.js +0 -301
- package/components/kanban/state/EzKanbanDragDrop.js +0 -185
- package/components/layout/EzLayout.js +0 -291
- package/components/mask/EzMask.js +0 -32
- package/components/orgchart/EzOrgChart.js +0 -919
- package/components/panel/EzPanel.js +0 -91
- package/components/paper/EzPaper.js +0 -24
- package/components/picker/EzPicker.js +0 -85
- package/components/radio/EzRadio.js +0 -112
- package/components/searchfilter/EzSearchFilter.js +0 -417
- package/components/select/EzSelect.js +0 -547
- package/components/skeleton/EzSkeleton.js +0 -42
- package/components/store/EzStore.js +0 -298
- package/components/switch/EzSwitch.js +0 -87
- package/components/tabs/EzTabPanel.js +0 -361
- package/components/textarea/EzTextarea.js +0 -132
- package/components/timepicker/EzTimePicker.js +0 -462
- package/components/tooltip/EzTooltip.js +0 -140
- package/components/tree/EzTree.js +0 -506
- package/core/EzError.js +0 -39
- package/core/styleShortcuts.js +0 -316
- package/islands/StaticHtmlRenderer.js +0 -344
- package/islands/ViteIslandsPlugin.js +0 -793
- package/islands/analyzer.js +0 -362
- package/islands/runtime.js +0 -387
|
@@ -11,6 +11,14 @@ const tooltipCls = cx(tooltipStyles);
|
|
|
11
11
|
|
|
12
12
|
declare const ez: {
|
|
13
13
|
_controllers: Record<string, EzController | undefined>;
|
|
14
|
+
_internal: {
|
|
15
|
+
state: { breakpoint: string };
|
|
16
|
+
variants: Record<string, string[]>;
|
|
17
|
+
};
|
|
18
|
+
_registry: Record<string, unknown>;
|
|
19
|
+
_loader: {
|
|
20
|
+
hasVariants(name: string): boolean;
|
|
21
|
+
};
|
|
14
22
|
getController(name: string): Promise<EzController | null>;
|
|
15
23
|
getControllerSync(name: string): EzController | null;
|
|
16
24
|
getDeepValue(obj: unknown, path: string[]): unknown;
|
|
@@ -349,6 +357,16 @@ export class EzBaseComponent {
|
|
|
349
357
|
}
|
|
350
358
|
}
|
|
351
359
|
|
|
360
|
+
/**
|
|
361
|
+
* Setup repaint effect for components with breakpoint variants.
|
|
362
|
+
* Note: This is now handled by the renderer for all components.
|
|
363
|
+
* Kept for potential manual use by class-based components.
|
|
364
|
+
*/
|
|
365
|
+
protected _setupRepaintEffect(_el: HTMLElement): void {
|
|
366
|
+
// Repaint is now handled by the renderer in _setupRepaintEffect
|
|
367
|
+
// This method is kept for backward compatibility
|
|
368
|
+
}
|
|
369
|
+
|
|
352
370
|
private _applyValueBind(
|
|
353
371
|
el: HTMLElement,
|
|
354
372
|
bind: BindConfig,
|
|
@@ -499,6 +517,10 @@ export class EzBaseComponent {
|
|
|
499
517
|
childCfg.css = this.config.css;
|
|
500
518
|
childCfg._styleModule = this.config._styleModule;
|
|
501
519
|
}
|
|
520
|
+
// Propagate _isRepaint to skip onLoad in children during breakpoint repaint
|
|
521
|
+
if ((this.config as { _isRepaint?: boolean })._isRepaint) {
|
|
522
|
+
(childCfg as { _isRepaint?: boolean })._isRepaint = true;
|
|
523
|
+
}
|
|
502
524
|
// Pass 'this' as inheritedState so children are added to _children
|
|
503
525
|
const childEl = await ez._createElement(childCfg, null, this);
|
|
504
526
|
|
|
@@ -568,10 +590,10 @@ export class EzBaseComponent {
|
|
|
568
590
|
|
|
569
591
|
let result = true;
|
|
570
592
|
switch (operator) {
|
|
571
|
-
case '>': result = left > right; break;
|
|
572
|
-
case '<': result = left < right; break;
|
|
573
|
-
case '>=': result = left >= right; break;
|
|
574
|
-
case '<=': result = left <= right; break;
|
|
593
|
+
case '>': result = (left as number) > (right as number); break;
|
|
594
|
+
case '<': result = (left as number) < (right as number); break;
|
|
595
|
+
case '>=': result = (left as number) >= (right as number); break;
|
|
596
|
+
case '<=': result = (left as number) <= (right as number); break;
|
|
575
597
|
case '!=': result = left != right; break;
|
|
576
598
|
case '==': result = left == right; break;
|
|
577
599
|
}
|
|
@@ -5,7 +5,8 @@ declare const ez: {
|
|
|
5
5
|
items: EzComponentConfig[],
|
|
6
6
|
controller: string | null,
|
|
7
7
|
state: unknown,
|
|
8
|
-
css: string | null
|
|
8
|
+
css: string | null,
|
|
9
|
+
isRepaint?: boolean
|
|
9
10
|
): Promise<Node[]>;
|
|
10
11
|
};
|
|
11
12
|
|
|
@@ -75,7 +76,8 @@ export class EzComponent extends EzBaseComponent {
|
|
|
75
76
|
items,
|
|
76
77
|
this.config.controller || null,
|
|
77
78
|
null,
|
|
78
|
-
(typeof this.config.css === 'string' ? this.config.css : null) as string | null
|
|
79
|
+
(typeof this.config.css === 'string' ? this.config.css : null) as string | null,
|
|
80
|
+
(this.config as { _isRepaint?: boolean })._isRepaint || false
|
|
79
81
|
);
|
|
80
82
|
|
|
81
83
|
for (const child of children) {
|
package/components/EzIcon.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { EzBaseComponent, EzBaseComponentConfig } from './EzBaseComponent.js';
|
|
2
2
|
|
|
3
3
|
type IconType = 'solid' | 'regular' | 'brands' | 'light' | 'thin' | 'duotone';
|
|
4
|
-
type IconSize = 'xs' | 'sm' | 'lg' | 'xl' | '2x' | '3x' | '4x' | '5x';
|
|
4
|
+
type IconSize = 'xxs' | 'xs' | 'sm' | 'lg' | 'xl' | '2x' | '3x' | '4x' | '5x';
|
|
5
5
|
|
|
6
6
|
const SEMANTIC_COLORS: Record<string, string> = {
|
|
7
7
|
primary: 'var(--ez-primary)',
|
|
@@ -16,6 +16,7 @@ const SEMANTIC_COLORS: Record<string, string> = {
|
|
|
16
16
|
};
|
|
17
17
|
|
|
18
18
|
const SIZE_MAP: Record<string, string> = {
|
|
19
|
+
xxs: '0.65em',
|
|
19
20
|
xs: '0.75em',
|
|
20
21
|
sm: '0.875em',
|
|
21
22
|
lg: '1.25em',
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { EzBaseComponent, EzBaseComponentConfig } from "./EzBaseComponent.js";
|
|
2
2
|
|
|
3
3
|
declare const ez: {
|
|
4
|
-
_createChildElements(items: unknown[], controller: string | null, parent: unknown, css?: string | null): Promise<Node[]>;
|
|
4
|
+
_createChildElements(items: unknown[], controller: string | null, parent: unknown, css?: string | null, isRepaint?: boolean): Promise<Node[]>;
|
|
5
5
|
};
|
|
6
6
|
|
|
7
7
|
type DOMEventHandler<E extends Event = Event> = (e: E) => void;
|
|
@@ -295,7 +295,8 @@ export class HtmlWrapper extends EzBaseComponent {
|
|
|
295
295
|
cfg.items,
|
|
296
296
|
cfg.controller || null,
|
|
297
297
|
this,
|
|
298
|
-
(cfg.css as string) || null
|
|
298
|
+
(cfg.css as string) || null,
|
|
299
|
+
(cfg as { _isRepaint?: boolean })._isRepaint || false
|
|
299
300
|
);
|
|
300
301
|
children.forEach(child => el.appendChild(child));
|
|
301
302
|
}
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
visibility: hidden;
|
|
11
11
|
transition: opacity 0.3s ease, visibility 0.3s ease;
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
&:global(.open) {
|
|
14
14
|
opacity: 1;
|
|
15
15
|
visibility: visible;
|
|
16
16
|
}
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
// Open state
|
|
57
|
-
|
|
57
|
+
&:global(.open) {
|
|
58
58
|
&.left,
|
|
59
59
|
&.right {
|
|
60
60
|
transform: translateX(0);
|
|
@@ -5,7 +5,7 @@ import { EzComponent } from '../EzComponent.js';
|
|
|
5
5
|
|
|
6
6
|
declare const ez: {
|
|
7
7
|
_createElement(config: unknown, controller?: string | null, parent?: unknown): Promise<HTMLElement>;
|
|
8
|
-
_createChildElements(items: unknown[], controller: string | null, parent: unknown): Promise<Node[]>;
|
|
8
|
+
_createChildElements(items: unknown[], controller: string | null, parent: unknown, css?: string | null, isRepaint?: boolean): Promise<Node[]>;
|
|
9
9
|
};
|
|
10
10
|
|
|
11
11
|
const cls = cx(styles);
|
|
@@ -280,7 +280,7 @@ export class EzTabPanel extends EzComponent {
|
|
|
280
280
|
return item;
|
|
281
281
|
});
|
|
282
282
|
|
|
283
|
-
const children = await ez._createChildElements(items, this.config.controller || null, this);
|
|
283
|
+
const children = await ez._createChildElements(items, this.config.controller || null, this, null, (this.config as { _isRepaint?: boolean })._isRepaint || false);
|
|
284
284
|
for (const child of children) {
|
|
285
285
|
pane.appendChild(child);
|
|
286
286
|
}
|
|
@@ -38,6 +38,7 @@ export interface EzTreeConfig extends EzComponentExtendedConfig {
|
|
|
38
38
|
defaultExpandLevel?: number;
|
|
39
39
|
|
|
40
40
|
selectable?: boolean;
|
|
41
|
+
autoSelectFirst?: boolean;
|
|
41
42
|
showIcon?: boolean;
|
|
42
43
|
showLine?: boolean;
|
|
43
44
|
|
|
@@ -90,6 +91,7 @@ export class EzTree extends EzComponent {
|
|
|
90
91
|
childrenKey: 'children',
|
|
91
92
|
labelKey: 'label',
|
|
92
93
|
selectable: true,
|
|
94
|
+
autoSelectFirst: true,
|
|
93
95
|
showIcon: true,
|
|
94
96
|
showLine: false,
|
|
95
97
|
defaultExpandAll: false,
|
|
@@ -130,7 +132,11 @@ export class EzTree extends EzComponent {
|
|
|
130
132
|
|
|
131
133
|
const existingBind = (this.config.bind && typeof this.config.bind === 'object') ? this.config.bind : {};
|
|
132
134
|
this.config.bind = { ...existingBind, data: this._dataStateKey };
|
|
133
|
-
|
|
135
|
+
|
|
136
|
+
// Skip loading during repaint - data already exists in controller state
|
|
137
|
+
if (!(this.config as { _isRepaint?: boolean })._isRepaint) {
|
|
138
|
+
void this._loadData();
|
|
139
|
+
}
|
|
134
140
|
}
|
|
135
141
|
}
|
|
136
142
|
|
|
@@ -330,8 +336,8 @@ export class EzTree extends EzComponent {
|
|
|
330
336
|
const data = await this._store.load();
|
|
331
337
|
state[this._dataStateKey] = data;
|
|
332
338
|
|
|
333
|
-
// Select first item by default
|
|
334
|
-
if (Array.isArray(data) && data.length > 0) {
|
|
339
|
+
// Select first item by default (if enabled)
|
|
340
|
+
if (this.config.autoSelectFirst && Array.isArray(data) && data.length > 0) {
|
|
335
341
|
const firstNode = data[0] as EzTreeNode;
|
|
336
342
|
const key = this._getNodeKey(firstNode);
|
|
337
343
|
this._select(key, firstNode);
|
package/core/ez.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { EzState, ControllerDefinition, GridControllerDefinition, GridBehaviorDe
|
|
|
6
6
|
import { EzModel, ModelDefinition, Model } from './EzModel.js';
|
|
7
7
|
import { EzServices } from './services.js';
|
|
8
8
|
import { EzEventBus, EventHandler } from './eventBus.js';
|
|
9
|
+
import { deepSignal } from 'deepsignal';
|
|
9
10
|
import * as EzGridQuery from '../components/grid/query/EzGridQuery.js';
|
|
10
11
|
import dayjs from 'dayjs';
|
|
11
12
|
import customParseFormat from 'dayjs/plugin/customParseFormat';
|
|
@@ -42,6 +43,15 @@ interface EzContext {
|
|
|
42
43
|
controller: string | null;
|
|
43
44
|
}
|
|
44
45
|
|
|
46
|
+
interface EzInternalState {
|
|
47
|
+
breakpoint: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
interface EzInternal {
|
|
51
|
+
state: EzInternalState;
|
|
52
|
+
variants: Record<string, string[]>; // { 'Department': ['desktop', 'mobile'] }
|
|
53
|
+
}
|
|
54
|
+
|
|
45
55
|
export interface EzFramework {
|
|
46
56
|
_registry: Record<string, unknown>;
|
|
47
57
|
_controllers: Record<string, ControllerDefinition>;
|
|
@@ -50,6 +60,7 @@ export interface EzFramework {
|
|
|
50
60
|
_refs: Record<string, unknown>;
|
|
51
61
|
_registryMeta: Record<string, RegistryMeta>;
|
|
52
62
|
_context: EzContext;
|
|
63
|
+
_internal: EzInternal;
|
|
53
64
|
_gridControllerDefinitions: Record<string, GridControllerDefinition>;
|
|
54
65
|
_gridStateRegistry: Record<string, unknown>;
|
|
55
66
|
_gridBehaviorDefinitions: Record<string, GridBehaviorDefinition>;
|
|
@@ -171,7 +182,7 @@ export interface EzFramework {
|
|
|
171
182
|
getController(name: string): Promise<ControllerDefinition> | ControllerDefinition;
|
|
172
183
|
getControllerSync(name: string): ControllerDefinition;
|
|
173
184
|
_createElement(config: ComponentConfig, controllerName?: string | null, inheritedState?: unknown): Promise<Node>;
|
|
174
|
-
_createChildElements(items: ComponentConfig[], controllerName: string | null, inheritedState: unknown, parentCss?: string | null): Promise<Node[]>;
|
|
185
|
+
_createChildElements(items: ComponentConfig[], controllerName: string | null, inheritedState: unknown, parentCss?: string | null, isRepaint?: boolean): Promise<Node[]>;
|
|
175
186
|
routes(routeDefs: RouteDefinition[]): void;
|
|
176
187
|
go(path: string): void;
|
|
177
188
|
navigate(viewName: string, route?: RouteInfo | null): Promise<void>;
|
|
@@ -230,6 +241,7 @@ export interface EzFramework {
|
|
|
230
241
|
isTablet(): boolean;
|
|
231
242
|
isDesktop(): boolean;
|
|
232
243
|
_initBreakpoints(): void;
|
|
244
|
+
_hasAnyResponsiveModules(): boolean;
|
|
233
245
|
_updateBreakpoint(): void;
|
|
234
246
|
format(value: unknown, type?: FormatType, options?: FormatOptions): string;
|
|
235
247
|
capitalize(str: string): string;
|
|
@@ -251,6 +263,18 @@ const ez: EzFramework = {
|
|
|
251
263
|
view: null,
|
|
252
264
|
controller: null
|
|
253
265
|
},
|
|
266
|
+
_internal: {
|
|
267
|
+
state: deepSignal({
|
|
268
|
+
breakpoint: (() => {
|
|
269
|
+
if (typeof window === 'undefined') return 'desktop';
|
|
270
|
+
const width = window.innerWidth;
|
|
271
|
+
if (width < 768) return 'mobile';
|
|
272
|
+
if (width < 1024) return 'tablet';
|
|
273
|
+
return 'desktop';
|
|
274
|
+
})()
|
|
275
|
+
}),
|
|
276
|
+
variants: {}
|
|
277
|
+
},
|
|
254
278
|
_gridControllerDefinitions: {},
|
|
255
279
|
_gridStateRegistry: {},
|
|
256
280
|
_gridBehaviorDefinitions: {},
|
|
@@ -433,8 +457,8 @@ const ez: EzFramework = {
|
|
|
433
457
|
return this._renderer!.createElement(config, controllerName, inheritedState);
|
|
434
458
|
},
|
|
435
459
|
|
|
436
|
-
async _createChildElements(items: ComponentConfig[], controllerName: string | null, inheritedState: unknown, parentCss: string | null = null): Promise<Node[]> {
|
|
437
|
-
return this._renderer!.createChildElements(items, controllerName || '', inheritedState, parentCss);
|
|
460
|
+
async _createChildElements(items: ComponentConfig[], controllerName: string | null, inheritedState: unknown, parentCss: string | null = null, isRepaint: boolean = false): Promise<Node[]> {
|
|
461
|
+
return this._renderer!.createChildElements(items, controllerName || '', inheritedState, parentCss, isRepaint);
|
|
438
462
|
},
|
|
439
463
|
|
|
440
464
|
routes(routeDefs: RouteDefinition[]): void {
|
|
@@ -665,6 +689,14 @@ const ez: EzFramework = {
|
|
|
665
689
|
return this._currentBreakpoint === 'desktop';
|
|
666
690
|
},
|
|
667
691
|
|
|
692
|
+
_hasAnyResponsiveModules(): boolean {
|
|
693
|
+
if (!this._loader) return false;
|
|
694
|
+
const modules = this._loader.getModules();
|
|
695
|
+
return Object.keys(modules).some(p =>
|
|
696
|
+
p.includes('.mobile.') || p.includes('.tablet.')
|
|
697
|
+
);
|
|
698
|
+
},
|
|
699
|
+
|
|
668
700
|
_updateBreakpoint(): void {
|
|
669
701
|
const width = window.innerWidth;
|
|
670
702
|
const { mobile, tablet } = this._breakpoints;
|
|
@@ -682,6 +714,10 @@ const ez: EzFramework = {
|
|
|
682
714
|
const oldBreakpoint = this._currentBreakpoint;
|
|
683
715
|
this._currentBreakpoint = newBreakpoint;
|
|
684
716
|
document.documentElement.setAttribute('data-ez-breakpoint', newBreakpoint);
|
|
717
|
+
|
|
718
|
+
// Update reactive state - this triggers repaint on components with variants
|
|
719
|
+
this._internal.state.breakpoint = newBreakpoint;
|
|
720
|
+
|
|
685
721
|
this._eventBus?.emit('breakpoint:change', {
|
|
686
722
|
breakpoint: newBreakpoint,
|
|
687
723
|
previous: oldBreakpoint
|
package/core/loader.ts
CHANGED
|
@@ -7,6 +7,13 @@ export interface ModuleMeta {
|
|
|
7
7
|
|
|
8
8
|
export type ModuleMap = Record<string, () => Promise<unknown>>;
|
|
9
9
|
|
|
10
|
+
export interface BreakpointModules {
|
|
11
|
+
framework: ModuleMap;
|
|
12
|
+
desktop: ModuleMap;
|
|
13
|
+
mobile: ModuleMap;
|
|
14
|
+
tablet: ModuleMap;
|
|
15
|
+
}
|
|
16
|
+
|
|
10
17
|
export type LoadPriority = 'high' | 'low' | 'idle';
|
|
11
18
|
|
|
12
19
|
export type NetworkStrategy = 'fast' | 'medium' | 'slow' | 'offline';
|
|
@@ -63,6 +70,10 @@ interface EzInstance {
|
|
|
63
70
|
route?: string | null;
|
|
64
71
|
view?: string | null;
|
|
65
72
|
};
|
|
73
|
+
_internal: {
|
|
74
|
+
state: { breakpoint: string };
|
|
75
|
+
variants: Record<string, string[]>;
|
|
76
|
+
};
|
|
66
77
|
_router?: EzRouter;
|
|
67
78
|
_currentBreakpoint: string;
|
|
68
79
|
registerComponent(name: string, component: unknown): void;
|
|
@@ -73,7 +84,13 @@ interface EzInstance {
|
|
|
73
84
|
|
|
74
85
|
export class EzLoader {
|
|
75
86
|
private ez: EzInstance;
|
|
76
|
-
private _modules: ModuleMap = {};
|
|
87
|
+
private _modules: ModuleMap = {}; // Combined flat map for backward compat
|
|
88
|
+
private _frameworkModules: ModuleMap = {};
|
|
89
|
+
private _desktopModules: ModuleMap = {};
|
|
90
|
+
private _mobileModules: ModuleMap = {};
|
|
91
|
+
private _tabletModules: ModuleMap = {};
|
|
92
|
+
// Cache for variant definitions (ES modules only execute once)
|
|
93
|
+
private _variantCache: Record<string, unknown> = {}; // 'Department@mobile' -> definition
|
|
77
94
|
private _moduleIndex: Record<string, string[]> = {};
|
|
78
95
|
private _moduleMeta: Record<string, ModuleMeta> = {};
|
|
79
96
|
private _currentModulePath: string | null = null;
|
|
@@ -196,38 +213,100 @@ export class EzLoader {
|
|
|
196
213
|
};
|
|
197
214
|
}
|
|
198
215
|
|
|
199
|
-
installModules(mods: ModuleMap): void {
|
|
200
|
-
|
|
216
|
+
installModules(mods: BreakpointModules | ModuleMap): void {
|
|
217
|
+
// Support both old flat format and new breakpoint-separated format
|
|
218
|
+
if ('framework' in mods && 'desktop' in mods) {
|
|
219
|
+
const breakpointMods = mods as BreakpointModules;
|
|
220
|
+
this._frameworkModules = breakpointMods.framework;
|
|
221
|
+
this._desktopModules = breakpointMods.desktop;
|
|
222
|
+
this._mobileModules = breakpointMods.mobile || {};
|
|
223
|
+
this._tabletModules = breakpointMods.tablet || {};
|
|
224
|
+
|
|
225
|
+
// Combined flat map for backward compatibility
|
|
226
|
+
this._modules = {
|
|
227
|
+
...breakpointMods.framework,
|
|
228
|
+
...breakpointMods.desktop,
|
|
229
|
+
...breakpointMods.mobile,
|
|
230
|
+
...breakpointMods.tablet
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
// Build variants registry
|
|
234
|
+
this._buildVariantsRegistry();
|
|
235
|
+
} else {
|
|
236
|
+
// Legacy flat format
|
|
237
|
+
this._modules = mods as ModuleMap;
|
|
238
|
+
}
|
|
239
|
+
|
|
201
240
|
this._moduleIndex = {};
|
|
202
241
|
|
|
203
|
-
for (const path of Object.keys(
|
|
242
|
+
for (const path of Object.keys(this._modules)) {
|
|
204
243
|
this._moduleMeta[path] = {
|
|
205
244
|
path,
|
|
206
245
|
defines: []
|
|
207
246
|
};
|
|
208
247
|
const file = path.split('/').pop();
|
|
209
248
|
if (!file || (!file.endsWith('.js') && !file.endsWith('.ts'))) continue;
|
|
249
|
+
// Skip TypeScript declaration files
|
|
250
|
+
if (file.endsWith('.d.ts')) continue;
|
|
210
251
|
|
|
211
252
|
const eztype = file.replace(/\.(js|ts)$/, '');
|
|
212
253
|
|
|
213
254
|
this._moduleIndex[eztype] ??= [];
|
|
214
255
|
this._moduleIndex[eztype].push(path);
|
|
215
256
|
}
|
|
257
|
+
}
|
|
216
258
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
259
|
+
private _buildVariantsRegistry(): void {
|
|
260
|
+
const variants: Record<string, string[]> = {};
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
// Extract component names from desktop modules
|
|
264
|
+
for (const path of Object.keys(this._desktopModules)) {
|
|
265
|
+
const file = path.split('/').pop();
|
|
266
|
+
if (!file || (!file.endsWith('.js') && !file.endsWith('.ts'))) continue;
|
|
267
|
+
if (file.endsWith('.d.ts')) continue;
|
|
268
|
+
|
|
269
|
+
const componentName = file.replace(/\.(js|ts)$/, '');
|
|
270
|
+
variants[componentName] = ['desktop'];
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Check for mobile variants
|
|
274
|
+
for (const path of Object.keys(this._mobileModules)) {
|
|
275
|
+
const file = path.split('/').pop();
|
|
276
|
+
if (!file) continue;
|
|
277
|
+
|
|
278
|
+
// Department.mobile.js -> Department
|
|
279
|
+
const match = file.match(/^(.+)\.mobile\.(js|ts)$/);
|
|
280
|
+
if (match) {
|
|
281
|
+
const componentName = match[1];
|
|
282
|
+
if (variants[componentName]) {
|
|
283
|
+
variants[componentName].push('mobile');
|
|
284
|
+
}
|
|
229
285
|
}
|
|
230
286
|
}
|
|
287
|
+
|
|
288
|
+
// Check for tablet variants
|
|
289
|
+
for (const path of Object.keys(this._tabletModules)) {
|
|
290
|
+
const file = path.split('/').pop();
|
|
291
|
+
if (!file) continue;
|
|
292
|
+
|
|
293
|
+
const match = file.match(/^(.+)\.tablet\.(js|ts)$/);
|
|
294
|
+
if (match) {
|
|
295
|
+
const componentName = match[1];
|
|
296
|
+
if (variants[componentName]) {
|
|
297
|
+
variants[componentName].push('tablet');
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Only keep components with multiple variants
|
|
303
|
+
for (const name in variants) {
|
|
304
|
+
if (variants[name].length === 1) {
|
|
305
|
+
delete variants[name];
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
this.ez._internal.variants = variants;
|
|
231
310
|
}
|
|
232
311
|
|
|
233
312
|
async loadModule(path: string): Promise<unknown> {
|
|
@@ -253,21 +332,38 @@ export class EzLoader {
|
|
|
253
332
|
if (this.ez._registry[eztype]) return;
|
|
254
333
|
|
|
255
334
|
const lowerEztype = eztype.toLowerCase();
|
|
256
|
-
const keys = Object.keys(this._modules);
|
|
257
335
|
const breakpoint = this.ez._currentBreakpoint;
|
|
258
336
|
|
|
259
|
-
// Try responsive variant first (e.g., Department.mobile.js)
|
|
260
337
|
let path: string | undefined;
|
|
261
338
|
|
|
262
|
-
|
|
263
|
-
|
|
339
|
+
// Use breakpoint-specific registry if available
|
|
340
|
+
if (breakpoint === 'mobile' && Object.keys(this._mobileModules).length > 0) {
|
|
341
|
+
path = Object.keys(this._mobileModules).find(p => {
|
|
264
342
|
const filename = p.split(/[/\\]/).pop()?.toLowerCase() || '';
|
|
265
|
-
return filename === `${lowerEztype}
|
|
343
|
+
return filename === `${lowerEztype}.mobile.js` || filename === `${lowerEztype}.mobile.ts`;
|
|
344
|
+
});
|
|
345
|
+
} else if (breakpoint === 'tablet' && Object.keys(this._tabletModules).length > 0) {
|
|
346
|
+
path = Object.keys(this._tabletModules).find(p => {
|
|
347
|
+
const filename = p.split(/[/\\]/).pop()?.toLowerCase() || '';
|
|
348
|
+
return filename === `${lowerEztype}.tablet.js` || filename === `${lowerEztype}.tablet.ts`;
|
|
266
349
|
});
|
|
267
350
|
}
|
|
268
351
|
|
|
269
|
-
// Fallback to
|
|
352
|
+
// Fallback to desktop/framework modules
|
|
270
353
|
if (!path) {
|
|
354
|
+
const desktopKeys = Object.keys(this._desktopModules);
|
|
355
|
+
const frameworkKeys = Object.keys(this._frameworkModules);
|
|
356
|
+
const allKeys = [...desktopKeys, ...frameworkKeys];
|
|
357
|
+
|
|
358
|
+
path = allKeys.find(p => {
|
|
359
|
+
const filename = p.split(/[/\\]/).pop()?.toLowerCase() || '';
|
|
360
|
+
return filename === `${lowerEztype}.js` || filename === `${lowerEztype}.ts`;
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// Final fallback to combined modules (legacy support)
|
|
365
|
+
if (!path) {
|
|
366
|
+
const keys = Object.keys(this._modules);
|
|
271
367
|
path = keys.find(p => {
|
|
272
368
|
const filename = p.split(/[/\\]/).pop()?.toLowerCase() || '';
|
|
273
369
|
return filename === `${lowerEztype}.js` || filename === `${lowerEztype}.ts`;
|
|
@@ -278,6 +374,17 @@ export class EzLoader {
|
|
|
278
374
|
return;
|
|
279
375
|
}
|
|
280
376
|
|
|
377
|
+
// Determine which variant we're loading
|
|
378
|
+
const isVariant = path.includes('.mobile.') || path.includes('.tablet.');
|
|
379
|
+
const variantType = path.includes('.mobile.') ? 'mobile' : path.includes('.tablet.') ? 'tablet' : 'desktop';
|
|
380
|
+
const cacheKey = `${eztype}@${variantType}`;
|
|
381
|
+
|
|
382
|
+
// Check variant cache first (ES modules only execute once, so ez.define won't re-run)
|
|
383
|
+
if (this._variantCache[cacheKey]) {
|
|
384
|
+
this.ez.registerComponent(eztype, this._variantCache[cacheKey]);
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
|
|
281
388
|
const module = await this.loadModuleWithRetry(path) as Record<string, unknown>;
|
|
282
389
|
|
|
283
390
|
let exported = module.default || Object.values(module).find(v => typeof v === 'function');
|
|
@@ -287,7 +394,12 @@ export class EzLoader {
|
|
|
287
394
|
}
|
|
288
395
|
|
|
289
396
|
if (!exported) {
|
|
290
|
-
throw new Error(`[ez] Module loaded for ${eztype} but exports no class/function or ez.define`);
|
|
397
|
+
throw new Error(`[ez] Module loaded for ${eztype} but exports no class/function or ez.define. Path: ${path}`);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// Cache the variant definition for future use
|
|
401
|
+
if (isVariant || this.hasVariants(eztype)) {
|
|
402
|
+
this._variantCache[cacheKey] = exported;
|
|
291
403
|
}
|
|
292
404
|
|
|
293
405
|
this.ez.registerComponent(eztype, exported);
|
|
@@ -298,6 +410,14 @@ export class EzLoader {
|
|
|
298
410
|
}
|
|
299
411
|
}
|
|
300
412
|
|
|
413
|
+
hasVariants(componentName: string): boolean {
|
|
414
|
+
return componentName in this.ez._internal.variants;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
getVariants(componentName: string): string[] {
|
|
418
|
+
return this.ez._internal.variants[componentName] || [];
|
|
419
|
+
}
|
|
420
|
+
|
|
301
421
|
async resolveController(name: string | object | null): Promise<unknown> {
|
|
302
422
|
if (!name) return null;
|
|
303
423
|
|