ezfw-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.
Files changed (154) hide show
  1. package/components/EzBaseComponent.ts +648 -0
  2. package/components/EzComponent.ts +89 -0
  3. package/components/EzInput.module.scss +183 -0
  4. package/components/EzInput.ts +104 -0
  5. package/components/EzLabel.ts +22 -0
  6. package/components/EzOutlet.ts +181 -0
  7. package/components/HtmlWrapper.ts +305 -0
  8. package/components/avatar/EzAvatar.module.scss +200 -0
  9. package/components/avatar/EzAvatar.ts +130 -0
  10. package/components/badge/EzBadge.module.scss +202 -0
  11. package/components/badge/EzBadge.ts +77 -0
  12. package/components/button/EzButton.module.scss +402 -0
  13. package/components/button/EzButton.ts +175 -0
  14. package/components/button/EzButtonGroup.ts +48 -0
  15. package/components/card/EzCard.module.scss +71 -0
  16. package/components/card/EzCard.ts +120 -0
  17. package/components/chart/EzBarChart.ts +47 -0
  18. package/components/chart/EzChart.module.scss +14 -0
  19. package/components/chart/EzChart.ts +279 -0
  20. package/components/chart/EzDoughnutChart.ts +47 -0
  21. package/components/chart/EzLineChart.ts +53 -0
  22. package/components/checkbox/EzCheckbox.module.scss +145 -0
  23. package/components/checkbox/EzCheckbox.ts +115 -0
  24. package/components/dataview/EzDataView.module.scss +115 -0
  25. package/components/dataview/EzDataView.ts +355 -0
  26. package/components/dataview/modes/EzDataViewCards.ts +322 -0
  27. package/components/dataview/modes/EzDataViewGrid.ts +76 -0
  28. package/components/datepicker/EzDatePicker.module.scss +348 -0
  29. package/components/datepicker/EzDatePicker.ts +519 -0
  30. package/components/dialog/EzDialog.module.scss +180 -0
  31. package/components/dropdown/EzDropdown.module.scss +107 -0
  32. package/components/dropdown/EzDropdown.ts +235 -0
  33. package/components/feed/EzActivityFeed.module.scss +90 -0
  34. package/components/feed/EzActivityFeed.ts +78 -0
  35. package/components/form/EzForm.ts +364 -0
  36. package/components/form/EzValidators.test.js +421 -0
  37. package/components/form/EzValidators.ts +202 -0
  38. package/components/grid/EzGrid.scss +88 -0
  39. package/components/grid/EzGrid.ts +1085 -0
  40. package/components/grid/EzGridContainer.ts +104 -0
  41. package/components/grid/body/EzGridBody.scss +283 -0
  42. package/components/grid/body/EzGridBody.ts +549 -0
  43. package/components/grid/body/EzGridCell.ts +211 -0
  44. package/components/grid/body/EzGridRow.ts +196 -0
  45. package/components/grid/filter/EzGridFilters.scss +78 -0
  46. package/components/grid/filter/EzGridFilters.ts +285 -0
  47. package/components/grid/footer/EzGridFooter.scss +136 -0
  48. package/components/grid/footer/EzGridFooter.ts +448 -0
  49. package/components/grid/header/EzGridHeader.scss +199 -0
  50. package/components/grid/header/EzGridHeader.ts +430 -0
  51. package/components/grid/query/EzGridQuery.ts +81 -0
  52. package/components/grid/state/EzGridColumns.ts +155 -0
  53. package/components/grid/state/EzGridController.ts +470 -0
  54. package/components/grid/state/EzGridLifecycle.ts +136 -0
  55. package/components/grid/state/EzGridNormalizers.test.js +273 -0
  56. package/components/grid/state/EzGridNormalizers.ts +162 -0
  57. package/components/grid/state/EzGridParts.ts +233 -0
  58. package/components/grid/state/EzGridPersistence.ts +140 -0
  59. package/components/grid/state/EzGridRemote.test.js +573 -0
  60. package/components/grid/state/EzGridRemote.ts +335 -0
  61. package/components/grid/state/EzGridSelection.ts +231 -0
  62. package/components/grid/state/EzGridSort.ts +286 -0
  63. package/components/grid/title/EzGridActionBar.ts +98 -0
  64. package/components/grid/title/EzGridTitle.ts +114 -0
  65. package/components/grid/title/EzGridTitleBar.scss +65 -0
  66. package/components/grid/title/EzGridTitleBar.ts +87 -0
  67. package/components/grid/types.ts +607 -0
  68. package/components/panel/EzPanel.module.scss +133 -0
  69. package/components/panel/EzPanel.ts +147 -0
  70. package/components/radio/EzRadio.module.scss +190 -0
  71. package/components/radio/EzRadio.ts +149 -0
  72. package/components/select/EzSelect.module.scss +153 -0
  73. package/components/select/EzSelect.ts +238 -0
  74. package/components/skeleton/EzSkeleton.module.scss +95 -0
  75. package/components/skeleton/EzSkeleton.ts +70 -0
  76. package/components/store/EzStore.ts +344 -0
  77. package/components/switch/EzSwitch.module.scss +164 -0
  78. package/components/switch/EzSwitch.ts +117 -0
  79. package/components/tabs/EzTabPanel.module.scss +181 -0
  80. package/components/tabs/EzTabPanel.ts +402 -0
  81. package/components/textarea/EzTextarea.module.scss +131 -0
  82. package/components/textarea/EzTextarea.ts +161 -0
  83. package/components/timepicker/EzTimePicker.module.scss +282 -0
  84. package/components/timepicker/EzTimePicker.ts +540 -0
  85. package/components/toast/EzToast.module.scss +291 -0
  86. package/components/tooltip/EzTooltip.module.scss +124 -0
  87. package/components/tooltip/EzTooltip.ts +153 -0
  88. package/core/EzComponentTypes.ts +693 -0
  89. package/core/EzError.ts +63 -0
  90. package/core/EzModel.ts +268 -0
  91. package/core/EzTypes.ts +328 -0
  92. package/core/eventBus.ts +284 -0
  93. package/core/ez.ts +617 -0
  94. package/core/loader.ts +725 -0
  95. package/core/renderer.ts +1010 -0
  96. package/core/router.ts +490 -0
  97. package/core/services.ts +124 -0
  98. package/core/state.ts +142 -0
  99. package/core/utils.ts +81 -0
  100. package/package.json +51 -0
  101. package/services/RouteUI.js +17 -0
  102. package/services/crypto.js +64 -0
  103. package/services/dialog.js +222 -0
  104. package/services/fetchApi.js +63 -0
  105. package/services/firebase.js +30 -0
  106. package/services/toast.js +214 -0
  107. package/template/doc/EzDocs.js +15 -0
  108. package/template/doc/EzDocs.module.scss +627 -0
  109. package/template/doc/EzDocsController.js +164 -0
  110. package/template/doc/data/activityfeed/EzActivityFeedDoc.js +42 -0
  111. package/template/doc/data/avatar/EzAvatarDoc.js +71 -0
  112. package/template/doc/data/badge/EzBadgeDoc.js +92 -0
  113. package/template/doc/data/button/EzButtonDoc.js +77 -0
  114. package/template/doc/data/buttongroup/EzButtonGroupDoc.js +102 -0
  115. package/template/doc/data/card/EzCardDoc.js +39 -0
  116. package/template/doc/data/chart/EzChartDoc.js +60 -0
  117. package/template/doc/data/checkbox/EzCheckboxDoc.js +67 -0
  118. package/template/doc/data/component/EzComponentDoc.js +34 -0
  119. package/template/doc/data/cssmodules/CSSModulesDoc.js +70 -0
  120. package/template/doc/data/datepicker/EzDatePickerDoc.js +126 -0
  121. package/template/doc/data/dialog/EzDialogDoc.js +217 -0
  122. package/template/doc/data/dropdown/EzDropdownDoc.js +178 -0
  123. package/template/doc/data/form/EzFormDoc.js +90 -0
  124. package/template/doc/data/grid/EzGridDoc.js +99 -0
  125. package/template/doc/data/input/EzInputDoc.js +92 -0
  126. package/template/doc/data/label/EzLabelDoc.js +40 -0
  127. package/template/doc/data/model/EzModelDoc.js +53 -0
  128. package/template/doc/data/outlet/EzOutletDoc.js +63 -0
  129. package/template/doc/data/panel/EzPanelDoc.js +214 -0
  130. package/template/doc/data/radio/EzRadioDoc.js +174 -0
  131. package/template/doc/data/router/EzRouterDoc.js +75 -0
  132. package/template/doc/data/select/EzSelectDoc.js +37 -0
  133. package/template/doc/data/skeleton/EzSkeletonDoc.js +149 -0
  134. package/template/doc/data/switch/EzSwitchDoc.js +82 -0
  135. package/template/doc/data/tabpanel/EzTabPanelDoc.js +44 -0
  136. package/template/doc/data/textarea/EzTextareaDoc.js +131 -0
  137. package/template/doc/data/timepicker/EzTimePickerDoc.js +107 -0
  138. package/template/doc/data/tooltip/EzTooltipDoc.js +193 -0
  139. package/template/doc/data/validators/EzValidatorsDoc.js +37 -0
  140. package/template/doc/sidebar/EzDocsSidebar.js +32 -0
  141. package/template/doc/sidebar/category/EzDocsCategory.js +33 -0
  142. package/template/doc/sidebar/item/EzDocsComponentItem.js +24 -0
  143. package/template/doc/viewer/EzDocsViewer.js +18 -0
  144. package/template/doc/viewer/codepanel/EzDocsCodePanel.js +51 -0
  145. package/template/doc/viewer/content/EzDocsContent.js +315 -0
  146. package/template/doc/viewer/header/EzDocsViewerHeader.js +46 -0
  147. package/template/doc/viewer/showcase/EzDocsShowcase.js +59 -0
  148. package/template/doc/viewer/showcase/EzDocsShowcaseSection.js +25 -0
  149. package/template/doc/viewer/showcase/EzDocsVariantItem.js +29 -0
  150. package/template/doc/welcome/EzDocsWelcome.js +48 -0
  151. package/themes/ez-theme.scss +179 -0
  152. package/themes/nature-fresh.scss +169 -0
  153. package/types/global.d.ts +21 -0
  154. package/utils/cssModules.js +81 -0
