@cruxjs/client 0.0.1 → 0.0.3
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/README.md +82 -3
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +44 -1
- package/dist/index.d.ts +44 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -8,12 +8,12 @@
|
|
|
8
8
|
</div>
|
|
9
9
|
|
|
10
10
|
<div align="center">
|
|
11
|
-
<img src="https://img.shields.io/badge/v-0.0.
|
|
11
|
+
<img src="https://img.shields.io/badge/v-0.0.3-black"/>
|
|
12
12
|
<img src="https://img.shields.io/badge/🔥-@cruxjs-black"/>
|
|
13
13
|
<br>
|
|
14
14
|
<img src="https://img.shields.io/badge/coverage----%25-brightgreen" alt="Test Coverage" />
|
|
15
|
-
<img src="https://img.shields.io/github/issues/cruxjs/client?style=flat" alt="Github Repo Issues" />
|
|
16
|
-
<img src="https://img.shields.io/github/stars/cruxjs/client?style=social" alt="GitHub Repo stars" />
|
|
15
|
+
<img src="https://img.shields.io/github/issues/cruxjs-org/client?style=flat" alt="Github Repo Issues" />
|
|
16
|
+
<img src="https://img.shields.io/github/stars/cruxjs-org/client?style=social" alt="GitHub Repo stars" />
|
|
17
17
|
</div>
|
|
18
18
|
<br>
|
|
19
19
|
|
|
@@ -73,6 +73,40 @@
|
|
|
73
73
|
// App is live and routing is reactive
|
|
74
74
|
```
|
|
75
75
|
|
|
76
|
+
- ### 1.5 With Internationalization (i18n)
|
|
77
|
+
|
|
78
|
+
> Setup multi-language support with translations:
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
import { ClientManager } from '@cruxjs/client';
|
|
82
|
+
|
|
83
|
+
const HomePage = () => <div>Welcome Home</div>;
|
|
84
|
+
const NotFoundPage = () => <div>404 - Not Found</div>;
|
|
85
|
+
|
|
86
|
+
// Create manager with i18n config
|
|
87
|
+
const app = new ClientManager({
|
|
88
|
+
routes: {
|
|
89
|
+
'/': HomePage
|
|
90
|
+
},
|
|
91
|
+
notFoundComponent: NotFoundPage,
|
|
92
|
+
i18n: {
|
|
93
|
+
defaultLanguage: 'en',
|
|
94
|
+
supportedLanguages: ['en', 'ar'],
|
|
95
|
+
// TODO: short it
|
|
96
|
+
basePath: 'http://localhost:3000/static/dist/i18n' // Path to translation files
|
|
97
|
+
},
|
|
98
|
+
debug: true
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// Boot and ready
|
|
102
|
+
await app.boot();
|
|
103
|
+
await app.ready('#app');
|
|
104
|
+
|
|
105
|
+
// Now use translations in components or hooks
|
|
106
|
+
const { t } = app.getI18n();
|
|
107
|
+
console.log(t('welcome')); // Gets translation key
|
|
108
|
+
```
|
|
109
|
+
|
|
76
110
|
- ### 2. With Lifecycle Hooks
|
|
77
111
|
|
|
78
112
|
```typescript
|
|
@@ -391,6 +425,51 @@
|
|
|
391
425
|
}
|
|
392
426
|
```
|
|
393
427
|
|
|
428
|
+
### Internationalization (i18n)
|
|
429
|
+
|
|
430
|
+
- #### `getI18n(): I18nInstance`
|
|
431
|
+
> Access the i18n instance for translations
|
|
432
|
+
> - Returns `undefined` if i18n not initialized
|
|
433
|
+
> - Call after `boot()` if i18n config provided
|
|
434
|
+
|
|
435
|
+
```typescript
|
|
436
|
+
const i18n = app.getI18n();
|
|
437
|
+
if (i18n) {
|
|
438
|
+
const greeting = i18n.t('greeting');
|
|
439
|
+
console.log(greeting);
|
|
440
|
+
}
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
- #### `getTranslation(key: string, defaultValue?: string): string`
|
|
444
|
+
> Safely get a translation with fallback
|
|
445
|
+
> - Returns default value if key not found
|
|
446
|
+
> - Returns key if i18n not initialized
|
|
447
|
+
> - Safe to call anytime
|
|
448
|
+
|
|
449
|
+
```typescript
|
|
450
|
+
// In lifecycle hook or component
|
|
451
|
+
const message = app.getTranslation('welcome', 'Welcome!');
|
|
452
|
+
console.log(message); // 'Welcome!' if not found in i18n
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
### i18n Utilities
|
|
456
|
+
|
|
457
|
+
- #### `useTranslation(): { t: (key: string, defaultValue?: string) => string }`
|
|
458
|
+
> Composable hook to use translations in components
|
|
459
|
+
> - Safe even if i18n not initialized
|
|
460
|
+
> - Returns both `t()` and `getTranslation()` methods
|
|
461
|
+
|
|
462
|
+
```typescript
|
|
463
|
+
import { useTranslation } from '@cruxjs/client';
|
|
464
|
+
|
|
465
|
+
// In your components
|
|
466
|
+
const { t } = useTranslation();
|
|
467
|
+
|
|
468
|
+
const HomePage = () => {
|
|
469
|
+
return <div>{t('home.title', 'Welcome')}</div>;
|
|
470
|
+
};
|
|
471
|
+
```
|
|
472
|
+
|
|
394
473
|
<!-- ╚═════════════════════════════════════════════════════════════════╝ -->
|
|
395
474
|
|
|
396
475
|
|
package/dist/index.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
'use strict';var signals=require('@minejs/signals'),browser=require('@minejs/browser');var
|
|
1
|
+
'use strict';var signals=require('@minejs/signals'),i18n=require('@minejs/i18n'),browser=require('@minejs/browser');var a=class{constructor(t){this.lifecycle="booting";this.hooks={};this.routeComponents={};this.currentPathSignal=signals.signal(window.location.pathname??"/");this.config=t,this.debug=t.debug??false,this.log("[INIT] Creating ClientManager"),this.routeComponents=t.routes,this.eventsManager=new browser.EventsManager,this.windowManager=new browser.WindowManager;let e=Object.entries(t.routes).map(([n,o])=>({path:n,component:o}));this.router=browser.createRouter({routes:e,notFoundComponent:t.notFoundComponent}),this.router.afterEach(n=>{this.currentPathSignal.set(n.path);}),this.log("[INIT] ClientManager created");}on(t,e,n,o){return typeof t=="string"&&t.startsWith("on")?(this.hooks[t]=e,this):this.eventsManager.on(t,e,n,o)}async boot(){if(this.lifecycle!=="booting"){console.warn("[ClientManager] Already booted or destroyed");return}this.log("\u26A1 Phase: BOOT");try{this.config.i18n&&await this.setupI18n(this.config.i18n),this.hooks.onBoot&&(this.log("\u2192 Calling onBoot hook"),await this.hooks.onBoot()),this.log("\u2713 BOOT phase complete");}catch(t){throw console.error("[ClientManager] Boot failed:",t),t}}async ready(t){if(this.lifecycle!=="booting"){console.warn("[ClientManager] Cannot ready - not in booting phase");return}this.log("\u26A1 Phase: READY");try{this.mount(t),this.log("\u2192 Router mounted"),this.debug&&(globalThis.__CLIENT_MANAGER__=this,this.log("\u2192 ClientManager available globally")),this.hooks.onReady&&(this.log("\u2192 Calling onReady hook"),await this.hooks.onReady()),this.lifecycle="ready",this.log("\u2713 READY phase complete"),this.log("\u2713 App is ready!");}catch(e){throw console.error("[ClientManager] Ready failed:",e),e}}async destroy(){if(this.lifecycle==="destroyed"){console.warn("[ClientManager] Already destroyed");return}this.lifecycle="destroying",this.log("\u26A1 Phase: DESTROY");try{this.hooks.onDestroy&&(this.log("\u2192 Calling onDestroy hook"),await this.hooks.onDestroy()),this.eventsManager.destroy(),this.windowManager.destroy(),this.lifecycle="destroyed",this.log("\u2713 DESTROY phase complete");}catch(t){throw console.error("[ClientManager] Destroy failed:",t),t}}async setupI18n(t){this.log("Setting up i18n...");try{await i18n.setupAuto({defaultLanguage:t.defaultLanguage,supportedLanguages:t.supportedLanguages,basePath:t.basePath,fileExtension:t.fileExtension||"json"}),this.log(`i18n ready \u2192 ${t.supportedLanguages.join(", ")}`);}catch(e){throw this.log("Failed to setup i18n"+e.message),e}}navigate(t){this.router.push(t);}mount(t){let e=typeof t=="string"?document.querySelector(t):t;if(!e){console.warn("[ClientManager] Mount target not found:",t);return}signals.effect(()=>{let n=this.currentPathSignal(),o=this.routeComponents[n]||this.config.notFoundComponent||null;if(e.innerHTML="",o){let s=o();s instanceof Node&&e.appendChild(s);}else e.innerHTML="<p>No component found for this route</p>";this.log(`\u2192 Route changed to: ${n}`);}),this.log("\u2192 Routing setup complete");}getCurrentPath(){return this.currentPathSignal}createLinkHandler(t){return e=>{e.preventDefault(),this.navigate(t);}}getRouter(){return this.router}off(t,e,n){this.eventsManager.off(t,e,n);}getEventsManager(){return this.eventsManager}getViewport(){return this.windowManager.getViewport()}getWindowManager(){return this.windowManager}getI18n(){return i18n.getI18n()}getTranslation(t,e){let n=i18n.getI18n();return n?n.t(t)??e??t:(console.warn("[ClientManager] i18n not initialized. Using default value or key."),e??t)}getPhase(){return this.lifecycle}isReady(){return this.lifecycle==="ready"}log(t){this.debug&&console.log(`[ClientManager] ${t}`);}};function C(){let r=i18n.getI18n();return {getTranslation:(t,e)=>r?r.t(t)??e??t:e??t,t:(t,e)=>r?r.t(t)??e??t:e??t}}exports.ClientManager=a;exports.useTranslation=C;//# sourceMappingURL=index.cjs.map
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"names":["ClientManager","config","signal","EventsManager","WindowManager","routesArray","path","component","createRouter","to","eventOrTarget","callbackOrEvent","handler","options","err","mountSelector","selector","container","effect","currentPath","Component","node","e","target","event","message"],"mappings":"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","file":"index.cjs","sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\r\n// src/index.ts\r\n//\r\n// Made with ❤️ by Maysara.\r\n\r\n\r\n// ╔════════════════════════════════════════ PACK ════════════════════════════════════════╗\r\n\r\n import { signal, effect } from '@minejs/signals';\r\n import { EventsManager, Router, WindowManager, createRouter } from '@minejs/browser';\r\n import * as types from './types';\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝\r\n\r\n\r\n\r\n// ╔════════════════════════════════════════ CORE ════════════════════════════════════════╗\r\n\r\n export class ClientManager {\r\n\r\n // ┌──────────────────────────────── INIT ──────────────────────────────┐\r\n\r\n private router : Router;\r\n private eventsManager : EventsManager;\r\n private windowManager : WindowManager;\r\n private lifecycle : 'booting' | 'ready' | 'destroying' | 'destroyed' = 'booting';\r\n private config : types.ClientManagerConfig;\r\n private hooks : types.ClientManagerHooks = {};\r\n private debug : boolean;\r\n private routeComponents : Record<string, types.RouteComponent> = {};\r\n private currentPathSignal = signal<string>(window.location.pathname ?? '/');\r\n\r\n constructor(config: types.ClientManagerConfig) {\r\n this.config = config;\r\n this.debug = config.debug ?? false;\r\n\r\n this.log('[INIT] Creating ClientManager');\r\n\r\n // Store route components provided by user\r\n this.routeComponents = config.routes;\r\n\r\n // Initialize managers from @minejs/browser\r\n this.eventsManager = new EventsManager();\r\n this.windowManager = new WindowManager();\r\n\r\n // Initialize router with user-provided routes\r\n const routesArray = Object.entries(config.routes).map(([path, component]) => ({\r\n path,\r\n component\r\n }));\r\n\r\n this.router = createRouter({\r\n routes: routesArray,\r\n notFoundComponent: config.notFoundComponent,\r\n });\r\n\r\n // Connect router changes to signal for automatic re-rendering\r\n this.router.afterEach((to) => {\r\n this.currentPathSignal.set(to.path);\r\n });\r\n\r\n this.log('[INIT] ClientManager created');\r\n }\r\n\r\n // └────────────────────────────────────────────────────────────────────┘\r\n\r\n\r\n // ┌──────────────────────────────── ──── ──────────────────────────────┐\r\n\r\n /**\r\n * Setup lifecycle hooks OR bind events\r\n * Overloaded: on(event: 'onBoot'|'onReady'|'onDestroy', callback) - lifecycle\r\n * on(target, event, handler) - event binding\r\n */\r\n on(event: keyof types.ClientManagerHooks, callback: any): this;\r\n on<K extends keyof HTMLElementEventMap>(\r\n target: EventTarget,\r\n event: K | string,\r\n handler: EventListener,\r\n options?: AddEventListenerOptions\r\n ): () => void;\r\n on(\r\n eventOrTarget: keyof types.ClientManagerHooks | EventTarget,\r\n callbackOrEvent?: any,\r\n handler?: EventListener,\r\n options?: AddEventListenerOptions\r\n ): this | (() => void) {\r\n // Check if this is a lifecycle hook call\r\n if (typeof eventOrTarget === 'string' && eventOrTarget.startsWith('on')) {\r\n this.hooks[eventOrTarget as keyof types.ClientManagerHooks] = callbackOrEvent;\r\n return this;\r\n }\r\n\r\n // Otherwise it's an event binding\r\n return this.eventsManager.on(\r\n eventOrTarget as EventTarget,\r\n callbackOrEvent as string,\r\n handler as EventListener,\r\n options\r\n );\r\n }\r\n\r\n // └────────────────────────────────────────────────────────────────────┘\r\n\r\n\r\n // ┌──────────────────────────────── ──── ──────────────────────────────┐\r\n\r\n /**\r\n * Bootstrap the app - Phase 1: BOOT\r\n */\r\n async boot(): Promise<void> {\r\n if (this.lifecycle !== 'booting') {\r\n console.warn('[ClientManager] Already booted or destroyed');\r\n return;\r\n }\r\n\r\n this.log('⚡ Phase: BOOT');\r\n\r\n try {\r\n // Call user onBoot hook\r\n if (this.hooks.onBoot) {\r\n this.log('→ Calling onBoot hook');\r\n await this.hooks.onBoot();\r\n }\r\n\r\n this.log('✓ BOOT phase complete');\r\n } catch (err) {\r\n console.error('[ClientManager] Boot failed:', err);\r\n throw err;\r\n }\r\n }\r\n\r\n /**\r\n * Ready the app - Phase 2: READY\r\n * Mount to DOM and make everything live\r\n */\r\n async ready(mountSelector: string | HTMLElement): Promise<void> {\r\n if (this.lifecycle !== 'booting') {\r\n console.warn('[ClientManager] Cannot ready - not in booting phase');\r\n return;\r\n }\r\n\r\n this.log('⚡ Phase: READY');\r\n\r\n try {\r\n // Mount router\r\n this.mount(mountSelector);\r\n this.log('→ Router mounted');\r\n\r\n // Setup global access for debugging\r\n if (this.debug) {\r\n (globalThis as any).__CLIENT_MANAGER__ = this;\r\n this.log('→ ClientManager available globally');\r\n }\r\n\r\n // Call user onReady hook\r\n if (this.hooks.onReady) {\r\n this.log('→ Calling onReady hook');\r\n await this.hooks.onReady();\r\n }\r\n\r\n this.lifecycle = 'ready';\r\n this.log('✓ READY phase complete');\r\n this.log('✓ App is ready!');\r\n } catch (err) {\r\n console.error('[ClientManager] Ready failed:', err);\r\n throw err;\r\n }\r\n }\r\n\r\n /**\r\n * Shutdown the app - Phase 3: DESTROY\r\n */\r\n async destroy(): Promise<void> {\r\n if (this.lifecycle === 'destroyed') {\r\n console.warn('[ClientManager] Already destroyed');\r\n return;\r\n }\r\n\r\n this.lifecycle = 'destroying';\r\n this.log('⚡ Phase: DESTROY');\r\n\r\n try {\r\n // Call user onDestroy hook\r\n if (this.hooks.onDestroy) {\r\n this.log('→ Calling onDestroy hook');\r\n await this.hooks.onDestroy();\r\n }\r\n\r\n // Cleanup managers\r\n this.eventsManager.destroy();\r\n this.windowManager.destroy();\r\n\r\n this.lifecycle = 'destroyed';\r\n this.log('✓ DESTROY phase complete');\r\n } catch (err) {\r\n console.error('[ClientManager] Destroy failed:', err);\r\n throw err;\r\n }\r\n }\r\n\r\n // └────────────────────────────────────────────────────────────────────┘\r\n\r\n\r\n // ┌──────────────────────────────── ──── ──────────────────────────────┐\r\n\r\n /**\r\n * Navigate to path\r\n */\r\n navigate(path: string): void {\r\n this.router.push(path);\r\n }\r\n\r\n /**\r\n * Mount router to DOM element and setup reactive routing\r\n * Automatically re-renders when route changes\r\n */\r\n mount(selector: string | HTMLElement): void {\r\n const container = typeof selector === 'string'\r\n ? document.querySelector(selector)\r\n : selector;\r\n\r\n if (!container) {\r\n console.warn('[ClientManager] Mount target not found:', selector);\r\n return;\r\n }\r\n\r\n // Setup reactive routing effect - re-renders when currentPathSignal changes\r\n effect(() => {\r\n const currentPath = this.currentPathSignal();\r\n const Component = this.routeComponents[currentPath]\r\n || this.config.notFoundComponent\r\n || null;\r\n\r\n // Clear and mount new component\r\n container.innerHTML = '';\r\n\r\n if (Component) {\r\n const node = Component();\r\n if (node instanceof Node) {\r\n container.appendChild(node);\r\n }\r\n } else {\r\n container.innerHTML = '<p>No component found for this route</p>';\r\n }\r\n\r\n this.log(`→ Route changed to: ${currentPath}`);\r\n });\r\n\r\n this.log('→ Routing setup complete');\r\n }\r\n\r\n /**\r\n * Get current path signal for reactivity\r\n */\r\n getCurrentPath() {\r\n return this.currentPathSignal;\r\n }\r\n\r\n /**\r\n * Create navigation link handler\r\n */\r\n createLinkHandler(path: string) {\r\n return (e: MouseEvent) => {\r\n e.preventDefault();\r\n this.navigate(path);\r\n };\r\n }\r\n\r\n /**\r\n * Get underlying router for advanced usage\r\n */\r\n getRouter() {\r\n return this.router;\r\n }\r\n\r\n // └────────────────────────────────────────────────────────────────────┘\r\n\r\n\r\n // ┌──────────────────────────────── ──── ──────────────────────────────┐\r\n\r\n /**\r\n * Unbind event\r\n */\r\n off(target: EventTarget, event: string, handler: EventListener): void {\r\n this.eventsManager.off(target, event, handler);\r\n }\r\n\r\n /**\r\n * Get events manager directly\r\n */\r\n getEventsManager(): EventsManager {\r\n return this.eventsManager;\r\n }\r\n\r\n // └────────────────────────────────────────────────────────────────────┘\r\n\r\n\r\n // ┌──────────────────────────────── ──── ──────────────────────────────┐\r\n\r\n /**\r\n * Get viewport info as reactive signal\r\n */\r\n getViewport() {\r\n return this.windowManager.getViewport();\r\n }\r\n\r\n /**\r\n * Get window manager directly\r\n */\r\n getWindowManager(): WindowManager {\r\n return this.windowManager;\r\n }\r\n\r\n // └────────────────────────────────────────────────────────────────────┘\r\n\r\n\r\n // ┌──────────────────────────────── ──── ──────────────────────────────┐\r\n\r\n /**\r\n * Get lifecycle phase\r\n */\r\n getPhase() {\r\n return this.lifecycle;\r\n }\r\n\r\n /**\r\n * Check if ready\r\n */\r\n isReady(): boolean {\r\n return this.lifecycle === 'ready';\r\n }\r\n\r\n /**\r\n * Internal logging\r\n */\r\n private log(message: string): void {\r\n if (this.debug) {\r\n console.log(`[ClientManager] ${message}`);\r\n }\r\n }\r\n\r\n // └────────────────────────────────────────────────────────────────────┘\r\n\r\n }\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝\r\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":["ClientManager","config","signal","EventsManager","WindowManager","routesArray","path","component","createRouter","to","eventOrTarget","callbackOrEvent","handler","options","err","mountSelector","setupAuto","selector","container","effect","currentPath","Component","node","target","event","getI18n","key","defaultValue","i18n","message","useTranslation"],"mappings":"wHAmBiBA,CAAAA,CAAN,KAAoB,CAcnB,WAAA,CAAYC,EAAmC,CAP/C,IAAA,CAAQ,SAAA,CAAyE,SAAA,CAEjF,KAAQ,KAAA,CAAiD,EAAC,CAE1D,IAAA,CAAQ,gBAA6D,EAAC,CACtE,IAAA,CAAQ,iBAAA,CAAsBC,cAAAA,CAAe,MAAA,CAAO,QAAA,CAAS,QAAA,EAAY,GAAG,CAAA,CAGxE,IAAA,CAAK,MAAA,CAASD,CAAAA,CACd,KAAK,KAAA,CAAQA,CAAAA,CAAO,KAAA,EAAS,KAAA,CAE7B,KAAK,GAAA,CAAI,+BAA+B,CAAA,CAGxC,IAAA,CAAK,gBAAkBA,CAAAA,CAAO,MAAA,CAG9B,IAAA,CAAK,aAAA,CAAgB,IAAIE,qBAAAA,CACzB,IAAA,CAAK,aAAA,CAAgB,IAAIC,sBAGzB,IAAMC,CAAAA,CAAc,MAAA,CAAO,OAAA,CAAQJ,EAAO,MAAM,CAAA,CAAE,GAAA,CAAI,CAAC,CAACK,CAAAA,CAAMC,CAAS,CAAA,IAAO,CAC1E,IAAA,CAAAD,CAAAA,CACA,SAAA,CAAAC,CACJ,EAAE,CAAA,CAEF,IAAA,CAAK,MAAA,CAASC,oBAAAA,CAAa,CACvB,MAAA,CAAQH,CAAAA,CACR,iBAAA,CAAmBJ,CAAAA,CAAO,iBAC9B,CAAC,CAAA,CAGD,IAAA,CAAK,MAAA,CAAO,UAAWQ,CAAAA,EAAO,CAC1B,IAAA,CAAK,iBAAA,CAAkB,IAAIA,CAAAA,CAAG,IAAI,EACtC,CAAC,EAED,IAAA,CAAK,GAAA,CAAI,8BAA8B,EAC3C,CAmBA,EAAA,CACIC,CAAAA,CACAC,CAAAA,CACAC,EACAC,CAAAA,CACmB,CAEnB,OAAI,OAAOH,GAAkB,QAAA,EAAYA,CAAAA,CAAc,UAAA,CAAW,IAAI,GAClE,IAAA,CAAK,KAAA,CAAMA,CAA+C,CAAA,CAAIC,EACvD,IAAA,EAIJ,IAAA,CAAK,aAAA,CAAc,EAAA,CACtBD,EACAC,CAAAA,CACAC,CAAAA,CACAC,CACJ,CACJ,CAUA,MAAM,IAAA,EAAsB,CACxB,GAAI,KAAK,SAAA,GAAc,SAAA,CAAW,CAC9B,OAAA,CAAQ,IAAA,CAAK,6CAA6C,CAAA,CAC1D,MACJ,CAEA,IAAA,CAAK,GAAA,CAAI,oBAAe,CAAA,CAExB,GAAI,CAEI,IAAA,CAAK,MAAA,CAAO,IAAA,EAChB,MAAM,IAAA,CAAK,SAAA,CAAU,IAAA,CAAK,MAAA,CAAO,IAAK,CAAA,CAGlC,IAAA,CAAK,KAAA,CAAM,MAAA,GACX,KAAK,GAAA,CAAI,4BAAuB,CAAA,CAChC,MAAM,KAAK,KAAA,CAAM,MAAA,EAAO,CAAA,CAG5B,IAAA,CAAK,IAAI,4BAAuB,EACpC,CAAA,MAASC,CAAAA,CAAK,CACV,MAAA,OAAA,CAAQ,KAAA,CAAM,8BAAA,CAAgCA,CAAG,CAAA,CAC3CA,CACV,CACJ,CAMA,MAAM,KAAA,CAAMC,CAAAA,CAAoD,CAC5D,GAAI,KAAK,SAAA,GAAc,SAAA,CAAW,CAC9B,OAAA,CAAQ,KAAK,qDAAqD,CAAA,CAClE,MACJ,CAEA,KAAK,GAAA,CAAI,qBAAgB,CAAA,CAEzB,GAAI,CAEA,IAAA,CAAK,KAAA,CAAMA,CAAa,CAAA,CACxB,KAAK,GAAA,CAAI,uBAAkB,CAAA,CAGvB,IAAA,CAAK,KAAA,GACJ,UAAA,CAAmB,kBAAA,CAAqB,IAAA,CACzC,KAAK,GAAA,CAAI,yCAAoC,CAAA,CAAA,CAI7C,IAAA,CAAK,MAAM,OAAA,GACX,IAAA,CAAK,GAAA,CAAI,6BAAwB,EACjC,MAAM,IAAA,CAAK,KAAA,CAAM,OAAA,IAGrB,IAAA,CAAK,SAAA,CAAY,OAAA,CACjB,IAAA,CAAK,IAAI,6BAAwB,CAAA,CACjC,IAAA,CAAK,GAAA,CAAI,sBAAiB,EAC9B,CAAA,MAASD,CAAAA,CAAK,CACV,cAAQ,KAAA,CAAM,+BAAA,CAAiCA,CAAG,CAAA,CAC5CA,CACV,CACJ,CAKA,MAAM,SAAyB,CAC3B,GAAI,IAAA,CAAK,SAAA,GAAc,YAAa,CAChC,OAAA,CAAQ,IAAA,CAAK,mCAAmC,EAChD,MACJ,CAEA,IAAA,CAAK,SAAA,CAAY,aACjB,IAAA,CAAK,GAAA,CAAI,uBAAkB,CAAA,CAE3B,GAAI,CAEI,IAAA,CAAK,KAAA,CAAM,SAAA,GACX,KAAK,GAAA,CAAI,+BAA0B,CAAA,CACnC,MAAM,KAAK,KAAA,CAAM,SAAA,EAAU,CAAA,CAI/B,IAAA,CAAK,aAAA,CAAc,OAAA,EAAQ,CAC3B,IAAA,CAAK,cAAc,OAAA,EAAQ,CAE3B,IAAA,CAAK,SAAA,CAAY,YACjB,IAAA,CAAK,GAAA,CAAI,+BAA0B,EACvC,OAASA,CAAAA,CAAK,CACV,MAAA,OAAA,CAAQ,KAAA,CAAM,kCAAmCA,CAAG,CAAA,CAC9CA,CACV,CACJ,CA8BC,MAAM,SAAA,CAAUb,CAAAA,CAAoB,CACjC,KAAK,GAAA,CAAI,oBAAoB,CAAA,CAE7B,GAAI,CACA,MAAMe,cAAAA,CAAU,CACZ,eAAA,CAAiBf,CAAAA,CAAO,eAAA,CACxB,kBAAA,CAAoBA,CAAAA,CAAO,mBAC3B,QAAA,CAAUA,CAAAA,CAAO,QAAA,CACjB,aAAA,CAAeA,EAAO,aAAA,EAAiB,MAC3C,CAAC,CAAA,CAED,KAAK,GAAA,CAAI,CAAA,kBAAA,EAAgBA,CAAAA,CAAO,kBAAA,CAAoB,KAAK,IAAI,CAAC,CAAA,CAAE,EACpE,OAASa,CAAAA,CAAK,CACV,MAAA,IAAA,CAAK,GAAA,CAAI,uBAA0BA,CAAAA,CAAc,OAAO,CAAA,CAClDA,CACV,CACJ,CAKA,QAAA,CAASR,CAAAA,CAAoB,CACzB,IAAA,CAAK,MAAA,CAAO,IAAA,CAAKA,CAAI,EACzB,CAMA,KAAA,CAAMW,CAAAA,CAAsC,CACxC,IAAMC,CAAAA,CAAY,OAAOD,CAAAA,EAAa,QAAA,CAChC,SAAS,aAAA,CAAcA,CAAQ,CAAA,CAC/BA,CAAAA,CAEN,GAAI,CAACC,CAAAA,CAAW,CACZ,OAAA,CAAQ,KAAK,yCAAA,CAA2CD,CAAQ,CAAA,CAChE,MACJ,CAGAE,cAAAA,CAAO,IAAM,CACT,IAAMC,EAAc,IAAA,CAAK,iBAAA,EAAkB,CACrCC,CAAAA,CAAY,IAAA,CAAK,eAAA,CAAgBD,CAAW,CAAA,EAC3C,KAAK,MAAA,CAAO,iBAAA,EACZ,IAAA,CAKP,GAFAF,EAAU,SAAA,CAAY,EAAA,CAElBG,CAAAA,CAAW,CACX,IAAMC,CAAAA,CAAOD,CAAAA,EAAU,CACnBC,CAAAA,YAAgB,MAChBJ,CAAAA,CAAU,WAAA,CAAYI,CAAI,EAElC,MACIJ,CAAAA,CAAU,SAAA,CAAY,0CAAA,CAG1B,IAAA,CAAK,IAAI,CAAA,yBAAA,EAAuBE,CAAW,CAAA,CAAE,EACjD,CAAC,CAAA,CAED,IAAA,CAAK,GAAA,CAAI,+BAA0B,EACvC,CAKA,cAAA,EAAiB,CACb,OAAO,IAAA,CAAK,iBAChB,CAKA,iBAAA,CAAkBd,EAAc,CAC5B,OAAQ,CAAA,EAAkB,CACtB,EAAE,cAAA,EAAe,CACjB,IAAA,CAAK,QAAA,CAASA,CAAI,EACtB,CACJ,CAKA,SAAA,EAAY,CACR,OAAO,IAAA,CAAK,MAChB,CAUA,IAAIiB,CAAAA,CAAqBC,CAAAA,CAAeZ,CAAAA,CAA8B,CAClE,KAAK,aAAA,CAAc,GAAA,CAAIW,CAAAA,CAAQC,CAAAA,CAAOZ,CAAO,EACjD,CAKA,gBAAA,EAAkC,CAC9B,OAAO,IAAA,CAAK,aAChB,CAUA,aAAc,CACV,OAAO,IAAA,CAAK,aAAA,CAAc,aAC9B,CAKA,gBAAA,EAAkC,CAC9B,OAAO,IAAA,CAAK,aAChB,CAUA,OAAA,EAAU,CACN,OAAOa,YAAAA,EACX,CAKA,eAAeC,CAAAA,CAAaC,CAAAA,CAAuB,CAC/C,IAAMC,EAAOH,YAAAA,EAAQ,CACrB,OAAKG,CAAAA,CAIEA,CAAAA,CAAK,CAAA,CAAEF,CAAG,CAAA,EAAKC,GAAgBD,CAAAA,EAHlC,OAAA,CAAQ,IAAA,CAAK,mEAAmE,EACzEC,CAAAA,EAAgBD,CAAAA,CAG/B,CAUA,QAAA,EAAW,CACP,OAAO,IAAA,CAAK,SAChB,CAKA,SAAmB,CACf,OAAO,IAAA,CAAK,SAAA,GAAc,OAC9B,CAKQ,GAAA,CAAIG,CAAAA,CAAuB,CAC3B,KAAK,KAAA,EACL,OAAA,CAAQ,GAAA,CAAI,CAAA,gBAAA,EAAmBA,CAAO,CAAA,CAAE,EAEhD,CAIR,EAWO,SAASC,CAAAA,EAAiB,CAC7B,IAAMF,EAAOH,YAAAA,EAAQ,CACrB,OAAO,CACH,eAAgB,CAACC,CAAAA,CAAaC,CAAAA,GACrBC,CAAAA,CAGEA,EAAK,CAAA,CAAEF,CAAG,CAAA,EAAKC,CAAAA,EAAgBD,EAF3BC,CAAAA,EAAgBD,CAAAA,CAI/B,CAAA,CAAG,CAACA,EAAaC,CAAAA,GACRC,CAAAA,CAGEA,CAAAA,CAAK,CAAA,CAAEF,CAAG,CAAA,EAAKC,CAAAA,EAAgBD,CAAAA,CAF3BC,CAAAA,EAAgBD,CAInC,CACJ","file":"index.cjs","sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\r\n// src/index.ts\r\n//\r\n// Made with ❤️ by Maysara.\r\n\r\n\r\n// ╔════════════════════════════════════════ PACK ════════════════════════════════════════╗\r\n\r\n import { signal, effect } from '@minejs/signals';\r\n import { I18nConfig, setupAuto, getI18n } from '@minejs/i18n';\r\n import { EventsManager, Router, WindowManager, createRouter } from '@minejs/browser';\r\n import * as types from './types';\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝\r\n\r\n\r\n\r\n// ╔════════════════════════════════════════ CORE ════════════════════════════════════════╗\r\n\r\n export class ClientManager {\r\n\r\n // ┌──────────────────────────────── INIT ──────────────────────────────┐\r\n\r\n private router : Router;\r\n private eventsManager : EventsManager;\r\n private windowManager : WindowManager;\r\n private lifecycle : 'booting' | 'ready' | 'destroying' | 'destroyed' = 'booting';\r\n private config : types.ClientManagerConfig;\r\n private hooks : types.ClientManagerHooks = {};\r\n private debug : boolean;\r\n private routeComponents : Record<string, types.RouteComponent> = {};\r\n private currentPathSignal = signal<string>(window.location.pathname ?? '/');\r\n\r\n constructor(config: types.ClientManagerConfig) {\r\n this.config = config;\r\n this.debug = config.debug ?? false;\r\n\r\n this.log('[INIT] Creating ClientManager');\r\n\r\n // Store route components provided by user\r\n this.routeComponents = config.routes;\r\n\r\n // Initialize managers from @minejs/browser\r\n this.eventsManager = new EventsManager();\r\n this.windowManager = new WindowManager();\r\n\r\n // Initialize router with user-provided routes\r\n const routesArray = Object.entries(config.routes).map(([path, component]) => ({\r\n path,\r\n component\r\n }));\r\n\r\n this.router = createRouter({\r\n routes: routesArray,\r\n notFoundComponent: config.notFoundComponent,\r\n });\r\n\r\n // Connect router changes to signal for automatic re-rendering\r\n this.router.afterEach((to) => {\r\n this.currentPathSignal.set(to.path);\r\n });\r\n\r\n this.log('[INIT] ClientManager created');\r\n }\r\n\r\n // └────────────────────────────────────────────────────────────────────┘\r\n\r\n\r\n // ┌──────────────────────────────── ──── ──────────────────────────────┐\r\n\r\n /**\r\n * Setup lifecycle hooks OR bind events\r\n * Overloaded: on(event: 'onBoot'|'onReady'|'onDestroy', callback) - lifecycle\r\n * on(target, event, handler) - event binding\r\n */\r\n on(event: keyof types.ClientManagerHooks, callback: any): this;\r\n on<K extends keyof HTMLElementEventMap>(\r\n target: EventTarget,\r\n event: K | string,\r\n handler: EventListener,\r\n options?: AddEventListenerOptions\r\n ): () => void;\r\n on(\r\n eventOrTarget: keyof types.ClientManagerHooks | EventTarget,\r\n callbackOrEvent?: any,\r\n handler?: EventListener,\r\n options?: AddEventListenerOptions\r\n ): this | (() => void) {\r\n // Check if this is a lifecycle hook call\r\n if (typeof eventOrTarget === 'string' && eventOrTarget.startsWith('on')) {\r\n this.hooks[eventOrTarget as keyof types.ClientManagerHooks] = callbackOrEvent;\r\n return this;\r\n }\r\n\r\n // Otherwise it's an event binding\r\n return this.eventsManager.on(\r\n eventOrTarget as EventTarget,\r\n callbackOrEvent as string,\r\n handler as EventListener,\r\n options\r\n );\r\n }\r\n\r\n // └────────────────────────────────────────────────────────────────────┘\r\n\r\n\r\n // ┌──────────────────────────────── ──── ──────────────────────────────┐\r\n\r\n /**\r\n * Bootstrap the app - Phase 1: BOOT\r\n */\r\n async boot(): Promise<void> {\r\n if (this.lifecycle !== 'booting') {\r\n console.warn('[ClientManager] Already booted or destroyed');\r\n return;\r\n }\r\n\r\n this.log('⚡ Phase: BOOT');\r\n\r\n try {\r\n // Setup i18n\r\n if (this.config.i18n)\r\n await this.setupI18n(this.config.i18n!);\r\n\r\n // Call user onBoot hook\r\n if (this.hooks.onBoot) {\r\n this.log('→ Calling onBoot hook');\r\n await this.hooks.onBoot();\r\n }\r\n\r\n this.log('✓ BOOT phase complete');\r\n } catch (err) {\r\n console.error('[ClientManager] Boot failed:', err);\r\n throw err;\r\n }\r\n }\r\n\r\n /**\r\n * Ready the app - Phase 2: READY\r\n * Mount to DOM and make everything live\r\n */\r\n async ready(mountSelector: string | HTMLElement): Promise<void> {\r\n if (this.lifecycle !== 'booting') {\r\n console.warn('[ClientManager] Cannot ready - not in booting phase');\r\n return;\r\n }\r\n\r\n this.log('⚡ Phase: READY');\r\n\r\n try {\r\n // Mount router\r\n this.mount(mountSelector);\r\n this.log('→ Router mounted');\r\n\r\n // Setup global access for debugging\r\n if (this.debug) {\r\n (globalThis as any).__CLIENT_MANAGER__ = this;\r\n this.log('→ ClientManager available globally');\r\n }\r\n\r\n // Call user onReady hook\r\n if (this.hooks.onReady) {\r\n this.log('→ Calling onReady hook');\r\n await this.hooks.onReady();\r\n }\r\n\r\n this.lifecycle = 'ready';\r\n this.log('✓ READY phase complete');\r\n this.log('✓ App is ready!');\r\n } catch (err) {\r\n console.error('[ClientManager] Ready failed:', err);\r\n throw err;\r\n }\r\n }\r\n\r\n /**\r\n * Shutdown the app - Phase 3: DESTROY\r\n */\r\n async destroy(): Promise<void> {\r\n if (this.lifecycle === 'destroyed') {\r\n console.warn('[ClientManager] Already destroyed');\r\n return;\r\n }\r\n\r\n this.lifecycle = 'destroying';\r\n this.log('⚡ Phase: DESTROY');\r\n\r\n try {\r\n // Call user onDestroy hook\r\n if (this.hooks.onDestroy) {\r\n this.log('→ Calling onDestroy hook');\r\n await this.hooks.onDestroy();\r\n }\r\n\r\n // Cleanup managers\r\n this.eventsManager.destroy();\r\n this.windowManager.destroy();\r\n\r\n this.lifecycle = 'destroyed';\r\n this.log('✓ DESTROY phase complete');\r\n } catch (err) {\r\n console.error('[ClientManager] Destroy failed:', err);\r\n throw err;\r\n }\r\n }\r\n\r\n // └────────────────────────────────────────────────────────────────────┘\r\n\r\n\r\n // ┌──────────────────────────────── ──── ──────────────────────────────┐\r\n\r\n /**\r\n * Initializes internationalization (i18n) support\r\n *\r\n * Loads language files and configures the i18n system based on:\r\n * - Default language\r\n * - Supported languages\r\n * - Base path for translation files\r\n * - File extension (json, cjson, etc.)\r\n *\r\n * @param {AppConfig} config - Application configuration with i18n settings\r\n * @param {Logger} logger - Logger instance for logging setup progress\r\n * @returns {Promise<void>}\r\n * @throws {Error} If i18n setup or language file loading fails\r\n *\r\n * @example\r\n * await setupI18n({\r\n * i18n: {\r\n * defaultLanguage: 'en',\r\n * supportedLanguages: ['en', 'ar'],\r\n * basePath: './src/i18n'\r\n * }\r\n * }, logger);\r\n */\r\n async setupI18n(config: I18nConfig) {\r\n this.log('Setting up i18n...');\r\n\r\n try {\r\n await setupAuto({\r\n defaultLanguage: config.defaultLanguage,\r\n supportedLanguages: config.supportedLanguages,\r\n basePath: config.basePath!,\r\n fileExtension: config.fileExtension || 'json'\r\n });\r\n\r\n this.log(`i18n ready → ${config.supportedLanguages!.join(', ')}`);\r\n } catch (err) {\r\n this.log('Failed to setup i18n' + (err as Error).message);\r\n throw err;\r\n }\r\n }\r\n\r\n /**\r\n * Navigate to path\r\n */\r\n navigate(path: string): void {\r\n this.router.push(path);\r\n }\r\n\r\n /**\r\n * Mount router to DOM element and setup reactive routing\r\n * Automatically re-renders when route changes\r\n */\r\n mount(selector: string | HTMLElement): void {\r\n const container = typeof selector === 'string'\r\n ? document.querySelector(selector)\r\n : selector;\r\n\r\n if (!container) {\r\n console.warn('[ClientManager] Mount target not found:', selector);\r\n return;\r\n }\r\n\r\n // Setup reactive routing effect - re-renders when currentPathSignal changes\r\n effect(() => {\r\n const currentPath = this.currentPathSignal();\r\n const Component = this.routeComponents[currentPath]\r\n || this.config.notFoundComponent\r\n || null;\r\n\r\n // Clear and mount new component\r\n container.innerHTML = '';\r\n\r\n if (Component) {\r\n const node = Component();\r\n if (node instanceof Node) {\r\n container.appendChild(node);\r\n }\r\n } else {\r\n container.innerHTML = '<p>No component found for this route</p>';\r\n }\r\n\r\n this.log(`→ Route changed to: ${currentPath}`);\r\n });\r\n\r\n this.log('→ Routing setup complete');\r\n }\r\n\r\n /**\r\n * Get current path signal for reactivity\r\n */\r\n getCurrentPath() {\r\n return this.currentPathSignal;\r\n }\r\n\r\n /**\r\n * Create navigation link handler\r\n */\r\n createLinkHandler(path: string) {\r\n return (e: MouseEvent) => {\r\n e.preventDefault();\r\n this.navigate(path);\r\n };\r\n }\r\n\r\n /**\r\n * Get underlying router for advanced usage\r\n */\r\n getRouter() {\r\n return this.router;\r\n }\r\n\r\n // └────────────────────────────────────────────────────────────────────┘\r\n\r\n\r\n // ┌──────────────────────────────── ──── ──────────────────────────────┐\r\n\r\n /**\r\n * Unbind event\r\n */\r\n off(target: EventTarget, event: string, handler: EventListener): void {\r\n this.eventsManager.off(target, event, handler);\r\n }\r\n\r\n /**\r\n * Get events manager directly\r\n */\r\n getEventsManager(): EventsManager {\r\n return this.eventsManager;\r\n }\r\n\r\n // └────────────────────────────────────────────────────────────────────┘\r\n\r\n\r\n // ┌──────────────────────────────── ──── ──────────────────────────────┐\r\n\r\n /**\r\n * Get viewport info as reactive signal\r\n */\r\n getViewport() {\r\n return this.windowManager.getViewport();\r\n }\r\n\r\n /**\r\n * Get window manager directly\r\n */\r\n getWindowManager(): WindowManager {\r\n return this.windowManager;\r\n }\r\n\r\n // └────────────────────────────────────────────────────────────────────┘\r\n\r\n\r\n // ┌──────────────────────────────── ──── ──────────────────────────────┐\r\n\r\n /**\r\n * Get i18n instance for translations\r\n */\r\n getI18n() {\r\n return getI18n();\r\n }\r\n\r\n /**\r\n * Get translation string\r\n */\r\n getTranslation(key: string, defaultValue?: string) {\r\n const i18n = getI18n();\r\n if (!i18n) {\r\n console.warn('[ClientManager] i18n not initialized. Using default value or key.');\r\n return defaultValue ?? key;\r\n }\r\n return i18n.t(key) ?? defaultValue ?? key;\r\n }\r\n\r\n // └────────────────────────────────────────────────────────────────────┘\r\n\r\n\r\n // ┌──────────────────────────────── ──── ──────────────────────────────┐\r\n\r\n /**\r\n * Get lifecycle phase\r\n */\r\n getPhase() {\r\n return this.lifecycle;\r\n }\r\n\r\n /**\r\n * Check if ready\r\n */\r\n isReady(): boolean {\r\n return this.lifecycle === 'ready';\r\n }\r\n\r\n /**\r\n * Internal logging\r\n */\r\n private log(message: string): void {\r\n if (this.debug) {\r\n console.log(`[ClientManager] ${message}`);\r\n }\r\n }\r\n\r\n // └────────────────────────────────────────────────────────────────────┘\r\n\r\n }\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝\r\n\r\n\r\n// ╔════════════════════════════════════════ UTILS ═════════════════════════════════════════╗\r\n\r\n /**\r\n * Helper to safely get translation\r\n * Use this in components to access translations\r\n */\r\n export function useTranslation() {\r\n const i18n = getI18n();\r\n return {\r\n getTranslation: (key: string, defaultValue?: string) => {\r\n if (!i18n) {\r\n return defaultValue ?? key;\r\n }\r\n return i18n.t(key) ?? defaultValue ?? key;\r\n },\r\n t: (key: string, defaultValue?: string) => {\r\n if (!i18n) {\r\n return defaultValue ?? key;\r\n }\r\n return i18n.t(key) ?? defaultValue ?? key;\r\n }\r\n };\r\n }\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝\r\n"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import * as _minejs_i18n from '@minejs/i18n';
|
|
2
|
+
import { I18nConfig } from '@minejs/i18n';
|
|
1
3
|
import * as _minejs_browser from '@minejs/browser';
|
|
2
4
|
import { Router, EventsManager, WindowManager } from '@minejs/browser';
|
|
3
5
|
import * as _minejs_signals from '@minejs/signals';
|
|
@@ -8,6 +10,7 @@ interface ClientManagerConfig {
|
|
|
8
10
|
routes: Record<string, RouteComponent>;
|
|
9
11
|
notFoundComponent?: RouteComponent;
|
|
10
12
|
debug?: boolean;
|
|
13
|
+
i18n?: I18nConfig;
|
|
11
14
|
}
|
|
12
15
|
interface ClientManagerHooks {
|
|
13
16
|
onBoot?: () => void | Promise<void>;
|
|
@@ -46,6 +49,30 @@ declare class ClientManager {
|
|
|
46
49
|
* Shutdown the app - Phase 3: DESTROY
|
|
47
50
|
*/
|
|
48
51
|
destroy(): Promise<void>;
|
|
52
|
+
/**
|
|
53
|
+
* Initializes internationalization (i18n) support
|
|
54
|
+
*
|
|
55
|
+
* Loads language files and configures the i18n system based on:
|
|
56
|
+
* - Default language
|
|
57
|
+
* - Supported languages
|
|
58
|
+
* - Base path for translation files
|
|
59
|
+
* - File extension (json, cjson, etc.)
|
|
60
|
+
*
|
|
61
|
+
* @param {AppConfig} config - Application configuration with i18n settings
|
|
62
|
+
* @param {Logger} logger - Logger instance for logging setup progress
|
|
63
|
+
* @returns {Promise<void>}
|
|
64
|
+
* @throws {Error} If i18n setup or language file loading fails
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* await setupI18n({
|
|
68
|
+
* i18n: {
|
|
69
|
+
* defaultLanguage: 'en',
|
|
70
|
+
* supportedLanguages: ['en', 'ar'],
|
|
71
|
+
* basePath: './src/i18n'
|
|
72
|
+
* }
|
|
73
|
+
* }, logger);
|
|
74
|
+
*/
|
|
75
|
+
setupI18n(config: I18nConfig): Promise<void>;
|
|
49
76
|
/**
|
|
50
77
|
* Navigate to path
|
|
51
78
|
*/
|
|
@@ -83,6 +110,14 @@ declare class ClientManager {
|
|
|
83
110
|
* Get window manager directly
|
|
84
111
|
*/
|
|
85
112
|
getWindowManager(): WindowManager;
|
|
113
|
+
/**
|
|
114
|
+
* Get i18n instance for translations
|
|
115
|
+
*/
|
|
116
|
+
getI18n(): _minejs_i18n.I18nManager;
|
|
117
|
+
/**
|
|
118
|
+
* Get translation string
|
|
119
|
+
*/
|
|
120
|
+
getTranslation(key: string, defaultValue?: string): string;
|
|
86
121
|
/**
|
|
87
122
|
* Get lifecycle phase
|
|
88
123
|
*/
|
|
@@ -96,5 +131,13 @@ declare class ClientManager {
|
|
|
96
131
|
*/
|
|
97
132
|
private log;
|
|
98
133
|
}
|
|
134
|
+
/**
|
|
135
|
+
* Helper to safely get translation
|
|
136
|
+
* Use this in components to access translations
|
|
137
|
+
*/
|
|
138
|
+
declare function useTranslation(): {
|
|
139
|
+
getTranslation: (key: string, defaultValue?: string) => string;
|
|
140
|
+
t: (key: string, defaultValue?: string) => string;
|
|
141
|
+
};
|
|
99
142
|
|
|
100
|
-
export { ClientManager };
|
|
143
|
+
export { ClientManager, useTranslation };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import * as _minejs_i18n from '@minejs/i18n';
|
|
2
|
+
import { I18nConfig } from '@minejs/i18n';
|
|
1
3
|
import * as _minejs_browser from '@minejs/browser';
|
|
2
4
|
import { Router, EventsManager, WindowManager } from '@minejs/browser';
|
|
3
5
|
import * as _minejs_signals from '@minejs/signals';
|
|
@@ -8,6 +10,7 @@ interface ClientManagerConfig {
|
|
|
8
10
|
routes: Record<string, RouteComponent>;
|
|
9
11
|
notFoundComponent?: RouteComponent;
|
|
10
12
|
debug?: boolean;
|
|
13
|
+
i18n?: I18nConfig;
|
|
11
14
|
}
|
|
12
15
|
interface ClientManagerHooks {
|
|
13
16
|
onBoot?: () => void | Promise<void>;
|
|
@@ -46,6 +49,30 @@ declare class ClientManager {
|
|
|
46
49
|
* Shutdown the app - Phase 3: DESTROY
|
|
47
50
|
*/
|
|
48
51
|
destroy(): Promise<void>;
|
|
52
|
+
/**
|
|
53
|
+
* Initializes internationalization (i18n) support
|
|
54
|
+
*
|
|
55
|
+
* Loads language files and configures the i18n system based on:
|
|
56
|
+
* - Default language
|
|
57
|
+
* - Supported languages
|
|
58
|
+
* - Base path for translation files
|
|
59
|
+
* - File extension (json, cjson, etc.)
|
|
60
|
+
*
|
|
61
|
+
* @param {AppConfig} config - Application configuration with i18n settings
|
|
62
|
+
* @param {Logger} logger - Logger instance for logging setup progress
|
|
63
|
+
* @returns {Promise<void>}
|
|
64
|
+
* @throws {Error} If i18n setup or language file loading fails
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* await setupI18n({
|
|
68
|
+
* i18n: {
|
|
69
|
+
* defaultLanguage: 'en',
|
|
70
|
+
* supportedLanguages: ['en', 'ar'],
|
|
71
|
+
* basePath: './src/i18n'
|
|
72
|
+
* }
|
|
73
|
+
* }, logger);
|
|
74
|
+
*/
|
|
75
|
+
setupI18n(config: I18nConfig): Promise<void>;
|
|
49
76
|
/**
|
|
50
77
|
* Navigate to path
|
|
51
78
|
*/
|
|
@@ -83,6 +110,14 @@ declare class ClientManager {
|
|
|
83
110
|
* Get window manager directly
|
|
84
111
|
*/
|
|
85
112
|
getWindowManager(): WindowManager;
|
|
113
|
+
/**
|
|
114
|
+
* Get i18n instance for translations
|
|
115
|
+
*/
|
|
116
|
+
getI18n(): _minejs_i18n.I18nManager;
|
|
117
|
+
/**
|
|
118
|
+
* Get translation string
|
|
119
|
+
*/
|
|
120
|
+
getTranslation(key: string, defaultValue?: string): string;
|
|
86
121
|
/**
|
|
87
122
|
* Get lifecycle phase
|
|
88
123
|
*/
|
|
@@ -96,5 +131,13 @@ declare class ClientManager {
|
|
|
96
131
|
*/
|
|
97
132
|
private log;
|
|
98
133
|
}
|
|
134
|
+
/**
|
|
135
|
+
* Helper to safely get translation
|
|
136
|
+
* Use this in components to access translations
|
|
137
|
+
*/
|
|
138
|
+
declare function useTranslation(): {
|
|
139
|
+
getTranslation: (key: string, defaultValue?: string) => string;
|
|
140
|
+
t: (key: string, defaultValue?: string) => string;
|
|
141
|
+
};
|
|
99
142
|
|
|
100
|
-
export { ClientManager };
|
|
143
|
+
export { ClientManager, useTranslation };
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {signal,effect}from'@minejs/signals';import {EventsManager,WindowManager,createRouter}from'@minejs/browser';var
|
|
1
|
+
import {signal,effect}from'@minejs/signals';import {setupAuto,getI18n}from'@minejs/i18n';import {EventsManager,WindowManager,createRouter}from'@minejs/browser';var a=class{constructor(t){this.lifecycle="booting";this.hooks={};this.routeComponents={};this.currentPathSignal=signal(window.location.pathname??"/");this.config=t,this.debug=t.debug??false,this.log("[INIT] Creating ClientManager"),this.routeComponents=t.routes,this.eventsManager=new EventsManager,this.windowManager=new WindowManager;let e=Object.entries(t.routes).map(([n,o])=>({path:n,component:o}));this.router=createRouter({routes:e,notFoundComponent:t.notFoundComponent}),this.router.afterEach(n=>{this.currentPathSignal.set(n.path);}),this.log("[INIT] ClientManager created");}on(t,e,n,o){return typeof t=="string"&&t.startsWith("on")?(this.hooks[t]=e,this):this.eventsManager.on(t,e,n,o)}async boot(){if(this.lifecycle!=="booting"){console.warn("[ClientManager] Already booted or destroyed");return}this.log("\u26A1 Phase: BOOT");try{this.config.i18n&&await this.setupI18n(this.config.i18n),this.hooks.onBoot&&(this.log("\u2192 Calling onBoot hook"),await this.hooks.onBoot()),this.log("\u2713 BOOT phase complete");}catch(t){throw console.error("[ClientManager] Boot failed:",t),t}}async ready(t){if(this.lifecycle!=="booting"){console.warn("[ClientManager] Cannot ready - not in booting phase");return}this.log("\u26A1 Phase: READY");try{this.mount(t),this.log("\u2192 Router mounted"),this.debug&&(globalThis.__CLIENT_MANAGER__=this,this.log("\u2192 ClientManager available globally")),this.hooks.onReady&&(this.log("\u2192 Calling onReady hook"),await this.hooks.onReady()),this.lifecycle="ready",this.log("\u2713 READY phase complete"),this.log("\u2713 App is ready!");}catch(e){throw console.error("[ClientManager] Ready failed:",e),e}}async destroy(){if(this.lifecycle==="destroyed"){console.warn("[ClientManager] Already destroyed");return}this.lifecycle="destroying",this.log("\u26A1 Phase: DESTROY");try{this.hooks.onDestroy&&(this.log("\u2192 Calling onDestroy hook"),await this.hooks.onDestroy()),this.eventsManager.destroy(),this.windowManager.destroy(),this.lifecycle="destroyed",this.log("\u2713 DESTROY phase complete");}catch(t){throw console.error("[ClientManager] Destroy failed:",t),t}}async setupI18n(t){this.log("Setting up i18n...");try{await setupAuto({defaultLanguage:t.defaultLanguage,supportedLanguages:t.supportedLanguages,basePath:t.basePath,fileExtension:t.fileExtension||"json"}),this.log(`i18n ready \u2192 ${t.supportedLanguages.join(", ")}`);}catch(e){throw this.log("Failed to setup i18n"+e.message),e}}navigate(t){this.router.push(t);}mount(t){let e=typeof t=="string"?document.querySelector(t):t;if(!e){console.warn("[ClientManager] Mount target not found:",t);return}effect(()=>{let n=this.currentPathSignal(),o=this.routeComponents[n]||this.config.notFoundComponent||null;if(e.innerHTML="",o){let s=o();s instanceof Node&&e.appendChild(s);}else e.innerHTML="<p>No component found for this route</p>";this.log(`\u2192 Route changed to: ${n}`);}),this.log("\u2192 Routing setup complete");}getCurrentPath(){return this.currentPathSignal}createLinkHandler(t){return e=>{e.preventDefault(),this.navigate(t);}}getRouter(){return this.router}off(t,e,n){this.eventsManager.off(t,e,n);}getEventsManager(){return this.eventsManager}getViewport(){return this.windowManager.getViewport()}getWindowManager(){return this.windowManager}getI18n(){return getI18n()}getTranslation(t,e){let n=getI18n();return n?n.t(t)??e??t:(console.warn("[ClientManager] i18n not initialized. Using default value or key."),e??t)}getPhase(){return this.lifecycle}isReady(){return this.lifecycle==="ready"}log(t){this.debug&&console.log(`[ClientManager] ${t}`);}};function C(){let r=getI18n();return {getTranslation:(t,e)=>r?r.t(t)??e??t:e??t,t:(t,e)=>r?r.t(t)??e??t:e??t}}export{a as ClientManager,C as useTranslation};//# sourceMappingURL=index.js.map
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"names":["ClientManager","config","signal","EventsManager","WindowManager","routesArray","path","component","createRouter","to","eventOrTarget","callbackOrEvent","handler","options","err","mountSelector","selector","container","effect","currentPath","Component","node","e","target","event","message"],"mappings":"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","file":"index.js","sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\r\n// src/index.ts\r\n//\r\n// Made with ❤️ by Maysara.\r\n\r\n\r\n// ╔════════════════════════════════════════ PACK ════════════════════════════════════════╗\r\n\r\n import { signal, effect } from '@minejs/signals';\r\n import { EventsManager, Router, WindowManager, createRouter } from '@minejs/browser';\r\n import * as types from './types';\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝\r\n\r\n\r\n\r\n// ╔════════════════════════════════════════ CORE ════════════════════════════════════════╗\r\n\r\n export class ClientManager {\r\n\r\n // ┌──────────────────────────────── INIT ──────────────────────────────┐\r\n\r\n private router : Router;\r\n private eventsManager : EventsManager;\r\n private windowManager : WindowManager;\r\n private lifecycle : 'booting' | 'ready' | 'destroying' | 'destroyed' = 'booting';\r\n private config : types.ClientManagerConfig;\r\n private hooks : types.ClientManagerHooks = {};\r\n private debug : boolean;\r\n private routeComponents : Record<string, types.RouteComponent> = {};\r\n private currentPathSignal = signal<string>(window.location.pathname ?? '/');\r\n\r\n constructor(config: types.ClientManagerConfig) {\r\n this.config = config;\r\n this.debug = config.debug ?? false;\r\n\r\n this.log('[INIT] Creating ClientManager');\r\n\r\n // Store route components provided by user\r\n this.routeComponents = config.routes;\r\n\r\n // Initialize managers from @minejs/browser\r\n this.eventsManager = new EventsManager();\r\n this.windowManager = new WindowManager();\r\n\r\n // Initialize router with user-provided routes\r\n const routesArray = Object.entries(config.routes).map(([path, component]) => ({\r\n path,\r\n component\r\n }));\r\n\r\n this.router = createRouter({\r\n routes: routesArray,\r\n notFoundComponent: config.notFoundComponent,\r\n });\r\n\r\n // Connect router changes to signal for automatic re-rendering\r\n this.router.afterEach((to) => {\r\n this.currentPathSignal.set(to.path);\r\n });\r\n\r\n this.log('[INIT] ClientManager created');\r\n }\r\n\r\n // └────────────────────────────────────────────────────────────────────┘\r\n\r\n\r\n // ┌──────────────────────────────── ──── ──────────────────────────────┐\r\n\r\n /**\r\n * Setup lifecycle hooks OR bind events\r\n * Overloaded: on(event: 'onBoot'|'onReady'|'onDestroy', callback) - lifecycle\r\n * on(target, event, handler) - event binding\r\n */\r\n on(event: keyof types.ClientManagerHooks, callback: any): this;\r\n on<K extends keyof HTMLElementEventMap>(\r\n target: EventTarget,\r\n event: K | string,\r\n handler: EventListener,\r\n options?: AddEventListenerOptions\r\n ): () => void;\r\n on(\r\n eventOrTarget: keyof types.ClientManagerHooks | EventTarget,\r\n callbackOrEvent?: any,\r\n handler?: EventListener,\r\n options?: AddEventListenerOptions\r\n ): this | (() => void) {\r\n // Check if this is a lifecycle hook call\r\n if (typeof eventOrTarget === 'string' && eventOrTarget.startsWith('on')) {\r\n this.hooks[eventOrTarget as keyof types.ClientManagerHooks] = callbackOrEvent;\r\n return this;\r\n }\r\n\r\n // Otherwise it's an event binding\r\n return this.eventsManager.on(\r\n eventOrTarget as EventTarget,\r\n callbackOrEvent as string,\r\n handler as EventListener,\r\n options\r\n );\r\n }\r\n\r\n // └────────────────────────────────────────────────────────────────────┘\r\n\r\n\r\n // ┌──────────────────────────────── ──── ──────────────────────────────┐\r\n\r\n /**\r\n * Bootstrap the app - Phase 1: BOOT\r\n */\r\n async boot(): Promise<void> {\r\n if (this.lifecycle !== 'booting') {\r\n console.warn('[ClientManager] Already booted or destroyed');\r\n return;\r\n }\r\n\r\n this.log('⚡ Phase: BOOT');\r\n\r\n try {\r\n // Call user onBoot hook\r\n if (this.hooks.onBoot) {\r\n this.log('→ Calling onBoot hook');\r\n await this.hooks.onBoot();\r\n }\r\n\r\n this.log('✓ BOOT phase complete');\r\n } catch (err) {\r\n console.error('[ClientManager] Boot failed:', err);\r\n throw err;\r\n }\r\n }\r\n\r\n /**\r\n * Ready the app - Phase 2: READY\r\n * Mount to DOM and make everything live\r\n */\r\n async ready(mountSelector: string | HTMLElement): Promise<void> {\r\n if (this.lifecycle !== 'booting') {\r\n console.warn('[ClientManager] Cannot ready - not in booting phase');\r\n return;\r\n }\r\n\r\n this.log('⚡ Phase: READY');\r\n\r\n try {\r\n // Mount router\r\n this.mount(mountSelector);\r\n this.log('→ Router mounted');\r\n\r\n // Setup global access for debugging\r\n if (this.debug) {\r\n (globalThis as any).__CLIENT_MANAGER__ = this;\r\n this.log('→ ClientManager available globally');\r\n }\r\n\r\n // Call user onReady hook\r\n if (this.hooks.onReady) {\r\n this.log('→ Calling onReady hook');\r\n await this.hooks.onReady();\r\n }\r\n\r\n this.lifecycle = 'ready';\r\n this.log('✓ READY phase complete');\r\n this.log('✓ App is ready!');\r\n } catch (err) {\r\n console.error('[ClientManager] Ready failed:', err);\r\n throw err;\r\n }\r\n }\r\n\r\n /**\r\n * Shutdown the app - Phase 3: DESTROY\r\n */\r\n async destroy(): Promise<void> {\r\n if (this.lifecycle === 'destroyed') {\r\n console.warn('[ClientManager] Already destroyed');\r\n return;\r\n }\r\n\r\n this.lifecycle = 'destroying';\r\n this.log('⚡ Phase: DESTROY');\r\n\r\n try {\r\n // Call user onDestroy hook\r\n if (this.hooks.onDestroy) {\r\n this.log('→ Calling onDestroy hook');\r\n await this.hooks.onDestroy();\r\n }\r\n\r\n // Cleanup managers\r\n this.eventsManager.destroy();\r\n this.windowManager.destroy();\r\n\r\n this.lifecycle = 'destroyed';\r\n this.log('✓ DESTROY phase complete');\r\n } catch (err) {\r\n console.error('[ClientManager] Destroy failed:', err);\r\n throw err;\r\n }\r\n }\r\n\r\n // └────────────────────────────────────────────────────────────────────┘\r\n\r\n\r\n // ┌──────────────────────────────── ──── ──────────────────────────────┐\r\n\r\n /**\r\n * Navigate to path\r\n */\r\n navigate(path: string): void {\r\n this.router.push(path);\r\n }\r\n\r\n /**\r\n * Mount router to DOM element and setup reactive routing\r\n * Automatically re-renders when route changes\r\n */\r\n mount(selector: string | HTMLElement): void {\r\n const container = typeof selector === 'string'\r\n ? document.querySelector(selector)\r\n : selector;\r\n\r\n if (!container) {\r\n console.warn('[ClientManager] Mount target not found:', selector);\r\n return;\r\n }\r\n\r\n // Setup reactive routing effect - re-renders when currentPathSignal changes\r\n effect(() => {\r\n const currentPath = this.currentPathSignal();\r\n const Component = this.routeComponents[currentPath]\r\n || this.config.notFoundComponent\r\n || null;\r\n\r\n // Clear and mount new component\r\n container.innerHTML = '';\r\n\r\n if (Component) {\r\n const node = Component();\r\n if (node instanceof Node) {\r\n container.appendChild(node);\r\n }\r\n } else {\r\n container.innerHTML = '<p>No component found for this route</p>';\r\n }\r\n\r\n this.log(`→ Route changed to: ${currentPath}`);\r\n });\r\n\r\n this.log('→ Routing setup complete');\r\n }\r\n\r\n /**\r\n * Get current path signal for reactivity\r\n */\r\n getCurrentPath() {\r\n return this.currentPathSignal;\r\n }\r\n\r\n /**\r\n * Create navigation link handler\r\n */\r\n createLinkHandler(path: string) {\r\n return (e: MouseEvent) => {\r\n e.preventDefault();\r\n this.navigate(path);\r\n };\r\n }\r\n\r\n /**\r\n * Get underlying router for advanced usage\r\n */\r\n getRouter() {\r\n return this.router;\r\n }\r\n\r\n // └────────────────────────────────────────────────────────────────────┘\r\n\r\n\r\n // ┌──────────────────────────────── ──── ──────────────────────────────┐\r\n\r\n /**\r\n * Unbind event\r\n */\r\n off(target: EventTarget, event: string, handler: EventListener): void {\r\n this.eventsManager.off(target, event, handler);\r\n }\r\n\r\n /**\r\n * Get events manager directly\r\n */\r\n getEventsManager(): EventsManager {\r\n return this.eventsManager;\r\n }\r\n\r\n // └────────────────────────────────────────────────────────────────────┘\r\n\r\n\r\n // ┌──────────────────────────────── ──── ──────────────────────────────┐\r\n\r\n /**\r\n * Get viewport info as reactive signal\r\n */\r\n getViewport() {\r\n return this.windowManager.getViewport();\r\n }\r\n\r\n /**\r\n * Get window manager directly\r\n */\r\n getWindowManager(): WindowManager {\r\n return this.windowManager;\r\n }\r\n\r\n // └────────────────────────────────────────────────────────────────────┘\r\n\r\n\r\n // ┌──────────────────────────────── ──── ──────────────────────────────┐\r\n\r\n /**\r\n * Get lifecycle phase\r\n */\r\n getPhase() {\r\n return this.lifecycle;\r\n }\r\n\r\n /**\r\n * Check if ready\r\n */\r\n isReady(): boolean {\r\n return this.lifecycle === 'ready';\r\n }\r\n\r\n /**\r\n * Internal logging\r\n */\r\n private log(message: string): void {\r\n if (this.debug) {\r\n console.log(`[ClientManager] ${message}`);\r\n }\r\n }\r\n\r\n // └────────────────────────────────────────────────────────────────────┘\r\n\r\n }\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝\r\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":["ClientManager","config","signal","EventsManager","WindowManager","routesArray","path","component","createRouter","to","eventOrTarget","callbackOrEvent","handler","options","err","mountSelector","setupAuto","selector","container","effect","currentPath","Component","node","target","event","getI18n","key","defaultValue","i18n","message","useTranslation"],"mappings":"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","file":"index.js","sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\r\n// src/index.ts\r\n//\r\n// Made with ❤️ by Maysara.\r\n\r\n\r\n// ╔════════════════════════════════════════ PACK ════════════════════════════════════════╗\r\n\r\n import { signal, effect } from '@minejs/signals';\r\n import { I18nConfig, setupAuto, getI18n } from '@minejs/i18n';\r\n import { EventsManager, Router, WindowManager, createRouter } from '@minejs/browser';\r\n import * as types from './types';\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝\r\n\r\n\r\n\r\n// ╔════════════════════════════════════════ CORE ════════════════════════════════════════╗\r\n\r\n export class ClientManager {\r\n\r\n // ┌──────────────────────────────── INIT ──────────────────────────────┐\r\n\r\n private router : Router;\r\n private eventsManager : EventsManager;\r\n private windowManager : WindowManager;\r\n private lifecycle : 'booting' | 'ready' | 'destroying' | 'destroyed' = 'booting';\r\n private config : types.ClientManagerConfig;\r\n private hooks : types.ClientManagerHooks = {};\r\n private debug : boolean;\r\n private routeComponents : Record<string, types.RouteComponent> = {};\r\n private currentPathSignal = signal<string>(window.location.pathname ?? '/');\r\n\r\n constructor(config: types.ClientManagerConfig) {\r\n this.config = config;\r\n this.debug = config.debug ?? false;\r\n\r\n this.log('[INIT] Creating ClientManager');\r\n\r\n // Store route components provided by user\r\n this.routeComponents = config.routes;\r\n\r\n // Initialize managers from @minejs/browser\r\n this.eventsManager = new EventsManager();\r\n this.windowManager = new WindowManager();\r\n\r\n // Initialize router with user-provided routes\r\n const routesArray = Object.entries(config.routes).map(([path, component]) => ({\r\n path,\r\n component\r\n }));\r\n\r\n this.router = createRouter({\r\n routes: routesArray,\r\n notFoundComponent: config.notFoundComponent,\r\n });\r\n\r\n // Connect router changes to signal for automatic re-rendering\r\n this.router.afterEach((to) => {\r\n this.currentPathSignal.set(to.path);\r\n });\r\n\r\n this.log('[INIT] ClientManager created');\r\n }\r\n\r\n // └────────────────────────────────────────────────────────────────────┘\r\n\r\n\r\n // ┌──────────────────────────────── ──── ──────────────────────────────┐\r\n\r\n /**\r\n * Setup lifecycle hooks OR bind events\r\n * Overloaded: on(event: 'onBoot'|'onReady'|'onDestroy', callback) - lifecycle\r\n * on(target, event, handler) - event binding\r\n */\r\n on(event: keyof types.ClientManagerHooks, callback: any): this;\r\n on<K extends keyof HTMLElementEventMap>(\r\n target: EventTarget,\r\n event: K | string,\r\n handler: EventListener,\r\n options?: AddEventListenerOptions\r\n ): () => void;\r\n on(\r\n eventOrTarget: keyof types.ClientManagerHooks | EventTarget,\r\n callbackOrEvent?: any,\r\n handler?: EventListener,\r\n options?: AddEventListenerOptions\r\n ): this | (() => void) {\r\n // Check if this is a lifecycle hook call\r\n if (typeof eventOrTarget === 'string' && eventOrTarget.startsWith('on')) {\r\n this.hooks[eventOrTarget as keyof types.ClientManagerHooks] = callbackOrEvent;\r\n return this;\r\n }\r\n\r\n // Otherwise it's an event binding\r\n return this.eventsManager.on(\r\n eventOrTarget as EventTarget,\r\n callbackOrEvent as string,\r\n handler as EventListener,\r\n options\r\n );\r\n }\r\n\r\n // └────────────────────────────────────────────────────────────────────┘\r\n\r\n\r\n // ┌──────────────────────────────── ──── ──────────────────────────────┐\r\n\r\n /**\r\n * Bootstrap the app - Phase 1: BOOT\r\n */\r\n async boot(): Promise<void> {\r\n if (this.lifecycle !== 'booting') {\r\n console.warn('[ClientManager] Already booted or destroyed');\r\n return;\r\n }\r\n\r\n this.log('⚡ Phase: BOOT');\r\n\r\n try {\r\n // Setup i18n\r\n if (this.config.i18n)\r\n await this.setupI18n(this.config.i18n!);\r\n\r\n // Call user onBoot hook\r\n if (this.hooks.onBoot) {\r\n this.log('→ Calling onBoot hook');\r\n await this.hooks.onBoot();\r\n }\r\n\r\n this.log('✓ BOOT phase complete');\r\n } catch (err) {\r\n console.error('[ClientManager] Boot failed:', err);\r\n throw err;\r\n }\r\n }\r\n\r\n /**\r\n * Ready the app - Phase 2: READY\r\n * Mount to DOM and make everything live\r\n */\r\n async ready(mountSelector: string | HTMLElement): Promise<void> {\r\n if (this.lifecycle !== 'booting') {\r\n console.warn('[ClientManager] Cannot ready - not in booting phase');\r\n return;\r\n }\r\n\r\n this.log('⚡ Phase: READY');\r\n\r\n try {\r\n // Mount router\r\n this.mount(mountSelector);\r\n this.log('→ Router mounted');\r\n\r\n // Setup global access for debugging\r\n if (this.debug) {\r\n (globalThis as any).__CLIENT_MANAGER__ = this;\r\n this.log('→ ClientManager available globally');\r\n }\r\n\r\n // Call user onReady hook\r\n if (this.hooks.onReady) {\r\n this.log('→ Calling onReady hook');\r\n await this.hooks.onReady();\r\n }\r\n\r\n this.lifecycle = 'ready';\r\n this.log('✓ READY phase complete');\r\n this.log('✓ App is ready!');\r\n } catch (err) {\r\n console.error('[ClientManager] Ready failed:', err);\r\n throw err;\r\n }\r\n }\r\n\r\n /**\r\n * Shutdown the app - Phase 3: DESTROY\r\n */\r\n async destroy(): Promise<void> {\r\n if (this.lifecycle === 'destroyed') {\r\n console.warn('[ClientManager] Already destroyed');\r\n return;\r\n }\r\n\r\n this.lifecycle = 'destroying';\r\n this.log('⚡ Phase: DESTROY');\r\n\r\n try {\r\n // Call user onDestroy hook\r\n if (this.hooks.onDestroy) {\r\n this.log('→ Calling onDestroy hook');\r\n await this.hooks.onDestroy();\r\n }\r\n\r\n // Cleanup managers\r\n this.eventsManager.destroy();\r\n this.windowManager.destroy();\r\n\r\n this.lifecycle = 'destroyed';\r\n this.log('✓ DESTROY phase complete');\r\n } catch (err) {\r\n console.error('[ClientManager] Destroy failed:', err);\r\n throw err;\r\n }\r\n }\r\n\r\n // └────────────────────────────────────────────────────────────────────┘\r\n\r\n\r\n // ┌──────────────────────────────── ──── ──────────────────────────────┐\r\n\r\n /**\r\n * Initializes internationalization (i18n) support\r\n *\r\n * Loads language files and configures the i18n system based on:\r\n * - Default language\r\n * - Supported languages\r\n * - Base path for translation files\r\n * - File extension (json, cjson, etc.)\r\n *\r\n * @param {AppConfig} config - Application configuration with i18n settings\r\n * @param {Logger} logger - Logger instance for logging setup progress\r\n * @returns {Promise<void>}\r\n * @throws {Error} If i18n setup or language file loading fails\r\n *\r\n * @example\r\n * await setupI18n({\r\n * i18n: {\r\n * defaultLanguage: 'en',\r\n * supportedLanguages: ['en', 'ar'],\r\n * basePath: './src/i18n'\r\n * }\r\n * }, logger);\r\n */\r\n async setupI18n(config: I18nConfig) {\r\n this.log('Setting up i18n...');\r\n\r\n try {\r\n await setupAuto({\r\n defaultLanguage: config.defaultLanguage,\r\n supportedLanguages: config.supportedLanguages,\r\n basePath: config.basePath!,\r\n fileExtension: config.fileExtension || 'json'\r\n });\r\n\r\n this.log(`i18n ready → ${config.supportedLanguages!.join(', ')}`);\r\n } catch (err) {\r\n this.log('Failed to setup i18n' + (err as Error).message);\r\n throw err;\r\n }\r\n }\r\n\r\n /**\r\n * Navigate to path\r\n */\r\n navigate(path: string): void {\r\n this.router.push(path);\r\n }\r\n\r\n /**\r\n * Mount router to DOM element and setup reactive routing\r\n * Automatically re-renders when route changes\r\n */\r\n mount(selector: string | HTMLElement): void {\r\n const container = typeof selector === 'string'\r\n ? document.querySelector(selector)\r\n : selector;\r\n\r\n if (!container) {\r\n console.warn('[ClientManager] Mount target not found:', selector);\r\n return;\r\n }\r\n\r\n // Setup reactive routing effect - re-renders when currentPathSignal changes\r\n effect(() => {\r\n const currentPath = this.currentPathSignal();\r\n const Component = this.routeComponents[currentPath]\r\n || this.config.notFoundComponent\r\n || null;\r\n\r\n // Clear and mount new component\r\n container.innerHTML = '';\r\n\r\n if (Component) {\r\n const node = Component();\r\n if (node instanceof Node) {\r\n container.appendChild(node);\r\n }\r\n } else {\r\n container.innerHTML = '<p>No component found for this route</p>';\r\n }\r\n\r\n this.log(`→ Route changed to: ${currentPath}`);\r\n });\r\n\r\n this.log('→ Routing setup complete');\r\n }\r\n\r\n /**\r\n * Get current path signal for reactivity\r\n */\r\n getCurrentPath() {\r\n return this.currentPathSignal;\r\n }\r\n\r\n /**\r\n * Create navigation link handler\r\n */\r\n createLinkHandler(path: string) {\r\n return (e: MouseEvent) => {\r\n e.preventDefault();\r\n this.navigate(path);\r\n };\r\n }\r\n\r\n /**\r\n * Get underlying router for advanced usage\r\n */\r\n getRouter() {\r\n return this.router;\r\n }\r\n\r\n // └────────────────────────────────────────────────────────────────────┘\r\n\r\n\r\n // ┌──────────────────────────────── ──── ──────────────────────────────┐\r\n\r\n /**\r\n * Unbind event\r\n */\r\n off(target: EventTarget, event: string, handler: EventListener): void {\r\n this.eventsManager.off(target, event, handler);\r\n }\r\n\r\n /**\r\n * Get events manager directly\r\n */\r\n getEventsManager(): EventsManager {\r\n return this.eventsManager;\r\n }\r\n\r\n // └────────────────────────────────────────────────────────────────────┘\r\n\r\n\r\n // ┌──────────────────────────────── ──── ──────────────────────────────┐\r\n\r\n /**\r\n * Get viewport info as reactive signal\r\n */\r\n getViewport() {\r\n return this.windowManager.getViewport();\r\n }\r\n\r\n /**\r\n * Get window manager directly\r\n */\r\n getWindowManager(): WindowManager {\r\n return this.windowManager;\r\n }\r\n\r\n // └────────────────────────────────────────────────────────────────────┘\r\n\r\n\r\n // ┌──────────────────────────────── ──── ──────────────────────────────┐\r\n\r\n /**\r\n * Get i18n instance for translations\r\n */\r\n getI18n() {\r\n return getI18n();\r\n }\r\n\r\n /**\r\n * Get translation string\r\n */\r\n getTranslation(key: string, defaultValue?: string) {\r\n const i18n = getI18n();\r\n if (!i18n) {\r\n console.warn('[ClientManager] i18n not initialized. Using default value or key.');\r\n return defaultValue ?? key;\r\n }\r\n return i18n.t(key) ?? defaultValue ?? key;\r\n }\r\n\r\n // └────────────────────────────────────────────────────────────────────┘\r\n\r\n\r\n // ┌──────────────────────────────── ──── ──────────────────────────────┐\r\n\r\n /**\r\n * Get lifecycle phase\r\n */\r\n getPhase() {\r\n return this.lifecycle;\r\n }\r\n\r\n /**\r\n * Check if ready\r\n */\r\n isReady(): boolean {\r\n return this.lifecycle === 'ready';\r\n }\r\n\r\n /**\r\n * Internal logging\r\n */\r\n private log(message: string): void {\r\n if (this.debug) {\r\n console.log(`[ClientManager] ${message}`);\r\n }\r\n }\r\n\r\n // └────────────────────────────────────────────────────────────────────┘\r\n\r\n }\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝\r\n\r\n\r\n// ╔════════════════════════════════════════ UTILS ═════════════════════════════════════════╗\r\n\r\n /**\r\n * Helper to safely get translation\r\n * Use this in components to access translations\r\n */\r\n export function useTranslation() {\r\n const i18n = getI18n();\r\n return {\r\n getTranslation: (key: string, defaultValue?: string) => {\r\n if (!i18n) {\r\n return defaultValue ?? key;\r\n }\r\n return i18n.t(key) ?? defaultValue ?? key;\r\n },\r\n t: (key: string, defaultValue?: string) => {\r\n if (!i18n) {\r\n return defaultValue ?? key;\r\n }\r\n return i18n.t(key) ?? defaultValue ?? key;\r\n }\r\n };\r\n }\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝\r\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cruxjs/client",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"description": "Pure management layer for client-side applications. Routing, lifecycle, events, and viewport management without dictating your architecture.",
|
|
5
5
|
"keywords": ["cruxjs", "client"],
|
|
6
6
|
"license": "MIT",
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"@minejs/browser": "^0.0.4",
|
|
44
|
+
"@minejs/i18n": "^0.0.4",
|
|
44
45
|
"@minejs/signals": "^0.0.6",
|
|
45
46
|
"@minejsx/render": "^0.0.3"
|
|
46
47
|
},
|