@hamak/ui-shell-impl 0.1.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.
- package/.turbo/turbo-build.log +1 -0
- package/dist/core/DefaultFeatureManager.d.ts +24 -0
- package/dist/core/DefaultFeatureManager.d.ts.map +1 -0
- package/dist/core/DefaultFeatureManager.js +80 -0
- package/dist/core/DefaultLayoutManager.d.ts +19 -0
- package/dist/core/DefaultLayoutManager.d.ts.map +1 -0
- package/dist/core/DefaultLayoutManager.js +59 -0
- package/dist/core/DefaultRouter.d.ts +30 -0
- package/dist/core/DefaultRouter.d.ts.map +1 -0
- package/dist/core/DefaultRouter.js +111 -0
- package/dist/core/DefaultShell.d.ts +30 -0
- package/dist/core/DefaultShell.d.ts.map +1 -0
- package/dist/core/DefaultShell.js +147 -0
- package/dist/core/DefaultThemeManager.d.ts +27 -0
- package/dist/core/DefaultThemeManager.d.ts.map +1 -0
- package/dist/core/DefaultThemeManager.js +76 -0
- package/dist/core/index.d.ts +10 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +9 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +39 -0
- package/dist/plugin/ShellPluginFactory.d.ts +11 -0
- package/dist/plugin/ShellPluginFactory.d.ts.map +1 -0
- package/dist/plugin/ShellPluginFactory.js +73 -0
- package/dist/plugin/index.d.ts +6 -0
- package/dist/plugin/index.d.ts.map +1 -0
- package/dist/plugin/index.js +5 -0
- package/dist/providers/CSSVariablesThemeProvider.d.ts +16 -0
- package/dist/providers/CSSVariablesThemeProvider.d.ts.map +1 -0
- package/dist/providers/CSSVariablesThemeProvider.js +47 -0
- package/dist/providers/HashRouterStrategy.d.ts +20 -0
- package/dist/providers/HashRouterStrategy.d.ts.map +1 -0
- package/dist/providers/HashRouterStrategy.js +57 -0
- package/dist/providers/HistoryRouterStrategy.d.ts +21 -0
- package/dist/providers/HistoryRouterStrategy.d.ts.map +1 -0
- package/dist/providers/HistoryRouterStrategy.js +64 -0
- package/dist/providers/LocalStorageProvider.d.ts +13 -0
- package/dist/providers/LocalStorageProvider.d.ts.map +1 -0
- package/dist/providers/LocalStorageProvider.js +50 -0
- package/dist/providers/MemoryStorageProvider.d.ts +14 -0
- package/dist/providers/MemoryStorageProvider.d.ts.map +1 -0
- package/dist/providers/MemoryStorageProvider.js +22 -0
- package/dist/providers/index.d.ts +10 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +9 -0
- package/dist/utils/index.d.ts +6 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +5 -0
- package/dist/utils/viewport-utils.d.ts +16 -0
- package/dist/utils/viewport-utils.d.ts.map +1 -0
- package/dist/utils/viewport-utils.js +52 -0
- package/package.json +37 -0
- package/src/core/DefaultFeatureManager.ts +101 -0
- package/src/core/DefaultLayoutManager.ts +74 -0
- package/src/core/DefaultRouter.ts +135 -0
- package/src/core/DefaultShell.ts +176 -0
- package/src/core/DefaultThemeManager.ts +99 -0
- package/src/core/index.ts +10 -0
- package/src/index.ts +51 -0
- package/src/plugin/ShellPluginFactory.ts +98 -0
- package/src/plugin/index.ts +6 -0
- package/src/providers/CSSVariablesThemeProvider.ts +60 -0
- package/src/providers/HashRouterStrategy.ts +71 -0
- package/src/providers/HistoryRouterStrategy.ts +78 -0
- package/src/providers/LocalStorageProvider.ts +53 -0
- package/src/providers/MemoryStorageProvider.ts +30 -0
- package/src/providers/index.ts +10 -0
- package/src/utils/index.ts +6 -0
- package/src/utils/viewport-utils.ts +64 -0
- package/tsconfig.json +20 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
$ tsc -p tsconfig.json
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default Feature Manager Implementation
|
|
3
|
+
*/
|
|
4
|
+
import type { IFeatureManager } from '@amk/ui-shell-api';
|
|
5
|
+
import type { FeatureFlags } from '@amk/ui-shell-api';
|
|
6
|
+
export declare class DefaultFeatureManager implements IFeatureManager {
|
|
7
|
+
private features;
|
|
8
|
+
private listeners;
|
|
9
|
+
constructor(initialFeatures?: FeatureFlags);
|
|
10
|
+
isEnabled(key: string): boolean;
|
|
11
|
+
get<T = any>(key: string, defaultValue?: T): T;
|
|
12
|
+
set(key: string, value: any): void;
|
|
13
|
+
enable(key: string): void;
|
|
14
|
+
disable(key: string): void;
|
|
15
|
+
toggle(key: string): void;
|
|
16
|
+
update(updates: FeatureFlags): void;
|
|
17
|
+
getAll(): Readonly<FeatureFlags>;
|
|
18
|
+
has(key: string): boolean;
|
|
19
|
+
subscribe(key: string, listener: (value: any) => void): () => void;
|
|
20
|
+
subscribeAll(listener: (key: string, value: any) => void): () => void;
|
|
21
|
+
destroy(): void;
|
|
22
|
+
private notifyListeners;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=DefaultFeatureManager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DefaultFeatureManager.d.ts","sourceRoot":"","sources":["../../src/core/DefaultFeatureManager.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEtD,qBAAa,qBAAsB,YAAW,eAAe;IAC3D,OAAO,CAAC,QAAQ,CAAe;IAC/B,OAAO,CAAC,SAAS,CAAqD;gBAE1D,eAAe,GAAE,YAAiB;IAI9C,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAK/B,GAAG,CAAC,CAAC,GAAG,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,CAAC,GAAG,CAAC;IAK9C,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,IAAI;IAQlC,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAIzB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAI1B,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAIzB,MAAM,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI;IAMnC,MAAM,IAAI,QAAQ,CAAC,YAAY,CAAC;IAIhC,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAIzB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,GAAG,MAAM,IAAI;IAiBlE,YAAY,CAAC,QAAQ,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK,IAAI,GAAG,MAAM,IAAI;IAcrE,OAAO,IAAI,IAAI;IAIf,OAAO,CAAC,eAAe;CAMxB"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default Feature Manager Implementation
|
|
3
|
+
*/
|
|
4
|
+
export class DefaultFeatureManager {
|
|
5
|
+
features;
|
|
6
|
+
listeners = new Map();
|
|
7
|
+
constructor(initialFeatures = {}) {
|
|
8
|
+
this.features = { ...initialFeatures };
|
|
9
|
+
}
|
|
10
|
+
isEnabled(key) {
|
|
11
|
+
const value = this.features[key];
|
|
12
|
+
return typeof value === 'boolean' ? value : Boolean(value);
|
|
13
|
+
}
|
|
14
|
+
get(key, defaultValue) {
|
|
15
|
+
const value = this.features[key];
|
|
16
|
+
return (value !== undefined ? value : defaultValue);
|
|
17
|
+
}
|
|
18
|
+
set(key, value) {
|
|
19
|
+
const oldValue = this.features[key];
|
|
20
|
+
if (oldValue === value)
|
|
21
|
+
return;
|
|
22
|
+
this.features[key] = value;
|
|
23
|
+
this.notifyListeners(key, value);
|
|
24
|
+
}
|
|
25
|
+
enable(key) {
|
|
26
|
+
this.set(key, true);
|
|
27
|
+
}
|
|
28
|
+
disable(key) {
|
|
29
|
+
this.set(key, false);
|
|
30
|
+
}
|
|
31
|
+
toggle(key) {
|
|
32
|
+
this.set(key, !this.isEnabled(key));
|
|
33
|
+
}
|
|
34
|
+
update(updates) {
|
|
35
|
+
Object.entries(updates).forEach(([key, value]) => {
|
|
36
|
+
this.set(key, value);
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
getAll() {
|
|
40
|
+
return { ...this.features };
|
|
41
|
+
}
|
|
42
|
+
has(key) {
|
|
43
|
+
return key in this.features;
|
|
44
|
+
}
|
|
45
|
+
subscribe(key, listener) {
|
|
46
|
+
if (!this.listeners.has(key)) {
|
|
47
|
+
this.listeners.set(key, new Set());
|
|
48
|
+
}
|
|
49
|
+
this.listeners.get(key).add(listener);
|
|
50
|
+
return () => {
|
|
51
|
+
const listeners = this.listeners.get(key);
|
|
52
|
+
if (listeners) {
|
|
53
|
+
listeners.delete(listener);
|
|
54
|
+
if (listeners.size === 0) {
|
|
55
|
+
this.listeners.delete(key);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
subscribeAll(listener) {
|
|
61
|
+
const unsubscribers = [];
|
|
62
|
+
const currentKeys = Object.keys(this.features);
|
|
63
|
+
currentKeys.forEach(key => {
|
|
64
|
+
const unsub = this.subscribe(key, value => listener(key, value));
|
|
65
|
+
unsubscribers.push(unsub);
|
|
66
|
+
});
|
|
67
|
+
return () => {
|
|
68
|
+
unsubscribers.forEach(unsub => unsub());
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
destroy() {
|
|
72
|
+
this.listeners.clear();
|
|
73
|
+
}
|
|
74
|
+
notifyListeners(key, value) {
|
|
75
|
+
const listeners = this.listeners.get(key);
|
|
76
|
+
if (listeners) {
|
|
77
|
+
listeners.forEach(listener => listener(value));
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default Layout Manager Implementation
|
|
3
|
+
*/
|
|
4
|
+
import type { ILayoutManager } from '@amk/ui-shell-api';
|
|
5
|
+
import type { LayoutSlot, LayoutArea } from '@amk/ui-shell-api';
|
|
6
|
+
export declare class DefaultLayoutManager implements ILayoutManager {
|
|
7
|
+
private slots;
|
|
8
|
+
private listeners;
|
|
9
|
+
registerSlot(slot: LayoutSlot): () => void;
|
|
10
|
+
unregisterSlot(slot: LayoutSlot): void;
|
|
11
|
+
getSlots(area: LayoutArea): LayoutSlot[];
|
|
12
|
+
getAreas(): LayoutArea[];
|
|
13
|
+
hasSlots(area: LayoutArea): boolean;
|
|
14
|
+
subscribe(listener: () => void): () => void;
|
|
15
|
+
clear(): void;
|
|
16
|
+
destroy(): void;
|
|
17
|
+
private notifyListeners;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=DefaultLayoutManager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DefaultLayoutManager.d.ts","sourceRoot":"","sources":["../../src/core/DefaultLayoutManager.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEhE,qBAAa,oBAAqB,YAAW,cAAc;IACzD,OAAO,CAAC,KAAK,CAA2C;IACxD,OAAO,CAAC,SAAS,CAA8B;IAE/C,YAAY,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,IAAI;IAY1C,cAAc,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI;IAYtC,QAAQ,CAAC,IAAI,EAAE,UAAU,GAAG,UAAU,EAAE;IAWxC,QAAQ,IAAI,UAAU,EAAE;IAIxB,QAAQ,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO;IAKnC,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI;IAK3C,KAAK,IAAI,IAAI;IAKb,OAAO,IAAI,IAAI;IAKf,OAAO,CAAC,eAAe;CAGxB"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default Layout Manager Implementation
|
|
3
|
+
*/
|
|
4
|
+
export class DefaultLayoutManager {
|
|
5
|
+
slots = new Map();
|
|
6
|
+
listeners = new Set();
|
|
7
|
+
registerSlot(slot) {
|
|
8
|
+
const area = slot.area;
|
|
9
|
+
if (!this.slots.has(area)) {
|
|
10
|
+
this.slots.set(area, new Set());
|
|
11
|
+
}
|
|
12
|
+
this.slots.get(area).add(slot);
|
|
13
|
+
this.notifyListeners();
|
|
14
|
+
return () => this.unregisterSlot(slot);
|
|
15
|
+
}
|
|
16
|
+
unregisterSlot(slot) {
|
|
17
|
+
const area = slot.area;
|
|
18
|
+
const slots = this.slots.get(area);
|
|
19
|
+
if (slots) {
|
|
20
|
+
slots.delete(slot);
|
|
21
|
+
if (slots.size === 0) {
|
|
22
|
+
this.slots.delete(area);
|
|
23
|
+
}
|
|
24
|
+
this.notifyListeners();
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
getSlots(area) {
|
|
28
|
+
const slots = this.slots.get(area);
|
|
29
|
+
if (!slots)
|
|
30
|
+
return [];
|
|
31
|
+
return Array.from(slots).sort((a, b) => {
|
|
32
|
+
const priorityA = a.priority || 0;
|
|
33
|
+
const priorityB = b.priority || 0;
|
|
34
|
+
return priorityB - priorityA; // Higher priority first
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
getAreas() {
|
|
38
|
+
return Array.from(this.slots.keys());
|
|
39
|
+
}
|
|
40
|
+
hasSlots(area) {
|
|
41
|
+
const slots = this.slots.get(area);
|
|
42
|
+
return Boolean(slots && slots.size > 0);
|
|
43
|
+
}
|
|
44
|
+
subscribe(listener) {
|
|
45
|
+
this.listeners.add(listener);
|
|
46
|
+
return () => this.listeners.delete(listener);
|
|
47
|
+
}
|
|
48
|
+
clear() {
|
|
49
|
+
this.slots.clear();
|
|
50
|
+
this.notifyListeners();
|
|
51
|
+
}
|
|
52
|
+
destroy() {
|
|
53
|
+
this.slots.clear();
|
|
54
|
+
this.listeners.clear();
|
|
55
|
+
}
|
|
56
|
+
notifyListeners() {
|
|
57
|
+
this.listeners.forEach(listener => listener());
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default Router Implementation
|
|
3
|
+
*/
|
|
4
|
+
import type { IRouter } from '@amk/ui-shell-api';
|
|
5
|
+
import type { RouteConfig, RouterOptions } from '@amk/ui-shell-api';
|
|
6
|
+
import type { IRouterStrategy } from '@amk/ui-shell-spi';
|
|
7
|
+
import type { NavigationGuard } from '@amk/ui-shell-spi';
|
|
8
|
+
export declare class DefaultRouter implements IRouter {
|
|
9
|
+
private routes;
|
|
10
|
+
private currentRoute;
|
|
11
|
+
private guards;
|
|
12
|
+
private listeners;
|
|
13
|
+
private strategy;
|
|
14
|
+
constructor(options: RouterOptions, strategy: IRouterStrategy);
|
|
15
|
+
private registerRoutes;
|
|
16
|
+
push(path: string): Promise<boolean>;
|
|
17
|
+
replace(path: string): Promise<boolean>;
|
|
18
|
+
back(): void;
|
|
19
|
+
forward(): void;
|
|
20
|
+
getCurrentRoute(): RouteConfig | null;
|
|
21
|
+
subscribe(listener: (route: RouteConfig) => void): () => void;
|
|
22
|
+
loadRouteComponent(route: RouteConfig): Promise<any>;
|
|
23
|
+
addGuard(guard: NavigationGuard): () => void;
|
|
24
|
+
destroy(): void;
|
|
25
|
+
private matchRoute;
|
|
26
|
+
private runGuards;
|
|
27
|
+
private setupNavigationListener;
|
|
28
|
+
private notifyListeners;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=DefaultRouter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DefaultRouter.d.ts","sourceRoot":"","sources":["../../src/core/DefaultRouter.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACpE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEzD,qBAAa,aAAc,YAAW,OAAO;IAC3C,OAAO,CAAC,MAAM,CAAuC;IACrD,OAAO,CAAC,YAAY,CAA4B;IAChD,OAAO,CAAC,MAAM,CAAyB;IACvC,OAAO,CAAC,SAAS,CAAgD;IACjE,OAAO,CAAC,QAAQ,CAAkB;gBAEtB,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,eAAe;IAO7D,OAAO,CAAC,cAAc;IAShB,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAqBpC,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAiB7C,IAAI,IAAI,IAAI;IAIZ,OAAO,IAAI,IAAI;IAIf,eAAe,IAAI,WAAW,GAAG,IAAI;IAIrC,SAAS,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,GAAG,MAAM,IAAI;IAKvD,kBAAkB,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC;IAU1D,QAAQ,CAAC,KAAK,EAAE,eAAe,GAAG,MAAM,IAAI;IAU5C,OAAO,IAAI,IAAI;IAMf,OAAO,CAAC,UAAU;YAIJ,SAAS;IAQvB,OAAO,CAAC,uBAAuB;IAM/B,OAAO,CAAC,eAAe;CAGxB"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default Router Implementation
|
|
3
|
+
*/
|
|
4
|
+
export class DefaultRouter {
|
|
5
|
+
routes = new Map();
|
|
6
|
+
currentRoute = null;
|
|
7
|
+
guards = [];
|
|
8
|
+
listeners = new Set();
|
|
9
|
+
strategy;
|
|
10
|
+
constructor(options, strategy) {
|
|
11
|
+
this.strategy = strategy;
|
|
12
|
+
this.guards = [];
|
|
13
|
+
this.registerRoutes(options.routes);
|
|
14
|
+
this.setupNavigationListener();
|
|
15
|
+
}
|
|
16
|
+
registerRoutes(routes) {
|
|
17
|
+
routes.forEach(route => {
|
|
18
|
+
this.routes.set(route.path, route);
|
|
19
|
+
if (route.children) {
|
|
20
|
+
this.registerRoutes(route.children);
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
async push(path) {
|
|
25
|
+
const route = this.matchRoute(path);
|
|
26
|
+
if (!route) {
|
|
27
|
+
console.error(`Route not found: ${path}`);
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
if (!(await this.runGuards(route))) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
if (route.beforeEnter && !(await route.beforeEnter(route, this.currentRoute))) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
this.currentRoute = route;
|
|
37
|
+
this.strategy.push(path);
|
|
38
|
+
this.notifyListeners(route);
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
async replace(path) {
|
|
42
|
+
const route = this.matchRoute(path);
|
|
43
|
+
if (!route) {
|
|
44
|
+
console.error(`Route not found: ${path}`);
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
if (!(await this.runGuards(route))) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
this.currentRoute = route;
|
|
51
|
+
this.strategy.replace(path);
|
|
52
|
+
this.notifyListeners(route);
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
back() {
|
|
56
|
+
this.strategy.back();
|
|
57
|
+
}
|
|
58
|
+
forward() {
|
|
59
|
+
this.strategy.forward();
|
|
60
|
+
}
|
|
61
|
+
getCurrentRoute() {
|
|
62
|
+
return this.currentRoute;
|
|
63
|
+
}
|
|
64
|
+
subscribe(listener) {
|
|
65
|
+
this.listeners.add(listener);
|
|
66
|
+
return () => this.listeners.delete(listener);
|
|
67
|
+
}
|
|
68
|
+
async loadRouteComponent(route) {
|
|
69
|
+
try {
|
|
70
|
+
const component = await route.component();
|
|
71
|
+
return component;
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
console.error(`Failed to load route component for ${route.path}:`, error);
|
|
75
|
+
throw error;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
addGuard(guard) {
|
|
79
|
+
this.guards.push(guard);
|
|
80
|
+
return () => {
|
|
81
|
+
const index = this.guards.indexOf(guard);
|
|
82
|
+
if (index > -1) {
|
|
83
|
+
this.guards.splice(index, 1);
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
destroy() {
|
|
88
|
+
this.strategy.destroy();
|
|
89
|
+
this.listeners.clear();
|
|
90
|
+
this.guards = [];
|
|
91
|
+
}
|
|
92
|
+
matchRoute(path) {
|
|
93
|
+
return this.routes.get(path) || null;
|
|
94
|
+
}
|
|
95
|
+
async runGuards(to) {
|
|
96
|
+
for (const guard of this.guards) {
|
|
97
|
+
const result = await guard(to, this.currentRoute);
|
|
98
|
+
if (!result)
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
setupNavigationListener() {
|
|
104
|
+
this.strategy.listen((path) => {
|
|
105
|
+
this.push(path);
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
notifyListeners(route) {
|
|
109
|
+
this.listeners.forEach(listener => listener(route));
|
|
110
|
+
}
|
|
111
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default Shell Implementation
|
|
3
|
+
*/
|
|
4
|
+
import type { IShell, IThemeManager, IFeatureManager, IRouter } from '@amk/ui-shell-api';
|
|
5
|
+
import type { ShellConfig, ShellContext, ShellEventListener, ShellEventType } from '@amk/ui-shell-api';
|
|
6
|
+
export declare class DefaultShell implements IShell {
|
|
7
|
+
private themeManager;
|
|
8
|
+
private featureManager;
|
|
9
|
+
private router;
|
|
10
|
+
private eventListeners;
|
|
11
|
+
private viewportState;
|
|
12
|
+
private config;
|
|
13
|
+
private isReady;
|
|
14
|
+
constructor(config?: ShellConfig);
|
|
15
|
+
initialize(): Promise<void>;
|
|
16
|
+
getContext(): ShellContext;
|
|
17
|
+
getThemeManager(): IThemeManager;
|
|
18
|
+
getFeatureManager(): IFeatureManager;
|
|
19
|
+
getRouter(): IRouter | null;
|
|
20
|
+
setRouter(router: IRouter): void;
|
|
21
|
+
emit(type: ShellEventType, payload?: any): void;
|
|
22
|
+
on(type: ShellEventType | '*', listener: ShellEventListener): () => void;
|
|
23
|
+
off(type: ShellEventType | '*', listener: ShellEventListener): void;
|
|
24
|
+
ready(): boolean;
|
|
25
|
+
getConfig(): Readonly<ShellConfig>;
|
|
26
|
+
destroy(): void;
|
|
27
|
+
private setupViewportListener;
|
|
28
|
+
private handleResize;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=DefaultShell.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DefaultShell.d.ts","sourceRoot":"","sources":["../../src/core/DefaultShell.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACzF,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAc,kBAAkB,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAMnH,qBAAa,YAAa,YAAW,MAAM;IACzC,OAAO,CAAC,YAAY,CAAgB;IACpC,OAAO,CAAC,cAAc,CAAkB;IACxC,OAAO,CAAC,MAAM,CAAwB;IACtC,OAAO,CAAC,cAAc,CAAmD;IACzE,OAAO,CAAC,aAAa,CAGnB;IACF,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,OAAO,CAAS;gBAEZ,MAAM,GAAE,WAAgB;IAc9B,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAuBjC,UAAU,IAAI,YAAY;IAiB1B,eAAe,IAAI,aAAa;IAIhC,iBAAiB,IAAI,eAAe;IAIpC,SAAS,IAAI,OAAO,GAAG,IAAI;IAI3B,SAAS,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI;IAOhC,IAAI,CAAC,IAAI,EAAE,cAAc,EAAE,OAAO,CAAC,EAAE,GAAG,GAAG,IAAI;IAkB/C,EAAE,CAAC,IAAI,EAAE,cAAc,GAAG,GAAG,EAAE,QAAQ,EAAE,kBAAkB,GAAG,MAAM,IAAI;IAiBxE,GAAG,CAAC,IAAI,EAAE,cAAc,GAAG,GAAG,EAAE,QAAQ,EAAE,kBAAkB,GAAG,IAAI;IAOnE,KAAK,IAAI,OAAO;IAIhB,SAAS,IAAI,QAAQ,CAAC,WAAW,CAAC;IAIlC,OAAO,IAAI,IAAI;IAaf,OAAO,CAAC,qBAAqB;IAK7B,OAAO,CAAC,YAAY,CAUlB;CACH"}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default Shell Implementation
|
|
3
|
+
*/
|
|
4
|
+
import { DefaultThemeManager } from './DefaultThemeManager';
|
|
5
|
+
import { DefaultFeatureManager } from './DefaultFeatureManager';
|
|
6
|
+
import { LocalStorageProvider } from '../providers/LocalStorageProvider';
|
|
7
|
+
import { CSSVariablesThemeProvider } from '../providers/CSSVariablesThemeProvider';
|
|
8
|
+
export class DefaultShell {
|
|
9
|
+
themeManager;
|
|
10
|
+
featureManager;
|
|
11
|
+
router = null;
|
|
12
|
+
eventListeners = new Map();
|
|
13
|
+
viewportState = {
|
|
14
|
+
width: typeof window !== 'undefined' ? window.innerWidth : 0,
|
|
15
|
+
height: typeof window !== 'undefined' ? window.innerHeight : 0,
|
|
16
|
+
};
|
|
17
|
+
config;
|
|
18
|
+
isReady = false;
|
|
19
|
+
constructor(config = {}) {
|
|
20
|
+
this.config = config;
|
|
21
|
+
// Create theme manager with providers
|
|
22
|
+
const storageProvider = new LocalStorageProvider();
|
|
23
|
+
const themeProvider = new CSSVariablesThemeProvider();
|
|
24
|
+
this.themeManager = new DefaultThemeManager(config.theme, storageProvider, themeProvider);
|
|
25
|
+
// Create feature manager
|
|
26
|
+
this.featureManager = new DefaultFeatureManager(config.features);
|
|
27
|
+
this.setupViewportListener();
|
|
28
|
+
}
|
|
29
|
+
async initialize() {
|
|
30
|
+
if (this.isReady) {
|
|
31
|
+
console.warn('Shell is already initialized');
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
// Apply initial theme
|
|
35
|
+
this.themeManager.setTheme(this.themeManager.getTheme());
|
|
36
|
+
// Subscribe to theme changes
|
|
37
|
+
this.themeManager.subscribe(theme => {
|
|
38
|
+
this.emit('theme:changed', { theme });
|
|
39
|
+
});
|
|
40
|
+
// Subscribe to feature changes
|
|
41
|
+
this.featureManager.subscribeAll((key, value) => {
|
|
42
|
+
this.emit('feature:toggled', { key, value });
|
|
43
|
+
});
|
|
44
|
+
this.isReady = true;
|
|
45
|
+
this.emit('shell:ready', {});
|
|
46
|
+
}
|
|
47
|
+
getContext() {
|
|
48
|
+
return {
|
|
49
|
+
theme: this.themeManager.getTheme(),
|
|
50
|
+
setTheme: (mode) => this.themeManager.setTheme(mode),
|
|
51
|
+
features: this.featureManager.getAll(),
|
|
52
|
+
isFeatureEnabled: (key) => this.featureManager.isEnabled(key),
|
|
53
|
+
getFeature: (key, defaultValue) => this.featureManager.get(key, defaultValue),
|
|
54
|
+
viewport: {
|
|
55
|
+
width: this.viewportState.width,
|
|
56
|
+
height: this.viewportState.height,
|
|
57
|
+
isMobile: this.viewportState.width < 768,
|
|
58
|
+
isTablet: this.viewportState.width >= 768 && this.viewportState.width < 1024,
|
|
59
|
+
isDesktop: this.viewportState.width >= 1024,
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
getThemeManager() {
|
|
64
|
+
return this.themeManager;
|
|
65
|
+
}
|
|
66
|
+
getFeatureManager() {
|
|
67
|
+
return this.featureManager;
|
|
68
|
+
}
|
|
69
|
+
getRouter() {
|
|
70
|
+
return this.router;
|
|
71
|
+
}
|
|
72
|
+
setRouter(router) {
|
|
73
|
+
this.router = router;
|
|
74
|
+
this.router.subscribe(route => {
|
|
75
|
+
this.emit('route:changed', { route });
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
emit(type, payload) {
|
|
79
|
+
const event = {
|
|
80
|
+
type,
|
|
81
|
+
payload,
|
|
82
|
+
timestamp: Date.now(),
|
|
83
|
+
};
|
|
84
|
+
const listeners = this.eventListeners.get(type);
|
|
85
|
+
if (listeners) {
|
|
86
|
+
listeners.forEach(listener => listener(event));
|
|
87
|
+
}
|
|
88
|
+
const wildcardListeners = this.eventListeners.get('*');
|
|
89
|
+
if (wildcardListeners) {
|
|
90
|
+
wildcardListeners.forEach(listener => listener(event));
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
on(type, listener) {
|
|
94
|
+
if (!this.eventListeners.has(type)) {
|
|
95
|
+
this.eventListeners.set(type, new Set());
|
|
96
|
+
}
|
|
97
|
+
this.eventListeners.get(type).add(listener);
|
|
98
|
+
return () => {
|
|
99
|
+
const listeners = this.eventListeners.get(type);
|
|
100
|
+
if (listeners) {
|
|
101
|
+
listeners.delete(listener);
|
|
102
|
+
if (listeners.size === 0) {
|
|
103
|
+
this.eventListeners.delete(type);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
off(type, listener) {
|
|
109
|
+
const listeners = this.eventListeners.get(type);
|
|
110
|
+
if (listeners) {
|
|
111
|
+
listeners.delete(listener);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
ready() {
|
|
115
|
+
return this.isReady;
|
|
116
|
+
}
|
|
117
|
+
getConfig() {
|
|
118
|
+
return { ...this.config };
|
|
119
|
+
}
|
|
120
|
+
destroy() {
|
|
121
|
+
this.themeManager.destroy();
|
|
122
|
+
this.featureManager.destroy();
|
|
123
|
+
if (this.router) {
|
|
124
|
+
this.router.destroy();
|
|
125
|
+
}
|
|
126
|
+
if (typeof window !== 'undefined') {
|
|
127
|
+
window.removeEventListener('resize', this.handleResize);
|
|
128
|
+
}
|
|
129
|
+
this.eventListeners.clear();
|
|
130
|
+
this.isReady = false;
|
|
131
|
+
}
|
|
132
|
+
setupViewportListener() {
|
|
133
|
+
if (typeof window === 'undefined')
|
|
134
|
+
return;
|
|
135
|
+
window.addEventListener('resize', this.handleResize);
|
|
136
|
+
}
|
|
137
|
+
handleResize = () => {
|
|
138
|
+
if (typeof window === 'undefined')
|
|
139
|
+
return;
|
|
140
|
+
this.viewportState.width = window.innerWidth;
|
|
141
|
+
this.viewportState.height = window.innerHeight;
|
|
142
|
+
this.emit('viewport:resized', {
|
|
143
|
+
width: this.viewportState.width,
|
|
144
|
+
height: this.viewportState.height,
|
|
145
|
+
});
|
|
146
|
+
};
|
|
147
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default Theme Manager Implementation
|
|
3
|
+
*/
|
|
4
|
+
import type { IThemeManager } from '@amk/ui-shell-api';
|
|
5
|
+
import type { ThemeMode, ThemeConfig } from '@amk/ui-shell-api';
|
|
6
|
+
import type { IStorageProvider, IThemeProvider } from '@amk/ui-shell-spi';
|
|
7
|
+
export declare class DefaultThemeManager implements IThemeManager {
|
|
8
|
+
private currentTheme;
|
|
9
|
+
private config;
|
|
10
|
+
private listeners;
|
|
11
|
+
private storageProvider;
|
|
12
|
+
private themeProvider;
|
|
13
|
+
constructor(config: ThemeConfig | undefined, storageProvider: IStorageProvider, themeProvider: IThemeProvider);
|
|
14
|
+
getTheme(): ThemeMode;
|
|
15
|
+
getResolvedTheme(): 'light' | 'dark';
|
|
16
|
+
setTheme(mode: ThemeMode): void;
|
|
17
|
+
toggleTheme(): void;
|
|
18
|
+
subscribe(listener: (theme: ThemeMode) => void): () => void;
|
|
19
|
+
setCSSVariables(variables: Record<string, string>): void;
|
|
20
|
+
destroy(): void;
|
|
21
|
+
private setupSystemThemeListener;
|
|
22
|
+
private applyTheme;
|
|
23
|
+
private loadPersistedTheme;
|
|
24
|
+
private persistTheme;
|
|
25
|
+
private notifyListeners;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=DefaultThemeManager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DefaultThemeManager.d.ts","sourceRoot":"","sources":["../../src/core/DefaultThemeManager.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChE,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAI1E,qBAAa,mBAAoB,YAAW,aAAa;IACvD,OAAO,CAAC,YAAY,CAAuB;IAC3C,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,SAAS,CAA8C;IAC/D,OAAO,CAAC,eAAe,CAAmB;IAC1C,OAAO,CAAC,aAAa,CAAiB;gBAGpC,MAAM,yBAAkC,EACxC,eAAe,EAAE,gBAAgB,EACjC,aAAa,EAAE,cAAc;IAU/B,QAAQ,IAAI,SAAS;IAIrB,gBAAgB,IAAI,OAAO,GAAG,MAAM;IAOpC,QAAQ,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI;IAS/B,WAAW,IAAI,IAAI;IAKnB,SAAS,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,GAAG,MAAM,IAAI;IAK3D,eAAe,CAAC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI;IAKxD,OAAO,IAAI,IAAI;IAKf,OAAO,CAAC,wBAAwB;IAShC,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,kBAAkB;IAQ1B,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,eAAe;CAGxB"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default Theme Manager Implementation
|
|
3
|
+
*/
|
|
4
|
+
const THEME_STORAGE_KEY = 'ui-shell-theme';
|
|
5
|
+
export class DefaultThemeManager {
|
|
6
|
+
currentTheme = 'system';
|
|
7
|
+
config;
|
|
8
|
+
listeners = new Set();
|
|
9
|
+
storageProvider;
|
|
10
|
+
themeProvider;
|
|
11
|
+
constructor(config = { mode: 'system' }, storageProvider, themeProvider) {
|
|
12
|
+
this.config = config;
|
|
13
|
+
this.storageProvider = storageProvider;
|
|
14
|
+
this.themeProvider = themeProvider;
|
|
15
|
+
this.currentTheme = config.mode || this.loadPersistedTheme();
|
|
16
|
+
this.setupSystemThemeListener();
|
|
17
|
+
this.applyTheme();
|
|
18
|
+
}
|
|
19
|
+
getTheme() {
|
|
20
|
+
return this.currentTheme;
|
|
21
|
+
}
|
|
22
|
+
getResolvedTheme() {
|
|
23
|
+
if (this.currentTheme === 'system') {
|
|
24
|
+
return this.themeProvider.getSystemPreference();
|
|
25
|
+
}
|
|
26
|
+
return this.currentTheme;
|
|
27
|
+
}
|
|
28
|
+
setTheme(mode) {
|
|
29
|
+
if (this.currentTheme === mode)
|
|
30
|
+
return;
|
|
31
|
+
this.currentTheme = mode;
|
|
32
|
+
this.persistTheme(mode);
|
|
33
|
+
this.applyTheme();
|
|
34
|
+
this.notifyListeners();
|
|
35
|
+
}
|
|
36
|
+
toggleTheme() {
|
|
37
|
+
const resolved = this.getResolvedTheme();
|
|
38
|
+
this.setTheme(resolved === 'light' ? 'dark' : 'light');
|
|
39
|
+
}
|
|
40
|
+
subscribe(listener) {
|
|
41
|
+
this.listeners.add(listener);
|
|
42
|
+
return () => this.listeners.delete(listener);
|
|
43
|
+
}
|
|
44
|
+
setCSSVariables(variables) {
|
|
45
|
+
this.config.cssVariables = { ...this.config.cssVariables, ...variables };
|
|
46
|
+
this.applyTheme();
|
|
47
|
+
}
|
|
48
|
+
destroy() {
|
|
49
|
+
this.themeProvider.destroy();
|
|
50
|
+
this.listeners.clear();
|
|
51
|
+
}
|
|
52
|
+
setupSystemThemeListener() {
|
|
53
|
+
this.themeProvider.onSystemThemeChange(() => {
|
|
54
|
+
if (this.currentTheme === 'system') {
|
|
55
|
+
this.applyTheme();
|
|
56
|
+
this.notifyListeners();
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
applyTheme() {
|
|
61
|
+
this.themeProvider.applyTheme(this.currentTheme, this.config);
|
|
62
|
+
}
|
|
63
|
+
loadPersistedTheme() {
|
|
64
|
+
const stored = this.storageProvider.getItem(THEME_STORAGE_KEY);
|
|
65
|
+
if (stored === 'light' || stored === 'dark' || stored === 'system') {
|
|
66
|
+
return stored;
|
|
67
|
+
}
|
|
68
|
+
return 'system';
|
|
69
|
+
}
|
|
70
|
+
persistTheme(mode) {
|
|
71
|
+
this.storageProvider.setItem(THEME_STORAGE_KEY, mode);
|
|
72
|
+
}
|
|
73
|
+
notifyListeners() {
|
|
74
|
+
this.listeners.forEach(listener => listener(this.currentTheme));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core Implementations
|
|
3
|
+
* Export all core implementation classes
|
|
4
|
+
*/
|
|
5
|
+
export * from './DefaultShell';
|
|
6
|
+
export * from './DefaultThemeManager';
|
|
7
|
+
export * from './DefaultFeatureManager';
|
|
8
|
+
export * from './DefaultLayoutManager';
|
|
9
|
+
export * from './DefaultRouter';
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,gBAAgB,CAAC;AAC/B,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,wBAAwB,CAAC;AACvC,cAAc,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core Implementations
|
|
3
|
+
* Export all core implementation classes
|
|
4
|
+
*/
|
|
5
|
+
export * from './DefaultShell';
|
|
6
|
+
export * from './DefaultThemeManager';
|
|
7
|
+
export * from './DefaultFeatureManager';
|
|
8
|
+
export * from './DefaultLayoutManager';
|
|
9
|
+
export * from './DefaultRouter';
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UI Shell Implementation
|
|
3
|
+
* Concrete implementations of UI Shell interfaces
|
|
4
|
+
*
|
|
5
|
+
* This package provides default implementations that can be used directly
|
|
6
|
+
* or extended for custom behavior.
|
|
7
|
+
*/
|
|
8
|
+
export declare const version = "0.1.0";
|
|
9
|
+
export * from './core';
|
|
10
|
+
export * from './providers';
|
|
11
|
+
export * from './utils';
|
|
12
|
+
export * from './plugin';
|
|
13
|
+
import { DefaultShell } from './core/DefaultShell';
|
|
14
|
+
import { DefaultLayoutManager } from './core/DefaultLayoutManager';
|
|
15
|
+
import type { ShellConfig } from '@amk/ui-shell-api';
|
|
16
|
+
export declare function createShell(config?: ShellConfig): DefaultShell;
|
|
17
|
+
export declare function createLayoutManager(): DefaultLayoutManager;
|
|
18
|
+
export declare function getShell(config?: ShellConfig): DefaultShell;
|
|
19
|
+
export declare function resetShell(): void;
|
|
20
|
+
//# sourceMappingURL=index.d.ts.map
|