package/core/ez.ts ADDED
@@ -0,0 +1,617 @@
1
+ import { EzError } from './EzError.js';
2
+ import { EzRouter, RouteDefinition, MiddlewareFunction, RouteInfo } from './router.js';
3
+ import { EzLoader } from './loader.js';
4
+ import { EzRenderer, ComponentConfig } from './renderer.js';
5
+ import { EzState, ControllerDefinition, GridControllerDefinition, GridBehaviorDefinition } from './state.js';
6
+ import { EzModel, ModelDefinition, Model } from './EzModel.js';
7
+ import { EzServices } from './services.js';
8
+ import { EzEventBus, EventHandler } from './eventBus.js';
9
+ import * as EzGridQuery from '../components/grid/query/EzGridQuery.js';
10
+ import moment from 'moment';
11
+ import { createEzStore } from '../components/store/EzStore.js';
12
+ import { EzUtils, format, capitalize, capitalizeWords, truncate, pluralize, FormatType, FormatOptions } from './utils.js';
13
+ import { ComponentDefinition, ControllerDef } from './EzTypes.js';
14
+
15
+ declare global {
16
+ interface Window {
17
+ ez: EzFramework;
18
+ }
19
+ }
20
+
21
+ interface StoreEntry {
22
+ config: Record<string, unknown>;
23
+ instance: unknown | null;
24
+ }
25
+
26
+ interface RegistryMeta {
27
+ definedIn?: string;
28
+ }
29
+
30
+ interface EzContext {
31
+ route: string | null;
32
+ view: string | null;
33
+ controller: string | null;
34
+ }
35
+
36
+ export interface EzFramework {
37
+ _registry: Record<string, unknown>;
38
+ _controllers: Record<string, ControllerDefinition>;
39
+ _models: Record<string, Model>;
40
+ _stores: Record<string, StoreEntry>;
41
+ _refs: Record<string, unknown>;
42
+ _registryMeta: Record<string, RegistryMeta>;
43
+ _context: EzContext;
44
+ _gridControllerDefinitions: Record<string, GridControllerDefinition>;
45
+ _gridStateRegistry: Record<string, unknown>;
46
+ _gridBehaviorDefinitions: Record<string, GridBehaviorDefinition>;
47
+ _gridBehaviors: Record<string, GridBehaviorDefinition>;
48
+ _styles: Record<string, () => Promise<unknown>>;
49
+ _stylesCache: Record<string, Record<string, string>>;
50
+ _theme: string;
51
+ _availableThemes: string[];
52
+ _router: EzRouter | null;
53
+ _loader: EzLoader | null;
54
+ _renderer: EzRenderer | null;
55
+ _state: EzState | null;
56
+ _model: EzModel | null;
57
+ _services: EzServices | null;
58
+ _eventBus: EzEventBus | null;
59
+ grid?: { query: typeof EzGridQuery };
60
+ moment: typeof moment;
61
+
62
+ _initModules(): Promise<void>;
63
+ /**
64
+ * Define a component in the Ez framework
65
+ * @param name - Unique component name (used as eztype)
66
+ * @param definition - Component configuration:
67
+ * ```
68
+ * {
69
+ * eztype?: string // Base component to extend
70
+ * css?: string // CSS module name (auto-loads .module.scss)
71
+ * controller?: string // Controller name for state
72
+ * cls?: string | string[] // CSS classes
73
+ * layout?: 'hbox'|'vbox' // Flex direction
74
+ * style?: object // Inline styles
75
+ * items?: ItemConfig[] // Static children
76
+ * template?: (props) => ItemConfig // Dynamic render function
77
+ * bind?: BindConfig // Data bindings
78
+ * itemRender?: (item, index) => ItemConfig // List item renderer
79
+ * onLoad?: string // Async load method (controller)
80
+ * fallback?: ItemConfig // Loading placeholder
81
+ * ref?: string // Reference name for ez._refs
82
+ * }
83
+ * ```
84
+ */
85
+ define(name: string, definition: ComponentDefinition): void;
86
+ /**
87
+ * Define a controller for state management
88
+ * @param name - Controller name (referenced by component's controller property)
89
+ * @param definition - Controller configuration:
90
+ * ```
91
+ * {
92
+ * state?: object // Reactive state (auto-wrapped with deepSignal)
93
+ * onInit?: () => void // Called when controller initializes
94
+ * onRouteChange?: (params) => void // Called on route changes
95
+ * onDestroy?: () => void // Called when controller is destroyed
96
+ * [methodName]: Function // Custom methods (access state via this.state)
97
+ * }
98
+ * ```
99
+ */
100
+ defineController(name: string, definition: ControllerDefinition): void;
101
+ /**
102
+ * Define a grid-specific controller for EzGrid lifecycle hooks
103
+ * @param name - Grid controller name (referenced by grid's gridController property)
104
+ * @param definition - Grid controller configuration:
105
+ * ```
106
+ * {
107
+ * onInit?: () => void // Called when grid initializes
108
+ * beforeDataRender?: () => void // Called before rendering data
109
+ * afterDataRender?: () => void // Called after rendering data
110
+ * onSelectionChange?: (rows) => void // Called when selection changes
111
+ * onRowClick?: (row, e) => void // Called on row click
112
+ * onCellClick?: (cell, row, e) => void // Called on cell click
113
+ * }
114
+ * ```
115
+ */
116
+ defineGridController(name: string, definition: GridControllerDefinition): void;
117
+ /**
118
+ * Define a data model for type coercion and validation
119
+ * @param name - Model name (referenced by component's model property)
120
+ * @param definition - Model configuration:
121
+ * ```
122
+ * {
123
+ * fields: [
124
+ * { name: 'id', type: 'int', primaryKey: true },
125
+ * { name: 'email', type: 'string' },
126
+ * { name: 'age', type: 'int', default: 0 },
127
+ * { name: 'active', type: 'bool', nullable: true },
128
+ * { name: 'fullName', compute: (r) => r.first + ' ' + r.last }
129
+ * ]
130
+ * }
131
+ * ```
132
+ * Types: 'int' | 'float' | 'string' | 'bool' | 'date' | 'array' | 'object'
133
+ */
134
+ defineModel(name: string, definition: ModelDefinition): void;
135
+ getModel(name: string): Model | null;
136
+ getModelSync(name: string): Model;
137
+ /**
138
+ * Define a global store for shared state across components
139
+ * @param name - Store name
140
+ * @param config - Store configuration:
141
+ * ```
142
+ * {
143
+ * state: { ... }, // Initial state
144
+ * getters: { ... }, // Computed values
145
+ * actions: { ... } // Methods to modify state
146
+ * }
147
+ * ```
148
+ */
149
+ defineStore(name: string, config: Record<string, unknown>): void;
150
+ getStore(name: string): unknown;
151
+ registerComponent(eztype: string, cls: unknown): void;
152
+ installModules(mods: Record<string, () => Promise<unknown>>): void;
153
+ installStyles(styles: Record<string, () => Promise<unknown>>): void;
154
+ hasStyles(name: string): boolean;
155
+ resolveStyles(name: string): Promise<Record<string, string> | null>;
156
+ get(name: string): unknown;
157
+ getController(name: string): Promise<ControllerDefinition> | ControllerDefinition;
158
+ getControllerSync(name: string): ControllerDefinition;
159
+ _createElement(config: ComponentConfig, controllerName?: string | null, inheritedState?: unknown): Promise<Node>;
160
+ _createChildElements(items: ComponentConfig[], controllerName: string | null, inheritedState: unknown, parentCss?: string | null): Promise<Node[]>;
161
+ routes(routeDefs: RouteDefinition[]): void;
162
+ go(path: string): void;
163
+ navigate(viewName: string, route?: RouteInfo | null): Promise<void>;
164
+ installPopStateListener(): void;
165
+ prefetch(path: string): Promise<void>;
166
+ enableLinkPrefetch(): void;
167
+ beforeEach(fn: MiddlewareFunction): () => void;
168
+ afterEach(fn: MiddlewareFunction): () => void;
169
+ onNavigationError(fn: (error: Error, context: unknown) => void): () => void;
170
+ setNetworkAware(enabled?: boolean): void;
171
+ getNetworkInfo(): { strategy: string; online: boolean; effectiveType: string | null; saveData: boolean };
172
+ addPreloadHint(eztype: string): boolean;
173
+ addPreloadHintsForRoute(routePath: string): Promise<number>;
174
+ addPreloadHintsForAdjacentRoutes(): Promise<number>;
175
+ _onLocationChange(): Promise<void>;
176
+ on(event: string, fn: EventHandler['fn'], context?: object | null): () => boolean;
177
+ once(event: string, fn: EventHandler['fn'], context?: object | null): () => boolean;
178
+ off(event: string, fn?: EventHandler['fn']): boolean;
179
+ emit(event: string, data?: unknown): boolean;
180
+ emitAsync(event: string, data?: unknown): Promise<boolean>;
181
+ eventNamespace(namespace: string): unknown;
182
+ /**
183
+ * Define a reusable grid behavior (plugin pattern)
184
+ * @param name - Behavior name (referenced by grid's behaviors array)
185
+ * @param definition - Behavior configuration:
186
+ * ```
187
+ * {
188
+ * onInit?: (grid) => void // Called when behavior attaches
189
+ * onDestroy?: (grid) => void // Called when behavior detaches
190
+ * onDataChange?: (data, grid) => void // Called when data changes
191
+ * [hookName]: Function // Custom hooks
192
+ * }
193
+ * ```
194
+ */
195
+ defineGridBehavior(name: string, definition: GridBehaviorDefinition): void;
196
+ getGridBehavior(name: string): Promise<GridBehaviorDefinition | null>;
197
+ getDeepValue(obj: Record<string, unknown>, pathArray: string[]): unknown;
198
+ setDeepValue(obj: Record<string, unknown>, props: string[], value: unknown): void;
199
+ handleFrameworkError(error: Error | EzError): void;
200
+ _api: unknown;
201
+ _fb: unknown;
202
+ crypto: unknown;
203
+ routeUI: unknown;
204
+ dialog: unknown;
205
+ toast: unknown;
206
+ setTheme(themeName: string): void;
207
+ getTheme(): string;
208
+ getAvailableThemes(): string[];
209
+ toggleDarkMode(): string;
210
+ isDarkMode(): boolean;
211
+ _initTheme(): void;
212
+ format(value: unknown, type?: FormatType, options?: FormatOptions): string;
213
+ capitalize(str: string): string;
214
+ capitalizeWords(str: string): string;
215
+ truncate(str: string, length: number, suffix?: string): string;
216
+ pluralize(count: number, singular: string, plural?: string): string;
217
+ utils: typeof EzUtils;
218
+ }
219
+
220
+ const ez: EzFramework = {
221
+ _registry: {},
222
+ _controllers: {},
223
+ _models: {},
224
+ _stores: {},
225
+ _refs: {},
226
+ _registryMeta: {},
227
+ _context: {
228
+ route: null,
229
+ view: null,
230
+ controller: null
231
+ },
232
+ _gridControllerDefinitions: {},
233
+ _gridStateRegistry: {},
234
+ _gridBehaviorDefinitions: {},
235
+ _gridBehaviors: {},
236
+ _styles: {},
237
+ _stylesCache: {},
238
+ _theme: 'ocean-depths',
239
+ _availableThemes: [
240
+ 'ocean-depths',
241
+ 'ocean-depths-dark',
242
+ 'nature-fresh',
243
+ 'nature-fresh-dark'
244
+ ],
245
+ _router: null,
246
+ _loader: null,
247
+ _renderer: null,
248
+ _state: null,
249
+ _model: null,
250
+ _services: null,
251
+ _eventBus: null,
252
+ moment: moment,
253
+
254
+ async _initModules(): Promise<void> {
255
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
256
+ const ezInstance = this as any;
257
+ this._loader = new EzLoader(ezInstance);
258
+ this._router = new EzRouter(ezInstance);
259
+ this._renderer = new EzRenderer(ezInstance);
260
+ this._state = new EzState(ezInstance);
261
+ this._model = new EzModel(ezInstance);
262
+ this._services = new EzServices(ezInstance);
263
+ this._eventBus = new EzEventBus();
264
+ await this._services.init();
265
+ },
266
+
267
+ define(name: string, definition: ComponentDefinition): void {
268
+ if (this._registry[name]) {
269
+ throw new EzError({
270
+ code: 'EZ_DEFINE_DUPLICATE',
271
+ message: `Component "${name}" is already defined`,
272
+ details: {
273
+ eztype: name,
274
+ definedIn: this._registryMeta[name]?.definedIn,
275
+ redefinedIn: this._loader?.getCurrentModulePath()
276
+ }
277
+ });
278
+ }
279
+
280
+ this._registry[name] = definition;
281
+
282
+ this._registryMeta[name] = {
283
+ definedIn: this._loader?.getCurrentModulePath() || undefined
284
+ };
285
+ },
286
+
287
+ defineController(name: string, definition: ControllerDefinition): void {
288
+ this._state!.defineController(name, definition);
289
+ },
290
+
291
+ defineGridController(name: string, definition: GridControllerDefinition): void {
292
+ this._state!.defineGridController(name, definition);
293
+ },
294
+
295
+ defineModel(name: string, definition: ModelDefinition): void {
296
+ this._model!.defineModel(name, definition);
297
+ },
298
+
299
+ getModel(name: string): Model | null {
300
+ return this._model!.getModel(name);
301
+ },
302
+
303
+ getModelSync(name: string): Model {
304
+ return this._model!.getModelSync(name);
305
+ },
306
+
307
+ defineStore(name: string, config: Record<string, unknown>): void {
308
+ if (this._stores[name]) {
309
+ console.warn(`[Ez] Store "${name}" already defined, overwriting.`);
310
+ }
311
+ this._stores[name] = { config, instance: null };
312
+ },
313
+
314
+ getStore(name: string): unknown {
315
+ const entry = this._stores[name];
316
+ if (!entry) {
317
+ console.warn(`[Ez] Store "${name}" not found.`);
318
+ return null;
319
+ }
320
+ if (!entry.instance) {
321
+ entry.instance = createEzStore(entry.config as Parameters<typeof createEzStore>[0]);
322
+ }
323
+ return entry.instance;
324
+ },
325
+
326
+ registerComponent(eztype: string, cls: unknown): void {
327
+ this._registry[eztype] = cls;
328
+ },
329
+
330
+ installModules(mods: Record<string, () => Promise<unknown>>): void {
331
+ this._loader!.installModules(mods);
332
+ },
333
+
334
+ installStyles(styles: Record<string, () => Promise<unknown>>): void {
335
+ this._styles = styles;
336
+ },
337
+
338
+ hasStyles(name: string): boolean {
339
+ if (!name || !this._styles) return false;
340
+ const lowerName = name.toLowerCase();
341
+ return Object.keys(this._styles).some(p => {
342
+ const fileName = p.split('/').pop()?.toLowerCase();
343
+ return fileName === `${lowerName}.module.scss`;
344
+ });
345
+ },
346
+
347
+ async resolveStyles(name: string): Promise<Record<string, string> | null> {
348
+ if (this._stylesCache[name]) {
349
+ return this._stylesCache[name];
350
+ }
351
+
352
+ const lowerName = name.toLowerCase();
353
+ const path = Object.keys(this._styles).find(p => {
354
+ const fileName = p.split('/').pop()?.toLowerCase();
355
+ return fileName === `${lowerName}.module.scss`;
356
+ });
357
+
358
+ if (!path) {
359
+ const env = (import.meta as unknown as { env?: { DEV?: boolean } }).env;
360
+ if (env?.DEV) {
361
+ console.warn(`[Ez] CSS module "${name}" not found`);
362
+ }
363
+ return null;
364
+ }
365
+
366
+ const module = await this._styles[path]();
367
+ const styles = (module as { default?: Record<string, string> }).default || module;
368
+
369
+ this._stylesCache[name] = styles as Record<string, string>;
370
+
371
+ return styles as Record<string, string>;
372
+ },
373
+
374
+ get(name: string): unknown {
375
+ return this._registry[name];
376
+ },
377
+
378
+ getController(name: string): Promise<ControllerDefinition> | ControllerDefinition {
379
+ return this._state!.getController(name);
380
+ },
381
+
382
+ getControllerSync(name: string): ControllerDefinition {
383
+ return this._state!.getControllerSync(name);
384
+ },
385
+
386
+ async _createElement(config: ComponentConfig, controllerName: string | null = null, inheritedState: unknown = null): Promise<Node> {
387
+ return this._renderer!.createElement(config, controllerName, inheritedState);
388
+ },
389
+
390
+ async _createChildElements(items: ComponentConfig[], controllerName: string | null, inheritedState: unknown, parentCss: string | null = null): Promise<Node[]> {
391
+ return this._renderer!.createChildElements(items, controllerName || '', inheritedState, parentCss);
392
+ },
393
+
394
+ routes(routeDefs: RouteDefinition[]): void {
395
+ this._router!.routes(routeDefs);
396
+ },
397
+
398
+ go(path: string): void {
399
+ this._router!.go(path);
400
+ },
401
+
402
+ async navigate(viewName: string, route: RouteInfo | null = null): Promise<void> {
403
+ return this._router!.navigate(viewName, route);
404
+ },
405
+
406
+ installPopStateListener(): void {
407
+ this._router!.installPopStateListener();
408
+ },
409
+
410
+ async prefetch(path: string): Promise<void> {
411
+ return this._router!.prefetch(path);
412
+ },
413
+
414
+ enableLinkPrefetch(): void {
415
+ this._router!.enableLinkPrefetch();
416
+ },
417
+
418
+ beforeEach(fn: MiddlewareFunction): () => void {
419
+ return this._router!.beforeEach(fn);
420
+ },
421
+
422
+ afterEach(fn: MiddlewareFunction): () => void {
423
+ return this._router!.afterEach(fn);
424
+ },
425
+
426
+ onNavigationError(fn: (error: Error, context: unknown) => void): () => void {
427
+ return this._router!.onError(fn as Parameters<EzRouter['onError']>[0]);
428
+ },
429
+
430
+ setNetworkAware(enabled: boolean = true): void {
431
+ this._loader!.setNetworkAware(enabled);
432
+ },
433
+
434
+ getNetworkInfo(): { strategy: string; online: boolean; effectiveType: string | null; saveData: boolean } {
435
+ return this._loader!.getNetworkInfo();
436
+ },
437
+
438
+ addPreloadHint(eztype: string): boolean {
439
+ return this._loader!.addPreloadHint(eztype);
440
+ },
441
+
442
+ async addPreloadHintsForRoute(routePath: string): Promise<number> {
443
+ return this._loader!.addPreloadHintsForRoute(routePath);
444
+ },
445
+
446
+ async addPreloadHintsForAdjacentRoutes(): Promise<number> {
447
+ return this._loader!.addPreloadHintsForAdjacentRoutes();
448
+ },
449
+
450
+ async _onLocationChange(): Promise<void> {
451
+ return (this._router as unknown as { _onLocationChange(): Promise<void> })._onLocationChange();
452
+ },
453
+
454
+ on(event: string, fn: EventHandler['fn'], context?: object | null): () => boolean {
455
+ return this._eventBus!.on(event, fn, context);
456
+ },
457
+
458
+ once(event: string, fn: EventHandler['fn'], context?: object | null): () => boolean {
459
+ return this._eventBus!.once(event, fn, context);
460
+ },
461
+
462
+ off(event: string, fn?: EventHandler['fn']): boolean {
463
+ return this._eventBus!.off(event, fn);
464
+ },
465
+
466
+ emit(event: string, data?: unknown): boolean {
467
+ return this._eventBus!.emit(event, data);
468
+ },
469
+
470
+ async emitAsync(event: string, data?: unknown): Promise<boolean> {
471
+ return this._eventBus!.emitAsync(event, data);
472
+ },
473
+
474
+ eventNamespace(namespace: string): unknown {
475
+ return this._eventBus!.namespace(namespace);
476
+ },
477
+
478
+ defineGridBehavior(name: string, definition: GridBehaviorDefinition): void {
479
+ this._state!.defineGridBehavior(name, definition);
480
+ },
481
+
482
+ async getGridBehavior(name: string): Promise<GridBehaviorDefinition | null> {
483
+ return this._state!.getGridBehavior(name);
484
+ },
485
+
486
+ getDeepValue(obj: Record<string, unknown>, pathArray: string[]): unknown {
487
+ return this._state!.getDeepValue(obj, pathArray);
488
+ },
489
+
490
+ setDeepValue(obj: Record<string, unknown>, props: string[], value: unknown): void {
491
+ this._state!.setDeepValue(obj, props, value);
492
+ },
493
+
494
+ handleFrameworkError(error: Error | EzError): void {
495
+ if (error instanceof EzError) {
496
+ console.group(`EZ Framework Error — ${error.code}`);
497
+ console.error(error.message);
498
+ console.info('Source:', error.source);
499
+ console.info('Route:', error.route);
500
+ console.info('View:', error.view);
501
+ if (error.details) {
502
+ console.info('Details:', error.details);
503
+ }
504
+ if (error.cause) {
505
+ console.error('Cause:', error.cause);
506
+ }
507
+ console.groupEnd();
508
+ } else {
509
+ console.error('[EZ] Unhandled error:', error);
510
+ }
511
+ },
512
+
513
+ get _api(): unknown {
514
+ return this._services?.api;
515
+ },
516
+
517
+ get _fb(): unknown {
518
+ return this._services?.firebase;
519
+ },
520
+
521
+ get crypto(): unknown {
522
+ return this._services?.crypto;
523
+ },
524
+
525
+ get routeUI(): unknown {
526
+ return this._services?.routeUI;
527
+ },
528
+
529
+ get dialog(): unknown {
530
+ return this._services?.dialog;
531
+ },
532
+
533
+ get toast(): unknown {
534
+ return this._services?.toast;
535
+ },
536
+
537
+ setTheme(themeName: string): void {
538
+ if (!this._availableThemes.includes(themeName)) {
539
+ console.warn(`[ez] Theme "${themeName}" not found. Available: ${this._availableThemes.join(', ')}`);
540
+ return;
541
+ }
542
+
543
+ this._theme = themeName;
544
+ document.documentElement.setAttribute('data-ez-theme', themeName);
545
+
546
+ try {
547
+ localStorage.setItem('ez-theme', themeName);
548
+ } catch {
549
+ // localStorage not available
550
+ }
551
+
552
+ this._eventBus?.emit('theme:change', { theme: themeName });
553
+ },
554
+
555
+ getTheme(): string {
556
+ return this._theme;
557
+ },
558
+
559
+ getAvailableThemes(): string[] {
560
+ return [...this._availableThemes];
561
+ },
562
+
563
+ toggleDarkMode(): string {
564
+ const current = this._theme;
565
+ let newTheme: string;
566
+
567
+ if (current.endsWith('-dark')) {
568
+ newTheme = current.replace('-dark', '');
569
+ } else {
570
+ newTheme = current + '-dark';
571
+ }
572
+
573
+ if (this._availableThemes.includes(newTheme)) {
574
+ this.setTheme(newTheme);
575
+ }
576
+
577
+ return this._theme;
578
+ },
579
+
580
+ isDarkMode(): boolean {
581
+ return this._theme.endsWith('-dark');
582
+ },
583
+
584
+ _initTheme(): void {
585
+ try {
586
+ const saved = localStorage.getItem('ez-theme');
587
+ if (saved && this._availableThemes.includes(saved)) {
588
+ this.setTheme(saved);
589
+ return;
590
+ }
591
+ } catch {
592
+ // localStorage not available
593
+ }
594
+
595
+ document.documentElement.setAttribute('data-ez-theme', this._theme);
596
+ },
597
+
598
+ format,
599
+ capitalize,
600
+ capitalizeWords,
601
+ truncate,
602
+ pluralize,
603
+ utils: EzUtils
604
+ };
605
+
606
+ await ez._initModules();
607
+ ez._initTheme();
608
+
609
+ window.ez = ez;
610
+
611
+ ez.grid ??= { query: EzGridQuery };
612
+ ez.grid.query = EzGridQuery;
613
+
614
+ export { ez };
615
+ export default ez;
616
+ export type { ComponentDefinition, ControllerDef, ItemConfig, BindConfig } from './EzTypes.js';
617
+ export type { EzItemConfig } from './EzComponentTypes.js';