@vived/core 1.5.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -48
- package/dist/cjs/AppObject/AppObjectComponent.js +8 -10
- package/dist/cjs/AppObject/AppObjectComponent.js.map +1 -1
- package/dist/cjs/AppObject/AppObjectEntityRepo.js +99 -11
- package/dist/cjs/AppObject/AppObjectEntityRepo.js.map +1 -1
- package/dist/cjs/AppObject/AppObjectPM.js +106 -6
- package/dist/cjs/AppObject/AppObjectPM.js.map +1 -1
- package/dist/cjs/AppObject/AppObjectRepo.js +11 -1
- package/dist/cjs/AppObject/AppObjectRepo.js.map +1 -1
- package/dist/cjs/AppObject/AppObjectSingletonEntity.js +40 -0
- package/dist/cjs/AppObject/AppObjectSingletonEntity.js.map +1 -0
- package/dist/cjs/AppObject/AppObjectSingletonEntityRepo.js +54 -0
- package/dist/cjs/AppObject/AppObjectSingletonEntityRepo.js.map +1 -0
- package/dist/cjs/AppObject/AppObjectSingletonPM.js +69 -0
- package/dist/cjs/AppObject/AppObjectSingletonPM.js.map +1 -0
- package/dist/cjs/AppObject/AppObjectSingletonUC.js +60 -0
- package/dist/cjs/AppObject/AppObjectSingletonUC.js.map +1 -0
- package/dist/cjs/AppObject/index.js +4 -0
- package/dist/cjs/AppObject/index.js.map +1 -1
- package/dist/cjs/DomainFactories/Entities/DomainFactoryRepo.js +5 -0
- package/dist/cjs/DomainFactories/Entities/DomainFactoryRepo.js.map +1 -1
- package/dist/cjs/Entities/ObserverList.js +6 -0
- package/dist/cjs/Entities/ObserverList.js.map +1 -1
- package/dist/cjs/ExampleFeature/Entities/ExampleRepo.js +11 -22
- package/dist/cjs/ExampleFeature/Entities/ExampleRepo.js.map +1 -1
- package/dist/cjs/ExampleFeature/Entities/ExampleSingletonEntity.js +1 -3
- package/dist/cjs/ExampleFeature/Entities/ExampleSingletonEntity.js.map +1 -1
- package/dist/cjs/ExampleFeature/Factory/ExampleFeatureFactory.js +65 -0
- package/dist/cjs/ExampleFeature/Factory/ExampleFeatureFactory.js.map +1 -0
- package/dist/cjs/ExampleFeature/PMs/ExamplePM.js +5 -13
- package/dist/cjs/ExampleFeature/PMs/ExamplePM.js.map +1 -1
- package/dist/cjs/ExampleFeature/PMs/ExampleSingletonPM.js +8 -12
- package/dist/cjs/ExampleFeature/PMs/ExampleSingletonPM.js.map +1 -1
- package/dist/cjs/ExampleFeature/UCs/EditExampleStringUC.js +0 -4
- package/dist/cjs/ExampleFeature/UCs/EditExampleStringUC.js.map +1 -1
- package/dist/cjs/ExampleFeature/UCs/ToggleExampleBooleanUC.js +2 -8
- package/dist/cjs/ExampleFeature/UCs/ToggleExampleBooleanUC.js.map +1 -1
- package/dist/cjs/index.js +0 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/esm/AppObject/AppObjectComponent.js +8 -10
- package/dist/esm/AppObject/AppObjectComponent.js.map +1 -1
- package/dist/esm/AppObject/AppObjectEntityRepo.js +99 -11
- package/dist/esm/AppObject/AppObjectEntityRepo.js.map +1 -1
- package/dist/esm/AppObject/AppObjectPM.js +107 -7
- package/dist/esm/AppObject/AppObjectPM.js.map +1 -1
- package/dist/esm/AppObject/AppObjectRepo.js +11 -1
- package/dist/esm/AppObject/AppObjectRepo.js.map +1 -1
- package/dist/esm/AppObject/AppObjectSingletonEntity.js +36 -0
- package/dist/esm/AppObject/AppObjectSingletonEntity.js.map +1 -0
- package/dist/esm/AppObject/AppObjectSingletonEntityRepo.js +50 -0
- package/dist/esm/AppObject/AppObjectSingletonEntityRepo.js.map +1 -0
- package/dist/esm/AppObject/AppObjectSingletonPM.js +65 -0
- package/dist/esm/AppObject/AppObjectSingletonPM.js.map +1 -0
- package/dist/esm/AppObject/AppObjectSingletonUC.js +56 -0
- package/dist/esm/AppObject/AppObjectSingletonUC.js.map +1 -0
- package/dist/esm/AppObject/index.js +4 -0
- package/dist/esm/AppObject/index.js.map +1 -1
- package/dist/esm/DomainFactories/Entities/DomainFactoryRepo.js +4 -0
- package/dist/esm/DomainFactories/Entities/DomainFactoryRepo.js.map +1 -1
- package/dist/esm/Entities/ObserverList.js +6 -0
- package/dist/esm/Entities/ObserverList.js.map +1 -1
- package/dist/esm/ExampleFeature/Entities/ExampleRepo.js +11 -22
- package/dist/esm/ExampleFeature/Entities/ExampleRepo.js.map +1 -1
- package/dist/esm/ExampleFeature/Entities/ExampleSingletonEntity.js +2 -4
- package/dist/esm/ExampleFeature/Entities/ExampleSingletonEntity.js.map +1 -1
- package/dist/esm/ExampleFeature/Factory/ExampleFeatureFactory.js +60 -0
- package/dist/esm/ExampleFeature/Factory/ExampleFeatureFactory.js.map +1 -0
- package/dist/esm/ExampleFeature/PMs/ExamplePM.js +5 -13
- package/dist/esm/ExampleFeature/PMs/ExamplePM.js.map +1 -1
- package/dist/esm/ExampleFeature/PMs/ExampleSingletonPM.js +9 -13
- package/dist/esm/ExampleFeature/PMs/ExampleSingletonPM.js.map +1 -1
- package/dist/esm/ExampleFeature/UCs/EditExampleStringUC.js +0 -4
- package/dist/esm/ExampleFeature/UCs/EditExampleStringUC.js.map +1 -1
- package/dist/esm/ExampleFeature/UCs/ToggleExampleBooleanUC.js +3 -9
- package/dist/esm/ExampleFeature/UCs/ToggleExampleBooleanUC.js.map +1 -1
- package/dist/esm/index.js +0 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/types/AppObject/AppObjectComponent.d.ts +6 -4
- package/dist/types/AppObject/AppObjectComponent.d.ts.map +1 -1
- package/dist/types/AppObject/AppObjectEntityRepo.d.ts +72 -7
- package/dist/types/AppObject/AppObjectEntityRepo.d.ts.map +1 -1
- package/dist/types/AppObject/AppObjectPM.d.ts +82 -2
- package/dist/types/AppObject/AppObjectPM.d.ts.map +1 -1
- package/dist/types/AppObject/AppObjectRepo.d.ts +13 -0
- package/dist/types/AppObject/AppObjectRepo.d.ts.map +1 -1
- package/dist/types/AppObject/AppObjectSingletonEntity.d.ts +31 -0
- package/dist/types/AppObject/AppObjectSingletonEntity.d.ts.map +1 -0
- package/dist/types/AppObject/AppObjectSingletonEntityRepo.d.ts +46 -0
- package/dist/types/AppObject/AppObjectSingletonEntityRepo.d.ts.map +1 -0
- package/dist/types/AppObject/AppObjectSingletonPM.d.ts +60 -0
- package/dist/types/AppObject/AppObjectSingletonPM.d.ts.map +1 -0
- package/dist/types/AppObject/AppObjectSingletonUC.d.ts +51 -0
- package/dist/types/AppObject/AppObjectSingletonUC.d.ts.map +1 -0
- package/dist/types/AppObject/index.d.ts +4 -0
- package/dist/types/AppObject/index.d.ts.map +1 -1
- package/dist/types/DomainFactories/Entities/DomainFactoryRepo.d.ts +1 -0
- package/dist/types/DomainFactories/Entities/DomainFactoryRepo.d.ts.map +1 -1
- package/dist/types/Entities/ObserverList.d.ts +4 -0
- package/dist/types/Entities/ObserverList.d.ts.map +1 -1
- package/dist/types/ExampleFeature/Entities/ExampleRepo.d.ts +0 -4
- package/dist/types/ExampleFeature/Entities/ExampleRepo.d.ts.map +1 -1
- package/dist/types/ExampleFeature/Entities/ExampleSingletonEntity.d.ts +2 -2
- package/dist/types/ExampleFeature/Entities/ExampleSingletonEntity.d.ts.map +1 -1
- package/dist/types/ExampleFeature/Factory/ExampleFeatureFactory.d.ts +44 -0
- package/dist/types/ExampleFeature/Factory/ExampleFeatureFactory.d.ts.map +1 -0
- package/dist/types/ExampleFeature/PMs/ExampleSingletonPM.d.ts +4 -4
- package/dist/types/ExampleFeature/PMs/ExampleSingletonPM.d.ts.map +1 -1
- package/dist/types/ExampleFeature/UCs/ToggleExampleBooleanUC.d.ts +2 -2
- package/dist/types/ExampleFeature/UCs/ToggleExampleBooleanUC.d.ts.map +1 -1
- package/dist/types/index.d.ts +0 -1
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +2 -2
- package/dist/cjs/ExampleFeature/Adapters/index.js +0 -19
- package/dist/cjs/ExampleFeature/Adapters/index.js.map +0 -1
- package/dist/cjs/ExampleFeature/Controllers/index.js +0 -19
- package/dist/cjs/ExampleFeature/Controllers/index.js.map +0 -1
- package/dist/cjs/ExampleFeature/Entities/index.js +0 -20
- package/dist/cjs/ExampleFeature/Entities/index.js.map +0 -1
- package/dist/cjs/ExampleFeature/Factory/index.js +0 -18
- package/dist/cjs/ExampleFeature/Factory/index.js.map +0 -1
- package/dist/cjs/ExampleFeature/Factory/setupExampleFeature.js +0 -29
- package/dist/cjs/ExampleFeature/Factory/setupExampleFeature.js.map +0 -1
- package/dist/cjs/ExampleFeature/Mocks/index.js +0 -21
- package/dist/cjs/ExampleFeature/Mocks/index.js.map +0 -1
- package/dist/cjs/ExampleFeature/PMs/index.js +0 -19
- package/dist/cjs/ExampleFeature/PMs/index.js.map +0 -1
- package/dist/cjs/ExampleFeature/UCs/index.js +0 -19
- package/dist/cjs/ExampleFeature/UCs/index.js.map +0 -1
- package/dist/cjs/ExampleFeature/index.js +0 -24
- package/dist/cjs/ExampleFeature/index.js.map +0 -1
- package/dist/esm/ExampleFeature/Adapters/index.js +0 -3
- package/dist/esm/ExampleFeature/Adapters/index.js.map +0 -1
- package/dist/esm/ExampleFeature/Controllers/index.js +0 -3
- package/dist/esm/ExampleFeature/Controllers/index.js.map +0 -1
- package/dist/esm/ExampleFeature/Entities/index.js +0 -4
- package/dist/esm/ExampleFeature/Entities/index.js.map +0 -1
- package/dist/esm/ExampleFeature/Factory/index.js +0 -2
- package/dist/esm/ExampleFeature/Factory/index.js.map +0 -1
- package/dist/esm/ExampleFeature/Factory/setupExampleFeature.js +0 -26
- package/dist/esm/ExampleFeature/Factory/setupExampleFeature.js.map +0 -1
- package/dist/esm/ExampleFeature/Mocks/index.js +0 -5
- package/dist/esm/ExampleFeature/Mocks/index.js.map +0 -1
- package/dist/esm/ExampleFeature/PMs/index.js +0 -3
- package/dist/esm/ExampleFeature/PMs/index.js.map +0 -1
- package/dist/esm/ExampleFeature/UCs/index.js +0 -3
- package/dist/esm/ExampleFeature/UCs/index.js.map +0 -1
- package/dist/esm/ExampleFeature/index.js +0 -8
- package/dist/esm/ExampleFeature/index.js.map +0 -1
- package/dist/types/ExampleFeature/Adapters/index.d.ts +0 -3
- package/dist/types/ExampleFeature/Adapters/index.d.ts.map +0 -1
- package/dist/types/ExampleFeature/Controllers/index.d.ts +0 -3
- package/dist/types/ExampleFeature/Controllers/index.d.ts.map +0 -1
- package/dist/types/ExampleFeature/Entities/index.d.ts +0 -4
- package/dist/types/ExampleFeature/Entities/index.d.ts.map +0 -1
- package/dist/types/ExampleFeature/Factory/index.d.ts +0 -2
- package/dist/types/ExampleFeature/Factory/index.d.ts.map +0 -1
- package/dist/types/ExampleFeature/Factory/setupExampleFeature.d.ts +0 -17
- package/dist/types/ExampleFeature/Factory/setupExampleFeature.d.ts.map +0 -1
- package/dist/types/ExampleFeature/Mocks/index.d.ts +0 -5
- package/dist/types/ExampleFeature/Mocks/index.d.ts.map +0 -1
- package/dist/types/ExampleFeature/PMs/index.d.ts +0 -3
- package/dist/types/ExampleFeature/PMs/index.d.ts.map +0 -1
- package/dist/types/ExampleFeature/UCs/index.d.ts +0 -3
- package/dist/types/ExampleFeature/UCs/index.d.ts.map +0 -1
- package/dist/types/ExampleFeature/index.d.ts +0 -8
- package/dist/types/ExampleFeature/index.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -66,54 +66,7 @@ The package includes a fully implemented `ExampleFeature` that demonstrates the
|
|
|
66
66
|
- **Controllers/** - Simplified API for UI interaction
|
|
67
67
|
- **Adapters/** - Connect UI frameworks to PMs
|
|
68
68
|
- **Mocks/** - Test doubles for unit testing
|
|
69
|
-
|
|
70
|
-
### Implementation Patterns
|
|
71
|
-
The example demonstrates:
|
|
72
|
-
- Observable entities with memoized properties
|
|
73
|
-
- Presentation managers with view model transformation
|
|
74
|
-
- Use cases that implement business logic
|
|
75
|
-
- Controllers that provide a simple UI-friendly API
|
|
76
|
-
- Component registration and retrieval
|
|
77
|
-
- Singleton component access
|
|
78
|
-
- Test patterns for each component type
|
|
79
|
-
|
|
80
|
-
### Sample Usage
|
|
81
|
-
|
|
82
|
-
```typescript
|
|
83
|
-
// Create a repository and an app object
|
|
84
|
-
const repo = makeAppObjectRepo();
|
|
85
|
-
const appObject = repo.getOrCreate("myObject");
|
|
86
|
-
|
|
87
|
-
// Add an entity component to hold state
|
|
88
|
-
class MyEntity extends AppObjectEntity {
|
|
89
|
-
private _value = 0;
|
|
90
|
-
|
|
91
|
-
get value() { return this._value; }
|
|
92
|
-
set value(newValue: number) {
|
|
93
|
-
this._value = newValue;
|
|
94
|
-
this.notifyOnChange();
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// Add the entity to the app object
|
|
99
|
-
const entity = new MyEntity(appObject, "MyEntity");
|
|
100
|
-
|
|
101
|
-
// Create a presentation manager
|
|
102
|
-
class MyPM extends AppObjectPM<{value: number}> {
|
|
103
|
-
vmsAreEqual(a: {value: number}, b: {value: number}): boolean {
|
|
104
|
-
return a.value === b.value;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
updateViewModel() {
|
|
108
|
-
const entity = this.getCachedLocalComponent<MyEntity>("MyEntity");
|
|
109
|
-
if (entity) {
|
|
110
|
-
this.doUpdateView({value: entity.value});
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// The architecture enables clean, testable, and maintainable code
|
|
116
|
-
```
|
|
69
|
+
- **Factory/** - Factories for creating the features at runtime
|
|
117
70
|
|
|
118
71
|
Refer to the `ExampleFeature` directory for a complete implementation example that shows the interaction between all component types.
|
|
119
72
|
|
|
@@ -45,17 +45,16 @@ class AppObjectComponent {
|
|
|
45
45
|
* Results are cached for better performance in repeated calls
|
|
46
46
|
*
|
|
47
47
|
* @param type The component type identifier to look up
|
|
48
|
-
* @returns The singleton component cast to type T
|
|
48
|
+
* @returns The singleton component cast to type T
|
|
49
|
+
* @throws Error if the component is not found
|
|
49
50
|
*/
|
|
50
51
|
getCachedSingleton(type) {
|
|
51
52
|
if (!this.cachedComponents.has(type)) {
|
|
52
53
|
const component = this.appObjects.getSingleton(type);
|
|
53
54
|
if (!component) {
|
|
54
|
-
|
|
55
|
-
}
|
|
56
|
-
else {
|
|
57
|
-
this.cachedComponents.set(type, component);
|
|
55
|
+
throw new Error("Unable to get cached singleton type " + type);
|
|
58
56
|
}
|
|
57
|
+
this.cachedComponents.set(type, component);
|
|
59
58
|
}
|
|
60
59
|
return this.cachedComponents.get(type);
|
|
61
60
|
}
|
|
@@ -64,17 +63,16 @@ class AppObjectComponent {
|
|
|
64
63
|
* Results are cached for better performance in repeated calls
|
|
65
64
|
*
|
|
66
65
|
* @param type The component type identifier to look up
|
|
67
|
-
* @returns The component cast to type T
|
|
66
|
+
* @returns The component cast to type T
|
|
67
|
+
* @throws Error if the component is not found
|
|
68
68
|
*/
|
|
69
69
|
getCachedLocalComponent(type) {
|
|
70
70
|
if (!this.cachedComponents.has(type)) {
|
|
71
71
|
const component = this.appObject.getComponent(type);
|
|
72
72
|
if (!component) {
|
|
73
|
-
|
|
74
|
-
}
|
|
75
|
-
else {
|
|
76
|
-
this.cachedComponents.set(type, component);
|
|
73
|
+
throw new Error("Unable to get local component of type " + type);
|
|
77
74
|
}
|
|
75
|
+
this.cachedComponents.set(type, component);
|
|
78
76
|
}
|
|
79
77
|
return this.cachedComponents.get(type);
|
|
80
78
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AppObjectComponent.js","sourceRoot":"","sources":["../../../src/AppObject/AppObjectComponent.ts"],"names":[],"mappings":";;;AAGA;;;GAGG;AACH,IAAY,sBAkBX;AAlBD,WAAY,sBAAsB;IAChC,2CAA2C;IAC3C,2CAAiB,CAAA;IAEjB,oDAAoD;IACpD,qDAA2B,CAAA;IAE3B,+DAA+D;IAC/D,yCAAe,CAAA;IAEf,8CAA8C;IAC9C,mDAAyB,CAAA;IAEzB,sEAAsE;IACtE,uCAAa,CAAA;IAEb,yEAAyE;IACzE,6CAAmB,CAAA;AACrB,CAAC,EAlBW,sBAAsB,sCAAtB,sBAAsB,QAkBjC;AAED;;;;;;;;;;;GAWG;AACH,MAAa,kBAAkB;
|
|
1
|
+
{"version":3,"file":"AppObjectComponent.js","sourceRoot":"","sources":["../../../src/AppObject/AppObjectComponent.ts"],"names":[],"mappings":"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","sourcesContent":["import { AppObject } from \"./AppObject\";\r\nimport { AppObjectRepo } from \"./AppObjectRepo\";\r\n\r\n/**\r\n * Enumeration of the different types of components in the architecture.\r\n * Each type serves a specific purpose in the application structure.\r\n */\r\nexport enum AppObjectComponentType {\r\n /** Stores and manages application state */\r\n ENTITY = \"Entity\",\r\n\r\n /** Transforms application state into view models */\r\n PM = \"Presentation Manager\",\r\n\r\n /** Implements business logic and coordinates between layers */\r\n UC = \"Use Case\",\r\n\r\n /** Handles user inputs and external events */\r\n CONTROLLER = \"Controller\",\r\n\r\n /** Renders UI elements and interacts with the DOM/rendering system */\r\n VIEW = \"View\",\r\n\r\n /** Default type for components that don't fit the standard categories */\r\n UNKNOWN = \"Unknown\",\r\n}\r\n\r\n/**\r\n * Base class for all components in the AppObject architecture.\r\n *\r\n * Components attach to AppObjects and provide specific functionality based on their type.\r\n * The architecture follows a clean separation of concerns, with each component type\r\n * having a well-defined responsibility in the application.\r\n *\r\n * Components can communicate with each other through various mechanisms:\r\n * - Direct access via parent AppObject\r\n * - Repository-level singleton access\r\n * - Observer patterns for reactive updates\r\n */\r\nexport class AppObjectComponent {\r\n /** The type category of this component */\r\n readonly componentType: AppObjectComponentType =\r\n AppObjectComponentType.UNKNOWN;\r\n\r\n /** Unique type identifier for this specific component */\r\n readonly type: string;\r\n\r\n /** The parent AppObject this component is attached to */\r\n readonly appObject: AppObject;\r\n\r\n /**\r\n * Convenience accessor for the repository containing all AppObjects\r\n * @returns The application's AppObjectRepo\r\n */\r\n get appObjects(): AppObjectRepo {\r\n return this.appObject.appObjectRepo;\r\n }\r\n\r\n /** Cache for components to avoid repeated lookups */\r\n private cachedComponents = new Map<string, AppObjectComponent>();\r\n\r\n /**\r\n * Retrieves a singleton component of the specified type from the repository\r\n * Results are cached for better performance in repeated calls\r\n *\r\n * @param type The component type identifier to look up\r\n * @returns The singleton component cast to type T\r\n * @throws Error if the component is not found\r\n */\r\n getCachedSingleton<T extends AppObjectComponent>(type: string): T {\r\n if (!this.cachedComponents.has(type)) {\r\n const component = this.appObjects.getSingleton(type);\r\n if (!component) {\r\n throw new Error(\"Unable to get cached singleton type \" + type);\r\n }\r\n this.cachedComponents.set(type, component);\r\n }\r\n\r\n return this.cachedComponents.get(type) as T;\r\n }\r\n\r\n /**\r\n * Retrieves a component from the same AppObject as this component\r\n * Results are cached for better performance in repeated calls\r\n *\r\n * @param type The component type identifier to look up\r\n * @returns The component cast to type T\r\n * @throws Error if the component is not found\r\n */\r\n getCachedLocalComponent<T extends AppObjectComponent>(type: string): T {\r\n if (!this.cachedComponents.has(type)) {\r\n const component = this.appObject.getComponent(type);\r\n\r\n if (!component) {\r\n throw new Error(\"Unable to get local component of type \" + type);\r\n }\r\n this.cachedComponents.set(type, component);\r\n }\r\n\r\n return this.cachedComponents.get(type) as T;\r\n }\r\n\r\n /**\r\n * Retrieves a singleton component from the repository with customizable logging\r\n * Unlike getCachedSingleton, results are not cached\r\n *\r\n * @param type The component type identifier to look up\r\n * @param logType The severity level for logging if component isn't found\r\n * @returns The singleton component cast to type T, or undefined if not found\r\n */\r\n getSingleton<T extends AppObjectComponent>(\r\n type: string,\r\n logType: \"LOG\" | \"WARN\" | \"ERROR\" = \"WARN\"\r\n ): T | undefined {\r\n const comp = this.appObjects.getSingleton<T>(type);\r\n\r\n if (!comp) {\r\n const msg = \"Unable to get singleton type \" + type;\r\n switch (logType) {\r\n case \"ERROR\":\r\n this.error(msg);\r\n break;\r\n case \"LOG\":\r\n this.log(msg);\r\n break;\r\n case \"WARN\":\r\n this.warn(msg);\r\n break;\r\n }\r\n }\r\n\r\n return comp;\r\n }\r\n\r\n /**\r\n * Cleans up resources used by this component\r\n * Removes itself from the parent AppObject if it's still attached\r\n */\r\n dispose() {\r\n if (this.appObject.getComponent(this.type) === this) {\r\n this.appObject.removeComponent(this.type);\r\n }\r\n }\r\n\r\n /**\r\n * Logs an informational message to the application's logging system\r\n * Messages are prefixed with the AppObject ID and component type for easier debugging\r\n *\r\n * @param message The message to log\r\n */\r\n log(message: string) {\r\n this.appObjects.submitLog(`${this.appObject.id}/${this.type}`, message);\r\n }\r\n\r\n /**\r\n * Logs a warning message to the application's logging system\r\n * Messages are prefixed with the AppObject ID and component type for easier debugging\r\n *\r\n * @param message The warning message to log\r\n */\r\n warn(message: string) {\r\n this.appObjects.submitWarning(`${this.appObject.id}/${this.type}`, message);\r\n }\r\n\r\n /**\r\n * Logs an error message to the application's logging system\r\n * Messages are prefixed with the AppObject ID and component type for easier debugging\r\n *\r\n * @param message The error message to log\r\n */\r\n error(message: string) {\r\n this.appObjects.submitError(`${this.appObject.id}/${this.type}`, message);\r\n }\r\n\r\n /**\r\n * Creates a new component and attaches it to the specified AppObject\r\n *\r\n * @param appObject The parent AppObject this component will be attached to\r\n * @param type The unique type identifier for this component\r\n */\r\n constructor(appObject: AppObject, type: string) {\r\n this.appObject = appObject;\r\n this.type = type;\r\n appObject.addComponent(this);\r\n }\r\n}\r\n"]}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.AppObjectEntityRepo = void 0;
|
|
4
4
|
const Entities_1 = require("../Entities");
|
|
5
|
+
const Utilities_1 = require("../Utilities");
|
|
5
6
|
const AppObjectEntity_1 = require("./AppObjectEntity");
|
|
6
7
|
/**
|
|
7
8
|
* A repository for managing collections of AppObjectEntity instances.
|
|
@@ -10,20 +11,32 @@ const AppObjectEntity_1 = require("./AppObjectEntity");
|
|
|
10
11
|
* entities that are associated with specific AppObjects. It implements the observer
|
|
11
12
|
* pattern to notify listeners when entities are added or removed.
|
|
12
13
|
*
|
|
14
|
+
* Derived classes should override the `entityFactory` method to provide custom
|
|
15
|
+
* entity creation logic for the `create` method.
|
|
16
|
+
*
|
|
13
17
|
* @template T - The type of entities managed by this repository, must extend AppObjectEntity
|
|
14
18
|
* @extends AppObjectEntity
|
|
15
19
|
*
|
|
16
20
|
* @example
|
|
17
21
|
* ```typescript
|
|
18
|
-
* // Create a repository
|
|
19
|
-
*
|
|
22
|
+
* // Create a custom repository with entity factory
|
|
23
|
+
* class PlayerRepo extends AppObjectEntityRepo<PlayerEntity> {
|
|
24
|
+
* entityFactory(id: string): PlayerEntity {
|
|
25
|
+
* const appObject = makeAppObject(id, this.appObjects);
|
|
26
|
+
* return new PlayerEntity(appObject);
|
|
27
|
+
* }
|
|
28
|
+
* }
|
|
29
|
+
*
|
|
30
|
+
* const playerRepo = new PlayerRepo(repoAppObject);
|
|
31
|
+
*
|
|
32
|
+
* // Create a new player with auto-generated ID
|
|
33
|
+
* const player = playerRepo.create();
|
|
20
34
|
*
|
|
21
|
-
* //
|
|
22
|
-
* const
|
|
23
|
-
* playerRepo.add(player);
|
|
35
|
+
* // Create a new player with specific ID
|
|
36
|
+
* const specificPlayer = playerRepo.create("player1");
|
|
24
37
|
*
|
|
25
|
-
* // Get a player by its
|
|
26
|
-
* const retrievedPlayer = playerRepo.
|
|
38
|
+
* // Get a player by its ID
|
|
39
|
+
* const retrievedPlayer = playerRepo.getById("player1");
|
|
27
40
|
* ```
|
|
28
41
|
*/
|
|
29
42
|
class AppObjectEntityRepo extends AppObjectEntity_1.AppObjectEntity {
|
|
@@ -64,23 +77,33 @@ class AppObjectEntityRepo extends AppObjectEntity_1.AppObjectEntity {
|
|
|
64
77
|
this.removeEntityRemovedObserver = (observer) => {
|
|
65
78
|
this.onEntityRemovedObservers.remove(observer);
|
|
66
79
|
};
|
|
80
|
+
/**
|
|
81
|
+
* Checks if an entity exists for the given ID.
|
|
82
|
+
*
|
|
83
|
+
* @param {string} id - The ID of the AppObject
|
|
84
|
+
* @returns {boolean} True if an entity exists for the given ID, false otherwise
|
|
85
|
+
*/
|
|
86
|
+
this.has = (id) => {
|
|
87
|
+
return this.entityLookup.has(id);
|
|
88
|
+
};
|
|
67
89
|
/**
|
|
68
90
|
* Checks if an entity exists for the given AppObject ID.
|
|
69
91
|
*
|
|
70
92
|
* @param {string} appObjectID - The ID of the AppObject
|
|
71
93
|
* @returns {boolean} True if an entity exists for the given AppObject ID, false otherwise
|
|
94
|
+
* @deprecated Use has instead
|
|
72
95
|
*/
|
|
73
96
|
this.hasForAppObject = (appObjectID) => {
|
|
74
|
-
return this.
|
|
97
|
+
return this.has(appObjectID);
|
|
75
98
|
};
|
|
76
99
|
/**
|
|
77
|
-
* Removes the entity associated with the specified
|
|
100
|
+
* Removes the entity associated with the specified ID.
|
|
78
101
|
*
|
|
79
102
|
* Notifies all registered observers after the entity is removed.
|
|
80
103
|
*
|
|
81
104
|
* @param {string} id - The ID of the AppObject whose entity should be removed
|
|
82
105
|
*/
|
|
83
|
-
this.
|
|
106
|
+
this.removeById = (id) => {
|
|
84
107
|
const existing = this.entityLookup.get(id);
|
|
85
108
|
if (!existing)
|
|
86
109
|
return;
|
|
@@ -89,14 +112,50 @@ class AppObjectEntityRepo extends AppObjectEntity_1.AppObjectEntity {
|
|
|
89
112
|
this.notifyOnChange();
|
|
90
113
|
this.onEntityRemovedObservers.notify(existing);
|
|
91
114
|
};
|
|
115
|
+
/**
|
|
116
|
+
* Removes the entity associated with the specified AppObject ID.
|
|
117
|
+
*
|
|
118
|
+
* Notifies all registered observers after the entity is removed.
|
|
119
|
+
*
|
|
120
|
+
* @param {string} id - The ID of the AppObject whose entity should be removed
|
|
121
|
+
* @deprecated Use removeById instead
|
|
122
|
+
*/
|
|
123
|
+
this.removeForAppObject = (id) => {
|
|
124
|
+
this.removeById(id);
|
|
125
|
+
};
|
|
126
|
+
/**
|
|
127
|
+
* Removes all entities from the repository.
|
|
128
|
+
*
|
|
129
|
+
* Unsubscribes from all entity change observers and notifies all registered
|
|
130
|
+
* removal observers for each entity before clearing the repository.
|
|
131
|
+
*/
|
|
132
|
+
this.deleteAll = () => {
|
|
133
|
+
const entities = Array.from(this.entityLookup.values());
|
|
134
|
+
entities.forEach((entity) => {
|
|
135
|
+
entity.removeChangeObserver(this.notifyOnChange);
|
|
136
|
+
this.onEntityRemovedObservers.notify(entity);
|
|
137
|
+
});
|
|
138
|
+
this.entityLookup.clear();
|
|
139
|
+
this.notifyOnChange();
|
|
140
|
+
};
|
|
141
|
+
/**
|
|
142
|
+
* Gets the entity associated with the specified ID.
|
|
143
|
+
*
|
|
144
|
+
* @param {string} id - The ID of the AppObject
|
|
145
|
+
* @returns {T | undefined} The entity if found, undefined otherwise
|
|
146
|
+
*/
|
|
147
|
+
this.getById = (id) => {
|
|
148
|
+
return this.entityLookup.get(id);
|
|
149
|
+
};
|
|
92
150
|
/**
|
|
93
151
|
* Gets the entity associated with the specified AppObject ID.
|
|
94
152
|
*
|
|
95
153
|
* @param {string} appObjectID - The ID of the AppObject
|
|
96
154
|
* @returns {T | undefined} The entity if found, undefined otherwise
|
|
155
|
+
* @deprecated Use getById instead
|
|
97
156
|
*/
|
|
98
157
|
this.getForAppObject = (appObjectID) => {
|
|
99
|
-
return this.
|
|
158
|
+
return this.getById(appObjectID);
|
|
100
159
|
};
|
|
101
160
|
/**
|
|
102
161
|
* Gets all entities in the repository.
|
|
@@ -125,6 +184,35 @@ class AppObjectEntityRepo extends AppObjectEntity_1.AppObjectEntity {
|
|
|
125
184
|
this.notifyOnChange();
|
|
126
185
|
this.onEntityAddedObservers.notify(entity);
|
|
127
186
|
}
|
|
187
|
+
/**
|
|
188
|
+
* Creates a new entity with the specified or auto-generated ID and adds it to the repository.
|
|
189
|
+
*
|
|
190
|
+
* This method uses the `entityFactory` method to create the entity instance.
|
|
191
|
+
* If no ID is provided, a unique ID is generated automatically.
|
|
192
|
+
*
|
|
193
|
+
* @param {string} [id] - Optional ID for the entity. If not provided, a unique ID is generated.
|
|
194
|
+
* @returns {T} The newly created entity
|
|
195
|
+
*/
|
|
196
|
+
create(id) {
|
|
197
|
+
const idToUse = id ?? (0, Utilities_1.generateUniqueID)();
|
|
198
|
+
const entity = this.entityFactory(idToUse);
|
|
199
|
+
this.add(entity);
|
|
200
|
+
return entity;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Factory method for creating entity instances.
|
|
204
|
+
*
|
|
205
|
+
* This method must be overridden in derived classes to provide custom entity creation logic.
|
|
206
|
+
* It is called by the `create` method to instantiate new entities.
|
|
207
|
+
*
|
|
208
|
+
* @param {string} id - The ID to use for the new entity
|
|
209
|
+
* @returns {T} A new entity instance
|
|
210
|
+
* @throws {Error} If not overridden in derived class
|
|
211
|
+
*/
|
|
212
|
+
entityFactory(id) {
|
|
213
|
+
// Override this method in derived classes to provide entity creation logic
|
|
214
|
+
throw new Error("Entity factory not implemented.");
|
|
215
|
+
}
|
|
128
216
|
}
|
|
129
217
|
exports.AppObjectEntityRepo = AppObjectEntityRepo;
|
|
130
218
|
//# sourceMappingURL=AppObjectEntityRepo.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AppObjectEntityRepo.js","sourceRoot":"","sources":["../../../src/AppObject/AppObjectEntityRepo.ts"],"names":[],"mappings":";;;AAAA,0CAA2C;AAC3C,uDAAoD;AAEpD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAa,mBAEX,SAAQ,iCAAe;IAFzB;;QAGU,iBAAY,GAAG,IAAI,GAAG,EAAa,CAAC;QAEpC,2BAAsB,GAAG,IAAI,uBAAY,EAAK,CAAC;QACvD;;;;WAIG;QACH,2BAAsB,GAAG,CAAC,QAAkC,EAAE,EAAE;YAC9D,IAAI,CAAC,sBAAsB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC5C,CAAC,CAAC;QAEF;;;;WAIG;QACH,8BAAyB,GAAG,CAAC,QAAkC,EAAQ,EAAE;YACvE,IAAI,CAAC,sBAAsB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC/C,CAAC,CAAC;QAEM,6BAAwB,GAAG,IAAI,uBAAY,EAAK,CAAC;QACzD;;;;WAIG;QACH,6BAAwB,GAAG,CAAC,QAAoC,EAAE,EAAE;YAClE,IAAI,CAAC,wBAAwB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC9C,CAAC,CAAC;QAEF;;;;WAIG;QACH,gCAA2B,GAAG,CAC5B,QAAoC,EAC9B,EAAE;YACR,IAAI,CAAC,wBAAwB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QACjD,CAAC,CAAC;QAEF;;;;;WAKG;QACH,oBAAe,GAAG,CAAC,WAAmB,EAAW,EAAE;YACjD,OAAO,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QAC5C,CAAC,CAAC;QAsBF;;;;;;WAMG;QACH,uBAAkB,GAAG,CAAC,EAAU,EAAE,EAAE;YAClC,MAAM,QAAQ,GAAG,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAC3C,IAAI,CAAC,QAAQ;gBAAE,OAAO;YAEtB,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YAC7B,QAAQ,CAAC,oBAAoB,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;YACnD,IAAI,CAAC,cAAc,EAAE,CAAC;YACtB,IAAI,CAAC,wBAAwB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QACjD,CAAC,CAAC;QAEF;;;;;WAKG;QACH,oBAAe,GAAG,CAAC,WAAmB,EAAiB,EAAE;YACvD,OAAO,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QAC5C,CAAC,CAAC;QAEF;;;;WAIG;QACH,WAAM,GAAG,GAAQ,EAAE;YACjB,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC,CAAC;QAChD,CAAC,CAAC;IACJ,CAAC;IAvDC;;;;;;;OAOG;IACH,GAAG,CAAC,MAAS;QACX,MAAM,QAAQ,GAAG,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;QAC5D,IAAI,QAAQ,EAAE,CAAC;YACb,QAAQ,CAAC,oBAAoB,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QACrD,CAAC;QAED,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;QACnD,MAAM,CAAC,iBAAiB,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAC9C,IAAI,CAAC,cAAc,EAAE,CAAC;QACtB,IAAI,CAAC,sBAAsB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC7C,CAAC;CAqCF;AA9GD,kDA8GC","sourcesContent":["import { ObserverList } from \"../Entities\";\r\nimport { AppObjectEntity } from \"./AppObjectEntity\";\r\n\r\n/**\r\n * A repository for managing collections of AppObjectEntity instances.\r\n *\r\n * This generic class provides a centralized way to store, retrieve, and manage\r\n * entities that are associated with specific AppObjects. It implements the observer\r\n * pattern to notify listeners when entities are added or removed.\r\n *\r\n * @template T - The type of entities managed by this repository, must extend AppObjectEntity\r\n * @extends AppObjectEntity\r\n *\r\n * @example\r\n * ```typescript\r\n * // Create a repository for player entities\r\n * const playerRepo = new AppObjectEntityRepo<PlayerEntity>();\r\n *\r\n * // Add a new player\r\n * const player = new PlayerEntity(appObject);\r\n * playerRepo.add(player);\r\n *\r\n * // Get a player by its AppObject ID\r\n * const retrievedPlayer = playerRepo.getForAppObject(\"player1\");\r\n * ```\r\n */\r\nexport class AppObjectEntityRepo<\r\n T extends AppObjectEntity,\r\n> extends AppObjectEntity {\r\n private entityLookup = new Map<string, T>();\r\n\r\n private onEntityAddedObservers = new ObserverList<T>();\r\n /**\r\n * Registers an observer to be notified when an entity is added to the repository.\r\n *\r\n * @param {(addedEntity: T) => void} observer - The callback function to invoke when an entity is added\r\n */\r\n addEntityAddedObserver = (observer: (addedEntity: T) => void) => {\r\n this.onEntityAddedObservers.add(observer);\r\n };\r\n\r\n /**\r\n * Removes an observer previously registered for entity addition notifications.\r\n *\r\n * @param {(addedEntity: T) => void} observer - The callback function to remove\r\n */\r\n removeEntityAddedObserver = (observer: (addedEntity: T) => void): void => {\r\n this.onEntityAddedObservers.remove(observer);\r\n };\r\n\r\n private onEntityRemovedObservers = new ObserverList<T>();\r\n /**\r\n * Registers an observer to be notified when an entity is removed from the repository.\r\n *\r\n * @param {(removedEntity: T) => void} observer - The callback function to invoke when an entity is removed\r\n */\r\n addEntityRemovedObserver = (observer: (removedEntity: T) => void) => {\r\n this.onEntityRemovedObservers.add(observer);\r\n };\r\n\r\n /**\r\n * Removes an observer previously registered for entity removal notifications.\r\n *\r\n * @param {(removedEntity: T) => void} observer - The callback function to remove\r\n */\r\n removeEntityRemovedObserver = (\r\n observer: (removedEntity: T) => void\r\n ): void => {\r\n this.onEntityRemovedObservers.remove(observer);\r\n };\r\n\r\n /**\r\n * Checks if an entity exists for the given AppObject ID.\r\n *\r\n * @param {string} appObjectID - The ID of the AppObject\r\n * @returns {boolean} True if an entity exists for the given AppObject ID, false otherwise\r\n */\r\n hasForAppObject = (appObjectID: string): boolean => {\r\n return this.entityLookup.has(appObjectID);\r\n };\r\n\r\n /**\r\n * Adds an entity to the repository.\r\n *\r\n * If an entity with the same AppObject ID already exists, it is replaced.\r\n * Notifies all registered observers after the entity is added.\r\n *\r\n * @param {T} entity - The entity to add\r\n */\r\n add(entity: T) {\r\n const existing = this.entityLookup.get(entity.appObject.id);\r\n if (existing) {\r\n existing.removeChangeObserver(this.notifyOnChange);\r\n }\r\n\r\n this.entityLookup.set(entity.appObject.id, entity);\r\n entity.addChangeObserver(this.notifyOnChange);\r\n this.notifyOnChange();\r\n this.onEntityAddedObservers.notify(entity);\r\n }\r\n\r\n /**\r\n * Removes the entity associated with the specified AppObject ID.\r\n *\r\n * Notifies all registered observers after the entity is removed.\r\n *\r\n * @param {string} id - The ID of the AppObject whose entity should be removed\r\n */\r\n removeForAppObject = (id: string) => {\r\n const existing = this.entityLookup.get(id);\r\n if (!existing) return;\r\n\r\n this.entityLookup.delete(id);\r\n existing.removeChangeObserver(this.notifyOnChange);\r\n this.notifyOnChange();\r\n this.onEntityRemovedObservers.notify(existing);\r\n };\r\n\r\n /**\r\n * Gets the entity associated with the specified AppObject ID.\r\n *\r\n * @param {string} appObjectID - The ID of the AppObject\r\n * @returns {T | undefined} The entity if found, undefined otherwise\r\n */\r\n getForAppObject = (appObjectID: string): T | undefined => {\r\n return this.entityLookup.get(appObjectID);\r\n };\r\n\r\n /**\r\n * Gets all entities in the repository.\r\n *\r\n * @returns {T[]} An array of all entities\r\n */\r\n getAll = (): T[] => {\r\n return Array.from(this.entityLookup.values());\r\n };\r\n}\r\n"]}
|
|
1
|
+
{"version":3,"file":"AppObjectEntityRepo.js","sourceRoot":"","sources":["../../../src/AppObject/AppObjectEntityRepo.ts"],"names":[],"mappings":";;;AAAA,0CAA2C;AAC3C,4CAAgD;AAChD,uDAAoD;AAEpD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,MAAa,mBAEX,SAAQ,iCAAe;IAFzB;;QAGU,iBAAY,GAAG,IAAI,GAAG,EAAa,CAAC;QAEpC,2BAAsB,GAAG,IAAI,uBAAY,EAAK,CAAC;QACvD;;;;WAIG;QACH,2BAAsB,GAAG,CAAC,QAAkC,EAAE,EAAE;YAC9D,IAAI,CAAC,sBAAsB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC5C,CAAC,CAAC;QAEF;;;;WAIG;QACH,8BAAyB,GAAG,CAAC,QAAkC,EAAQ,EAAE;YACvE,IAAI,CAAC,sBAAsB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC/C,CAAC,CAAC;QAEM,6BAAwB,GAAG,IAAI,uBAAY,EAAK,CAAC;QACzD;;;;WAIG;QACH,6BAAwB,GAAG,CAAC,QAAoC,EAAE,EAAE;YAClE,IAAI,CAAC,wBAAwB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC9C,CAAC,CAAC;QAEF;;;;WAIG;QACH,gCAA2B,GAAG,CAC5B,QAAoC,EAC9B,EAAE;YACR,IAAI,CAAC,wBAAwB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QACjD,CAAC,CAAC;QAEF;;;;;WAKG;QACH,QAAG,GAAG,CAAC,EAAU,EAAW,EAAE;YAC5B,OAAO,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACnC,CAAC,CAAC;QAEF;;;;;;WAMG;QACH,oBAAe,GAAG,CAAC,WAAmB,EAAW,EAAE;YACjD,OAAO,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QAC/B,CAAC,CAAC;QAqDF;;;;;;WAMG;QACH,eAAU,GAAG,CAAC,EAAU,EAAE,EAAE;YAC1B,MAAM,QAAQ,GAAG,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAC3C,IAAI,CAAC,QAAQ;gBAAE,OAAO;YAEtB,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YAC7B,QAAQ,CAAC,oBAAoB,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;YACnD,IAAI,CAAC,cAAc,EAAE,CAAC;YACtB,IAAI,CAAC,wBAAwB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QACjD,CAAC,CAAC;QAEF;;;;;;;WAOG;QACH,uBAAkB,GAAG,CAAC,EAAU,EAAE,EAAE;YAClC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;QACtB,CAAC,CAAC;QAEF;;;;;WAKG;QACH,cAAS,GAAG,GAAG,EAAE;YACf,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC,CAAC;YAExD,QAAQ,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,EAAE;gBAC1B,MAAM,CAAC,oBAAoB,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;gBACjD,IAAI,CAAC,wBAAwB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YAC/C,CAAC,CAAC,CAAC;YAEH,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,CAAC;YAC1B,IAAI,CAAC,cAAc,EAAE,CAAC;QACxB,CAAC,CAAC;QAEF;;;;;WAKG;QACH,YAAO,GAAG,CAAC,EAAU,EAAiB,EAAE;YACtC,OAAO,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACnC,CAAC,CAAC;QAEF;;;;;;WAMG;QACH,oBAAe,GAAG,CAAC,WAAmB,EAAiB,EAAE;YACvD,OAAO,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;QACnC,CAAC,CAAC;QAEF;;;;WAIG;QACH,WAAM,GAAG,GAAQ,EAAE;YACjB,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC,CAAC;QAChD,CAAC,CAAC;IACJ,CAAC;IA/HC;;;;;;;OAOG;IACH,GAAG,CAAC,MAAS;QACX,MAAM,QAAQ,GAAG,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;QAC5D,IAAI,QAAQ,EAAE,CAAC;YACb,QAAQ,CAAC,oBAAoB,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QACrD,CAAC;QAED,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;QACnD,MAAM,CAAC,iBAAiB,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAC9C,IAAI,CAAC,cAAc,EAAE,CAAC;QACtB,IAAI,CAAC,sBAAsB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC7C,CAAC;IAED;;;;;;;;OAQG;IACH,MAAM,CAAC,EAAW;QAChB,MAAM,OAAO,GAAG,EAAE,IAAI,IAAA,4BAAgB,GAAE,CAAC;QACzC,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;QAC3C,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACjB,OAAO,MAAM,CAAC;IAChB,CAAC;IAED;;;;;;;;;OASG;IACH,aAAa,CAAC,EAAU;QACtB,2EAA2E;QAC3E,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;IACrD,CAAC;CA8EF;AAjMD,kDAiMC","sourcesContent":["import { ObserverList } from \"../Entities\";\r\nimport { generateUniqueID } from \"../Utilities\";\r\nimport { AppObjectEntity } from \"./AppObjectEntity\";\r\n\r\n/**\r\n * A repository for managing collections of AppObjectEntity instances.\r\n *\r\n * This generic class provides a centralized way to store, retrieve, and manage\r\n * entities that are associated with specific AppObjects. It implements the observer\r\n * pattern to notify listeners when entities are added or removed.\r\n *\r\n * Derived classes should override the `entityFactory` method to provide custom\r\n * entity creation logic for the `create` method.\r\n *\r\n * @template T - The type of entities managed by this repository, must extend AppObjectEntity\r\n * @extends AppObjectEntity\r\n *\r\n * @example\r\n * ```typescript\r\n * // Create a custom repository with entity factory\r\n * class PlayerRepo extends AppObjectEntityRepo<PlayerEntity> {\r\n * entityFactory(id: string): PlayerEntity {\r\n * const appObject = makeAppObject(id, this.appObjects);\r\n * return new PlayerEntity(appObject);\r\n * }\r\n * }\r\n *\r\n * const playerRepo = new PlayerRepo(repoAppObject);\r\n *\r\n * // Create a new player with auto-generated ID\r\n * const player = playerRepo.create();\r\n *\r\n * // Create a new player with specific ID\r\n * const specificPlayer = playerRepo.create(\"player1\");\r\n *\r\n * // Get a player by its ID\r\n * const retrievedPlayer = playerRepo.getById(\"player1\");\r\n * ```\r\n */\r\nexport class AppObjectEntityRepo<\r\n T extends AppObjectEntity,\r\n> extends AppObjectEntity {\r\n private entityLookup = new Map<string, T>();\r\n\r\n private onEntityAddedObservers = new ObserverList<T>();\r\n /**\r\n * Registers an observer to be notified when an entity is added to the repository.\r\n *\r\n * @param {(addedEntity: T) => void} observer - The callback function to invoke when an entity is added\r\n */\r\n addEntityAddedObserver = (observer: (addedEntity: T) => void) => {\r\n this.onEntityAddedObservers.add(observer);\r\n };\r\n\r\n /**\r\n * Removes an observer previously registered for entity addition notifications.\r\n *\r\n * @param {(addedEntity: T) => void} observer - The callback function to remove\r\n */\r\n removeEntityAddedObserver = (observer: (addedEntity: T) => void): void => {\r\n this.onEntityAddedObservers.remove(observer);\r\n };\r\n\r\n private onEntityRemovedObservers = new ObserverList<T>();\r\n /**\r\n * Registers an observer to be notified when an entity is removed from the repository.\r\n *\r\n * @param {(removedEntity: T) => void} observer - The callback function to invoke when an entity is removed\r\n */\r\n addEntityRemovedObserver = (observer: (removedEntity: T) => void) => {\r\n this.onEntityRemovedObservers.add(observer);\r\n };\r\n\r\n /**\r\n * Removes an observer previously registered for entity removal notifications.\r\n *\r\n * @param {(removedEntity: T) => void} observer - The callback function to remove\r\n */\r\n removeEntityRemovedObserver = (\r\n observer: (removedEntity: T) => void\r\n ): void => {\r\n this.onEntityRemovedObservers.remove(observer);\r\n };\r\n\r\n /**\r\n * Checks if an entity exists for the given ID.\r\n *\r\n * @param {string} id - The ID of the AppObject\r\n * @returns {boolean} True if an entity exists for the given ID, false otherwise\r\n */\r\n has = (id: string): boolean => {\r\n return this.entityLookup.has(id);\r\n };\r\n\r\n /**\r\n * Checks if an entity exists for the given AppObject ID.\r\n *\r\n * @param {string} appObjectID - The ID of the AppObject\r\n * @returns {boolean} True if an entity exists for the given AppObject ID, false otherwise\r\n * @deprecated Use has instead\r\n */\r\n hasForAppObject = (appObjectID: string): boolean => {\r\n return this.has(appObjectID);\r\n };\r\n\r\n /**\r\n * Adds an entity to the repository.\r\n *\r\n * If an entity with the same AppObject ID already exists, it is replaced.\r\n * Notifies all registered observers after the entity is added.\r\n *\r\n * @param {T} entity - The entity to add\r\n */\r\n add(entity: T) {\r\n const existing = this.entityLookup.get(entity.appObject.id);\r\n if (existing) {\r\n existing.removeChangeObserver(this.notifyOnChange);\r\n }\r\n\r\n this.entityLookup.set(entity.appObject.id, entity);\r\n entity.addChangeObserver(this.notifyOnChange);\r\n this.notifyOnChange();\r\n this.onEntityAddedObservers.notify(entity);\r\n }\r\n\r\n /**\r\n * Creates a new entity with the specified or auto-generated ID and adds it to the repository.\r\n *\r\n * This method uses the `entityFactory` method to create the entity instance.\r\n * If no ID is provided, a unique ID is generated automatically.\r\n *\r\n * @param {string} [id] - Optional ID for the entity. If not provided, a unique ID is generated.\r\n * @returns {T} The newly created entity\r\n */\r\n create(id?: string): T {\r\n const idToUse = id ?? generateUniqueID();\r\n const entity = this.entityFactory(idToUse);\r\n this.add(entity);\r\n return entity;\r\n }\r\n\r\n /**\r\n * Factory method for creating entity instances.\r\n *\r\n * This method must be overridden in derived classes to provide custom entity creation logic.\r\n * It is called by the `create` method to instantiate new entities.\r\n *\r\n * @param {string} id - The ID to use for the new entity\r\n * @returns {T} A new entity instance\r\n * @throws {Error} If not overridden in derived class\r\n */\r\n entityFactory(id: string): T {\r\n // Override this method in derived classes to provide entity creation logic\r\n throw new Error(\"Entity factory not implemented.\");\r\n }\r\n\r\n /**\r\n * Removes the entity associated with the specified ID.\r\n *\r\n * Notifies all registered observers after the entity is removed.\r\n *\r\n * @param {string} id - The ID of the AppObject whose entity should be removed\r\n */\r\n removeById = (id: string) => {\r\n const existing = this.entityLookup.get(id);\r\n if (!existing) return;\r\n\r\n this.entityLookup.delete(id);\r\n existing.removeChangeObserver(this.notifyOnChange);\r\n this.notifyOnChange();\r\n this.onEntityRemovedObservers.notify(existing);\r\n };\r\n\r\n /**\r\n * Removes the entity associated with the specified AppObject ID.\r\n *\r\n * Notifies all registered observers after the entity is removed.\r\n *\r\n * @param {string} id - The ID of the AppObject whose entity should be removed\r\n * @deprecated Use removeById instead\r\n */\r\n removeForAppObject = (id: string) => {\r\n this.removeById(id);\r\n };\r\n\r\n /**\r\n * Removes all entities from the repository.\r\n *\r\n * Unsubscribes from all entity change observers and notifies all registered\r\n * removal observers for each entity before clearing the repository.\r\n */\r\n deleteAll = () => {\r\n const entities = Array.from(this.entityLookup.values());\r\n\r\n entities.forEach((entity) => {\r\n entity.removeChangeObserver(this.notifyOnChange);\r\n this.onEntityRemovedObservers.notify(entity);\r\n });\r\n\r\n this.entityLookup.clear();\r\n this.notifyOnChange();\r\n };\r\n\r\n /**\r\n * Gets the entity associated with the specified ID.\r\n *\r\n * @param {string} id - The ID of the AppObject\r\n * @returns {T | undefined} The entity if found, undefined otherwise\r\n */\r\n getById = (id: string): T | undefined => {\r\n return this.entityLookup.get(id);\r\n };\r\n\r\n /**\r\n * Gets the entity associated with the specified AppObject ID.\r\n *\r\n * @param {string} appObjectID - The ID of the AppObject\r\n * @returns {T | undefined} The entity if found, undefined otherwise\r\n * @deprecated Use getById instead\r\n */\r\n getForAppObject = (appObjectID: string): T | undefined => {\r\n return this.getById(appObjectID);\r\n };\r\n\r\n /**\r\n * Gets all entities in the repository.\r\n *\r\n * @returns {T[]} An array of all entities\r\n */\r\n getAll = (): T[] => {\r\n return Array.from(this.entityLookup.values());\r\n };\r\n}\r\n"]}
|
|
@@ -24,13 +24,21 @@ class AppObjectPM extends AppObjectComponent_1.AppObjectComponent {
|
|
|
24
24
|
/** Identifies this as a Presentation Manager component */
|
|
25
25
|
this.componentType = AppObjectComponent_1.AppObjectComponentType.PM;
|
|
26
26
|
/** List of view update functions to notify when the view model changes */
|
|
27
|
-
this.
|
|
27
|
+
this.views = new Entities_1.ObserverList();
|
|
28
|
+
/**
|
|
29
|
+
* List of entities being observed by this PM.
|
|
30
|
+
* Used for automatic cleanup during disposal.
|
|
31
|
+
*/
|
|
32
|
+
this.observedEntities = [];
|
|
28
33
|
}
|
|
29
34
|
/**
|
|
30
35
|
* Gets the most recently generated view model
|
|
31
|
-
* @returns The last view model or
|
|
36
|
+
* @returns The last view model, or `defaultVM` if no view model has been generated yet,
|
|
37
|
+
* or undefined if neither exists
|
|
32
38
|
*/
|
|
33
39
|
get lastVM() {
|
|
40
|
+
if (!this._lastVM)
|
|
41
|
+
return this.defaultVM;
|
|
34
42
|
return this._lastVM;
|
|
35
43
|
}
|
|
36
44
|
/**
|
|
@@ -40,11 +48,92 @@ class AppObjectPM extends AppObjectComponent_1.AppObjectComponent {
|
|
|
40
48
|
* @param updateView Function to call with updated view models
|
|
41
49
|
*/
|
|
42
50
|
addView(updateView) {
|
|
43
|
-
this.
|
|
51
|
+
if (this.views.length === 0) {
|
|
52
|
+
// There are no views yet - generate the initial view model
|
|
53
|
+
this.formVM();
|
|
54
|
+
}
|
|
55
|
+
this.views.add(updateView);
|
|
44
56
|
// Immediately update the view with current view model if one exists
|
|
45
57
|
if (this._lastVM !== undefined) {
|
|
46
58
|
updateView(this._lastVM);
|
|
47
59
|
}
|
|
60
|
+
this.onViewAdded();
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Called when an observed entity changes.
|
|
64
|
+
* If views are registered, this method calls `formVM()` to regenerate the view model.
|
|
65
|
+
*
|
|
66
|
+
* This method is automatically invoked when entities registered via `observeEntity()`
|
|
67
|
+
* notify their observers. It implements lazy evaluation - view models are only
|
|
68
|
+
* formed when there are views to receive them.
|
|
69
|
+
*/
|
|
70
|
+
onEntityChanged() {
|
|
71
|
+
if (this.views.length === 0) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
this.formVM();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Forms and updates the view model based on current entity state.
|
|
80
|
+
*
|
|
81
|
+
* Override this method in derived classes to:
|
|
82
|
+
* 1. Read data from observed entities
|
|
83
|
+
* 2. Transform the data into a view model
|
|
84
|
+
* 3. Call `doUpdateView(vm)` with the new view model
|
|
85
|
+
*
|
|
86
|
+
* This method is only called when at least one view has been registered,
|
|
87
|
+
* implementing lazy evaluation to avoid unnecessary computation.
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* ```typescript
|
|
91
|
+
* formVM(): void {
|
|
92
|
+
* const entity = this.getCachedLocalComponent<MyEntity>(MyEntity.type);
|
|
93
|
+
* if (entity) {
|
|
94
|
+
* this.doUpdateView({
|
|
95
|
+
* displayName: entity.name,
|
|
96
|
+
* isActive: entity.active
|
|
97
|
+
* });
|
|
98
|
+
* }
|
|
99
|
+
* }
|
|
100
|
+
* ```
|
|
101
|
+
*/
|
|
102
|
+
formVM() {
|
|
103
|
+
// To be implemented by derived classes
|
|
104
|
+
// Form up the VM and then call doUpdateView(vm: T)
|
|
105
|
+
// This will only be called if at least one view has been registered
|
|
106
|
+
}
|
|
107
|
+
onViewAdded() {
|
|
108
|
+
// Can be overridden by derived classes to react when the first view is added
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Registers an entity to be observed for changes.
|
|
112
|
+
*
|
|
113
|
+
* When the entity notifies of changes (via `notifyOnChange()`), this PM will
|
|
114
|
+
* automatically call `onEntityChanged()`, which in turn calls `formVM()` if
|
|
115
|
+
* views are registered.
|
|
116
|
+
*
|
|
117
|
+
* The entity observer is automatically cleaned up when this PM is disposed.
|
|
118
|
+
*
|
|
119
|
+
* @param entity The entity to observe for changes
|
|
120
|
+
*
|
|
121
|
+
* @example
|
|
122
|
+
* ```typescript
|
|
123
|
+
* constructor(appObj: AppObject) {
|
|
124
|
+
* super(appObj, MyPM.type);
|
|
125
|
+
* const entity = appObj.getComponent<MyEntity>(MyEntity.type);
|
|
126
|
+
* if (entity) {
|
|
127
|
+
* this.observeEntity(entity);
|
|
128
|
+
* }
|
|
129
|
+
* }
|
|
130
|
+
* ```
|
|
131
|
+
*/
|
|
132
|
+
observeEntity(entity) {
|
|
133
|
+
entity.addChangeObserver(() => {
|
|
134
|
+
this.onEntityChanged();
|
|
135
|
+
});
|
|
136
|
+
this.observedEntities.push(entity);
|
|
48
137
|
}
|
|
49
138
|
/**
|
|
50
139
|
* Unregisters a previously added view update function
|
|
@@ -52,7 +141,7 @@ class AppObjectPM extends AppObjectComponent_1.AppObjectComponent {
|
|
|
52
141
|
* @param updateView The view update function to remove
|
|
53
142
|
*/
|
|
54
143
|
removeView(updateView) {
|
|
55
|
-
this.
|
|
144
|
+
this.views.remove(updateView);
|
|
56
145
|
}
|
|
57
146
|
/**
|
|
58
147
|
* Updates the view model and notifies all registered views if the model has changed
|
|
@@ -69,13 +158,24 @@ class AppObjectPM extends AppObjectComponent_1.AppObjectComponent {
|
|
|
69
158
|
return;
|
|
70
159
|
}
|
|
71
160
|
this._lastVM = vm;
|
|
72
|
-
this.
|
|
161
|
+
this.views.notify(vm);
|
|
73
162
|
}
|
|
74
163
|
/**
|
|
75
164
|
* Cleans up resources and detaches this PM from its parent AppObject
|
|
165
|
+
*
|
|
166
|
+
* This method:
|
|
167
|
+
* 1. Removes change observers from all observed entities
|
|
168
|
+
* 2. Clears all view observers
|
|
169
|
+
* 3. Removes this component from its parent AppObject if still attached
|
|
170
|
+
* 4. Calls the parent class dispose method
|
|
76
171
|
*/
|
|
77
172
|
dispose() {
|
|
78
|
-
this.
|
|
173
|
+
this.observedEntities.forEach((entity) => {
|
|
174
|
+
entity.removeChangeObserver(() => {
|
|
175
|
+
this.onEntityChanged();
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
this.views.clear();
|
|
79
179
|
if (this.appObject.getComponent(this.type) === this) {
|
|
80
180
|
this.appObject.removeComponent(this.type);
|
|
81
181
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AppObjectPM.js","sourceRoot":"","sources":["../../../src/AppObject/AppObjectPM.ts"],"names":[],"mappings":";;;AAAA,0CAA2C;AAC3C,
|
|
1
|
+
{"version":3,"file":"AppObjectPM.js","sourceRoot":"","sources":["../../../src/AppObject/AppObjectPM.ts"],"names":[],"mappings":"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","sourcesContent":["import { ObserverList } from \"../Entities\";\r\nimport {\r\n AppObjectComponent,\r\n AppObjectComponentType,\r\n} from \"./AppObjectComponent\";\r\nimport { AppObjectEntity } from \"./AppObjectEntity\";\r\n\r\n/**\r\n * Presentation Manager (PM) component that transforms application state into view models\r\n *\r\n * AppObjectPM acts as a mediator between application state (entities) and views.\r\n * It transforms raw application data into a format that's optimized for presentation,\r\n * implementing the presentation model or MVVM pattern.\r\n *\r\n * A PM typically:\r\n * 1. Observes entity changes\r\n * 2. Transforms entity data into view models\r\n * 3. Notifies attached views when view models change\r\n * 4. Handles view model caching and optimization\r\n *\r\n * @typeparam T The view model type this PM produces\r\n */\r\nexport abstract class AppObjectPM<T> extends AppObjectComponent {\r\n /** Identifies this as a Presentation Manager component */\r\n readonly componentType = AppObjectComponentType.PM;\r\n\r\n /**\r\n * Compares two view models to determine if they're equivalent\r\n * Used to prevent unnecessary view updates when the view model hasn't changed\r\n *\r\n * @param a First view model to compare\r\n * @param b Second view model to compare\r\n * @returns True if the view models are considered equal, false otherwise\r\n */\r\n abstract vmsAreEqual(a: T, b: T): boolean;\r\n\r\n /** The most recently generated view model */\r\n private _lastVM?: T;\r\n\r\n /**\r\n * Optional default view model to return when no view model has been generated yet.\r\n * If set, this value will be returned by the `lastVM` getter before any view model\r\n * has been created via `doUpdateView`.\r\n *\r\n * This is useful for providing initial state to views without requiring immediate\r\n * view model generation.\r\n */\r\n public defaultVM?: T;\r\n\r\n /**\r\n * Gets the most recently generated view model\r\n * @returns The last view model, or `defaultVM` if no view model has been generated yet,\r\n * or undefined if neither exists\r\n */\r\n get lastVM(): T | undefined {\r\n if (!this._lastVM) return this.defaultVM;\r\n return this._lastVM;\r\n }\r\n\r\n /** List of view update functions to notify when the view model changes */\r\n private views = new ObserverList<T>();\r\n\r\n /**\r\n * List of entities being observed by this PM.\r\n * Used for automatic cleanup during disposal.\r\n */\r\n private observedEntities: AppObjectEntity[] = [];\r\n\r\n /**\r\n * Registers a view update function to be called when the view model changes\r\n * If a view model already exists, the provided function is called immediately with that model\r\n *\r\n * @param updateView Function to call with updated view models\r\n */\r\n addView(updateView: (vm: T) => void): void {\r\n if (this.views.length === 0) {\r\n // There are no views yet - generate the initial view model\r\n this.formVM();\r\n }\r\n\r\n this.views.add(updateView);\r\n \r\n // Immediately update the view with current view model if one exists\r\n if (this._lastVM !== undefined) {\r\n updateView(this._lastVM);\r\n }\r\n\r\n this.onViewAdded();\r\n }\r\n\r\n /**\r\n * Called when an observed entity changes.\r\n * If views are registered, this method calls `formVM()` to regenerate the view model.\r\n *\r\n * This method is automatically invoked when entities registered via `observeEntity()`\r\n * notify their observers. It implements lazy evaluation - view models are only\r\n * formed when there are views to receive them.\r\n */\r\n onEntityChanged(): void {\r\n if (this.views.length === 0) {\r\n return;\r\n } else {\r\n this.formVM();\r\n }\r\n }\r\n\r\n /**\r\n * Forms and updates the view model based on current entity state.\r\n *\r\n * Override this method in derived classes to:\r\n * 1. Read data from observed entities\r\n * 2. Transform the data into a view model\r\n * 3. Call `doUpdateView(vm)` with the new view model\r\n *\r\n * This method is only called when at least one view has been registered,\r\n * implementing lazy evaluation to avoid unnecessary computation.\r\n *\r\n * @example\r\n * ```typescript\r\n * formVM(): void {\r\n * const entity = this.getCachedLocalComponent<MyEntity>(MyEntity.type);\r\n * if (entity) {\r\n * this.doUpdateView({\r\n * displayName: entity.name,\r\n * isActive: entity.active\r\n * });\r\n * }\r\n * }\r\n * ```\r\n */\r\n formVM(): void {\r\n // To be implemented by derived classes\r\n // Form up the VM and then call doUpdateView(vm: T)\r\n // This will only be called if at least one view has been registered\r\n }\r\n\r\n onViewAdded(): void {\r\n // Can be overridden by derived classes to react when the first view is added\r\n }\r\n\r\n /**\r\n * Registers an entity to be observed for changes.\r\n *\r\n * When the entity notifies of changes (via `notifyOnChange()`), this PM will\r\n * automatically call `onEntityChanged()`, which in turn calls `formVM()` if\r\n * views are registered.\r\n *\r\n * The entity observer is automatically cleaned up when this PM is disposed.\r\n *\r\n * @param entity The entity to observe for changes\r\n *\r\n * @example\r\n * ```typescript\r\n * constructor(appObj: AppObject) {\r\n * super(appObj, MyPM.type);\r\n * const entity = appObj.getComponent<MyEntity>(MyEntity.type);\r\n * if (entity) {\r\n * this.observeEntity(entity);\r\n * }\r\n * }\r\n * ```\r\n */\r\n protected observeEntity(entity: AppObjectEntity): void {\r\n entity.addChangeObserver(() => {\r\n this.onEntityChanged();\r\n });\r\n\r\n this.observedEntities.push(entity);\r\n }\r\n\r\n /**\r\n * Unregisters a previously added view update function\r\n *\r\n * @param updateView The view update function to remove\r\n */\r\n removeView(updateView: (vm: T) => void): void {\r\n this.views.remove(updateView);\r\n }\r\n\r\n /**\r\n * Updates the view model and notifies all registered views if the model has changed\r\n * This method should be called by derived classes when the view model needs to be updated\r\n *\r\n * The method performs equality checking to prevent unnecessary updates when\r\n * the new view model is equivalent to the previous one\r\n *\r\n * @param vm The new view model\r\n */\r\n doUpdateView(vm: T) {\r\n // Skip update if the view model hasn't changed\r\n if (this._lastVM && this.vmsAreEqual(this._lastVM, vm)) {\r\n return;\r\n }\r\n\r\n this._lastVM = vm;\r\n this.views.notify(vm);\r\n }\r\n\r\n /**\r\n * Cleans up resources and detaches this PM from its parent AppObject\r\n *\r\n * This method:\r\n * 1. Removes change observers from all observed entities\r\n * 2. Clears all view observers\r\n * 3. Removes this component from its parent AppObject if still attached\r\n * 4. Calls the parent class dispose method\r\n */\r\n dispose() {\r\n this.observedEntities.forEach((entity) => {\r\n entity.removeChangeObserver(() => {\r\n this.onEntityChanged();\r\n });\r\n });\r\n\r\n this.views.clear();\r\n if (this.appObject.getComponent(this.type) === this) {\r\n this.appObject.removeComponent(this.type);\r\n }\r\n super.dispose();\r\n }\r\n}\r\n"]}
|
|
@@ -127,10 +127,20 @@ class AppObjectRepoImp extends AppObjectRepo {
|
|
|
127
127
|
}
|
|
128
128
|
registerSingleton(component) {
|
|
129
129
|
if (this.singletons.has(component.type)) {
|
|
130
|
-
this.submitWarning("AppObjectRepo", `Singleton for type ${component.type} already exists.
|
|
130
|
+
this.submitWarning("AppObjectRepo", `Singleton for type ${component.type} already exists. Replacing`);
|
|
131
131
|
}
|
|
132
132
|
this.singletons.set(component.type, component);
|
|
133
133
|
}
|
|
134
|
+
unregisterSingleton(type) {
|
|
135
|
+
this.singletons.delete(type);
|
|
136
|
+
}
|
|
137
|
+
hasSingleton(type) {
|
|
138
|
+
if (this.singletons.has(type)) {
|
|
139
|
+
return true;
|
|
140
|
+
}
|
|
141
|
+
const components = this.getAllComponents(type);
|
|
142
|
+
return components.length === 1;
|
|
143
|
+
}
|
|
134
144
|
getSingleton(type) {
|
|
135
145
|
if (this.singletons.has(type)) {
|
|
136
146
|
return this.singletons.get(type);
|