@agile-ts/core
Advanced tools
Comparing version 0.0.5 to 0.0.6
# Change Log | ||
## 0.0.6 | ||
### Patch Changes | ||
- 86e6890: Updated Tests in Core | Fixed some Bugs | ||
All notable changes to this project will be documented in this file. | ||
@@ -10,8 +16,4 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. | ||
## 0.0.4 (2020-11-03) | ||
**Note:** Version bump only for package @agile-ts/core |
@@ -1,2 +0,2 @@ | ||
import { Runtime, Integration, State, Storage, StorageConfigInterface, Collection, DefaultItem, Computed, Event, EventConfig, DefaultEventPayload, Integrations, Observer, SubController } from "./internal"; | ||
import { Runtime, Integration, State, Storage, Collection, DefaultItem, Computed, Event, CreateEventConfigInterface, DefaultEventPayload, Integrations, Observer, SubController, Storages, CreateStorageConfigInterface, RegisterConfigInterface, Logger, CreateLoggerConfigInterface, StateConfigInterface } from "./internal"; | ||
export declare class Agile { | ||
@@ -6,5 +6,6 @@ config: AgileConfigInterface; | ||
subController: SubController; | ||
storage: Storage; | ||
storages: Storages; | ||
integrations: Integrations; | ||
static initialIntegrations: Integration[]; | ||
static logger: Logger; | ||
/** | ||
@@ -15,15 +16,9 @@ * @public | ||
*/ | ||
constructor(config?: AgileConfigInterface); | ||
constructor(config?: CreateAgileConfigInterface); | ||
/** | ||
* @public | ||
* Integrates framework into Agile | ||
* @param integration - Integration that gets registered/integrated | ||
*/ | ||
use(integration: Integration): this; | ||
/** | ||
* @public | ||
* Storage - Handy Interface for storing Items permanently | ||
* @param config - Config | ||
*/ | ||
Storage: (config: StorageConfigInterface) => Storage; | ||
Storage: (config: CreateStorageConfigInterface) => Storage; | ||
/** | ||
@@ -33,5 +28,5 @@ * @public | ||
* @param initialValue - Initial Value of the State | ||
* @param key - Key/Name of the State | ||
* @param config - Config | ||
*/ | ||
State: <ValueType>(initialValue: ValueType, key?: string | undefined) => State<ValueType>; | ||
State: <ValueType>(initialValue: ValueType, config?: StateConfigInterface) => State<ValueType>; | ||
/** | ||
@@ -42,3 +37,3 @@ * @public | ||
*/ | ||
Collection: <DataType = DefaultItem>(config?: import("./collection").CollectionConfigInterface | ((collection: Collection<DataType>) => import("./collection").CollectionConfigInterface) | undefined) => Collection<DataType>; | ||
Collection: <DataType = DefaultItem>(config?: import("./collection").CreateCollectionConfigInterface | ((collection: Collection<DataType>) => import("./collection").CreateCollectionConfigInterface) | undefined) => Collection<DataType>; | ||
/** | ||
@@ -56,14 +51,26 @@ * @public | ||
*/ | ||
Event: <PayloadType = DefaultEventPayload>(config?: EventConfig | undefined) => Event<PayloadType>; | ||
Event: <PayloadType = DefaultEventPayload>(config?: CreateEventConfigInterface | undefined) => Event<PayloadType>; | ||
/** | ||
* @public | ||
* Configures Agile Storage | ||
* @param storage - Storage that will get used as Agile Storage | ||
* Integrates framework into Agile | ||
* @param integration - Integration that gets registered/integrated | ||
*/ | ||
configureStorage(storage: Storage): void; | ||
integrate(integration: Integration): this; | ||
/** | ||
* @public | ||
* Registers new Storage as Agile Storage | ||
* @param storage - new Storage | ||
* @param config - Config | ||
*/ | ||
registerStorage(storage: Storage, config?: RegisterConfigInterface): this; | ||
/** | ||
* @public | ||
* Checks if Agile has any registered Integration | ||
*/ | ||
hasIntegration(): boolean; | ||
/** | ||
* @public | ||
* Checks if Agile has any registered Storage | ||
*/ | ||
hasStorage(): boolean; | ||
} | ||
@@ -75,6 +82,12 @@ /** | ||
*/ | ||
export interface AgileConfigInterface { | ||
logJobs?: boolean; | ||
export interface CreateAgileConfigInterface { | ||
logConfig?: CreateLoggerConfigInterface; | ||
waitForMount?: boolean; | ||
storageConfig?: StorageConfigInterface; | ||
localStorage?: boolean; | ||
} | ||
/** | ||
* @param waitForMount - If Agile should wait until the component mounts | ||
*/ | ||
export interface AgileConfigInterface { | ||
waitForMount: boolean; | ||
} |
@@ -12,3 +12,2 @@ "use strict"; | ||
constructor(config = {}) { | ||
this.config = config; | ||
//========================================================================================================= | ||
@@ -30,5 +29,5 @@ // Storage | ||
* @param initialValue - Initial Value of the State | ||
* @param key - Key/Name of the State | ||
* @param config - Config | ||
*/ | ||
this.State = (initialValue, key) => new internal_1.State(this, initialValue, key); | ||
this.State = (initialValue, config = {}) => new internal_1.State(this, initialValue, config); | ||
//========================================================================================================= | ||
@@ -52,5 +51,5 @@ // Collection | ||
*/ | ||
this.Computed = (computeFunction, deps | ||
// @ts-ignore | ||
) => new internal_1.Computed(this, computeFunction, deps); | ||
this.Computed = (computeFunction, deps) => new internal_1.Computed(this, computeFunction, { | ||
computedDeps: deps, | ||
}); | ||
//========================================================================================================= | ||
@@ -65,11 +64,31 @@ // Event | ||
this.Event = (config) => new internal_1.Event(this, config); | ||
config = internal_1.defineConfig(config, { | ||
localStorage: true, | ||
waitForMount: false, | ||
logConfig: {}, | ||
}); | ||
config.logConfig = internal_1.defineConfig(config.logConfig, { | ||
prefix: "Agile", | ||
active: true, | ||
level: internal_1.Logger.level.WARN, | ||
canUseCustomStyles: true, | ||
allowedTags: ["runtime", "storage", "subscription", "multieditor"], | ||
}); | ||
this.config = { | ||
waitForMount: config.waitForMount, | ||
}; | ||
this.integrations = new internal_1.Integrations(this); | ||
this.runtime = new internal_1.Runtime(this); | ||
this.subController = new internal_1.SubController(this); | ||
this.storage = new internal_1.Storage(config.storageConfig || {}); | ||
this.storages = new internal_1.Storages(this, { | ||
localStorage: config.localStorage, | ||
}); | ||
// Assign customized config to Logger | ||
Agile.logger = new internal_1.Logger(config.logConfig); | ||
// Create global instance of Agile | ||
internal_1.globalBind("__agile__", this); | ||
if (!internal_1.globalBind("__agile__", this)) | ||
Agile.logger.warn("Be careful with multiple Agile Instances in one Application!"); | ||
} | ||
//========================================================================================================= | ||
// Use | ||
// Integrate | ||
//========================================================================================================= | ||
@@ -81,3 +100,3 @@ /** | ||
*/ | ||
use(integration) { | ||
integrate(integration) { | ||
this.integrations.integrate(integration); | ||
@@ -87,16 +106,13 @@ return this; | ||
//========================================================================================================= | ||
// Set Storage | ||
// Register Storage | ||
//========================================================================================================= | ||
/** | ||
* @public | ||
* Configures Agile Storage | ||
* @param storage - Storage that will get used as Agile Storage | ||
* Registers new Storage as Agile Storage | ||
* @param storage - new Storage | ||
* @param config - Config | ||
*/ | ||
configureStorage(storage) { | ||
// Get Observers that are already saved into a storage | ||
const persistentInstances = this.storage.persistentInstances; | ||
// Define new Storage | ||
this.storage = storage; | ||
// Transfer already saved items into new Storage | ||
persistentInstances.forEach((persistent) => persistent.initialLoading(persistent.key)); | ||
registerStorage(storage, config = {}) { | ||
this.storages.register(storage, config); | ||
return this; | ||
} | ||
@@ -113,4 +129,20 @@ //========================================================================================================= | ||
} | ||
//========================================================================================================= | ||
// Has Storage | ||
//========================================================================================================= | ||
/** | ||
* @public | ||
* Checks if Agile has any registered Storage | ||
*/ | ||
hasStorage() { | ||
return this.storages.hasStorage(); | ||
} | ||
} | ||
exports.Agile = Agile; | ||
Agile.initialIntegrations = []; // External added Integrations | ||
Agile.initialIntegrations = []; // External added initial Integrations | ||
// Static Logger with default config -> will be overwritten by config of last created Agile Instance | ||
Agile.logger = new internal_1.Logger({ | ||
prefix: "Agile", | ||
active: true, | ||
level: internal_1.Logger.level.WARN, | ||
}); |
@@ -1,5 +0,5 @@ | ||
import { Agile, Collection, CollectionKey, GroupKey, ItemKey, Persistent, StorageKey } from "../internal"; | ||
import { Collection, CollectionKey, CreatePersistentConfigInterface, Group, GroupKey, ItemKey, Persistent, PersistentKey, StorageKey } from "../internal"; | ||
export declare class CollectionPersistent<DataType = any> extends Persistent { | ||
collection: () => Collection; | ||
private defaultGroupSideEffectKey; | ||
collection: () => Collection<DataType>; | ||
static defaultGroupSideEffectKey: string; | ||
static storageItemKeyPattern: string; | ||
@@ -10,39 +10,44 @@ static storageGroupKeyPattern: string; | ||
* Collection Persist Manager - Handles permanent storing of Collection Value | ||
* @param agileInstance - An instance of Agile | ||
* @param collection - Collection that gets stored | ||
* @param key - Key of Storage property | ||
* @param config - Config | ||
*/ | ||
constructor(agileInstance: Agile, collection: Collection<DataType>, key?: StorageKey); | ||
set key(value: StorageKey); | ||
get key(): StorageKey; | ||
constructor(collection: Collection<DataType>, config?: CreatePersistentConfigInterface); | ||
/** | ||
* @public | ||
* Sets Key/Name of Persistent | ||
* @internal | ||
* Updates Key/Name of Persistent | ||
* @param value - New Key/Name of Persistent | ||
*/ | ||
setKey(value: StorageKey): Promise<void>; | ||
setKey(value?: StorageKey): Promise<void>; | ||
/** | ||
* @internal | ||
* Loads Value from Storage | ||
* Loads/Saves Storage Value for the first Time | ||
*/ | ||
initialLoading(): Promise<void>; | ||
/** | ||
* @internal | ||
* Loads Collection from Storage | ||
* @param key - Prefix Key of Persisted Instances (default PersistentKey) | ||
* @return Success? | ||
*/ | ||
loadValue(): Promise<boolean>; | ||
loadPersistedValue(key?: PersistentKey): Promise<boolean>; | ||
/** | ||
* @internal | ||
* Saves/Updates Value in Storage | ||
* Sets everything up so that the Collection gets saved in the Storage | ||
* @param key - Prefix Key of Persisted Instances (default PersistentKey) | ||
* @return Success? | ||
*/ | ||
updateValue(): Promise<boolean>; | ||
persistValue(key?: PersistentKey): Promise<boolean>; | ||
/** | ||
* @internal | ||
* Removes Value form Storage | ||
* Removes Collection from the Storage | ||
* @param key - Prefix Key of Persisted Instances (default PersistentKey) | ||
* @return Success? | ||
*/ | ||
removeValue(): Promise<boolean>; | ||
removePersistedValue(key?: PersistentKey): Promise<boolean>; | ||
/** | ||
* @internal | ||
* Validates Storage Key | ||
* @param key - Key that gets validated | ||
* Formats Storage Key | ||
* @param key - Key that gets formatted | ||
*/ | ||
validateKey(key?: StorageKey): StorageKey | null; | ||
formatKey(key?: StorageKey): StorageKey | undefined; | ||
/** | ||
@@ -52,4 +57,5 @@ * @internal | ||
* @param group - Group | ||
* @param key - Prefix Key of Persisted Instances (default PersistentKey) | ||
*/ | ||
private rebuildStorageSideEffect; | ||
rebuildStorageSideEffect(group: Group<DataType>, key?: PersistentKey): void; | ||
/** | ||
@@ -56,0 +62,0 @@ * @internal |
@@ -18,20 +18,22 @@ "use strict"; | ||
* Collection Persist Manager - Handles permanent storing of Collection Value | ||
* @param agileInstance - An instance of Agile | ||
* @param collection - Collection that gets stored | ||
* @param key - Key of Storage property | ||
* @param config - Config | ||
*/ | ||
constructor(agileInstance, collection, key) { | ||
super(agileInstance); | ||
this.defaultGroupSideEffectKey = "rebuildStorage"; | ||
constructor(collection, config = {}) { | ||
super(collection.agileInstance(), { | ||
instantiate: false, | ||
}); | ||
config = internal_1.defineConfig(config, { | ||
instantiate: true, | ||
storageKeys: [], | ||
}); | ||
this.collection = () => collection; | ||
this.instantiatePersistent(key).then((success) => { | ||
collection.isPersisted = success; | ||
this.instantiatePersistent({ | ||
key: config.key, | ||
storageKeys: config.storageKeys, | ||
}); | ||
// Load/Store persisted Value/s for the first Time | ||
if (this.ready && config.instantiate) | ||
this.initialLoading(); | ||
} | ||
set key(value) { | ||
this.setKey(value); | ||
} | ||
get key() { | ||
return this._key; | ||
} | ||
//========================================================================================================= | ||
@@ -41,4 +43,4 @@ // Set Key | ||
/** | ||
* @public | ||
* Sets Key/Name of Persistent | ||
* @internal | ||
* Updates Key/Name of Persistent | ||
* @param value - New Key/Name of Persistent | ||
@@ -48,54 +50,77 @@ */ | ||
return __awaiter(this, void 0, void 0, function* () { | ||
// If persistent isn't ready try to init it with the new Key | ||
if (!this.ready) { | ||
this.instantiatePersistent(value).then((success) => { | ||
this.collection().isPersisted = success; | ||
}); | ||
const oldKey = this._key; | ||
const wasReady = this.ready; | ||
// Assign Key | ||
if (value === this._key) | ||
return; | ||
this._key = value || internal_1.Persistent.placeHolderKey; | ||
const isValid = this.validatePersistent(); | ||
// Try to Initial Load Value if persistent wasn't ready | ||
if (!wasReady) { | ||
if (isValid) | ||
yield this.initialLoading(); | ||
return; | ||
} | ||
// Check if key has changed | ||
if (value === this._key) | ||
return; | ||
// Remove value with old Key | ||
yield this.removeValue(); | ||
// Update Key | ||
this._key = value; | ||
// Set value with new Key | ||
yield this.updateValue(); | ||
// Remove value at old Key | ||
yield this.removePersistedValue(oldKey); | ||
// Assign Value to new Key | ||
if (isValid) | ||
yield this.persistValue(value); | ||
}); | ||
} | ||
//========================================================================================================= | ||
// Load Value | ||
// Initial Loading | ||
//========================================================================================================= | ||
/** | ||
* @internal | ||
* Loads Value from Storage | ||
* Loads/Saves Storage Value for the first Time | ||
*/ | ||
initialLoading() { | ||
const _super = Object.create(null, { | ||
initialLoading: { get: () => super.initialLoading } | ||
}); | ||
return __awaiter(this, void 0, void 0, function* () { | ||
_super.initialLoading.call(this).then(() => { | ||
this.collection().isPersisted = true; | ||
}); | ||
}); | ||
} | ||
//========================================================================================================= | ||
// Load Persisted Value | ||
//========================================================================================================= | ||
/** | ||
* @internal | ||
* Loads Collection from Storage | ||
* @param key - Prefix Key of Persisted Instances (default PersistentKey) | ||
* @return Success? | ||
*/ | ||
loadValue() { | ||
loadPersistedValue(key) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
if (!this.ready) | ||
return false; | ||
const _key = key || this._key; | ||
// Check if Collection is Persisted | ||
const isPersisted = yield this.agileInstance().storage.get(this.key); | ||
const isPersisted = yield this.agileInstance().storages.get(_key, this.defaultStorageKey); | ||
if (!isPersisted) | ||
return false; | ||
// Load Values into Collection | ||
// Loads Values into Collection | ||
const loadValuesIntoCollection = () => __awaiter(this, void 0, void 0, function* () { | ||
var _a, _b; | ||
const primaryKey = this.collection().config.primaryKey || "id"; | ||
// Get Default Group | ||
const defaultGroup = this.collection().getGroup(this.collection().config.defaultGroupKey || "default"); | ||
// Persist Default Group and instantiate it manually to await its instantiation | ||
const groupStorageKey = CollectionPersistent.getGroupStorageKey(defaultGroup.key, this.collection().key); | ||
defaultGroup.persist(groupStorageKey, { instantiate: false }); | ||
defaultGroup.isPersisted = | ||
(yield ((_a = defaultGroup.persistent) === null || _a === void 0 ? void 0 : _a.instantiatePersistent(groupStorageKey))) || false; | ||
// Add sideEffect to default Group which adds and removes Items from the Storage depending on the Group Value | ||
if (!defaultGroup.hasSideEffect(this.defaultGroupSideEffectKey)) | ||
defaultGroup.addSideEffect(this.defaultGroupSideEffectKey, () => this.rebuildStorageSideEffect(defaultGroup)); | ||
// Load Storage Value from Items | ||
for (let itemKey of defaultGroup.value) { | ||
const defaultGroup = this.collection().getGroup(this.collection().config.defaultGroupKey); | ||
if (!defaultGroup) | ||
return false; | ||
// Persist Default Group and load its Value manually to be 100% sure it got loaded | ||
defaultGroup.persist({ | ||
instantiate: false, | ||
followCollectionPersistKeyPattern: true, | ||
}); | ||
if ((_a = defaultGroup.persistent) === null || _a === void 0 ? void 0 : _a.ready) { | ||
yield ((_b = defaultGroup.persistent) === null || _b === void 0 ? void 0 : _b.initialLoading()); | ||
defaultGroup.isPersisted = true; | ||
} | ||
// Load Items into Collection | ||
for (let itemKey of defaultGroup._value) { | ||
const itemStorageKey = CollectionPersistent.getItemStorageKey(itemKey, _key); | ||
// Get Storage Value | ||
const storageValue = yield this.agileInstance().storage.get(CollectionPersistent.getItemStorageKey(itemKey, this.collection().key)); | ||
const storageValue = yield this.agileInstance().storages.get(itemStorageKey, this.defaultStorageKey); | ||
if (!storageValue) | ||
@@ -105,39 +130,43 @@ continue; | ||
this.collection().collect(storageValue); | ||
// Persist found Item that got created out of the Storage Value | ||
(_b = this.collection() | ||
.getItemById(storageValue[primaryKey])) === null || _b === void 0 ? void 0 : _b.persist(CollectionPersistent.getItemStorageKey(itemKey, this.collection().key)); | ||
} | ||
return true; | ||
}); | ||
yield loadValuesIntoCollection(); | ||
return true; | ||
const success = yield loadValuesIntoCollection(); | ||
// Persist Collection, so that the Storage Value updates dynamically if the Collection updates | ||
if (success) | ||
yield this.persistValue(_key); | ||
return success; | ||
}); | ||
} | ||
//========================================================================================================= | ||
// Set Value | ||
// Persist Value | ||
//========================================================================================================= | ||
/** | ||
* @internal | ||
* Saves/Updates Value in Storage | ||
* Sets everything up so that the Collection gets saved in the Storage | ||
* @param key - Prefix Key of Persisted Instances (default PersistentKey) | ||
* @return Success? | ||
*/ | ||
updateValue() { | ||
persistValue(key) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
if (!this.ready) | ||
return false; | ||
const _key = key || this._key; | ||
const defaultGroup = this.collection().getGroup(this.collection().config.defaultGroupKey); | ||
if (!defaultGroup) | ||
return false; | ||
// Set Collection to Persisted (in Storage) | ||
this.agileInstance().storage.set(this.key, true); | ||
// Get default Group | ||
const defaultGroup = this.collection().getGroup(this.collection().config.defaultGroupKey || "default"); | ||
this.agileInstance().storages.set(_key, true, this.storageKeys); | ||
// Persist default Group | ||
defaultGroup.persist({ followCollectionPattern: true }); | ||
if (!defaultGroup.isPersisted) | ||
defaultGroup.persist({ followCollectionPersistKeyPattern: true }); | ||
// Add sideEffect to default Group which adds and removes Items from the Storage depending on the Group Value | ||
if (!defaultGroup.hasSideEffect(this.defaultGroupSideEffectKey)) | ||
defaultGroup.addSideEffect(this.defaultGroupSideEffectKey, () => this.rebuildStorageSideEffect(defaultGroup)); | ||
defaultGroup.addSideEffect(CollectionPersistent.defaultGroupSideEffectKey, () => this.rebuildStorageSideEffect(defaultGroup, _key)); | ||
// Persist Collection Items | ||
for (let itemKey of defaultGroup.value) { | ||
const item = this.collection().getItemById(itemKey); | ||
const itemStorageKey = CollectionPersistent.getItemStorageKey(itemKey, this.collection().key); | ||
for (let itemKey of defaultGroup._value) { | ||
const item = this.collection().getItem(itemKey); | ||
const itemStorageKey = CollectionPersistent.getItemStorageKey(itemKey, _key); | ||
item === null || item === void 0 ? void 0 : item.persist(itemStorageKey); | ||
} | ||
this.collection().isPersisted = true; | ||
this.isPersisted = true; | ||
return true; | ||
@@ -147,10 +176,11 @@ }); | ||
//========================================================================================================= | ||
// Remove Value | ||
// Remove Persisted Value | ||
//========================================================================================================= | ||
/** | ||
* @internal | ||
* Removes Value form Storage | ||
* Removes Collection from the Storage | ||
* @param key - Prefix Key of Persisted Instances (default PersistentKey) | ||
* @return Success? | ||
*/ | ||
removeValue() { | ||
removePersistedValue(key) { | ||
var _a, _b; | ||
@@ -160,38 +190,39 @@ return __awaiter(this, void 0, void 0, function* () { | ||
return false; | ||
const _key = key || this._key; | ||
const defaultGroup = this.collection().getGroup(this.collection().config.defaultGroupKey); | ||
if (!defaultGroup) | ||
return false; | ||
// Set Collection to not Persisted | ||
this.agileInstance().storage.remove(this.key); | ||
// Get default Group | ||
const defaultGroup = this.collection().getGroup(this.collection().config.defaultGroupKey || "default"); | ||
this.agileInstance().storages.remove(_key, this.storageKeys); | ||
// Remove default Group from Storage | ||
(_a = defaultGroup.persistent) === null || _a === void 0 ? void 0 : _a.removeValue(); | ||
// Remove sideEffect from default Group | ||
defaultGroup.removeSideEffect(this.defaultGroupSideEffectKey); | ||
(_a = defaultGroup.persistent) === null || _a === void 0 ? void 0 : _a.removePersistedValue(); | ||
// Remove Rebuild Storage sideEffect from default Group | ||
defaultGroup.removeSideEffect(CollectionPersistent.defaultGroupSideEffectKey); | ||
// Remove Collection Items from Storage | ||
for (let itemKey of defaultGroup.value) { | ||
const item = this.collection().getItemById(itemKey); | ||
(_b = item === null || item === void 0 ? void 0 : item.persistent) === null || _b === void 0 ? void 0 : _b.removeValue(); | ||
for (let itemKey of defaultGroup._value) { | ||
const item = this.collection().getItem(itemKey); | ||
(_b = item === null || item === void 0 ? void 0 : item.persistent) === null || _b === void 0 ? void 0 : _b.removePersistedValue(); | ||
} | ||
this.collection().isPersisted = false; | ||
return false; | ||
this.isPersisted = false; | ||
return true; | ||
}); | ||
} | ||
//========================================================================================================= | ||
// Validate Key | ||
// Format Key | ||
//========================================================================================================= | ||
/** | ||
* @internal | ||
* Validates Storage Key | ||
* @param key - Key that gets validated | ||
* Formats Storage Key | ||
* @param key - Key that gets formatted | ||
*/ | ||
validateKey(key) { | ||
formatKey(key) { | ||
const collection = this.collection(); | ||
// Get key from Collection | ||
if (!key && collection.key) | ||
return collection.key; | ||
// Return null if no key found | ||
if (!key && collection._key) | ||
return collection._key; | ||
if (!key) | ||
return null; | ||
return; | ||
// Set Storage Key to Collection Key if Collection has no key | ||
if (!collection.key) | ||
collection.key = key; | ||
if (!collection._key) | ||
collection._key = key; | ||
return key; | ||
@@ -206,15 +237,24 @@ } | ||
* @param group - Group | ||
* @param key - Prefix Key of Persisted Instances (default PersistentKey) | ||
*/ | ||
rebuildStorageSideEffect(group) { | ||
rebuildStorageSideEffect(group, key) { | ||
var _a; | ||
const collection = group.collection(); | ||
// Return if only an ItemKey got updated -> length stayed the same | ||
if (group.previousStateValue.length === group.value.length) | ||
const _key = key || ((_a = collection.persistent) === null || _a === void 0 ? void 0 : _a._key); | ||
// Return if only a ItemKey got updated | ||
if (group.previousStateValue.length === group._value.length) | ||
return; | ||
const addedKeys = group.value.filter((key) => !group.previousStateValue.includes(key)); | ||
const removedKeys = group.previousStateValue.filter((key) => !group.value.includes(key)); | ||
const addedKeys = group._value.filter((key) => !group.previousStateValue.includes(key)); | ||
const removedKeys = group.previousStateValue.filter((key) => !group._value.includes(key)); | ||
// Persist Added Keys | ||
addedKeys.forEach((itemKey) => { | ||
const item = collection.getItemById(itemKey); | ||
if (!(item === null || item === void 0 ? void 0 : item.isPersisted)) | ||
item === null || item === void 0 ? void 0 : item.persist(CollectionPersistent.getItemStorageKey(itemKey, collection.key)); | ||
var _a; | ||
const item = collection.getItem(itemKey); | ||
const _itemKey = CollectionPersistent.getItemStorageKey(itemKey, _key); | ||
if (!item) | ||
return; | ||
if (!item.isPersisted) | ||
item.persist(_itemKey); | ||
else | ||
(_a = item.persistent) === null || _a === void 0 ? void 0 : _a.persistValue(_itemKey); | ||
}); | ||
@@ -224,5 +264,8 @@ // Unpersist removed Keys | ||
var _a; | ||
const item = collection.getItemById(itemKey); | ||
if (item === null || item === void 0 ? void 0 : item.isPersisted) | ||
(_a = item === null || item === void 0 ? void 0 : item.persistent) === null || _a === void 0 ? void 0 : _a.removeValue(); | ||
const item = collection.getItem(itemKey); | ||
const _itemKey = CollectionPersistent.getItemStorageKey(itemKey, _key); | ||
if (!item) | ||
return; | ||
if (item.isPersisted) | ||
(_a = item.persistent) === null || _a === void 0 ? void 0 : _a.removePersistedValue(_itemKey); | ||
}); | ||
@@ -240,10 +283,8 @@ } | ||
static getItemStorageKey(itemKey, collectionKey) { | ||
if (!itemKey) { | ||
console.error("Agile: Failed to build Item StorageKey"); | ||
if (!itemKey || !collectionKey) | ||
internal_1.Agile.logger.warn("Failed to build unique Item StorageKey!"); | ||
if (!itemKey) | ||
itemKey = "unknown"; | ||
} | ||
if (!collectionKey) { | ||
console.error("Agile: Failed to build Item StorageKey"); | ||
if (!collectionKey) | ||
collectionKey = "unknown"; | ||
} | ||
return this.storageItemKeyPattern | ||
@@ -263,10 +304,8 @@ .replace("${collectionKey}", collectionKey.toString()) | ||
static getGroupStorageKey(groupKey, collectionKey) { | ||
if (!groupKey) { | ||
console.error("Agile: Failed to build Group StorageKey"); | ||
if (!groupKey || !collectionKey) | ||
internal_1.Agile.logger.warn("Failed to build unique Group StorageKey!"); | ||
if (!groupKey) | ||
groupKey = "unknown"; | ||
} | ||
if (!collectionKey) { | ||
console.error("Agile: Failed to build Group StorageKey"); | ||
if (!collectionKey) | ||
collectionKey = "unknown"; | ||
} | ||
return this.storageGroupKeyPattern | ||
@@ -278,3 +317,4 @@ .replace("${collectionKey}", collectionKey.toString()) | ||
exports.CollectionPersistent = CollectionPersistent; | ||
CollectionPersistent.defaultGroupSideEffectKey = "rebuildGroupStorageValue"; | ||
CollectionPersistent.storageItemKeyPattern = "_${collectionKey}_item_${itemKey}"; | ||
CollectionPersistent.storageGroupKeyPattern = "_${collectionKey}_group_${groupKey}"; |
@@ -1,3 +0,4 @@ | ||
import { Agile, State, Collection, DefaultItem, ItemKey, Item, StorageKey, StatePersistentConfigInterface } from "../internal"; | ||
import { State, Collection, DefaultItem, ItemKey, Item, StatePersistentConfigInterface, PersistentKey, StateRuntimeJobConfigInterface } from "../internal"; | ||
export declare class Group<DataType = DefaultItem> extends State<Array<ItemKey>> { | ||
static rebuildGroupSideEffectKey: string; | ||
collection: () => Collection<DataType>; | ||
@@ -10,3 +11,2 @@ _output: Array<DataType>; | ||
* Group - Holds Items of Collection | ||
* @param agileInstance - An instance of Agile | ||
* @param collection - Collection to that the Group belongs | ||
@@ -16,3 +16,3 @@ * @param initialItems - Initial Key of Items in this Group | ||
*/ | ||
constructor(agileInstance: Agile, collection: Collection<DataType>, initialItems?: Array<ItemKey>, config?: GroupConfigInterface); | ||
constructor(collection: Collection<DataType>, initialItems?: Array<ItemKey>, config?: GroupConfigInterface); | ||
/** | ||
@@ -25,2 +25,7 @@ * @public | ||
* @public | ||
* Set Item Values of Group | ||
*/ | ||
set output(value: DataType[]); | ||
/** | ||
* @public | ||
* Get Items of Group | ||
@@ -31,2 +36,7 @@ */ | ||
* @public | ||
* Set Items of Group | ||
*/ | ||
set items(value: Array<Item<DataType>>); | ||
/** | ||
* @public | ||
* Checks if Group contains ItemKey | ||
@@ -57,2 +67,10 @@ * @param itemKey - ItemKey that gets checked | ||
* @public | ||
* Replaces oldItemKey with newItemKey | ||
* @param oldItemKey - Old ItemKey | ||
* @param newItemKey - New ItemKey | ||
* @param config - Config | ||
*/ | ||
replace(oldItemKey: ItemKey, newItemKey: ItemKey, config?: StateRuntimeJobConfigInterface): this; | ||
/** | ||
* @public | ||
* Stores Group Value into Agile Storage permanently | ||
@@ -65,6 +83,6 @@ * @param config - Config | ||
* Stores Group Value into Agile Storage permanently | ||
* @param key - Storage Key (Note: not needed if Group has key/name) | ||
* @param key - Key/Name of created Persistent (Note: Key required if Group has no set Key!) | ||
* @param config - Config | ||
*/ | ||
persist(key?: StorageKey, config?: GroupPersistConfigInterface): this; | ||
persist(key?: PersistentKey, config?: GroupPersistConfigInterface): this; | ||
/** | ||
@@ -95,5 +113,7 @@ * @internal | ||
* @param key - Key/Name of Group | ||
* @param isPlaceholder - If Group is initially a Placeholder | ||
*/ | ||
export interface GroupConfigInterface { | ||
key?: GroupKey; | ||
isPlaceholder?: boolean; | ||
} | ||
@@ -104,3 +124,3 @@ /** | ||
export interface GroupPersistConfigInterface extends StatePersistentConfigInterface { | ||
followCollectionPattern?: boolean; | ||
followCollectionPersistKeyPattern?: boolean; | ||
} |
@@ -9,3 +9,2 @@ "use strict"; | ||
* Group - Holds Items of Collection | ||
* @param agileInstance - An instance of Agile | ||
* @param collection - Collection to that the Group belongs | ||
@@ -15,4 +14,4 @@ * @param initialItems - Initial Key of Items in this Group | ||
*/ | ||
constructor(agileInstance, collection, initialItems, config) { | ||
super(agileInstance, initialItems || [], config === null || config === void 0 ? void 0 : config.key); | ||
constructor(collection, initialItems, config = {}) { | ||
super(collection.agileInstance(), initialItems || [], config); | ||
this._output = []; // Output of Group | ||
@@ -22,5 +21,5 @@ this._items = []; // Items of Group | ||
this.collection = () => collection; | ||
// Add rebuild to sideEffects so that it rebuilds the Group Output if the value changes | ||
this.addSideEffect("buildGroup", () => this.rebuild()); | ||
// Initial Build | ||
// Add rebuild to sideEffects to rebuild Group on Value Change | ||
this.addSideEffect(Group.rebuildGroupSideEffectKey, () => this.rebuild()); | ||
// Initial Rebuild | ||
this.rebuild(); | ||
@@ -33,5 +32,3 @@ } | ||
get output() { | ||
// Add Group to tracked Observers (for auto tracking used observers in computed function) | ||
if (this.agileInstance().runtime.trackObservers) | ||
this.agileInstance().runtime.foundObservers.add(this.observer); | ||
internal_1.ComputedTracker.tracked(this.observer); | ||
return this._output; | ||
@@ -41,10 +38,22 @@ } | ||
* @public | ||
* Set Item Values of Group | ||
*/ | ||
set output(value) { | ||
this._output = value; | ||
} | ||
/** | ||
* @public | ||
* Get Items of Group | ||
*/ | ||
get items() { | ||
// Add Group to tracked Observers (for auto tracking used observers in computed function) | ||
if (this.agileInstance().runtime.trackObservers) | ||
this.agileInstance().runtime.foundObservers.add(this.observer); | ||
internal_1.ComputedTracker.tracked(this.observer); | ||
return this._items.map((item) => item()); | ||
} | ||
/** | ||
* @public | ||
* Set Items of Group | ||
*/ | ||
set items(value) { | ||
this._items = value.map((item) => () => item); | ||
} | ||
//========================================================================================================= | ||
@@ -82,4 +91,5 @@ // Has | ||
const _itemKeys = internal_1.normalizeArray(itemKeys); | ||
const notExistingItemKeysInCollection = []; | ||
const notExistingItemKeys = []; | ||
let newGroupValue = internal_1.copy(this.nextStateValue); // Copying nextStateValue because somehow a reference exists between nextStateValue and value | ||
let newGroupValue = internal_1.copy(this.nextStateValue); | ||
config = internal_1.defineConfig(config, { | ||
@@ -92,17 +102,20 @@ background: false, | ||
if (!newGroupValue.includes(itemKey)) { | ||
console.error(`Agile: Couldn't find itemKey '${itemKey}' in Group!`, this); | ||
internal_1.Agile.logger.error(`Couldn't find ItemKey '${itemKey}' in Group '${this._key}'!`); | ||
notExistingItemKeys.push(itemKey); | ||
notExistingItemKeysInCollection.push(itemKey); | ||
return; | ||
} | ||
// Check if ItemKey exists in Collection | ||
if (!this.collection().getItemById(itemKey)) | ||
notExistingItemKeys.push(itemKey); | ||
if (!this.collection().getItem(itemKey)) | ||
notExistingItemKeysInCollection.push(itemKey); | ||
// Remove ItemKey from Group | ||
newGroupValue = newGroupValue.filter((key) => key !== itemKey); | ||
}); | ||
this.nextStateValue = newGroupValue; | ||
// Return if passed ItemKeys doesn't exist | ||
if (notExistingItemKeys.length >= _itemKeys.length) | ||
return this; | ||
// If all removed ItemKeys doesn't exist in Collection -> no rerender necessary since output doesn't change | ||
if (notExistingItemKeys.length >= _itemKeys.length) | ||
if (notExistingItemKeysInCollection.length >= _itemKeys.length) | ||
config.background = true; | ||
// Ingest nextStateValue into Runtime | ||
this.ingest({ background: config.background }); | ||
this.set(newGroupValue, { background: config.background }); | ||
return this; | ||
@@ -121,4 +134,5 @@ } | ||
const _itemKeys = internal_1.normalizeArray(itemKeys); | ||
const notExistingItemKeys = []; // ItemKeys that don't exist in Collection | ||
let newGroupValue = internal_1.copy(this.nextStateValue); // Copying nextStateValue because somehow a reference exists between nextStateValue and value | ||
const notExistingItemKeysInCollection = []; | ||
const existingItemKeys = []; | ||
let newGroupValue = internal_1.copy(this.nextStateValue); | ||
config = internal_1.defineConfig(config, { | ||
@@ -133,10 +147,13 @@ method: "push", | ||
// Check if ItemKey exists in Collection | ||
if (!this.collection().getItemById(itemKey)) | ||
notExistingItemKeys.push(itemKey); | ||
// Remove ItemKey from Group if it should get overwritten and exists | ||
if (!this.collection().getItem(itemKey)) | ||
notExistingItemKeysInCollection.push(itemKey); | ||
// Remove ItemKey from Group if it should get overwritten and already exists | ||
if (existsInGroup) { | ||
if (config.overwrite) | ||
if (config.overwrite) { | ||
newGroupValue = newGroupValue.filter((key) => key !== itemKey); | ||
else | ||
} | ||
else { | ||
existingItemKeys.push(itemKey); | ||
return; | ||
} | ||
} | ||
@@ -146,10 +163,28 @@ // Add new ItemKey to Group | ||
}); | ||
this.nextStateValue = newGroupValue; | ||
// If all added ItemKeys doesn't exist in Collection -> no rerender necessary since output doesn't change | ||
if (notExistingItemKeys.length >= _itemKeys.length) | ||
// Return if passed ItemKeys already exist | ||
if (existingItemKeys.length >= _itemKeys.length) | ||
return this; | ||
// If all added ItemKeys doesn't exist in Collection or already exist -> no rerender necessary since output doesn't change | ||
if (notExistingItemKeysInCollection.concat(existingItemKeys).length >= | ||
_itemKeys.length) | ||
config.background = true; | ||
// Ingest nextStateValue into Runtime | ||
this.ingest({ background: config.background }); | ||
this.set(newGroupValue, { background: config.background }); | ||
return this; | ||
} | ||
//========================================================================================================= | ||
// Replace | ||
//========================================================================================================= | ||
/** | ||
* @public | ||
* Replaces oldItemKey with newItemKey | ||
* @param oldItemKey - Old ItemKey | ||
* @param newItemKey - New ItemKey | ||
* @param config - Config | ||
*/ | ||
replace(oldItemKey, newItemKey, config = {}) { | ||
let newGroupValue = internal_1.copy(this._value); | ||
newGroupValue.splice(newGroupValue.indexOf(oldItemKey), 1, newItemKey); | ||
this.set(newGroupValue, config); | ||
return this; | ||
} | ||
persist(keyOrConfig = {}, config = {}) { | ||
@@ -160,3 +195,3 @@ let _config; | ||
_config = keyOrConfig; | ||
key = undefined; | ||
key = this._key; | ||
} | ||
@@ -170,7 +205,11 @@ else { | ||
followCollectionPattern: false, | ||
storageKeys: [], | ||
}); | ||
if (_config.followCollectionPattern) { | ||
key = internal_1.CollectionPersistent.getGroupStorageKey(key || this.key, this.collection().key); | ||
if (_config.followCollectionPersistKeyPattern) { | ||
key = internal_1.CollectionPersistent.getGroupStorageKey(key || this._key, this.collection()._key); | ||
} | ||
super.persist(key, { instantiate: _config.instantiate }); | ||
super.persist(key, { | ||
instantiate: _config.instantiate, | ||
storageKeys: _config.storageKeys, | ||
}); | ||
return this; | ||
@@ -191,5 +230,5 @@ } | ||
this._value.forEach((itemKey) => { | ||
let data = this.collection().data[itemKey]; | ||
if (data) | ||
groupItems.push(data); | ||
const item = this.collection().getItem(itemKey); | ||
if (item) | ||
groupItems.push(item); | ||
else | ||
@@ -203,5 +242,6 @@ notFoundItemKeys.push(itemKey); | ||
// Logging | ||
if (this.agileInstance().config.logJobs && notFoundItemKeys.length > 0) | ||
console.warn(`Agile: Couldn't find some Items in Collection '${this.key}'`, notFoundItemKeys); | ||
this._items = groupItems.map((item) => () => item); | ||
if (notFoundItemKeys.length > 0) { | ||
internal_1.Agile.logger.warn(`Couldn't find some Items in Collection '${this.collection()._key}' (${this._key})`, notFoundItemKeys); | ||
} | ||
this.items = groupItems; | ||
this._output = groupOutput; | ||
@@ -212,1 +252,2 @@ this.notFoundItemKeys = notFoundItemKeys; | ||
exports.Group = Group; | ||
Group.rebuildGroupSideEffectKey = "rebuildGroup"; |
@@ -1,5 +0,6 @@ | ||
import { Agile, Item, Group, GroupKey, Selector, SelectorKey, StateKey, StorageKey, GroupConfigInterface, CollectionPersistent, GroupAddConfig } from "../internal"; | ||
import { Agile, Item, Group, GroupKey, Selector, SelectorKey, StorageKey, GroupConfigInterface, CollectionPersistent, GroupAddConfig } from "../internal"; | ||
export declare class Collection<DataType = DefaultItem> { | ||
agileInstance: () => Agile; | ||
config: CollectionConfigInterface; | ||
private initialConfig; | ||
size: number; | ||
@@ -20,3 +21,3 @@ data: { | ||
* @public | ||
* Class that holds a List of Objects with key and causes rerender on subscribed Components | ||
* Collection - Class that holds a List of Objects with key and causes rerender on subscribed Components | ||
* @param agileInstance - An instance of Agile | ||
@@ -30,3 +31,3 @@ * @param config - Config | ||
*/ | ||
set key(value: StateKey | undefined); | ||
set key(value: CollectionKey | undefined); | ||
/** | ||
@@ -36,3 +37,3 @@ * @public | ||
*/ | ||
get key(): StateKey | undefined; | ||
get key(): CollectionKey | undefined; | ||
/** | ||
@@ -43,3 +44,3 @@ * @public | ||
*/ | ||
setKey(value: StateKey | undefined): void; | ||
setKey(value: CollectionKey | undefined): this; | ||
/** | ||
@@ -65,3 +66,5 @@ * @public | ||
*/ | ||
private initGroups; | ||
initGroups(groups: { | ||
[key: string]: Group<any>; | ||
} | string[]): void; | ||
/** | ||
@@ -71,11 +74,13 @@ * @internal | ||
*/ | ||
private initSelectors; | ||
initSelectors(selectors: { | ||
[key: string]: Selector<any>; | ||
} | string[]): void; | ||
/** | ||
* @public | ||
* Collect Item/s | ||
* @param items - Item/s that get collected and added to this Collection | ||
* @param groups - Add collected Item/s to certain Groups | ||
* @param data - Data that gets added to Collection | ||
* @param groupKeys - Add collected Item/s to certain Groups | ||
* @param config - Config | ||
*/ | ||
collect(items: DataType | Array<DataType>, groups?: GroupKey | Array<GroupKey>, config?: CollectConfigInterface<DataType>): this; | ||
collect(data: DataType | Array<DataType>, groupKeys?: GroupKey | Array<GroupKey>, config?: CollectConfigInterface<DataType>): this; | ||
/** | ||
@@ -88,3 +93,3 @@ * @public | ||
*/ | ||
update(itemKey: ItemKey, changes: DefaultItem | DataType, config?: UpdateConfigInterface): Item | undefined; | ||
update(itemKey: ItemKey, changes: DefaultItem | DataType, config?: UpdateConfigInterface): Item<DataType> | undefined; | ||
/** | ||
@@ -99,17 +104,17 @@ * @public | ||
* @public | ||
* Creates new Selector that represents an Item of the Collection | ||
* @param selectorKey - Name/Key of Selector | ||
* @param itemKey - Key of Item which the Selector represents | ||
* Get Group by Key/Name | ||
* @param groupKey - Key/Name of Group | ||
* @param config - Config | ||
*/ | ||
createSelector(selectorKey: SelectorKey, itemKey: ItemKey): Selector<DataType>; | ||
getGroup(groupKey: GroupKey | undefined, config?: GetGroupConfigInterface): Group<DataType> | undefined; | ||
/** | ||
* @public | ||
* Get Group by Key/Name | ||
* Get Group by Key/Name or a Reference to it if it doesn't exist yet | ||
* @param groupKey - Name/Key of Group | ||
*/ | ||
getGroup(groupKey: GroupKey): Group<DataType>; | ||
getGroupWithReference(groupKey: GroupKey): Group<DataType>; | ||
/** | ||
* @public | ||
* Removes Group by Key/Name | ||
* @param groupKey - Name/Key of Selector | ||
* @param groupKey - Name/Key of Group | ||
*/ | ||
@@ -119,6 +124,20 @@ removeGroup(groupKey: GroupKey): this; | ||
* @public | ||
* Creates new Selector that represents an Item of the Collection | ||
* @param selectorKey - Name/Key of Selector | ||
* @param itemKey - Key of Item which the Selector represents | ||
*/ | ||
createSelector(selectorKey: SelectorKey, itemKey: ItemKey): Selector<DataType>; | ||
/** | ||
* @public | ||
* Get Selector by Key/Name | ||
* @param selectorKey - Key/Name of Selector | ||
* @param config - Config | ||
*/ | ||
getSelector(selectorKey: SelectorKey | undefined, config?: GetSelectorConfigInterface): Selector<DataType> | undefined; | ||
/** | ||
* @public | ||
* Get Selector by Key/Name or a Reference to it if it doesn't exist yet | ||
* @param selectorKey - Name/Key of Selector | ||
*/ | ||
getSelector(selectorKey: SelectorKey): Selector<DataType> | undefined; | ||
getSelectorWithReference(selectorKey: SelectorKey): Selector<DataType>; | ||
/** | ||
@@ -132,29 +151,42 @@ * @public | ||
* @public | ||
* Remove Items from Group or from everywhere | ||
* @param itemKeys - ItemKey/s that get removed | ||
* Get Item by Key/Name | ||
* @param itemKey - ItemKey of Item | ||
* @param config - Config | ||
*/ | ||
remove(itemKeys: ItemKey | Array<ItemKey>): { | ||
fromGroups: (groups: Array<ItemKey> | ItemKey) => void; | ||
everywhere: () => void; | ||
}; | ||
getItem(itemKey: ItemKey | undefined, config?: GetItemConfigInterface): Item<DataType> | undefined; | ||
/** | ||
* @public | ||
* Get Item by Id | ||
* @param itemKey - ItemKey of Item that might get found | ||
* Get Item by Key/Name or a Reference to it if it doesn't exist yet | ||
* @param itemKey - Key/Name of Item | ||
*/ | ||
getItemById(itemKey: ItemKey): Item<DataType> | undefined; | ||
getItemWithReference(itemKey: ItemKey): Item<DataType>; | ||
/** | ||
* @public | ||
* Get Value of Item by Id | ||
* @param itemKey - ItemKey of ItemValue that might get found | ||
* Get Value of Item by Key/Name | ||
* @param itemKey - ItemKey of Item that holds the Value | ||
* @param config - Config | ||
*/ | ||
getValueById(itemKey: ItemKey): DataType | undefined; | ||
getItemValue(itemKey: ItemKey | undefined, config?: GetItemConfigInterface): DataType | undefined; | ||
/** | ||
* @public | ||
* Stores Collection Value in Agile Storage | ||
* @param key - Storage Key (Note: not needed if Collection has key/name) | ||
* Stores Collection Value into Agile Storage permanently | ||
* @param config - Config | ||
*/ | ||
persist(key?: StorageKey): this; | ||
persist(config?: CollectionPersistentConfigInterface): this; | ||
/** | ||
* @public | ||
* Stores Collection Value into Agile Storage permanently | ||
* @param key - Key/Name of created Persistent (Note: Key required if Collection has no set Key!) | ||
* @param config - Config | ||
*/ | ||
persist(key?: StorageKey, config?: CollectionPersistentConfigInterface): this; | ||
/** | ||
* @public | ||
* Callback Function that gets called if the persisted Value gets loaded into the Collection for the first Time | ||
* Note: Only useful for persisted Collections! | ||
* @param callback - Callback Function | ||
*/ | ||
onLoad(callback: (success: boolean) => void): this; | ||
/** | ||
* @public | ||
* Get count of registered Groups in Collection | ||
@@ -183,3 +215,3 @@ */ | ||
* @internal | ||
* Updates ItemKey of Item | ||
* Updates Key/Name of Item in all Instances (Group, Selector, ..) | ||
* @param oldItemKey - Old ItemKey | ||
@@ -189,5 +221,20 @@ * @param newItemKey - New ItemKey | ||
*/ | ||
private updateItemKey; | ||
updateItemKey(oldItemKey: ItemKey, newItemKey: ItemKey, config?: UpdateItemKeyConfigInterface): boolean; | ||
/** | ||
* @public | ||
* Gets GroupKeys that contain the passed ItemKey | ||
* @param itemKey - ItemKey | ||
*/ | ||
getGroupKeysThatHaveItemKey(itemKey: ItemKey): Array<GroupKey>; | ||
/** | ||
* @public | ||
* Remove Items from Collection | ||
* @param itemKeys - ItemKey/s that get removed | ||
*/ | ||
remove(itemKeys: ItemKey | Array<ItemKey>): { | ||
fromGroups: (groups: Array<ItemKey> | ItemKey) => void; | ||
everywhere: () => void; | ||
}; | ||
/** | ||
* @public | ||
* Removes Item/s from Group/s | ||
@@ -200,4 +247,4 @@ * @param itemKeys - ItemKey/s that get removed from Group/s | ||
* @public | ||
* Removes Item/s from Group/s | ||
* @param itemKeys - ItemKey/s of Item/s that get removed from Collection | ||
* Removes Item completely from Collection | ||
* @param itemKeys - ItemKey/s of Item/s | ||
*/ | ||
@@ -207,10 +254,7 @@ removeItems(itemKeys: ItemKey | Array<ItemKey>): void; | ||
* @internal | ||
* Creates/Updates Item from provided Data and adds it to the Collection | ||
* @param data - Data from which the Item gets created/updated | ||
* Updates existing or creates Item from provided Data | ||
* @param data - Data | ||
* @param config - Config | ||
*/ | ||
setData(data: DataType, config?: { | ||
patch?: boolean; | ||
background?: boolean; | ||
}): boolean; | ||
setData(data: DataType, config?: SetDataConfigInterface): boolean; | ||
/** | ||
@@ -236,3 +280,3 @@ * @internal | ||
*/ | ||
export interface CollectionConfigInterface { | ||
export interface CreateCollectionConfigInterface { | ||
groups?: { | ||
@@ -249,2 +293,10 @@ [key: string]: Group<any>; | ||
/** | ||
* @param primaryKey - Name of Property that holds the PrimaryKey (default = id) | ||
* @param defaultGroupKey - Key/Name of Default Group that holds all collected Items | ||
*/ | ||
export interface CollectionConfigInterface { | ||
primaryKey: string; | ||
defaultGroupKey: ItemKey; | ||
} | ||
/** | ||
* @param patch - If Item gets patched into existing Item with the same Id | ||
@@ -254,2 +306,3 @@ * @param method - Way of adding Item to Collection (push, unshift) | ||
* @param background - If collecting an Item happens in the background (-> not causing any rerender) | ||
* @param select - If collected Items get selected with a Selector | ||
*/ | ||
@@ -261,2 +314,3 @@ export interface CollectConfigInterface<DataType = any> { | ||
background?: boolean; | ||
select?: boolean; | ||
} | ||
@@ -279,10 +333,44 @@ /** | ||
* @param background - If assigning a new value happens in the background (-> not causing any rerender) | ||
* @param forceRerender - Force rerender no matter what happens | ||
* @param sideEffects - If Side Effects of State get executed | ||
* @param force - Force creating and performing Job | ||
* @param sideEffects - If Side Effects of Group gets executed | ||
*/ | ||
export interface RebuildGroupsThatIncludeItemKeyConfigInterface { | ||
background?: boolean; | ||
forceRerender?: boolean; | ||
force?: boolean; | ||
sideEffects?: boolean; | ||
} | ||
export declare type CollectionConfig<DataType = DefaultItem> = CollectionConfigInterface | ((collection: Collection<DataType>) => CollectionConfigInterface); | ||
/** | ||
* @param notExisting - If also official not existing Items like Placeholder get found | ||
*/ | ||
export interface GetItemConfigInterface { | ||
notExisting?: boolean; | ||
} | ||
/** | ||
* @param notExisting - If also official not existing Groups like Placeholder get found | ||
*/ | ||
export interface GetGroupConfigInterface { | ||
notExisting?: boolean; | ||
} | ||
/** | ||
* @param notExisting - If also official not existing Selectors like Placeholder get found | ||
*/ | ||
export interface GetSelectorConfigInterface { | ||
notExisting?: boolean; | ||
} | ||
/** | ||
* @param instantiate - If Persistent gets instantiated | ||
* @param storageKeys - Key/Name of Storages which gets used to persist the Collection Value (NOTE: If not passed the default Storage will be used) | ||
*/ | ||
export interface CollectionPersistentConfigInterface { | ||
instantiate?: boolean; | ||
storageKeys?: StorageKey[]; | ||
} | ||
/** | ||
* @param patch - If Data gets patched into existing Item | ||
* @param background - If assigning Data happens in background | ||
*/ | ||
export interface SetDataConfigInterface { | ||
patch?: boolean; | ||
background?: boolean; | ||
} | ||
export declare type CollectionConfig<DataType = DefaultItem> = CreateCollectionConfigInterface | ((collection: Collection<DataType>) => CreateCollectionConfigInterface); |
@@ -8,3 +8,3 @@ "use strict"; | ||
* @public | ||
* Class that holds a List of Objects with key and causes rerender on subscribed Components | ||
* Collection - Class that holds a List of Objects with key and causes rerender on subscribed Components | ||
* @param agileInstance - An instance of Agile | ||
@@ -16,10 +16,15 @@ * @param config - Config | ||
this.data = {}; // Collection Data | ||
this.isPersisted = false; // If Collection is stored in Storage | ||
this.isPersisted = false; // If Collection can be stored in Agile Storage (-> successfully integrated persistent) | ||
this.groups = {}; | ||
this.selectors = {}; | ||
this.agileInstance = () => agileInstance; | ||
if (typeof config === "function") | ||
config = config(this); | ||
this.config = internal_1.defineConfig(config, { | ||
// Set temp Config for creating proper Placeholder Items (of Selector) | ||
this.config = { | ||
defaultGroupKey: "default", | ||
primaryKey: "id", | ||
}; | ||
// Assign Properties | ||
let _config = typeof config === "function" ? config(this) : config; | ||
_config = internal_1.defineConfig(_config, { | ||
primaryKey: "id", | ||
groups: {}, | ||
@@ -29,5 +34,10 @@ selectors: {}, | ||
}); | ||
this._key = this.config.key; | ||
this.initGroups(); | ||
this.initSelectors(); | ||
this._key = _config.key; | ||
this.config = { | ||
defaultGroupKey: _config.defaultGroupKey, | ||
primaryKey: _config.primaryKey, | ||
}; | ||
this.initialConfig = _config; | ||
this.initGroups(_config.groups); | ||
this.initSelectors(_config.selectors); | ||
} | ||
@@ -57,10 +67,10 @@ /** | ||
setKey(value) { | ||
var _a, _b; | ||
const oldKey = this._key; | ||
// Update State Key | ||
this._key = value; | ||
// Update Key in PersistManager | ||
if (value !== undefined && | ||
this.persistent && | ||
this.persistent.key === oldKey) | ||
this.persistent.key = value; | ||
// Update Key in Persistent (only if oldKey equal to persistentKey -> otherwise the PersistentKey got formatted and will be set where other) | ||
if (value && ((_a = this.persistent) === null || _a === void 0 ? void 0 : _a._key) === oldKey) | ||
(_b = this.persistent) === null || _b === void 0 ? void 0 : _b.setKey(value); | ||
return this; | ||
} | ||
@@ -77,3 +87,3 @@ //========================================================================================================= | ||
Group(initialItems, config) { | ||
return new internal_1.Group(this.agileInstance(), this, initialItems, config); | ||
return new internal_1.Group(this, initialItems, config); | ||
} | ||
@@ -99,11 +109,10 @@ //========================================================================================================= | ||
*/ | ||
initGroups() { | ||
const groups = internal_1.copy(this.config.groups); | ||
initGroups(groups) { | ||
if (!groups) | ||
return; | ||
let groupsObject = {}; | ||
// If groups is Array of SelectorNames transform it to Selector Object | ||
// If groups is Array of GroupNames transform it to Group Object | ||
if (Array.isArray(groups)) { | ||
groups.forEach((groupKey) => { | ||
groupsObject[groupKey] = new internal_1.Group(this.agileInstance(), this, [], { | ||
groupsObject[groupKey] = new internal_1.Group(this, [], { | ||
key: groupKey, | ||
@@ -116,9 +125,9 @@ }); | ||
// Add default Group | ||
groupsObject[this.config.defaultGroupKey || "default"] = new internal_1.Group(this.agileInstance(), this, [], { | ||
key: this.config.defaultGroupKey || "default", | ||
groupsObject[this.config.defaultGroupKey] = new internal_1.Group(this, [], { | ||
key: this.config.defaultGroupKey, | ||
}); | ||
// Set Key/Name of Group to property Name | ||
for (let key in groupsObject) | ||
if (!groupsObject[key].key) | ||
groupsObject[key].key = key; | ||
if (!groupsObject[key]._key) | ||
groupsObject[key].setKey(key); | ||
this.groups = groupsObject; | ||
@@ -133,4 +142,3 @@ } | ||
*/ | ||
initSelectors() { | ||
const selectors = internal_1.copy(this.config.selectors); | ||
initSelectors(selectors) { | ||
if (!selectors) | ||
@@ -151,4 +159,4 @@ return; | ||
for (let key in selectorsObject) | ||
if (!selectorsObject[key].key) | ||
selectorsObject[key].key = key; | ||
if (!selectorsObject[key]._key) | ||
selectorsObject[key].setKey(key); | ||
this.selectors = selectorsObject; | ||
@@ -162,11 +170,11 @@ } | ||
* Collect Item/s | ||
* @param items - Item/s that get collected and added to this Collection | ||
* @param groups - Add collected Item/s to certain Groups | ||
* @param data - Data that gets added to Collection | ||
* @param groupKeys - Add collected Item/s to certain Groups | ||
* @param config - Config | ||
*/ | ||
collect(items, groups, config = {}) { | ||
const _items = internal_1.normalizeArray(items); | ||
const groupKeys = internal_1.normalizeArray(groups); | ||
const defaultGroupKey = this.config.defaultGroupKey || "default"; | ||
const primaryKey = this.config.primaryKey || "id"; | ||
collect(data, groupKeys, config = {}) { | ||
const _data = internal_1.normalizeArray(data); | ||
const _groupKeys = internal_1.normalizeArray(groupKeys); | ||
const defaultGroupKey = this.config.defaultGroupKey; | ||
const primaryKey = this.config.primaryKey; | ||
config = internal_1.defineConfig(config, { | ||
@@ -176,12 +184,11 @@ method: "push", | ||
patch: false, | ||
select: false, | ||
}); | ||
// Add default GroupKey, because Items get always added to default Group | ||
if (!groupKeys.includes(defaultGroupKey)) | ||
groupKeys.push(defaultGroupKey); | ||
if (!_groupKeys.includes(defaultGroupKey)) | ||
_groupKeys.push(defaultGroupKey); | ||
// Create not existing Groups | ||
groupKeys.forEach((groupName) => !this.groups[groupName] && this.createGroup(groupName)); | ||
// Instantiate Items | ||
_items.forEach((data, index) => { | ||
_groupKeys.forEach((key) => !this.groups[key] && this.createGroup(key)); | ||
_data.forEach((data, index) => { | ||
const itemKey = data[primaryKey]; | ||
const itemExistsInCollection = !!this.data[itemKey]; | ||
// Add Item to Collection | ||
@@ -194,15 +201,6 @@ const success = this.setData(data, { | ||
return this; | ||
// Ingest Groups that include the ItemKey into Runtime, which than rebuilds the Group (because output of group changed) | ||
if (!itemExistsInCollection) { | ||
for (let groupKey in this.groups) { | ||
const group = this.getGroup(groupKey); | ||
if (group.value.includes(itemKey)) { | ||
if (!config.background) | ||
group.ingest({ forceRerender: true, storage: false }); | ||
} | ||
} | ||
} | ||
// Add ItemKey to provided Groups | ||
groupKeys.forEach((groupKey) => { | ||
this.groups[groupKey].add(itemKey, { | ||
_groupKeys.forEach((groupKey) => { | ||
var _a; | ||
(_a = this.getGroup(groupKey)) === null || _a === void 0 ? void 0 : _a.add(itemKey, { | ||
method: config.method, | ||
@@ -212,2 +210,4 @@ background: config.background, | ||
}); | ||
if (config.select) | ||
this.createSelector(itemKey, itemKey); | ||
if (config.forEachItem) | ||
@@ -229,34 +229,34 @@ config.forEachItem(data, itemKey, index); | ||
update(itemKey, changes, config = {}) { | ||
if (!this.data.hasOwnProperty(itemKey)) { | ||
console.error(`Agile: ItemKey '${itemKey} doesn't exist in Collection!`, this); | ||
const item = this.getItem(itemKey, { notExisting: true }); | ||
const primaryKey = this.config.primaryKey; | ||
config = internal_1.defineConfig(config, { | ||
addNewProperties: false, | ||
background: false, | ||
}); | ||
if (!item) { | ||
internal_1.Agile.logger.error(`ItemKey '${itemKey}' doesn't exist in Collection '${this._key}'!`); | ||
return undefined; | ||
} | ||
if (!internal_1.isValidObject(changes)) { | ||
console.error(`Agile: Changes have to be an Object!`, this); | ||
internal_1.Agile.logger.error(`You have to pass an valid Changes Object to update '${itemKey}' in '${this._key}'!`); | ||
return undefined; | ||
} | ||
const item = this.data[itemKey]; | ||
const primaryKey = this.config.primaryKey || ""; | ||
config = internal_1.defineConfig(config, { | ||
addNewProperties: true, | ||
background: false, | ||
}); | ||
// Merge changes into ItemValue | ||
// Merge changes into current ItemValue | ||
const newItemValue = internal_1.flatMerge(internal_1.copy(item.nextStateValue), changes, { | ||
addNewProperties: config.addNewProperties, | ||
}); | ||
const oldItemKey = item.value[primaryKey]; | ||
const oldItemKey = item._value[primaryKey]; | ||
const newItemKey = newItemValue[primaryKey]; | ||
const updateItemKey = oldItemKey !== newItemKey; | ||
const updatedItemKey = oldItemKey !== newItemKey; | ||
// Apply changes to Item | ||
item.set(newItemValue, { | ||
background: config.background, | ||
storage: !updateItemKey, | ||
storage: !updatedItemKey, | ||
}); | ||
// Update ItemKey of Item | ||
if (updateItemKey) | ||
if (updatedItemKey) | ||
this.updateItemKey(oldItemKey, newItemKey, { | ||
background: config.background, | ||
}); | ||
return this.data[newItemValue[primaryKey]]; | ||
return item; | ||
} | ||
@@ -272,61 +272,62 @@ //========================================================================================================= | ||
*/ | ||
createGroup(groupKey, initialItems) { | ||
if (this.groups.hasOwnProperty(groupKey)) { | ||
console.warn(`Agile: Group with the name '${groupKey}' already exists!`); | ||
return this.groups[groupKey]; | ||
createGroup(groupKey, initialItems = []) { | ||
let group = this.getGroup(groupKey, { notExisting: true }); | ||
// Check if Group already exists | ||
if (group) { | ||
if (!group.isPlaceholder) { | ||
internal_1.Agile.logger.warn(`Group with the name '${groupKey}' already exists!`); | ||
return group; | ||
} | ||
group.set(initialItems, { overwrite: true }); | ||
return group; | ||
} | ||
// Create new Group | ||
const group = new internal_1.Group(this.agileInstance(), this, initialItems, { key: groupKey }); | ||
// Create Group | ||
group = new internal_1.Group(this, initialItems, { key: groupKey }); | ||
this.groups[groupKey] = group; | ||
// Logging | ||
if (this.agileInstance().config.logJobs) | ||
console.log(`Agile: Created new Group called '${groupKey}'`, group); | ||
return group; | ||
} | ||
//========================================================================================================= | ||
// Create Selector | ||
// Get Group | ||
//========================================================================================================= | ||
/** | ||
* @public | ||
* Creates new Selector that represents an Item of the Collection | ||
* @param selectorKey - Name/Key of Selector | ||
* @param itemKey - Key of Item which the Selector represents | ||
* Get Group by Key/Name | ||
* @param groupKey - Key/Name of Group | ||
* @param config - Config | ||
*/ | ||
createSelector(selectorKey, itemKey) { | ||
if (this.selectors.hasOwnProperty(selectorKey)) { | ||
console.warn(`Agile: The Selector with the name '${selectorKey}' already exists!`); | ||
return this.selectors[selectorKey]; | ||
} | ||
// Create new Selector | ||
const selector = new internal_1.Selector(this, itemKey, { | ||
key: selectorKey, | ||
getGroup(groupKey, config = {}) { | ||
config = internal_1.defineConfig(config, { | ||
notExisting: false, | ||
}); | ||
this.selectors[selectorKey] = selector; | ||
// Logging | ||
if (this.agileInstance().config.logJobs) | ||
console.log(`Agile: Created Selector called '${selectorKey}'`, selector); | ||
return selector; | ||
// Get Group | ||
const group = groupKey ? this.groups[groupKey] : undefined; | ||
// Check if Group exists | ||
if (!group || (!config.notExisting && group.isPlaceholder)) | ||
return undefined; | ||
internal_1.ComputedTracker.tracked(group.observer); | ||
return group; | ||
} | ||
//========================================================================================================= | ||
// Get Group | ||
// Get Group With Reference | ||
//========================================================================================================= | ||
/** | ||
* @public | ||
* Get Group by Key/Name | ||
* Get Group by Key/Name or a Reference to it if it doesn't exist yet | ||
* @param groupKey - Name/Key of Group | ||
*/ | ||
getGroup(groupKey) { | ||
if (!this.groups[groupKey]) { | ||
console.warn(`Agile: Group with the key/name '${groupKey}' doesn't exist!`); | ||
// Return empty group because it might get annoying to handle with undefined (can check if it exists with group.exists) | ||
const group = new internal_1.Group(this.agileInstance(), this, [], { | ||
key: "dummy", | ||
getGroupWithReference(groupKey) { | ||
let group = this.getGroup(groupKey, { notExisting: true }); | ||
// Create dummy Group to hold reference | ||
if (!group) { | ||
group = new internal_1.Group(this, [], { | ||
key: groupKey, | ||
isPlaceholder: true, | ||
}); | ||
group.isPlaceholder = true; | ||
return group; | ||
this.groups[groupKey] = group; | ||
} | ||
return this.groups[groupKey]; | ||
internal_1.ComputedTracker.tracked(group.observer); | ||
return group; | ||
} | ||
//========================================================================================================= | ||
// Remove Selector | ||
// Remove Group | ||
//========================================================================================================= | ||
@@ -336,7 +337,7 @@ /** | ||
* Removes Group by Key/Name | ||
* @param groupKey - Name/Key of Selector | ||
* @param groupKey - Name/Key of Group | ||
*/ | ||
removeGroup(groupKey) { | ||
if (!this.groups[groupKey]) { | ||
console.warn(`Agile: Group with the key/name '${groupKey}' doesn't exist!`); | ||
internal_1.Agile.logger.warn(`Group with the key/name '${groupKey}' doesn't exist!`); | ||
return this; | ||
@@ -348,2 +349,29 @@ } | ||
//========================================================================================================= | ||
// Create Selector | ||
//========================================================================================================= | ||
/** | ||
* @public | ||
* Creates new Selector that represents an Item of the Collection | ||
* @param selectorKey - Name/Key of Selector | ||
* @param itemKey - Key of Item which the Selector represents | ||
*/ | ||
createSelector(selectorKey, itemKey) { | ||
let selector = this.getSelector(selectorKey, { notExisting: true }); | ||
// Check if Selector already exists | ||
if (selector) { | ||
if (!selector.isPlaceholder) { | ||
internal_1.Agile.logger.warn(`Selector with the name '${selectorKey}' already exists!`); | ||
return selector; | ||
} | ||
selector.select(itemKey, { overwrite: true }); | ||
return selector; | ||
} | ||
// Create Selector | ||
selector = new internal_1.Selector(this, itemKey, { | ||
key: selectorKey, | ||
}); | ||
this.selectors[selectorKey] = selector; | ||
return selector; | ||
} | ||
//========================================================================================================= | ||
// Get Selector | ||
@@ -354,10 +382,37 @@ //========================================================================================================= | ||
* Get Selector by Key/Name | ||
* @param selectorKey - Key/Name of Selector | ||
* @param config - Config | ||
*/ | ||
getSelector(selectorKey, config = {}) { | ||
config = internal_1.defineConfig(config, { | ||
notExisting: false, | ||
}); | ||
// Get Selector | ||
const selector = selectorKey ? this.selectors[selectorKey] : undefined; | ||
// Check if Selector exists | ||
if (!selector || (!config.notExisting && selector.isPlaceholder)) | ||
return undefined; | ||
internal_1.ComputedTracker.tracked(selector.observer); | ||
return selector; | ||
} | ||
//========================================================================================================= | ||
// Get Selector With Reference | ||
//========================================================================================================= | ||
/** | ||
* @public | ||
* Get Selector by Key/Name or a Reference to it if it doesn't exist yet | ||
* @param selectorKey - Name/Key of Selector | ||
*/ | ||
getSelector(selectorKey) { | ||
if (!this.selectors[selectorKey]) { | ||
console.warn(`Agile: Selector with the key/name '${selectorKey}' doesn't exist!`); | ||
return undefined; | ||
getSelectorWithReference(selectorKey) { | ||
let selector = this.getSelector(selectorKey, { notExisting: true }); | ||
// Create dummy Selector to hold reference | ||
if (!selector) { | ||
selector = new internal_1.Selector(this, "unknown", { | ||
key: selectorKey, | ||
isPlaceholder: true, | ||
}); | ||
this.selectors[selectorKey] = selector; | ||
} | ||
return this.selectors[selectorKey]; | ||
internal_1.ComputedTracker.tracked(selector.observer); | ||
return selector; | ||
} | ||
@@ -373,6 +428,8 @@ //========================================================================================================= | ||
removeSelector(selectorKey) { | ||
var _a; | ||
if (!this.selectors[selectorKey]) { | ||
console.warn(`Agile: Selector with the key/name '${selectorKey}' doesn't exist!`); | ||
internal_1.Agile.logger.warn(`Selector with the key/name '${selectorKey}' doesn't exist!`); | ||
return this; | ||
} | ||
(_a = this.selectors[selectorKey]) === null || _a === void 0 ? void 0 : _a.unselect(); // Unselects current selected Item | ||
delete this.selectors[selectorKey]; | ||
@@ -382,30 +439,40 @@ return this; | ||
//========================================================================================================= | ||
// Remove | ||
// Get Item by Id | ||
//========================================================================================================= | ||
/** | ||
* @public | ||
* Remove Items from Group or from everywhere | ||
* @param itemKeys - ItemKey/s that get removed | ||
* Get Item by Key/Name | ||
* @param itemKey - ItemKey of Item | ||
* @param config - Config | ||
*/ | ||
remove(itemKeys) { | ||
return { | ||
fromGroups: (groups) => this.removeFromGroups(itemKeys, groups), | ||
everywhere: () => this.removeItems(itemKeys), | ||
}; | ||
getItem(itemKey, config = {}) { | ||
config = internal_1.defineConfig(config, { | ||
notExisting: false, | ||
}); | ||
// Get Item | ||
const item = itemKey ? this.data[itemKey] : undefined; | ||
// Check if Item exists | ||
if (!item || (!config.notExisting && !item.exists)) | ||
return undefined; | ||
internal_1.ComputedTracker.tracked(item.observer); | ||
return item; | ||
} | ||
//========================================================================================================= | ||
// Get Item by Id | ||
//========================================================================================================= | ||
/** | ||
* @public | ||
* Get Item by Id | ||
* @param itemKey - ItemKey of Item that might get found | ||
* Get Item by Key/Name or a Reference to it if it doesn't exist yet | ||
* @param itemKey - Key/Name of Item | ||
*/ | ||
getItemById(itemKey) { | ||
if (!this.data.hasOwnProperty(itemKey) || !this.data[itemKey].exists) | ||
return undefined; | ||
const item = this.data[itemKey]; | ||
// Add State to tracked Observers (for auto tracking used observers in computed function) | ||
if (this.agileInstance().runtime.trackObservers) | ||
this.agileInstance().runtime.foundObservers.add(item.observer); | ||
getItemWithReference(itemKey) { | ||
let item = this.getItem(itemKey, { notExisting: true }); | ||
// Create dummy Item to hold reference | ||
if (!item) { | ||
item = new internal_1.Item(this, { | ||
[this.config.primaryKey]: itemKey, | ||
dummy: "item", | ||
}, { | ||
isPlaceholder: true, | ||
}); | ||
this.data[itemKey] = item; | ||
} | ||
internal_1.ComputedTracker.tracked(item.observer); | ||
return item; | ||
@@ -418,7 +485,8 @@ } | ||
* @public | ||
* Get Value of Item by Id | ||
* @param itemKey - ItemKey of ItemValue that might get found | ||
* Get Value of Item by Key/Name | ||
* @param itemKey - ItemKey of Item that holds the Value | ||
* @param config - Config | ||
*/ | ||
getValueById(itemKey) { | ||
let item = this.getItemById(itemKey); | ||
getItemValue(itemKey, config = {}) { | ||
let item = this.getItem(itemKey, config); | ||
if (!item) | ||
@@ -428,23 +496,50 @@ return undefined; | ||
} | ||
persist(keyOrConfig = {}, config = {}) { | ||
let _config; | ||
let key; | ||
if (internal_1.isValidObject(keyOrConfig)) { | ||
_config = keyOrConfig; | ||
key = this._key; | ||
} | ||
else { | ||
_config = config || {}; | ||
key = keyOrConfig; | ||
} | ||
_config = internal_1.defineConfig(_config, { | ||
instantiate: true, | ||
storageKeys: [], | ||
}); | ||
if (this.persistent) | ||
internal_1.Agile.logger.warn(`By persisting the Collection '${this._key}' twice you overwrite the old Persistent Instance!`); | ||
// Create persistent -> Persist Value | ||
this.persistent = new internal_1.CollectionPersistent(this, { | ||
instantiate: _config.instantiate, | ||
storageKeys: _config.storageKeys, | ||
key: key, | ||
}); | ||
return this; | ||
} | ||
//========================================================================================================= | ||
// Persist | ||
// On Load | ||
//========================================================================================================= | ||
/** | ||
* @public | ||
* Stores Collection Value in Agile Storage | ||
* @param key - Storage Key (Note: not needed if Collection has key/name) | ||
* Callback Function that gets called if the persisted Value gets loaded into the Collection for the first Time | ||
* Note: Only useful for persisted Collections! | ||
* @param callback - Callback Function | ||
*/ | ||
persist(key) { | ||
// Update Persistent Key | ||
onLoad(callback) { | ||
if (this.persistent) { | ||
if (key) | ||
this.persistent.key = key; | ||
return this; | ||
this.persistent.onLoad = callback; | ||
// If Collection is already 'isPersisted' the loading was successful -> callback can be called | ||
if (this.isPersisted) | ||
callback(true); | ||
} | ||
// Create persistent -> Persist Value | ||
this.persistent = new internal_1.CollectionPersistent(this.agileInstance(), this, key); | ||
else { | ||
internal_1.Agile.logger.error(`Please make sure you persist the Collection '${this._key}' before using the 'onLoad' function!`); | ||
} | ||
return this; | ||
} | ||
//========================================================================================================= | ||
// Group Size | ||
// Get Group Count | ||
//========================================================================================================= | ||
@@ -462,3 +557,3 @@ /** | ||
//========================================================================================================= | ||
// Selector Size | ||
// Get Selector Count | ||
//========================================================================================================= | ||
@@ -484,18 +579,11 @@ /** | ||
var _a, _b; | ||
// Remove Items from Storage | ||
for (let key in this.data) { | ||
const item = this.getItemById(key); | ||
(_a = item === null || item === void 0 ? void 0 : item.persistent) === null || _a === void 0 ? void 0 : _a.removeValue(); | ||
} | ||
// Reset Groups | ||
for (let key in this.groups) | ||
(_b = this.getGroup(key)) === null || _b === void 0 ? void 0 : _b.reset(); | ||
// Reset Data | ||
this.data = {}; | ||
this.size = 0; | ||
// Reselect Items | ||
for (let key in this.selectors) { | ||
const selector = this.getSelector(key); | ||
selector === null || selector === void 0 ? void 0 : selector.select(selector === null || selector === void 0 ? void 0 : selector.itemKey, { force: true }); | ||
} | ||
// Reset Groups | ||
for (let key in this.groups) | ||
(_a = this.getGroup(key)) === null || _a === void 0 ? void 0 : _a.reset(); | ||
// Reset Selectors | ||
for (let key in this.selectors) | ||
(_b = this.getSelector(key)) === null || _b === void 0 ? void 0 : _b.reset(); | ||
} | ||
@@ -517,6 +605,4 @@ //========================================================================================================= | ||
_groupKeys.forEach((groupKey) => { | ||
const group = this.getGroup(groupKey); | ||
_itemKeys.forEach((itemKey) => { | ||
group === null || group === void 0 ? void 0 : group.add(itemKey, config); | ||
}); | ||
var _a; | ||
(_a = this.getGroup(groupKey)) === null || _a === void 0 ? void 0 : _a.add(_itemKeys, config); | ||
}); | ||
@@ -529,3 +615,3 @@ } | ||
* @internal | ||
* Updates ItemKey of Item | ||
* Updates Key/Name of Item in all Instances (Group, Selector, ..) | ||
* @param oldItemKey - Old ItemKey | ||
@@ -535,4 +621,5 @@ * @param newItemKey - New ItemKey | ||
*/ | ||
updateItemKey(oldItemKey, newItemKey, config) { | ||
const item = this.getItemById(oldItemKey); | ||
updateItemKey(oldItemKey, newItemKey, config = {}) { | ||
var _a; | ||
const item = this.getItem(oldItemKey, { notExisting: true }); | ||
config = internal_1.defineConfig(config, { | ||
@@ -542,3 +629,3 @@ background: false, | ||
if (!item || oldItemKey === newItemKey) | ||
return; | ||
return false; | ||
// Remove Item from old ItemKey and add Item to new ItemKey | ||
@@ -548,25 +635,64 @@ delete this.data[oldItemKey]; | ||
// Update Key/Name of Item | ||
item.key = newItemKey; | ||
// Update persist Key of Item (Doesn't get changed by setting new item key because PersistKey is not ItemKey) | ||
item.persist(internal_1.CollectionPersistent.getItemStorageKey(newItemKey, this.key)); | ||
// Update Groups | ||
for (let groupName in this.groups) { | ||
const group = this.getGroup(groupName); | ||
if (group.isPlaceholder || !group.has(oldItemKey)) | ||
item.setKey(newItemKey); | ||
// Update persist Key of Item (Doesn't get updated by updating key of Item because PersistKey is special formatted) | ||
(_a = item.persistent) === null || _a === void 0 ? void 0 : _a.setKey(internal_1.CollectionPersistent.getItemStorageKey(newItemKey, this._key)); | ||
// Update ItemKey in Groups | ||
for (let groupKey in this.groups) { | ||
const group = this.getGroup(groupKey, { notExisting: true }); | ||
if (!group || !group.has(oldItemKey)) | ||
continue; | ||
// Replace old ItemKey with new ItemKey | ||
const newGroupValue = internal_1.copy(group.value); | ||
newGroupValue.splice(newGroupValue.indexOf(oldItemKey), 1, newItemKey); | ||
group.set(newGroupValue, { background: config === null || config === void 0 ? void 0 : config.background }); | ||
group.replace(oldItemKey, newItemKey, { background: config === null || config === void 0 ? void 0 : config.background }); | ||
} | ||
// Update Selectors | ||
for (let selectorName in this.selectors) { | ||
const selector = this.getSelector(selectorName); | ||
if (!selector || selector.itemKey !== oldItemKey) | ||
// Update ItemKey in Selectors | ||
for (let selectorKey in this.selectors) { | ||
const selector = this.getSelector(selectorKey, { notExisting: true }); | ||
if (!selector) | ||
continue; | ||
// Replace old selected ItemKey with new ItemKey | ||
selector.select(newItemKey, { background: config === null || config === void 0 ? void 0 : config.background }); | ||
// Reselect Item in existing Selector which has selected the newItemKey | ||
if (selector.hasSelected(newItemKey)) { | ||
selector.select(newItemKey, { | ||
force: true, | ||
background: config === null || config === void 0 ? void 0 : config.background, | ||
}); | ||
} | ||
// Select newItemKey in existing Selector which has selected the oldItemKey | ||
if (selector.hasSelected(oldItemKey)) | ||
selector.select(newItemKey, { | ||
background: config === null || config === void 0 ? void 0 : config.background, | ||
}); | ||
} | ||
return true; | ||
} | ||
//========================================================================================================= | ||
// Get GroupKeys That Have ItemKey | ||
//========================================================================================================= | ||
/** | ||
* @public | ||
* Gets GroupKeys that contain the passed ItemKey | ||
* @param itemKey - ItemKey | ||
*/ | ||
getGroupKeysThatHaveItemKey(itemKey) { | ||
const groupKeys = []; | ||
for (let groupKey in this.groups) { | ||
const group = this.getGroup(groupKey, { notExisting: true }); | ||
if (group === null || group === void 0 ? void 0 : group.has(itemKey)) | ||
groupKeys.push(groupKey); | ||
} | ||
return groupKeys; | ||
} | ||
//========================================================================================================= | ||
// Remove | ||
//========================================================================================================= | ||
/** | ||
* @public | ||
* Remove Items from Collection | ||
* @param itemKeys - ItemKey/s that get removed | ||
*/ | ||
remove(itemKeys) { | ||
return { | ||
fromGroups: (groups) => this.removeFromGroups(itemKeys, groups), | ||
everywhere: () => this.removeItems(itemKeys), | ||
}; | ||
} | ||
//========================================================================================================= | ||
// Remove From Groups | ||
@@ -587,4 +713,4 @@ //========================================================================================================= | ||
_groupKeys.forEach((groupKey) => { | ||
const group = this.getGroup(groupKey); | ||
if (!group) | ||
const group = this.getGroup(groupKey, { notExisting: true }); | ||
if (!group || !group.has(itemKey)) | ||
return; | ||
@@ -594,4 +720,5 @@ group.remove(itemKey); | ||
}); | ||
// If Item got removed from every Groups in Collection, remove it completely | ||
if (removedFromGroupsCount >= this.getGroupCount()) | ||
// If Item got removed from every Groups the Item was in, remove it completely | ||
if (removedFromGroupsCount >= | ||
this.getGroupKeysThatHaveItemKey(itemKey).length) | ||
this.removeItems(itemKey); | ||
@@ -605,4 +732,4 @@ }); | ||
* @public | ||
* Removes Item/s from Group/s | ||
* @param itemKeys - ItemKey/s of Item/s that get removed from Collection | ||
* Removes Item completely from Collection | ||
* @param itemKeys - ItemKey/s of Item/s | ||
*/ | ||
@@ -613,3 +740,3 @@ removeItems(itemKeys) { | ||
var _a; | ||
const item = this.getItemById(itemKey); | ||
const item = this.getItem(itemKey, { notExisting: true }); | ||
if (!item) | ||
@@ -619,16 +746,16 @@ return; | ||
for (let groupKey in this.groups) { | ||
const group = this.getGroup(groupKey); | ||
if (group.has(itemKey)) | ||
group.remove(itemKey); | ||
const group = this.getGroup(groupKey, { notExisting: true }); | ||
if (group === null || group === void 0 ? void 0 : group.has(itemKey)) | ||
group === null || group === void 0 ? void 0 : group.remove(itemKey); | ||
} | ||
// Remove Selectors that represents this Item | ||
for (let selectorKey in this.selectors) { | ||
const selector = this.getSelector(selectorKey); | ||
if ((selector === null || selector === void 0 ? void 0 : selector.itemKey) === itemKey) | ||
this.removeSelector(selectorKey); | ||
} | ||
// Remove Item from Storage | ||
(_a = item.persistent) === null || _a === void 0 ? void 0 : _a.removeValue(); | ||
(_a = item.persistent) === null || _a === void 0 ? void 0 : _a.removePersistedValue(); | ||
// Remove Item from Collection | ||
delete this.data[itemKey]; | ||
// Reselect Item in Selectors (to create new dummyItem that holds reference) | ||
for (let selectorKey in this.selectors) { | ||
const selector = this.getSelector(selectorKey, { notExisting: true }); | ||
if (selector === null || selector === void 0 ? void 0 : selector.hasSelected(itemKey)) | ||
selector === null || selector === void 0 ? void 0 : selector.select(itemKey, { force: true }); | ||
} | ||
this.size--; | ||
@@ -642,9 +769,9 @@ }); | ||
* @internal | ||
* Creates/Updates Item from provided Data and adds it to the Collection | ||
* @param data - Data from which the Item gets created/updated | ||
* Updates existing or creates Item from provided Data | ||
* @param data - Data | ||
* @param config - Config | ||
*/ | ||
setData(data, config = {}) { | ||
const _data = data; // Transformed Data to any because of unknown Object (DataType) | ||
const primaryKey = this.config.primaryKey || "id"; | ||
const _data = internal_1.copy(data); // Transformed Data to any because of unknown Object (DataType) | ||
const primaryKey = this.config.primaryKey; | ||
config = internal_1.defineConfig(config, { | ||
@@ -655,25 +782,25 @@ patch: false, | ||
if (!internal_1.isValidObject(_data)) { | ||
console.error("Agile: Collections items has to be an object!"); | ||
internal_1.Agile.logger.error(`Item Data of Collection '${this._key}' has to be an valid Object!`); | ||
return false; | ||
} | ||
if (!_data.hasOwnProperty(primaryKey)) { | ||
console.error(`Agile: Collection Item needs a primary Key property called '${this.config.primaryKey}'!`); | ||
internal_1.Agile.logger.error(`Collection '${this._key}' Item Data has to contain a primaryKey property called '${this.config.primaryKey}'!`); | ||
return false; | ||
} | ||
const itemKey = _data[primaryKey]; | ||
let item = this.data[itemKey]; | ||
let item = this.getItem(itemKey, { notExisting: true }); | ||
const wasPlaceholder = (item === null || item === void 0 ? void 0 : item.isPlaceholder) || false; | ||
const createItem = !item; | ||
// Create or update Item | ||
if (item && config.patch) | ||
item = item.patch(_data, { background: config.background }); | ||
if (item && !config.patch) | ||
item = item.set(_data, { background: config.background }); | ||
if (!item) { | ||
if (!createItem && config.patch) | ||
item === null || item === void 0 ? void 0 : item.patch(_data, { background: config.background }); | ||
if (!createItem && !config.patch) | ||
item === null || item === void 0 ? void 0 : item.set(_data, { background: config.background }); | ||
if (createItem) { | ||
item = new internal_1.Item(this, _data); | ||
this.data[itemKey] = item; | ||
} | ||
// Increase size of Collection | ||
if (createItem || wasPlaceholder) | ||
this.size++; | ||
} | ||
// Reset isPlaceholder of Item since it got an value | ||
if (item.isPlaceholder) | ||
item.isPlaceholder = false; | ||
// Set new Item at itemKey | ||
this.data[itemKey] = item; | ||
return true; | ||
@@ -693,3 +820,2 @@ } | ||
background: false, | ||
forceRerender: !(config === null || config === void 0 ? void 0 : config.background), | ||
sideEffects: true, | ||
@@ -700,7 +826,7 @@ }); | ||
const group = this.getGroup(groupKey); | ||
if (group.has(itemKey)) { | ||
if (group === null || group === void 0 ? void 0 : group.has(itemKey)) { | ||
// group.rebuild(); Not necessary because a sideEffect of the Group is to rebuild it self | ||
group.ingest({ | ||
group === null || group === void 0 ? void 0 : group.ingest({ | ||
background: config === null || config === void 0 ? void 0 : config.background, | ||
forceRerender: config === null || config === void 0 ? void 0 : config.forceRerender, | ||
force: true, | ||
sideEffects: config === null || config === void 0 ? void 0 : config.sideEffects, | ||
@@ -707,0 +833,0 @@ storage: false, |
import { State, Collection, DefaultItem, StateKey } from "../internal"; | ||
export declare class Item<DataType = DefaultItem> extends State<DataType> { | ||
private collection; | ||
static updateGroupSideEffectKey: string; | ||
isSelected: boolean; | ||
collection: () => Collection<DataType>; | ||
/** | ||
@@ -9,14 +11,17 @@ * @public | ||
* @param data - Data that the Item holds | ||
* @param config - Config | ||
*/ | ||
constructor(collection: Collection, data: DataType); | ||
constructor(collection: Collection<DataType>, data: DataType, config?: ItemConfigInterface); | ||
/** | ||
* @public | ||
* Set Key/Name of Item | ||
* @internal | ||
* Updates Key/Name of State | ||
* @param value - New Key/Name of State | ||
*/ | ||
set key(value: StateKey | undefined); | ||
/** | ||
* @public | ||
* Get Key/Name of Item | ||
*/ | ||
get key(): StateKey | undefined; | ||
setKey(value: StateKey | undefined): this; | ||
} | ||
/** | ||
* @param isPlaceholder - If Item is initially a Placeholder | ||
*/ | ||
export interface ItemConfigInterface { | ||
isPlaceholder?: boolean; | ||
} |
@@ -11,33 +11,36 @@ "use strict"; | ||
* @param data - Data that the Item holds | ||
* @param config - Config | ||
*/ | ||
constructor(collection, data) { | ||
var _a; | ||
super(collection.agileInstance(), data); | ||
constructor(collection, data, config = {}) { | ||
super(collection.agileInstance(), data, { | ||
isPlaceholder: config.isPlaceholder, | ||
key: data[collection.config.primaryKey], | ||
}); | ||
this.isSelected = false; // If Item is selected by a Selector | ||
this.collection = () => collection; | ||
// Setting primaryKey of Data to Key/Name of Item | ||
this.key = data[((_a = collection.config) === null || _a === void 0 ? void 0 : _a.primaryKey) || "id"]; | ||
// Reassign Key to assign sideEffects | ||
this.setKey(data[collection.config.primaryKey]); | ||
} | ||
//========================================================================================================= | ||
// Set Key | ||
//========================================================================================================= | ||
/** | ||
* @public | ||
* Set Key/Name of Item | ||
* @internal | ||
* Updates Key/Name of State | ||
* @param value - New Key/Name of State | ||
*/ | ||
set key(value) { | ||
// Note can't use 'super.key' because of 'https://github.com/Microsoft/TypeScript/issues/338' | ||
this.setKey(value); | ||
setKey(value) { | ||
super.setKey(value); | ||
if (!value) | ||
return; | ||
// Update rebuildGroupThatIncludePrimaryKey SideEffect | ||
this.removeSideEffect("rebuildGroup"); | ||
this.addSideEffect("rebuildGroup", (properties) => this.collection().rebuildGroupsThatIncludeItemKey(value, properties)); | ||
return this; | ||
// Remove old rebuildGroupsThatIncludeItemKey sideEffect | ||
this.removeSideEffect(Item.updateGroupSideEffectKey); | ||
// Add rebuildGroupsThatIncludeItemKey to sideEffects to rebuild Groups that include this Item if it changes | ||
this.addSideEffect(Item.updateGroupSideEffectKey, (config) => this.collection().rebuildGroupsThatIncludeItemKey(value, config)); | ||
// Initial Rebuild | ||
this.collection().rebuildGroupsThatIncludeItemKey(value); | ||
return this; | ||
} | ||
/** | ||
* @public | ||
* Get Key/Name of Item | ||
*/ | ||
get key() { | ||
// Note can't use 'super.key' because of 'https://github.com/Microsoft/TypeScript/issues/338' | ||
// Can't remove this getter function.. because the setter function is set in this class -> Error if not setter and getter function set | ||
return this._key; | ||
} | ||
} | ||
exports.Item = Item; | ||
Item.updateGroupSideEffectKey = "rebuildGroup"; |
@@ -1,3 +0,5 @@ | ||
import { Collection, DefaultItem, Item, ItemKey, State } from "../internal"; | ||
import { Collection, DefaultItem, Item, ItemKey, State, StateRuntimeJobConfigInterface } from "../internal"; | ||
export declare class Selector<DataType = DefaultItem> extends State<DataType | undefined> { | ||
static dummyItemKey: string; | ||
static rebuildSelectorSideEffectKey: string; | ||
collection: () => Collection<DataType>; | ||
@@ -26,13 +28,24 @@ item: Item<DataType> | undefined; | ||
* @public | ||
* Select new ItemKey that the Selector will represents | ||
* Select new ItemKey | ||
* @param itemKey - New ItemKey | ||
* @param config - Config | ||
*/ | ||
select(itemKey: ItemKey, config?: SelectConfigInterface): this; | ||
select(itemKey: ItemKey, config?: StateRuntimeJobConfigInterface): this; | ||
/** | ||
* @public | ||
* Unselects current selected Item | ||
* @param config - Config | ||
*/ | ||
unselect(config?: StateRuntimeJobConfigInterface): this; | ||
/** | ||
* Checks if Selector has selected passed ItemKey | ||
* @param itemKey | ||
*/ | ||
hasSelected(itemKey: ItemKey): boolean; | ||
/** | ||
* @public | ||
* Rebuilds Selector | ||
* @param config - Config | ||
*/ | ||
rebuildSelector(config?: SelectConfigInterface): void; | ||
rebuildSelector(config?: StateRuntimeJobConfigInterface): void; | ||
} | ||
@@ -42,15 +55,7 @@ export declare type SelectorKey = string | number; | ||
* @param key - Key/Name of Selector | ||
* @param isPlaceholder - If Selector is initially a Placeholder | ||
*/ | ||
export interface SelectorConfigInterface { | ||
key?: SelectorKey; | ||
isPlaceholder?: boolean; | ||
} | ||
/** | ||
* @param background - If selecting a new Item happens in the background (-> not causing any rerender) | ||
* @param sideEffects - If Side Effects of Selector get executed | ||
* @param force - Force to select ItemKey | ||
*/ | ||
export interface SelectConfigInterface { | ||
background?: boolean; | ||
sideEffects?: boolean; | ||
force?: boolean; | ||
} |
@@ -13,10 +13,15 @@ "use strict"; | ||
*/ | ||
constructor(collection, itemKey, config) { | ||
super(collection.agileInstance(), collection.getValueById(itemKey)); | ||
constructor(collection, itemKey, config = {}) { | ||
super(collection.agileInstance(), undefined, config); | ||
config = internal_1.defineConfig(config, { | ||
isPlaceholder: false, | ||
}); | ||
this.collection = () => collection; | ||
this.item = undefined; | ||
this._itemKey = itemKey; | ||
this.key = config === null || config === void 0 ? void 0 : config.key; | ||
this._itemKey = Selector.dummyItemKey; | ||
this._key = config === null || config === void 0 ? void 0 : config.key; | ||
this.isPlaceholder = true; | ||
// Initial Select | ||
this.select(itemKey); | ||
if (!config.isPlaceholder) | ||
this.select(itemKey, { overwrite: true }); | ||
} | ||
@@ -37,5 +42,8 @@ /** | ||
} | ||
//========================================================================================================= | ||
// Select | ||
//========================================================================================================= | ||
/** | ||
* @public | ||
* Select new ItemKey that the Selector will represents | ||
* Select new ItemKey | ||
* @param itemKey - New ItemKey | ||
@@ -45,4 +53,6 @@ * @param config - Config | ||
select(itemKey, config = {}) { | ||
const oldItem = this.item; | ||
let newItem = this.collection().getItemById(itemKey); | ||
const oldItem = this.collection().getItem(this._itemKey, { | ||
notExisting: true, | ||
}); // Because this.item might be outdated | ||
let newItem = this.collection().getItemWithReference(itemKey); | ||
config = internal_1.defineConfig(config, { | ||
@@ -52,22 +62,16 @@ background: false, | ||
force: false, | ||
overwrite: (oldItem === null || oldItem === void 0 ? void 0 : oldItem.isPlaceholder) || false, | ||
storage: true, | ||
}); | ||
if ((oldItem === null || oldItem === void 0 ? void 0 : oldItem.key) === itemKey && !config.force) { | ||
console.warn(`Agile: Selector has already selected key '${itemKey}'!`); | ||
if (this.hasSelected(itemKey) && !config.force) { | ||
internal_1.Agile.logger.warn(`Selector has already selected '${itemKey}'!`); | ||
return this; | ||
} | ||
// Remove old Item from Collection if it is an Placeholder | ||
if (oldItem === null || oldItem === void 0 ? void 0 : oldItem.isPlaceholder) | ||
delete this.collection().data[this.itemKey]; | ||
// Create dummy Item to hold reference if Item with ItemKey doesn't exist | ||
if (!newItem) { | ||
newItem = new internal_1.Item(this.collection(), { id: itemKey }); | ||
newItem.isPlaceholder = true; | ||
this.collection().data[itemKey] = newItem; | ||
} | ||
// Remove Selector sideEffect from old Item | ||
oldItem === null || oldItem === void 0 ? void 0 : oldItem.removeSideEffect("rebuildSelector"); | ||
// Unselect old Item | ||
this.unselect({ background: true }); | ||
this._itemKey = itemKey; | ||
this.item = newItem; | ||
// Add Selector sideEffect to Item | ||
newItem.addSideEffect("rebuildSelector", () => this.rebuildSelector(config)); | ||
newItem.isSelected = true; | ||
// Add SideEffect to newItem, that rebuild this Selector depending on the current Item Value | ||
newItem.addSideEffect(Selector.rebuildSelectorSideEffectKey, (config) => this.rebuildSelector(config)); | ||
// Rebuild Selector for instantiating new 'selected' ItemKey properly | ||
@@ -78,6 +82,46 @@ this.rebuildSelector(config); | ||
//========================================================================================================= | ||
// RebuildSelector | ||
// Unselect | ||
//========================================================================================================= | ||
/** | ||
* @public | ||
* Unselects current selected Item | ||
* @param config - Config | ||
*/ | ||
unselect(config = {}) { | ||
// Because this.item might be outdated | ||
const item = this.collection().getItem(this._itemKey, { | ||
notExisting: true, | ||
}); | ||
// Unselect Item | ||
if (item) { | ||
item.isSelected = false; | ||
item.removeSideEffect(Selector.rebuildSelectorSideEffectKey); | ||
if (item.isPlaceholder) | ||
delete this.collection().data[this._itemKey]; | ||
} | ||
// Reset and rebuild Selector | ||
this.item = undefined; | ||
this._itemKey = Selector.dummyItemKey; | ||
this.rebuildSelector(config); | ||
this.isPlaceholder = true; | ||
return this; | ||
} | ||
//========================================================================================================= | ||
// Has Selected | ||
//========================================================================================================= | ||
/** | ||
* Checks if Selector has selected passed ItemKey | ||
* @param itemKey | ||
*/ | ||
hasSelected(itemKey) { | ||
const isSelected = this._itemKey === itemKey; | ||
if (!this.item) | ||
return isSelected; | ||
return isSelected && this.item.isSelected; | ||
} | ||
//========================================================================================================= | ||
// Rebuild Selector | ||
//========================================================================================================= | ||
/** | ||
* @public | ||
* Rebuilds Selector | ||
@@ -87,23 +131,13 @@ * @param config - Config | ||
rebuildSelector(config = {}) { | ||
var _a, _b; | ||
config = internal_1.defineConfig(config, { | ||
background: false, | ||
sideEffects: true, | ||
}); | ||
// Set Selector Value to undefined if Item doesn't exist | ||
if (!this.item || this.item.isPlaceholder) { | ||
this._value = undefined; | ||
this.set(undefined, config); | ||
return; | ||
} | ||
// Assign ItemValue to Selector | ||
this.nextStateValue = internal_1.copy((_a = this.item) === null || _a === void 0 ? void 0 : _a.value); | ||
// Fix initialStateValue and previousStateValue if they are still set from the Placeholder | ||
if (internal_1.equal(this.item.initialStateValue, { id: this.itemKey })) | ||
this.item.initialStateValue = internal_1.copy((_b = this.item) === null || _b === void 0 ? void 0 : _b.nextStateValue); | ||
if (internal_1.equal(this.item.previousStateValue, { id: this.itemKey })) | ||
this.item.previousStateValue = internal_1.copy(this.nextStateValue); | ||
// Ingest nextStateValue into Runtime | ||
this.ingest(config); | ||
// Set Selector Value to updated Item Value | ||
this.set(this.item._value, config); | ||
} | ||
} | ||
exports.Selector = Selector; | ||
Selector.dummyItemKey = "unknown"; | ||
Selector.rebuildSelectorSideEffectKey = "rebuildSelector"; |
@@ -1,2 +0,2 @@ | ||
import { State, Agile, Observer, StorageKey, StatePersistentConfigInterface } from "../internal"; | ||
import { State, Agile, Observer, StorageKey, StatePersistentConfigInterface, Event, StateConfigInterface } from "../internal"; | ||
export declare class Computed<ComputedValueType = any> extends State<ComputedValueType> { | ||
@@ -12,19 +12,8 @@ agileInstance: () => Agile; | ||
* @param computeFunction - Function for computing value | ||
* @param deps - Hard coded dependencies of Computed Function | ||
* @param config - Config | ||
*/ | ||
constructor(agileInstance: Agile, computeFunction: () => ComputedValueType, deps?: Array<Observer | State | Event>); | ||
constructor(agileInstance: Agile, computeFunction: () => ComputedValueType, config?: ComputedConfigInterface); | ||
/** | ||
* @public | ||
* Set Value of Computed | ||
*/ | ||
set value(value: ComputedValueType); | ||
/** | ||
* @public | ||
* Get Value of Computed | ||
*/ | ||
get value(): ComputedValueType; | ||
/** | ||
* @public | ||
* Recomputes Function Value | ||
* -> Calls ComputeFunction and updates Dependencies of it | ||
* Recomputes Value of Computed | ||
* @param config - Config | ||
@@ -40,3 +29,3 @@ */ | ||
*/ | ||
updateComputeFunction(computeFunction: () => ComputedValueType, deps?: Array<Observer | State | Event>, config?: RecomputeConfigInterface): void; | ||
updateComputeFunction(computeFunction: () => ComputedValueType, deps?: Array<Observer | State | Event>, config?: UpdateComputeFunctionInterface): void; | ||
/** | ||
@@ -47,2 +36,8 @@ * @internal | ||
computeValue(): ComputedValueType; | ||
/** | ||
* @internal | ||
* Gets Observer out of passed Instances | ||
* @param instances - Instances that hold an Observer | ||
*/ | ||
formatDeps(instances: Array<any>): Array<Observer>; | ||
patch(): this; | ||
@@ -53,2 +48,8 @@ persist(keyOrConfig?: StorageKey | StatePersistentConfigInterface, config?: StatePersistentConfigInterface): this; | ||
/** | ||
* @param computedDeps - Hard coded dependencies of Computed Function | ||
*/ | ||
export interface ComputedConfigInterface extends StateConfigInterface { | ||
computedDeps?: Array<Observer | State | Event>; | ||
} | ||
/** | ||
* @param background - If recomputing value happens in the background (-> not causing any rerender) | ||
@@ -61,1 +62,7 @@ * @param sideEffects - If Side Effects of Computed get executed | ||
} | ||
/** | ||
* @param overwriteDeps - If old hardCoded deps get overwritten | ||
*/ | ||
export interface UpdateComputeFunctionInterface extends RecomputeConfigInterface { | ||
overwriteDeps?: boolean; | ||
} |
@@ -11,35 +11,22 @@ "use strict"; | ||
* @param computeFunction - Function for computing value | ||
* @param deps - Hard coded dependencies of Computed Function | ||
* @param config - Config | ||
*/ | ||
constructor(agileInstance, computeFunction, deps = []) { | ||
super(agileInstance, computeFunction()); | ||
this.deps = []; // All Dependencies of Computed | ||
this.hardCodedDeps = []; | ||
constructor(agileInstance, computeFunction, config = {}) { | ||
super(agileInstance, computeFunction(), { | ||
key: config.key, | ||
deps: config.deps, | ||
}); | ||
this.deps = []; // All Dependencies of Computed (hardCoded and autoDetected) | ||
this.hardCodedDeps = []; // HardCoded Dependencies of Computed | ||
config = internal_1.defineConfig(config, { | ||
computedDeps: [], | ||
}); | ||
this.agileInstance = () => agileInstance; | ||
this.computeFunction = computeFunction; | ||
this.hardCodedDeps = deps | ||
.map((dep) => dep["observer"] || undefined) | ||
.filter((dep) => dep !== undefined); | ||
// Format hardCodedDeps | ||
this.hardCodedDeps = this.formatDeps(config.computedDeps); | ||
this.deps = this.hardCodedDeps; | ||
// Recompute for setting initial value and adding missing dependencies | ||
this.recompute(); | ||
} | ||
/** | ||
* @public | ||
* Set Value of Computed | ||
*/ | ||
set value(value) { | ||
console.error("Agile: You can't mutate Computed value!"); | ||
} | ||
/** | ||
* @public | ||
* Get Value of Computed | ||
*/ | ||
get value() { | ||
// Note can't use 'super.value' because of 'https://github.com/Microsoft/TypeScript/issues/338' | ||
// Can't remove this getter function.. since the setter function is set in this class -> Error if not setter and getter function set | ||
// Add State to tracked Observers (for auto tracking used observers in computed function) | ||
if (this.agileInstance().runtime.trackObservers) | ||
this.agileInstance().runtime.foundObservers.add(this.observer); | ||
return this._value; | ||
} | ||
//========================================================================================================= | ||
@@ -50,7 +37,6 @@ // Recompute | ||
* @public | ||
* Recomputes Function Value | ||
* -> Calls ComputeFunction and updates Dependencies of it | ||
* Recomputes Value of Computed | ||
* @param config - Config | ||
*/ | ||
recompute(config) { | ||
recompute(config = {}) { | ||
config = internal_1.defineConfig(config, { | ||
@@ -72,9 +58,22 @@ background: false, | ||
*/ | ||
updateComputeFunction(computeFunction, deps = [], config) { | ||
updateComputeFunction(computeFunction, deps = [], config = {}) { | ||
config = internal_1.defineConfig(config, { | ||
background: false, | ||
sideEffects: true, | ||
overwriteDeps: true, | ||
}); | ||
// Update deps | ||
const newDeps = this.formatDeps(deps); | ||
if (config.overwriteDeps) | ||
this.hardCodedDeps = newDeps; | ||
else | ||
this.hardCodedDeps = this.hardCodedDeps.concat(newDeps); | ||
this.deps = this.hardCodedDeps; | ||
// Update computeFunction | ||
this.computeFunction = computeFunction; | ||
this.hardCodedDeps = deps | ||
.map((dep) => dep["observer"] || undefined) | ||
.filter((dep) => dep !== undefined); | ||
// Recompute for setting initial Computed Function Value and adding missing Dependencies | ||
this.recompute(config); | ||
this.recompute({ | ||
background: config.background, | ||
sideEffects: config.sideEffects, | ||
}); | ||
} | ||
@@ -89,13 +88,11 @@ //========================================================================================================= | ||
computeValue() { | ||
this.agileInstance().runtime.trackObservers = true; | ||
// Auto track Observers the computeFunction might depend on | ||
internal_1.ComputedTracker.track(); | ||
const computedValue = this.computeFunction(); | ||
// Get tracked Observers and disable Tracking Observers | ||
let foundDeps = Array.from(this.agileInstance().runtime.getTrackedObservers()); | ||
let foundDeps = internal_1.ComputedTracker.getTrackedObservers(); | ||
// Handle foundDeps and hardCodedDeps | ||
const newDeps = []; | ||
this.hardCodedDeps.concat(foundDeps).forEach((observer) => { | ||
if (!observer) | ||
return; | ||
newDeps.push(observer); | ||
// Make this Observer depending on Observer -> If value of Observer changes it will ingest this Observer into the Runtime | ||
// Make this Observer depending on foundDep Observer | ||
observer.depend(this.observer); | ||
@@ -107,14 +104,36 @@ }); | ||
//========================================================================================================= | ||
// Overwriting some functions which can't be used in Computed | ||
// Format Deps | ||
//========================================================================================================= | ||
/** | ||
* @internal | ||
* Gets Observer out of passed Instances | ||
* @param instances - Instances that hold an Observer | ||
*/ | ||
formatDeps(instances) { | ||
const finalInstances = []; | ||
for (let instance of instances) { | ||
if (instance instanceof internal_1.Observer) { | ||
finalInstances.push(instance); | ||
continue; | ||
} | ||
if (instance !== undefined && | ||
instance["observer"] !== undefined && | ||
instance["observer"] instanceof internal_1.Observer) | ||
finalInstances.push(instance["observer"]); | ||
} | ||
return finalInstances; | ||
} | ||
//========================================================================================================= | ||
// Overwriting some functions which aren't allowed to use in Computed | ||
//========================================================================================================= | ||
patch() { | ||
console.error("Agile: You can't use patch method on Computed Function!"); | ||
internal_1.Agile.logger.error("You can't use patch method on ComputedState!"); | ||
return this; | ||
} | ||
persist(keyOrConfig = {}, config = {}) { | ||
console.error("Agile: You can't use persist method on Computed Function!"); | ||
internal_1.Agile.logger.error("You can't use persist method on ComputedState!"); | ||
return this; | ||
} | ||
invert() { | ||
console.error("Agile: You can't use invert method on Computed Function!"); | ||
internal_1.Agile.logger.error("You can't use invert method on ComputedState!"); | ||
return this; | ||
@@ -121,0 +140,0 @@ } |
@@ -1,2 +0,2 @@ | ||
import { Agile, Observer, Job, ObserverKey, Event } from "../internal"; | ||
import { Observer, RuntimeJob, ObserverKey, Event, SubscriptionContainer, IngestConfigInterface, RuntimeJobConfigInterface, RuntimeJobKey } from "../internal"; | ||
export declare class EventObserver<PayloadType = any> extends Observer { | ||
@@ -7,13 +7,12 @@ event: () => Event<PayloadType>; | ||
* Event Observer - Handles Event dependencies and ingests Event triggers into the Runtime | ||
* @param {Agile} agileInstance - An instance of Agile | ||
* @param {Event} event - Event | ||
* @param {Array<Observer>} deps - Initial Dependencies of the Event | ||
* @param {ObserverKey} key - Key/Name of Event Observer | ||
* @param event - Event | ||
* @param config - Config | ||
*/ | ||
constructor(agileInstance: Agile, event: Event<PayloadType>, deps?: Array<Observer>, key?: ObserverKey); | ||
constructor(event: Event<PayloadType>, config?: CreateEventObserverConfigInterface); | ||
/** | ||
* @internal | ||
* Triggers a rerender on Components which got subscribed by this Event | ||
* Ingests Event into Runtime and causes Rerender on Components that got subscribed by the Event (Observer) | ||
* @param config - Config | ||
*/ | ||
trigger(): void; | ||
trigger(config?: EventIngestConfigInterface): void; | ||
/** | ||
@@ -24,3 +23,19 @@ * @internal | ||
*/ | ||
perform(job: Job<this>): void; | ||
perform(job: RuntimeJob<this>): void; | ||
} | ||
/** | ||
* @param deps - Initial Dependencies of Event Observer | ||
* @param subs - Initial Subscriptions of Event Observer | ||
* @param key - Key/Name of Event Observer | ||
*/ | ||
export interface CreateEventObserverConfigInterface { | ||
deps?: Array<Observer>; | ||
subs?: Array<SubscriptionContainer>; | ||
key?: ObserverKey; | ||
} | ||
/** | ||
* @param key - Key/Name of Job that gets created | ||
*/ | ||
export interface EventIngestConfigInterface extends RuntimeJobConfigInterface, IngestConfigInterface { | ||
key?: RuntimeJobKey; | ||
} |
@@ -9,9 +9,11 @@ "use strict"; | ||
* Event Observer - Handles Event dependencies and ingests Event triggers into the Runtime | ||
* @param {Agile} agileInstance - An instance of Agile | ||
* @param {Event} event - Event | ||
* @param {Array<Observer>} deps - Initial Dependencies of the Event | ||
* @param {ObserverKey} key - Key/Name of Event Observer | ||
* @param event - Event | ||
* @param config - Config | ||
*/ | ||
constructor(agileInstance, event, deps, key) { | ||
super(agileInstance, deps, key); | ||
constructor(event, config = {}) { | ||
super(event.agileInstance(), { | ||
deps: config.deps, | ||
key: config.key, | ||
subs: config.subs, | ||
}); | ||
this.event = () => event; | ||
@@ -24,6 +26,22 @@ } | ||
* @internal | ||
* Triggers a rerender on Components which got subscribed by this Event | ||
* Ingests Event into Runtime and causes Rerender on Components that got subscribed by the Event (Observer) | ||
* @param config - Config | ||
*/ | ||
trigger() { | ||
this.agileInstance().runtime.ingest(this, {}); | ||
trigger(config = {}) { | ||
config = internal_1.defineConfig(config, { | ||
perform: true, | ||
background: false, | ||
sideEffects: true, | ||
force: false, | ||
}); | ||
// Create Job | ||
const job = new internal_1.RuntimeJob(this, { | ||
force: config.force, | ||
sideEffects: config.sideEffects, | ||
background: config.background, | ||
key: config.key || this._key, | ||
}); | ||
this.agileInstance().runtime.ingest(job, { | ||
perform: config.perform, | ||
}); | ||
} | ||
@@ -30,0 +48,0 @@ //========================================================================================================= |
@@ -1,6 +0,7 @@ | ||
import { Agile } from "../internal"; | ||
import { Agile, EventJob, Observer } from "../internal"; | ||
import { EventObserver } from "./event.observer"; | ||
export declare class Event<PayloadType = DefaultEventPayload> { | ||
agileInstance: () => Agile; | ||
config: EventConfig; | ||
config: EventConfigInterface; | ||
private initialConfig; | ||
_key?: EventKey; | ||
@@ -11,6 +12,6 @@ uses: number; | ||
}; | ||
private currentTimeout; | ||
private queue; | ||
enabled: boolean; | ||
observer: EventObserver; | ||
currentTimeout: any; | ||
queue: Array<EventJob>; | ||
payload: PayloadType; | ||
@@ -23,3 +24,3 @@ /** | ||
*/ | ||
constructor(agileInstance: Agile, config?: EventConfig); | ||
constructor(agileInstance: Agile, config?: CreateEventConfigInterface); | ||
/** | ||
@@ -36,2 +37,8 @@ * @public | ||
/** | ||
* @internal | ||
* Set Key/Name of Event | ||
* @param value - New Key/Name of Event | ||
*/ | ||
setKey(value: EventKey | undefined): this; | ||
/** | ||
* @public | ||
@@ -52,7 +59,7 @@ * Registers new Callback Function that will be called if this Event gets triggered | ||
* @public | ||
* Triggers Event | ||
* -> Calls all registered Callback Functions | ||
* Triggers Events | ||
* @param payload - Payload that gets passed into the Callback Functions | ||
* @param keys - Keys of Callback Functions that get triggered (Note: if not passed all registered Events will be triggered) | ||
*/ | ||
trigger(payload?: PayloadType): this; | ||
trigger(payload: PayloadType, keys?: string[]): this; | ||
/** | ||
@@ -81,10 +88,15 @@ * @public | ||
* @internal | ||
* Triggers Event | ||
* Triggers normal Event | ||
* @param payload - Payload that gets passed into the Callback Functions | ||
* @param keys - Keys of Callback Functions that get triggered (Note: if not passed all registered Events will be triggered) | ||
*/ | ||
private normalTrigger; | ||
normalTrigger(payload: PayloadType, keys?: string[]): void; | ||
/** | ||
* @internal | ||
* Triggers Event with some delay (config.delay) | ||
* Triggers async Event (Events with a delay) | ||
* @param payload - Payload that gets passed into the Callback Functions | ||
* @param delay - Delay until Events get triggered | ||
* @param keys - Keys of Callback Functions that get triggered (Note: if not passed all registered Events will be triggered) | ||
*/ | ||
private delayedTrigger; | ||
delayedTrigger(payload: PayloadType, delay: number, keys?: string[]): void; | ||
} | ||
@@ -95,3 +107,3 @@ export declare type EventKey = string | number; | ||
}; | ||
export declare type EventCallbackFunction<PayloadType = DefaultEventPayload> = (payload?: PayloadType) => void; | ||
export declare type EventCallbackFunction<PayloadType = DefaultEventPayload> = (payload: PayloadType) => void; | ||
/** | ||
@@ -101,6 +113,8 @@ * @param key - Key/Name of Event | ||
* @param maxUses - How often the Event can be used/triggered | ||
* @param delay - Delayed call of Event Callback Functions in seconds | ||
* @param delay - Delayed call of Event Callback Functions in milliseconds | ||
* @param overlap - If Events can overlap | ||
* @param rerender - If triggering an Event should cause a rerender | ||
* @param deps - Initial deps of State | ||
*/ | ||
export interface EventConfig { | ||
export interface CreateEventConfigInterface { | ||
key?: EventKey; | ||
@@ -110,3 +124,17 @@ enabled?: boolean; | ||
delay?: number; | ||
overlap?: boolean; | ||
rerender?: boolean; | ||
deps?: Array<Observer>; | ||
} | ||
/** | ||
* @param maxUses - How often the Event can be used/triggered | ||
* @param delay - Delayed call of Event Callback Functions in seconds | ||
* @param overlap - If Events can overlap | ||
* @param rerender - If triggering an Event should cause a rerender | ||
*/ | ||
export interface EventConfigInterface { | ||
maxUses?: number; | ||
delay?: number; | ||
overlap?: boolean; | ||
rerender: boolean; | ||
} |
@@ -16,13 +16,26 @@ "use strict"; | ||
this.callbacks = {}; // All 'subscribed' callback function | ||
this.queue = []; // Queue of delayed triggers | ||
this.enabled = true; | ||
this.queue = []; // Queue of delayed Events | ||
this.agileInstance = () => agileInstance; | ||
this.config = internal_1.defineConfig(config, { | ||
config = internal_1.defineConfig(config, { | ||
enabled: true, | ||
rerender: false, | ||
maxUses: undefined, | ||
delay: undefined, | ||
overlap: false, | ||
deps: [], | ||
}); | ||
this.observer = new event_observer_1.EventObserver(agileInstance, this, [], this.config.key); | ||
this._key = this.config.key; | ||
this.enabled = | ||
this.config.enabled !== undefined ? this.config.enabled : true; | ||
this._key = config.key; | ||
this.observer = new event_observer_1.EventObserver(this, { | ||
key: config.key, | ||
deps: config.deps, | ||
}); | ||
this.enabled = config.enabled; | ||
this.config = { | ||
rerender: config.rerender, | ||
delay: config.delay, | ||
maxUses: config.maxUses, | ||
overlap: config.overlap, | ||
}; | ||
this.initialConfig = config; | ||
} | ||
@@ -34,4 +47,3 @@ /** | ||
set key(value) { | ||
this._key = value; | ||
this.observer.key = value; | ||
this.setKey(value); | ||
} | ||
@@ -45,2 +57,15 @@ /** | ||
} | ||
//========================================================================================================= | ||
// Set Key | ||
//========================================================================================================= | ||
/** | ||
* @internal | ||
* Set Key/Name of Event | ||
* @param value - New Key/Name of Event | ||
*/ | ||
setKey(value) { | ||
this._key = value; | ||
this.observer._key = value; | ||
return this; | ||
} | ||
on(keyOrCallback, callback) { | ||
@@ -60,3 +85,3 @@ const generateKey = internal_1.isFunction(keyOrCallback); | ||
if (!internal_1.isFunction(_callback)) { | ||
console.error("Agile: A Event Callback Function has to be an function!"); | ||
internal_1.Agile.logger.error("A Event Callback Function has to be typeof Function!"); | ||
return this; | ||
@@ -66,3 +91,3 @@ } | ||
if (this.callbacks[key]) { | ||
console.error(`Agile: Event Callback Function with the key/name ${key} already exists!`); | ||
internal_1.Agile.logger.error(`Event Callback Function with the key/name '${key}' already exists!`); | ||
return this; | ||
@@ -78,13 +103,13 @@ } | ||
* @public | ||
* Triggers Event | ||
* -> Calls all registered Callback Functions | ||
* Triggers Events | ||
* @param payload - Payload that gets passed into the Callback Functions | ||
* @param keys - Keys of Callback Functions that get triggered (Note: if not passed all registered Events will be triggered) | ||
*/ | ||
trigger(payload) { | ||
trigger(payload, keys) { | ||
if (!this.enabled) | ||
return this; | ||
if (this.config.delay) | ||
this.delayedTrigger(payload); | ||
this.delayedTrigger(payload, this.config.delay, keys); | ||
else | ||
this.normalTrigger(payload); | ||
this.normalTrigger(payload, keys); | ||
return this; | ||
@@ -122,6 +147,8 @@ } | ||
reset() { | ||
this.enabled = this.config.enabled || true; | ||
this.enabled = this.initialConfig.enabled; | ||
this.uses = 0; | ||
if (this.currentTimeout) | ||
if (this.currentTimeout) { | ||
clearTimeout(this.currentTimeout); | ||
this.currentTimeout = undefined; | ||
} | ||
return this; | ||
@@ -146,8 +173,16 @@ } | ||
* @internal | ||
* Triggers Event | ||
* Triggers normal Event | ||
* @param payload - Payload that gets passed into the Callback Functions | ||
* @param keys - Keys of Callback Functions that get triggered (Note: if not passed all registered Events will be triggered) | ||
*/ | ||
normalTrigger(payload) { | ||
// Call registered Callbacks | ||
for (let key in this.callbacks) | ||
this.callbacks[key](payload); | ||
normalTrigger(payload, keys) { | ||
// Call wished Callback Functions | ||
if (!keys) { | ||
for (let key in this.callbacks) | ||
this.callbacks[key](payload); | ||
} | ||
else { | ||
for (let key of keys) | ||
this.callbacks[key](payload); | ||
} | ||
// Cause rerender | ||
@@ -166,21 +201,35 @@ if (this.config.rerender) | ||
* @internal | ||
* Triggers Event with some delay (config.delay) | ||
* Triggers async Event (Events with a delay) | ||
* @param payload - Payload that gets passed into the Callback Functions | ||
* @param delay - Delay until Events get triggered | ||
* @param keys - Keys of Callback Functions that get triggered (Note: if not passed all registered Events will be triggered) | ||
*/ | ||
delayedTrigger(payload) { | ||
// Check if a Timeout is currently active if so add payload to queue | ||
delayedTrigger(payload, delay, keys) { | ||
const eventJob = new internal_1.EventJob(payload, keys); | ||
// Execute Event no matter if another event is currently active | ||
if (this.config.overlap) { | ||
setTimeout(() => { | ||
this.normalTrigger(eventJob.payload, eventJob.keys); | ||
}, delay); | ||
return; | ||
} | ||
// Check if a Event(Timeout) is currently active if so add EventJob to queue | ||
if (this.currentTimeout !== undefined) { | ||
if (payload) | ||
this.queue.push(payload); | ||
this.queue.push(eventJob); | ||
return; | ||
} | ||
// Triggers Callback Functions and calls itself again if queue isn't empty | ||
const looper = (payload) => { | ||
// Executes EventJob and calls itself again if queue isn't empty to execute the next EventJob | ||
const looper = (eventJob) => { | ||
this.currentTimeout = setTimeout(() => { | ||
this.currentTimeout = undefined; | ||
this.normalTrigger(payload); | ||
if (this.queue.length > 0) | ||
looper(this.queue.shift()); | ||
}, this.config.delay); | ||
this.normalTrigger(eventJob.payload, eventJob.keys); | ||
if (this.queue.length > 0) { | ||
const nextEventJob = this.queue.shift(); | ||
if (nextEventJob) | ||
looper(nextEventJob); | ||
} | ||
}, delay); | ||
}; | ||
looper(payload); | ||
looper(eventJob); | ||
return; | ||
@@ -187,0 +236,0 @@ } |
import { Agile } from "./internal"; | ||
export * from "./internal"; | ||
export default Agile; |
@@ -0,0 +0,0 @@ "use strict"; |
@@ -13,12 +13,12 @@ import { Agile, Integration } from "../internal"; | ||
* @internal | ||
* Integrates Framework (Integration) into Agile | ||
* @param integration - Integration that gets registered/integrated | ||
* Integrates Framework(Integration) into Agile | ||
* @param integration - Integration/Framework that gets integrated | ||
*/ | ||
integrate(integration: Integration): Promise<void>; | ||
integrate(integration: Integration): Promise<boolean>; | ||
/** | ||
* @internal | ||
* Updates Integrations | ||
* -> calls 'updateMethod' in registered Integrations | ||
* Updates registered and ready Integrations | ||
* -> calls 'updateMethod' in all registered and ready Integrations | ||
* @param componentInstance - Component that gets updated | ||
* @param updatedData - Updated Properties with new Value (Note: properties with no value won't get passed) | ||
* @param updatedData - Properties that differ from the last Value | ||
*/ | ||
@@ -28,5 +28,5 @@ update(componentInstance: any, updatedData: Object): void; | ||
* @internal | ||
* Checks if Agile has registered any Integration | ||
* Check if at least one Integration got registered | ||
*/ | ||
hasIntegration(): boolean; | ||
} |
@@ -31,21 +31,23 @@ "use strict"; | ||
* @internal | ||
* Integrates Framework (Integration) into Agile | ||
* @param integration - Integration that gets registered/integrated | ||
* Integrates Framework(Integration) into Agile | ||
* @param integration - Integration/Framework that gets integrated | ||
*/ | ||
integrate(integration) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
// Check if integration is valid | ||
if (!integration.config.name) { | ||
console.error("Agile: Failed to integrate framework!"); | ||
return; | ||
// Check if Integration is valid | ||
if (!integration._key) { | ||
internal_1.Agile.logger.error("Failed to integrate framework! Invalid Integration!", integration._key); | ||
return false; | ||
} | ||
// Integrate Integration/Framework | ||
this.integrations.add(integration); | ||
if (integration.config.bind) | ||
integration.ready = yield integration.config.bind(this.agileInstance()); | ||
// Bind Framework to Agile | ||
if (integration.methods.bind) | ||
integration.ready = yield integration.methods.bind(this.agileInstance()); | ||
else | ||
integration.ready = true; | ||
// Integrate Framework | ||
this.integrations.add(integration); | ||
integration.integrated = true; | ||
// Logging | ||
if (this.agileInstance().config.logJobs) | ||
console.log(`Agile: Successfully integrated '${integration.config.name}'`); | ||
internal_1.Agile.logger.info(`Successfully integrated '${integration._key}'`); | ||
return true; | ||
}); | ||
@@ -58,16 +60,15 @@ } | ||
* @internal | ||
* Updates Integrations | ||
* -> calls 'updateMethod' in registered Integrations | ||
* Updates registered and ready Integrations | ||
* -> calls 'updateMethod' in all registered and ready Integrations | ||
* @param componentInstance - Component that gets updated | ||
* @param updatedData - Updated Properties with new Value (Note: properties with no value won't get passed) | ||
* @param updatedData - Properties that differ from the last Value | ||
*/ | ||
update(componentInstance, updatedData) { | ||
this.integrations.forEach((integration) => { | ||
// Check if integration is ready | ||
if (!integration.ready) { | ||
console.log(`Agile: Integration '${integration.config.name}' isn't ready yet!`); | ||
internal_1.Agile.logger.warn(`Integration '${integration.key}' isn't ready yet!`); | ||
return; | ||
} | ||
if (integration.config.updateMethod) | ||
integration.config.updateMethod(componentInstance, updatedData); | ||
if (integration.methods.updateMethod) | ||
integration.methods.updateMethod(componentInstance, updatedData); | ||
}); | ||
@@ -80,3 +81,3 @@ } | ||
* @internal | ||
* Checks if Agile has registered any Integration | ||
* Check if at least one Integration got registered | ||
*/ | ||
@@ -83,0 +84,0 @@ hasIntegration() { |
import { Agile } from "../internal"; | ||
export declare class Integration<F = any> { | ||
export declare class Integration<F = any, C = any> { | ||
_key: IntegrationKey; | ||
frameworkInstance?: F; | ||
ready: boolean; | ||
config: IntegrationConfig<F>; | ||
integrated: boolean; | ||
methods: IntegrationMethods<C>; | ||
/** | ||
* @public | ||
* Integration - Represents an Integration of Agile | ||
* Integration - Represents a Framework/Integration of Agile | ||
* @param config - Config | ||
*/ | ||
constructor(config: IntegrationConfig); | ||
constructor(config: CreateIntegrationConfig<F, C>); | ||
/** | ||
* @public | ||
* Set Value of Integration | ||
*/ | ||
set key(key: IntegrationKey); | ||
/** | ||
* @public | ||
* Get Value of Integration | ||
*/ | ||
get key(): IntegrationKey; | ||
} | ||
/** | ||
* @param name - Name of Integration | ||
* @param frameworkInstance - An Instance of the Framework which gets integrated (for instance in case of react you pass React) | ||
* @param bind - Will be called if the framework got successful integrated | ||
* @param updateMethod - Will be called if a Observer updates his subs (Only by Component based Subscription) | ||
* @param key - Key/Name of Integration | ||
* @param frameworkInstance - An Instance of the Framework that this Integration represents (for instance React) | ||
*/ | ||
export interface IntegrationConfig<F = any> { | ||
name?: string; | ||
export interface CreateIntegrationConfig<F = any, C = any> extends IntegrationMethods<C> { | ||
key: string; | ||
frameworkInstance?: F; | ||
bind?: (agileInstance: Agile) => boolean; | ||
updateMethod?: (componentInstance: any, updatedData: Object) => void; | ||
} | ||
/** | ||
* @param bind - Binds the Framework/Integration to Agile | Will be called after a successful integration | ||
* @param updateMethod - Will be called if a Observer updates his subs (Only in Component based Subscriptions!) | ||
*/ | ||
export interface IntegrationMethods<C = any> { | ||
bind?: (agileInstance: Agile) => Promise<boolean>; | ||
updateMethod?: (componentInstance: C, updatedData: Object) => void; | ||
} | ||
export declare type IntegrationKey = string | number; |
@@ -7,3 +7,3 @@ "use strict"; | ||
* @public | ||
* Integration - Represents an Integration of Agile | ||
* Integration - Represents a Framework/Integration of Agile | ||
* @param config - Config | ||
@@ -13,5 +13,25 @@ */ | ||
this.ready = false; | ||
this.config = config; | ||
this.integrated = false; | ||
this._key = config.key; | ||
this.frameworkInstance = config.frameworkInstance; | ||
this.methods = { | ||
bind: config.bind, | ||
updateMethod: config.updateMethod, | ||
}; | ||
} | ||
/** | ||
* @public | ||
* Set Value of Integration | ||
*/ | ||
set key(key) { | ||
this._key = key; | ||
} | ||
/** | ||
* @public | ||
* Get Value of Integration | ||
*/ | ||
get key() { | ||
return this._key; | ||
} | ||
} | ||
exports.Integration = Integration; |
@@ -0,15 +1,20 @@ | ||
export * from "./logger"; | ||
export * from "./utils"; | ||
export * from "./agile"; | ||
export * from "./runtime"; | ||
export * from "./runtime/observer"; | ||
export * from "./runtime/job"; | ||
export * from "./runtime/runtime.job"; | ||
export * from "./runtime/subscription/container/SubscriptionContainer"; | ||
export * from "./runtime/subscription/container/CallbackSubscriptionContainer"; | ||
export * from "./runtime/subscription/container/ComponentSubscriptionContainer"; | ||
export * from "./runtime/subscription/sub"; | ||
export * from "./storage"; | ||
export * from "./storage/persistent"; | ||
export * from "./runtime/subscription/sub.controller"; | ||
export * from "./storages"; | ||
export * from "./storages/storage"; | ||
export * from "./storages/persistent"; | ||
export * from "./state"; | ||
export * from "./state/state.observer"; | ||
export * from "./state/state.persistent"; | ||
export { Computed } from "./computed"; | ||
export * from "./state/state.runtime.job"; | ||
export * from "./computed"; | ||
export * from "./computed/computed.tracker"; | ||
export * from "./collection"; | ||
@@ -21,4 +26,5 @@ export * from "./collection/group"; | ||
export * from "./event"; | ||
export * from "./event/event.job"; | ||
export * from "./event/event.observer"; | ||
export * from "./integrations"; | ||
export * from "./integrations/integration"; | ||
export * from "./utils"; |
@@ -17,2 +17,6 @@ "use strict"; | ||
// !! All internal Agile modules must be imported from here!! | ||
// Logger | ||
__exportStar(require("./logger"), exports); | ||
// Utils | ||
__exportStar(require("./utils"), exports); | ||
// Agile | ||
@@ -23,10 +27,11 @@ __exportStar(require("./agile"), exports); | ||
__exportStar(require("./runtime/observer"), exports); | ||
__exportStar(require("./runtime/job"), exports); | ||
__exportStar(require("./runtime/runtime.job"), exports); | ||
__exportStar(require("./runtime/subscription/container/SubscriptionContainer"), exports); | ||
__exportStar(require("./runtime/subscription/container/CallbackSubscriptionContainer"), exports); | ||
__exportStar(require("./runtime/subscription/container/ComponentSubscriptionContainer"), exports); | ||
__exportStar(require("./runtime/subscription/sub"), exports); | ||
__exportStar(require("./runtime/subscription/sub.controller"), exports); | ||
// Storage | ||
__exportStar(require("./storage"), exports); | ||
__exportStar(require("./storage/persistent"), exports); | ||
__exportStar(require("./storages"), exports); | ||
__exportStar(require("./storages/storage"), exports); | ||
__exportStar(require("./storages/persistent"), exports); | ||
// State | ||
@@ -36,5 +41,6 @@ __exportStar(require("./state"), exports); | ||
__exportStar(require("./state/state.persistent"), exports); | ||
__exportStar(require("./state/state.runtime.job"), exports); | ||
// Computed | ||
var computed_1 = require("./computed"); | ||
Object.defineProperty(exports, "Computed", { enumerable: true, get: function () { return computed_1.Computed; } }); | ||
__exportStar(require("./computed"), exports); | ||
__exportStar(require("./computed/computed.tracker"), exports); | ||
// Collection | ||
@@ -48,6 +54,6 @@ __exportStar(require("./collection"), exports); | ||
__exportStar(require("./event"), exports); | ||
__exportStar(require("./event/event.job"), exports); | ||
__exportStar(require("./event/event.observer"), exports); | ||
// Integrations | ||
__exportStar(require("./integrations"), exports); | ||
__exportStar(require("./integrations/integration"), exports); | ||
// Utils | ||
__exportStar(require("./utils"), exports); |
@@ -1,10 +0,8 @@ | ||
import { Agile, SubscriptionContainer, Observer, Job, JobConfigInterface } from "../internal"; | ||
import { Agile, SubscriptionContainer, RuntimeJob } from "../internal"; | ||
export declare class Runtime { | ||
agileInstance: () => Agile; | ||
private currentJob; | ||
private jobQueue; | ||
private notReadyJobsToRerender; | ||
private jobsToRerender; | ||
trackObservers: boolean; | ||
foundObservers: Set<Observer>; | ||
currentJob: RuntimeJob | null; | ||
jobQueue: Array<RuntimeJob>; | ||
notReadyJobsToRerender: Set<RuntimeJob>; | ||
jobsToRerender: Array<RuntimeJob>; | ||
/** | ||
@@ -18,14 +16,13 @@ * @internal | ||
* @internal | ||
* Ingests Observer into Runtime | ||
* -> Creates Job which will be performed by the Runtime | ||
* @param observer - Observer that gets performed by the Runtime | ||
* Ingests Job into Runtime that gets performed | ||
* @param job - Job | ||
* @param config - Config | ||
*/ | ||
ingest(observer: Observer, config: JobConfigInterface): void; | ||
ingest(job: RuntimeJob, config?: IngestConfigInterface): void; | ||
/** | ||
* @internal | ||
* Performs Job and adds him to the rerender queue if necessary | ||
* Performs Job and adds it to the rerender queue if necessary | ||
* @param job - Job that gets performed | ||
*/ | ||
private perform; | ||
perform(job: RuntimeJob): void; | ||
/** | ||
@@ -35,14 +32,14 @@ * @internal | ||
*/ | ||
private updateSubscribers; | ||
updateSubscribers(): boolean; | ||
/** | ||
* @internal | ||
* Finds updated Key of SubscriptionContainer and adds it to 'changedObjectKeys' | ||
* Finds key of Observer (Job) in subsObject and adds it to 'changedObjectKeys' | ||
* @param subscriptionContainer - Object based SubscriptionContainer | ||
* @param job - Job that holds the SubscriptionContainer | ||
* @param job - Job that holds the searched Observer | ||
*/ | ||
handleObjectBasedSubscription(subscriptionContainer: SubscriptionContainer, job: Job): void; | ||
handleObjectBasedSubscription(subscriptionContainer: SubscriptionContainer, job: RuntimeJob): void; | ||
/** | ||
* @internal | ||
* Builds Object from 'changedObjectKeys' with new Values provided by Observers | ||
* @param subscriptionContainer - SubscriptionContainer from which the Object gets built | ||
* Builds Object out of changedObjectKeys with Observer Value | ||
* @param subscriptionContainer - Object based SubscriptionContainer | ||
*/ | ||
@@ -52,7 +49,8 @@ getObjectBasedProps(subscriptionContainer: SubscriptionContainer): { | ||
}; | ||
/** | ||
* @internal | ||
* Returns tracked Observers and stops Runtime from tracking anymore Observers | ||
*/ | ||
getTrackedObservers(): Set<Observer>; | ||
} | ||
/** | ||
* @param perform - If Job gets performed immediately | ||
*/ | ||
export interface IngestConfigInterface { | ||
perform?: boolean; | ||
} |
@@ -15,7 +15,4 @@ "use strict"; | ||
this.jobQueue = []; | ||
this.notReadyJobsToRerender = []; // Jobs that are performed but not ready to rerender (wait for mount) | ||
this.notReadyJobsToRerender = new Set(); // Jobs that got performed but aren't ready to get rerendered (wait for mount) | ||
this.jobsToRerender = []; // Jobs that are performed and will be rendered | ||
// Tracking - Used to track computed dependencies | ||
this.trackObservers = false; // Check if Runtime have to track Observers | ||
this.foundObservers = new Set(); // Observers that got tracked during the 'trackObservers' time | ||
this.agileInstance = () => agileInstance; | ||
@@ -28,23 +25,13 @@ } | ||
* @internal | ||
* Ingests Observer into Runtime | ||
* -> Creates Job which will be performed by the Runtime | ||
* @param observer - Observer that gets performed by the Runtime | ||
* Ingests Job into Runtime that gets performed | ||
* @param job - Job | ||
* @param config - Config | ||
*/ | ||
ingest(observer, config) { | ||
ingest(job, config = {}) { | ||
config = internal_1.defineConfig(config, { | ||
perform: true, | ||
background: false, | ||
sideEffects: true, | ||
}); | ||
const job = new internal_1.Job(observer, { | ||
background: config.background, | ||
sideEffects: config.sideEffects, | ||
storage: config.storage, | ||
}); | ||
this.jobQueue.push(job); | ||
// Logging | ||
if (this.agileInstance().config.logJobs) | ||
console.log(`Agile: Created Job(${job.observer.key})`, job); | ||
// Add Job to JobQueue (-> no Job get missing) | ||
this.jobQueue.push(job); | ||
internal_1.Agile.logger.if.tag(["runtime"]).info(`Created Job '${job._key}'`, job); | ||
// Perform Job | ||
@@ -62,3 +49,3 @@ if (config.perform) { | ||
* @internal | ||
* Performs Job and adds him to the rerender queue if necessary | ||
* Performs Job and adds it to the rerender queue if necessary | ||
* @param job - Job that gets performed | ||
@@ -75,5 +62,4 @@ */ | ||
// Logging | ||
if (this.agileInstance().config.logJobs) | ||
console.log(`Agile: Completed Job(${job.observer.key})`, job); | ||
// Perform Jobs as long as Jobs are in queue, if no job left update/rerender Subscribers of performed Jobs | ||
internal_1.Agile.logger.if.tag(["runtime"]).info(`Completed Job '${job._key}'`, job); | ||
// Perform Jobs as long as Jobs are left in queue, if no job left update/rerender Subscribers of jobsToRerender | ||
if (this.jobQueue.length > 0) { | ||
@@ -101,31 +87,37 @@ const performJob = this.jobQueue.shift(); | ||
updateSubscribers() { | ||
if (!this.agileInstance().integrations.hasIntegration()) { | ||
if (!this.agileInstance().hasIntegration()) { | ||
this.jobsToRerender = []; | ||
return; | ||
this.notReadyJobsToRerender = new Set(); | ||
return false; | ||
} | ||
if (this.jobsToRerender.length <= 0) | ||
return; | ||
// Subscriptions that has to be updated/rerendered (Set = For preventing double subscriptions without further checks) | ||
if (this.jobsToRerender.length <= 0 && | ||
this.notReadyJobsToRerender.size <= 0) | ||
return false; | ||
// Subscriptions that has to be updated/rerendered | ||
const subscriptionsToUpdate = new Set(); | ||
// Handle Object based Jobs and check if Job is ready | ||
this.jobsToRerender.concat(this.notReadyJobsToRerender).forEach((job) => { | ||
job.observer.subs.forEach((subscriptionContainer) => { | ||
// Check if Subscription is ready to rerender | ||
// Build final jobsToRerender and reset jobsToRerender Instances | ||
const jobsToRerender = this.jobsToRerender.concat(Array.from(this.notReadyJobsToRerender)); | ||
this.notReadyJobsToRerender = new Set(); | ||
this.jobsToRerender = []; | ||
// Check if Job Subscriptions are ready and add them to subscriptionsToUpdate | ||
jobsToRerender.forEach((job) => { | ||
job.subscriptionContainersToUpdate.forEach((subscriptionContainer) => { | ||
if (!subscriptionContainer.ready) { | ||
this.notReadyJobsToRerender.push(job); | ||
if (this.agileInstance().config.logJobs) | ||
console.warn("Agile: SubscriptionContainer/Component isn't ready to rerender!", subscriptionContainer); | ||
this.notReadyJobsToRerender.add(job); | ||
// Logging | ||
internal_1.Agile.logger.warn("SubscriptionContainer/Component isn't ready to rerender!", subscriptionContainer); | ||
return; | ||
} | ||
// Handle Object based Subscription | ||
if (subscriptionContainer.isObjectBased) | ||
this.handleObjectBasedSubscription(subscriptionContainer, job); | ||
subscriptionsToUpdate.add(subscriptionContainer); | ||
job.subscriptionContainersToUpdate.delete(subscriptionContainer); | ||
}); | ||
}); | ||
// Update Subscriptions that has to be updated/rerendered | ||
subscriptionsToUpdate.forEach((subscriptionContainer) => { | ||
// Call callback function if Callback based Subscription | ||
// Call 'callback function' if Callback based Subscription | ||
if (subscriptionContainer instanceof internal_1.CallbackSubscriptionContainer) | ||
subscriptionContainer.callback(); | ||
// Call update method if Component based Subscription | ||
// Call 'update method' if Component based Subscription | ||
if (subscriptionContainer instanceof internal_1.ComponentSubscriptionContainer) | ||
@@ -135,5 +127,6 @@ this.agileInstance().integrations.update(subscriptionContainer.component, this.getObjectBasedProps(subscriptionContainer)); | ||
// Logging | ||
if (this.agileInstance().config.logJobs) | ||
console.log("Agile: Updated/Rerendered Subscriptions ", subscriptionsToUpdate); | ||
this.jobsToRerender = []; | ||
internal_1.Agile.logger.if | ||
.tag(["runtime"]) | ||
.info("Updated/Rerendered Subscriptions", subscriptionsToUpdate); | ||
return true; | ||
} | ||
@@ -145,17 +138,17 @@ //========================================================================================================= | ||
* @internal | ||
* Finds updated Key of SubscriptionContainer and adds it to 'changedObjectKeys' | ||
* Finds key of Observer (Job) in subsObject and adds it to 'changedObjectKeys' | ||
* @param subscriptionContainer - Object based SubscriptionContainer | ||
* @param job - Job that holds the SubscriptionContainer | ||
* @param job - Job that holds the searched Observer | ||
*/ | ||
handleObjectBasedSubscription(subscriptionContainer, job) { | ||
let localKey = null; | ||
let foundKey = null; | ||
// Check if SubscriptionContainer is Object based | ||
if (!subscriptionContainer.isObjectBased) | ||
return; | ||
// Find localKey of Job Observer in SubscriptionContainer | ||
// Find Key of Job Observer in SubscriptionContainer | ||
for (let key in subscriptionContainer.subsObject) | ||
if (subscriptionContainer.subsObject[key] === job.observer) | ||
localKey = key; | ||
// Add localKey to changedObjectKeys | ||
if (localKey) | ||
subscriptionContainer.changedObjectKeys.push(localKey); | ||
foundKey = key; | ||
if (foundKey) | ||
subscriptionContainer.observerKeysToUpdate.push(foundKey); | ||
} | ||
@@ -167,33 +160,18 @@ //========================================================================================================= | ||
* @internal | ||
* Builds Object from 'changedObjectKeys' with new Values provided by Observers | ||
* @param subscriptionContainer - SubscriptionContainer from which the Object gets built | ||
* Builds Object out of changedObjectKeys with Observer Value | ||
* @param subscriptionContainer - Object based SubscriptionContainer | ||
*/ | ||
getObjectBasedProps(subscriptionContainer) { | ||
const finalObject = {}; | ||
// Map trough changed Keys and build finalObject | ||
subscriptionContainer.changedObjectKeys.forEach((changedKey) => { | ||
// Check if Observer at changedKey has value property, if so add it to final Object | ||
const props = {}; | ||
// Map trough observerKeysToUpdate and build object out of Observer value | ||
subscriptionContainer.observerKeysToUpdate.forEach((updatedKey) => { | ||
if (subscriptionContainer.subsObject && | ||
subscriptionContainer.subsObject[changedKey]["value"]) | ||
finalObject[changedKey] = | ||
subscriptionContainer.subsObject[changedKey]["value"]; | ||
subscriptionContainer.subsObject[updatedKey]["value"]) | ||
props[updatedKey] = | ||
subscriptionContainer.subsObject[updatedKey]["value"]; | ||
}); | ||
subscriptionContainer.changedObjectKeys = []; | ||
return finalObject; | ||
subscriptionContainer.observerKeysToUpdate = []; | ||
return props; | ||
} | ||
//========================================================================================================= | ||
// Get Tracked Observers | ||
//========================================================================================================= | ||
/** | ||
* @internal | ||
* Returns tracked Observers and stops Runtime from tracking anymore Observers | ||
*/ | ||
getTrackedObservers() { | ||
const finalFoundObservers = this.foundObservers; | ||
// Reset tracking | ||
this.trackObservers = false; | ||
this.foundObservers = new Set(); | ||
return finalFoundObservers; | ||
} | ||
} | ||
exports.Runtime = Runtime; |
@@ -1,2 +0,2 @@ | ||
import { Agile, StateKey, Job, SubscriptionContainer } from "../internal"; | ||
import { Agile, StateKey, RuntimeJob, SubscriptionContainer } from "../internal"; | ||
export declare type ObserverKey = string | number; | ||
@@ -12,8 +12,7 @@ export declare class Observer<ValueType = any> { | ||
* Observer - Handles subscriptions and dependencies of an Agile Class and is like an instance to the Runtime | ||
* Note: No stand alone class!! | ||
* @param agileInstance - An instance of Agile | ||
* @param value - Initial Value of Observer | ||
* @param deps - Initial Dependencies of Observer | ||
* @param key - Key/Name of Observer | ||
* @param config - Config | ||
*/ | ||
constructor(agileInstance: Agile, deps?: Array<Observer>, key?: ObserverKey, value?: ValueType); | ||
constructor(agileInstance: Agile, config?: CreateObserverConfigInterface<ValueType>); | ||
/** | ||
@@ -34,3 +33,3 @@ * @internal | ||
*/ | ||
perform(job: Job): void; | ||
perform(job: RuntimeJob): void; | ||
/** | ||
@@ -55,1 +54,13 @@ * @internal | ||
} | ||
/** | ||
* @param deps - Initial Dependencies of Observer | ||
* @param subs - Initial Subscriptions of Observer | ||
* @param key - Key/Name of Observer | ||
* @param value - Initial Value of Observer | ||
*/ | ||
export interface CreateObserverConfigInterface<ValueType = any> { | ||
deps?: Array<Observer>; | ||
subs?: Array<SubscriptionContainer>; | ||
key?: ObserverKey; | ||
value?: ValueType; | ||
} |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.Observer = void 0; | ||
const internal_1 = require("../internal"); | ||
class Observer { | ||
@@ -8,14 +9,19 @@ /** | ||
* Observer - Handles subscriptions and dependencies of an Agile Class and is like an instance to the Runtime | ||
* Note: No stand alone class!! | ||
* @param agileInstance - An instance of Agile | ||
* @param value - Initial Value of Observer | ||
* @param deps - Initial Dependencies of Observer | ||
* @param key - Key/Name of Observer | ||
* @param config - Config | ||
*/ | ||
constructor(agileInstance, deps, key, value) { | ||
constructor(agileInstance, config = {}) { | ||
var _a, _b; | ||
this.deps = new Set(); // Observers that depends on this Observer | ||
this.subs = new Set(); // SubscriptionContainers(Components) which this Observer has subscribed | ||
this.subs = new Set(); // SubscriptionContainers (Components) that this Observer has subscribed | ||
config = internal_1.defineConfig(config, { | ||
deps: [], | ||
subs: [], | ||
}); | ||
this.agileInstance = () => agileInstance; | ||
this._key = key; | ||
this.value = value; | ||
deps === null || deps === void 0 ? void 0 : deps.forEach((observer) => this.deps.add(observer)); | ||
this._key = config.key; | ||
this.value = config.value; | ||
(_a = config.deps) === null || _a === void 0 ? void 0 : _a.forEach((observer) => this.depend(observer)); | ||
(_b = config.subs) === null || _b === void 0 ? void 0 : _b.forEach((subscriptionContainer) => this.subscribe(subscriptionContainer)); | ||
} | ||
@@ -45,3 +51,3 @@ /** | ||
perform(job) { | ||
console.warn("Agile: Didn't set perform function in Observer ", this.key); | ||
internal_1.Agile.logger.warn("Perform function isn't Set in Observer! Be aware that Observer is no stand alone class!"); | ||
} | ||
@@ -69,4 +75,7 @@ //========================================================================================================= | ||
subscribe(subscriptionContainer) { | ||
if (!this.subs.has(subscriptionContainer)) | ||
if (!this.subs.has(subscriptionContainer)) { | ||
this.subs.add(subscriptionContainer); | ||
// Add this to subscriptionContainer to keep track of the Observers the subscriptionContainer hold | ||
subscriptionContainer.subs.add(this); | ||
} | ||
} | ||
@@ -82,6 +91,8 @@ //========================================================================================================= | ||
unsubscribe(subscriptionContainer) { | ||
if (this.subs.has(subscriptionContainer)) | ||
if (this.subs.has(subscriptionContainer)) { | ||
this.subs.delete(subscriptionContainer); | ||
subscriptionContainer.subs.delete(this); | ||
} | ||
} | ||
} | ||
exports.Observer = Observer; |
@@ -1,2 +0,2 @@ | ||
import { Observer, SubscriptionContainer } from "../../../internal"; | ||
import { Observer, SubscriptionContainer, SubscriptionContainerKeyType } from "../../../internal"; | ||
export declare class CallbackSubscriptionContainer extends SubscriptionContainer { | ||
@@ -9,4 +9,5 @@ callback: Function; | ||
* @param subs - Initial Subscriptions | ||
* @param key - Key/Name of Callback Subscription Container | ||
*/ | ||
constructor(callback: Function, subs?: Set<Observer>); | ||
constructor(callback: Function, subs?: Array<Observer>, key?: SubscriptionContainerKeyType); | ||
} |
@@ -11,5 +11,6 @@ "use strict"; | ||
* @param subs - Initial Subscriptions | ||
* @param key - Key/Name of Callback Subscription Container | ||
*/ | ||
constructor(callback, subs) { | ||
super(subs); | ||
constructor(callback, subs = [], key) { | ||
super(subs, key); | ||
this.callback = callback; | ||
@@ -16,0 +17,0 @@ } |
@@ -1,2 +0,2 @@ | ||
import { Observer, SubscriptionContainer } from "../../../internal"; | ||
import { Observer, SubscriptionContainer, SubscriptionContainerKeyType } from "../../../internal"; | ||
export declare class ComponentSubscriptionContainer extends SubscriptionContainer { | ||
@@ -9,4 +9,5 @@ component: any; | ||
* @param subs - Initial Subscriptions | ||
* @param key - Key/Name of Component Subscription Container | ||
*/ | ||
constructor(component: any, subs?: Set<Observer>); | ||
constructor(component: any, subs?: Array<Observer>, key?: SubscriptionContainerKeyType); | ||
} |
@@ -11,5 +11,6 @@ "use strict"; | ||
* @param subs - Initial Subscriptions | ||
* @param key - Key/Name of Component Subscription Container | ||
*/ | ||
constructor(component, subs) { | ||
super(subs); | ||
constructor(component, subs = [], key) { | ||
super(subs, key); | ||
this.component = component; | ||
@@ -16,0 +17,0 @@ } |
import { Observer } from "../../../internal"; | ||
export declare class SubscriptionContainer { | ||
key?: SubscriptionContainerKeyType; | ||
ready: boolean; | ||
subs: Set<Observer>; | ||
isObjectBased: boolean; | ||
changedObjectKeys: Array<string>; | ||
observerKeysToUpdate: Array<string>; | ||
subsObject?: { | ||
@@ -15,4 +16,6 @@ [key: string]: Observer; | ||
* @param subs - Initial Subscriptions | ||
* @param key - Key/Name of Subscription Container | ||
*/ | ||
constructor(subs?: Set<Observer>); | ||
constructor(subs?: Array<Observer>, key?: SubscriptionContainerKeyType); | ||
} | ||
export declare type SubscriptionContainerKeyType = string | number; |
@@ -10,13 +10,13 @@ "use strict"; | ||
* @param subs - Initial Subscriptions | ||
* @param key - Key/Name of Subscription Container | ||
*/ | ||
constructor(subs) { | ||
constructor(subs = [], key) { | ||
this.ready = false; | ||
this.subs = new Set([]); // Observers that are Subscribed to this SubscriptionContainer (Component) | ||
// For Object based Subscription | ||
this.isObjectBased = false; | ||
this.changedObjectKeys = []; // Holds temporary changed Object Keys (Runtime) | ||
if (subs) | ||
this.subs = subs; | ||
this.observerKeysToUpdate = []; // Holds temporary keys of Observers that got updated (Note: keys based on 'subsObject') | ||
this.subs = new Set(subs); | ||
this.key = key; | ||
} | ||
} | ||
exports.SubscriptionContainer = SubscriptionContainer; |
@@ -1,2 +0,2 @@ | ||
import { Agile, StorageKey, StateObserver, StatePersistent, Observer, StateJobConfigInterface, StatePersistentConfigInterface } from "../internal"; | ||
import { Agile, StorageKey, StateObserver, StatePersistent, Observer, PersistentKey, StateIngestConfigInterface, StateRuntimeJobConfigInterface } from "../internal"; | ||
export declare class State<ValueType = any> { | ||
@@ -12,3 +12,3 @@ agileInstance: () => Agile; | ||
nextStateValue: ValueType; | ||
observer: StateObserver; | ||
observer: StateObserver<ValueType>; | ||
sideEffects: { | ||
@@ -19,6 +19,7 @@ [key: string]: (properties?: { | ||
}; | ||
computeMethod?: ComputeMethod<ValueType>; | ||
isPersisted: boolean; | ||
persistent: StatePersistent | undefined; | ||
watchers: { | ||
[key: string]: (value: any) => void; | ||
[key: string]: StateWatcherCallback<ValueType>; | ||
}; | ||
@@ -30,6 +31,5 @@ /** | ||
* @param initialValue - Initial Value of State | ||
* @param key - Key/Name of State | ||
* @param deps - Initial deps of State | ||
* @param config - Config | ||
*/ | ||
constructor(agileInstance: Agile, initialValue: ValueType, key?: StateKey, deps?: Array<Observer>); | ||
constructor(agileInstance: Agile, initialValue: ValueType, config?: StateConfigInterface); | ||
/** | ||
@@ -57,7 +57,6 @@ * @public | ||
* @internal | ||
* Set Key/Name of State | ||
* https://github.com/microsoft/TypeScript/issues/338 | ||
* Updates Key/Name of State | ||
* @param value - New Key/Name of State | ||
*/ | ||
setKey(value: StateKey | undefined): void; | ||
setKey(value: StateKey | undefined): this; | ||
/** | ||
@@ -69,3 +68,3 @@ * @public | ||
*/ | ||
set(value: ValueType, config?: SetConfigInterface): this; | ||
set(value: ValueType, config?: StateRuntimeJobConfigInterface): this; | ||
/** | ||
@@ -76,3 +75,3 @@ * @internal | ||
*/ | ||
ingest(config?: StateJobConfigInterface): this; | ||
ingest(config?: StateIngestConfigInterface): this; | ||
/** | ||
@@ -88,9 +87,11 @@ * @public | ||
* Undoes latest State Value change | ||
* @param config - Config | ||
*/ | ||
undo(): void; | ||
undo(config?: StateRuntimeJobConfigInterface): this; | ||
/** | ||
* @public | ||
* Resets State to its initial Value | ||
* @param config - Config | ||
*/ | ||
reset(): this; | ||
reset(config?: StateRuntimeJobConfigInterface): this; | ||
/** | ||
@@ -110,10 +111,10 @@ * @public | ||
*/ | ||
watch(callback: Callback<ValueType>): string; | ||
watch(callback: StateWatcherCallback<ValueType>): string; | ||
/** | ||
* @public | ||
* Watches State and detects State changes | ||
* @param key - Key of Watcher Function | ||
* @param key - Key/Name of Watcher Function | ||
* @param callback - Callback Function that gets called if the State Value changes | ||
*/ | ||
watch(key: string, callback: Callback<ValueType>): this; | ||
watch(key: string, callback: StateWatcherCallback<ValueType>): this; | ||
/** | ||
@@ -130,7 +131,7 @@ * @public | ||
*/ | ||
onInaugurated(callback: Callback<ValueType>): void; | ||
onInaugurated(callback: StateWatcherCallback<ValueType>): void; | ||
/** | ||
* @public | ||
* Checks if watcher at given Key exists | ||
* @param key - Key of Watcher | ||
* @param key - Key/Name of Watcher | ||
*/ | ||
@@ -147,8 +148,15 @@ hasWatcher(key: string): boolean; | ||
* Stores State Value into Agile Storage permanently | ||
* @param key - Storage Key (Note: not needed if State has key/name) | ||
* @param key - Key/Name of created Persistent (Note: Key required if State has no set Key!) | ||
* @param config - Config | ||
*/ | ||
persist(key?: StorageKey, config?: StatePersistentConfigInterface): this; | ||
persist(key?: PersistentKey, config?: StatePersistentConfigInterface): this; | ||
/** | ||
* @public | ||
* Callback Function that gets called if the persisted Value gets loaded into the State for the first Time | ||
* Note: Only useful for persisted States! | ||
* @param callback - Callback Function | ||
*/ | ||
onLoad(callback: (success: boolean) => void): this; | ||
/** | ||
* @public | ||
* Creates fresh copy of State Value (-> No reference to State Value) | ||
@@ -181,5 +189,11 @@ */ | ||
/** | ||
* @public | ||
* Function that recomputes State Value if it changes | ||
* @param method - Computed Function | ||
*/ | ||
compute(method: ComputeMethod<ValueType>): this; | ||
/** | ||
* @internal | ||
* Adds SideEffect to State | ||
* @param key - Key of SideEffect | ||
* @param key - Key/Name of SideEffect | ||
* @param sideEffect - Callback Function that gets called on every State Value change | ||
@@ -205,5 +219,6 @@ */ | ||
* Checks if Value has correct valueType (js) | ||
* Note: If no valueType set, it returns true | ||
* @param value - Value that gets checked for its correct Type | ||
*/ | ||
private hasCorrectType; | ||
hasCorrectType(value: any): boolean; | ||
/** | ||
@@ -222,19 +237,26 @@ * @internal | ||
/** | ||
* @param background - If assigning a new value happens in the background (-> not causing any rerender) | ||
* @param sideEffects - If Side Effects of State get executed | ||
* @param storage - If State value gets saved in Agile Storage (only useful if State is persisted) | ||
* @param key - Key/Name of State | ||
* @param deps - Initial deps of State | ||
* @param isPlaceholder - If State is initially a Placeholder | ||
*/ | ||
export interface SetConfigInterface { | ||
background?: boolean; | ||
sideEffects?: boolean; | ||
storage?: boolean; | ||
export interface StateConfigInterface { | ||
key?: StateKey; | ||
deps?: Array<Observer>; | ||
isPlaceholder?: boolean; | ||
} | ||
/** | ||
* @param background - If assigning new value happens in the background (-> not causing any rerender) | ||
* @param addNewProperties - If new Properties gets added to the State Value | ||
*/ | ||
export interface PatchConfigInterface { | ||
export interface PatchConfigInterface extends StateRuntimeJobConfigInterface { | ||
addNewProperties?: boolean; | ||
background?: boolean; | ||
} | ||
export declare type Callback<T = any> = (value: T) => void; | ||
/** | ||
* @param instantiate - If Persistent gets instantiated | ||
* @param storageKeys - Key/Name of Storages which gets used to persist the State Value (NOTE: If not passed the default Storage will be used) | ||
*/ | ||
export interface StatePersistentConfigInterface { | ||
instantiate?: boolean; | ||
storageKeys?: StorageKey[]; | ||
} | ||
export declare type StateWatcherCallback<T = any> = (value: T) => void; | ||
export declare type ComputeMethod<T = any> = (value: T) => T; |
@@ -11,18 +11,28 @@ "use strict"; | ||
* @param initialValue - Initial Value of State | ||
* @param key - Key/Name of State | ||
* @param deps - Initial deps of State | ||
* @param config - Config | ||
*/ | ||
constructor(agileInstance, initialValue, key, deps = []) { | ||
constructor(agileInstance, initialValue, config = {}) { | ||
this.isSet = false; // If value is not the same as initialValue | ||
this.isPlaceholder = false; | ||
this.sideEffects = {}; // SideEffects of State (will be executed in Runtime) | ||
this.isPersisted = false; // If State is stored in Storage | ||
this.isPersisted = false; // If State can be stored in Agile Storage (-> successfully integrated persistent) | ||
this.watchers = {}; | ||
config = internal_1.defineConfig(config, { | ||
deps: [], | ||
isPlaceholder: false, | ||
}); | ||
this.agileInstance = () => agileInstance; | ||
this.initialStateValue = initialValue; | ||
this._key = key; | ||
this._key = config.key; | ||
this.observer = new internal_1.StateObserver(this, { | ||
key: config.key, | ||
deps: config.deps, | ||
}); | ||
this.initialStateValue = internal_1.copy(initialValue); | ||
this._value = internal_1.copy(initialValue); | ||
this.previousStateValue = internal_1.copy(initialValue); | ||
this.nextStateValue = internal_1.copy(initialValue); | ||
this.observer = new internal_1.StateObserver(agileInstance, this, deps, key); | ||
this.isPlaceholder = true; | ||
// Initial Set | ||
if (!config.isPlaceholder) | ||
this.set(initialValue, { overwrite: true }); | ||
} | ||
@@ -41,5 +51,3 @@ /** | ||
get value() { | ||
// Add State to tracked Observers (for auto tracking used observers in computed function) | ||
if (this.agileInstance().runtime.trackObservers) | ||
this.agileInstance().runtime.foundObservers.add(this.observer); | ||
internal_1.ComputedTracker.tracked(this.observer); | ||
return this._value; | ||
@@ -66,7 +74,7 @@ } | ||
* @internal | ||
* Set Key/Name of State | ||
* https://github.com/microsoft/TypeScript/issues/338 | ||
* Updates Key/Name of State | ||
* @param value - New Key/Name of State | ||
*/ | ||
setKey(value) { | ||
var _a, _b; | ||
const oldKey = this._key; | ||
@@ -76,8 +84,7 @@ // Update State Key | ||
// Update Key in Observer | ||
this.observer.key = value; | ||
// Update Key in PersistManager | ||
if (value !== undefined && | ||
this.persistent && | ||
this.persistent.key === oldKey) | ||
this.persistent.key = value; | ||
this.observer._key = value; | ||
// Update Key in Persistent (only if oldKey equal to persistentKey -> otherwise the PersistentKey got formatted and will be set where other) | ||
if (value && ((_a = this.persistent) === null || _a === void 0 ? void 0 : _a._key) === oldKey) | ||
(_b = this.persistent) === null || _b === void 0 ? void 0 : _b.setKey(value); | ||
return this; | ||
} | ||
@@ -97,13 +104,17 @@ //========================================================================================================= | ||
background: false, | ||
force: false, | ||
storage: true, | ||
overwrite: false, | ||
}); | ||
// Check value has correct Type (js) | ||
if (this.valueType && !this.hasCorrectType(value)) { | ||
console.warn(`Agile: Incorrect type (${typeof value}) was provided.`); | ||
return this; | ||
if (!this.hasCorrectType(value)) { | ||
const message = `Incorrect type (${typeof value}) was provided.`; | ||
if (!config.force) { | ||
internal_1.Agile.logger.error(message); | ||
return this; | ||
} | ||
internal_1.Agile.logger.warn(message); | ||
} | ||
// Check if value has changed | ||
if (internal_1.equal(this.nextStateValue, value)) | ||
return this; | ||
// Ingest new value into runtime | ||
this.observer.ingest(value, config); | ||
// Ingest new value into Runtime | ||
this.observer.ingestValue(value, config); | ||
return this; | ||
@@ -120,8 +131,3 @@ } | ||
ingest(config = {}) { | ||
config = internal_1.defineConfig(config, { | ||
sideEffects: true, | ||
background: false, | ||
forceRerender: false, | ||
}); | ||
this.observer.ingest(internal_1.internalIngestKey, config); | ||
this.observer.ingest(config); | ||
return this; | ||
@@ -142,3 +148,3 @@ } | ||
if (!supportedTypes.includes(type.name)) { | ||
console.warn(`Agile: '${type}' is not supported! Supported types: String, Boolean, Array, Object, Number`); | ||
internal_1.Agile.logger.warn(`'${type}' is not supported! Supported types: String, Boolean, Array, Object, Number`); | ||
return this; | ||
@@ -155,5 +161,7 @@ } | ||
* Undoes latest State Value change | ||
* @param config - Config | ||
*/ | ||
undo() { | ||
this.set(this.previousStateValue); | ||
undo(config = {}) { | ||
this.set(this.previousStateValue, config); | ||
return this; | ||
} | ||
@@ -166,7 +174,6 @@ //========================================================================================================= | ||
* Resets State to its initial Value | ||
* @param config - Config | ||
*/ | ||
reset() { | ||
var _a; | ||
this.set(this.initialStateValue); | ||
(_a = this.persistent) === null || _a === void 0 ? void 0 : _a.removeValue(); // Remove State from Storage (since its the initial Value) | ||
reset(config = {}) { | ||
this.set(this.initialStateValue, config); | ||
return this; | ||
@@ -187,19 +194,26 @@ } | ||
addNewProperties: true, | ||
sideEffects: true, | ||
background: false, | ||
force: false, | ||
storage: true, | ||
overwrite: false, | ||
}); | ||
if (!internal_1.isValidObject(this.nextStateValue)) { | ||
console.warn("Agile: You can't use the patch method on a non object States!"); | ||
internal_1.Agile.logger.error("You can't use the patch method on a non object based States!"); | ||
return this; | ||
} | ||
if (!internal_1.isValidObject(targetWithChanges)) { | ||
console.warn("Agile: TargetWithChanges has to be an object!"); | ||
internal_1.Agile.logger.error("TargetWithChanges has to be an Object!"); | ||
return this; | ||
} | ||
// Merge targetWithChanges into nextStateValue | ||
this.nextStateValue = internal_1.flatMerge(this.nextStateValue, targetWithChanges, { addNewProperties: config.addNewProperties }); | ||
// Check if value has been changed | ||
if (internal_1.equal(this.value, this.nextStateValue)) | ||
return this; | ||
this.nextStateValue = internal_1.flatMerge(internal_1.copy(this.nextStateValue), targetWithChanges, { addNewProperties: config.addNewProperties }); | ||
// Ingest updated nextStateValue into Runtime | ||
this.ingest({ background: config.background }); | ||
this.ingest({ | ||
background: config.background, | ||
force: config.force, | ||
overwrite: config.overwrite, | ||
sideEffects: config.sideEffects, | ||
storage: config.storage, | ||
}); | ||
return this; | ||
@@ -219,10 +233,10 @@ } | ||
} | ||
// Check if Callback is a Function | ||
// Check if Callback is valid Function | ||
if (!internal_1.isFunction(_callback)) { | ||
console.error("Agile: A Watcher Callback Function has to be an function!"); | ||
internal_1.Agile.logger.error("A Watcher Callback Function has to be typeof Function!"); | ||
return this; | ||
} | ||
// Check if Callback Function already exists | ||
// Check if watcherKey is already occupied | ||
if (this.watchers[key]) { | ||
console.error(`Agile: Watcher Callback Function with the key/name ${key} already exists!`); | ||
internal_1.Agile.logger.error(`Watcher Callback Function with the key/name '${key}' already exists!`); | ||
return this; | ||
@@ -251,5 +265,5 @@ } | ||
onInaugurated(callback) { | ||
const watcherKey = "InauguratedWatcher"; | ||
this.watch(watcherKey, () => { | ||
callback(this.getPublicValue()); | ||
const watcherKey = "InauguratedWatcherKey"; | ||
this.watch(watcherKey, (value) => { | ||
callback(value); | ||
this.removeWatcher(watcherKey); | ||
@@ -264,3 +278,3 @@ }); | ||
* Checks if watcher at given Key exists | ||
* @param key - Key of Watcher | ||
* @param key - Key/Name of Watcher | ||
*/ | ||
@@ -275,3 +289,3 @@ hasWatcher(key) { | ||
_config = keyOrConfig; | ||
key = undefined; | ||
key = this._key; | ||
} | ||
@@ -284,11 +298,33 @@ else { | ||
instantiate: true, | ||
storageKeys: [], | ||
}); | ||
// Update Persistent Key | ||
if (this.persistent) | ||
internal_1.Agile.logger.warn(`By persisting the State '${this._key}' twice you overwrite the old Persistent Instance!`); | ||
// Create persistent -> Persist Value | ||
this.persistent = new internal_1.StatePersistent(this, { | ||
instantiate: _config.instantiate, | ||
storageKeys: _config.storageKeys, | ||
key: key, | ||
}); | ||
return this; | ||
} | ||
//========================================================================================================= | ||
// On Load | ||
//========================================================================================================= | ||
/** | ||
* @public | ||
* Callback Function that gets called if the persisted Value gets loaded into the State for the first Time | ||
* Note: Only useful for persisted States! | ||
* @param callback - Callback Function | ||
*/ | ||
onLoad(callback) { | ||
if (this.persistent) { | ||
if (key) | ||
this.persistent.key = key; | ||
return this; | ||
this.persistent.onLoad = callback; | ||
// If State is already 'isPersisted' the loading was successful -> callback can be called | ||
if (this.isPersisted) | ||
callback(true); | ||
} | ||
// Create persistent -> Persist Value | ||
this.persistent = new internal_1.StatePersistent(this.agileInstance(), this, key, { instantiate: _config.instantiate }); | ||
else { | ||
internal_1.Agile.logger.error(`Please make sure you persist the State '${this._key}' before using the 'onLoad' function!`); | ||
} | ||
return this; | ||
@@ -314,3 +350,3 @@ } | ||
get exists() { | ||
return this.getPublicValue() !== undefined && !this.isPlaceholder; | ||
return this._value !== undefined && !this.isPlaceholder; | ||
} | ||
@@ -348,7 +384,24 @@ //========================================================================================================= | ||
invert() { | ||
if (typeof this._value !== "boolean") { | ||
console.warn("Agile: You can only invert boolean based States!"); | ||
if (typeof this._value === "boolean") { | ||
this.set(!this._value); | ||
} | ||
else { | ||
internal_1.Agile.logger.error("You can only invert boolean based States!"); | ||
} | ||
return this; | ||
} | ||
//========================================================================================================= | ||
// Compute | ||
//========================================================================================================= | ||
/** | ||
* @public | ||
* Function that recomputes State Value if it changes | ||
* @param method - Computed Function | ||
*/ | ||
compute(method) { | ||
if (!internal_1.isFunction(method)) { | ||
internal_1.Agile.logger.error("A computeMethod has to be a function!"); | ||
return this; | ||
} | ||
this.set(this._value); | ||
this.computeMethod = method; | ||
return this; | ||
@@ -362,3 +415,3 @@ } | ||
* Adds SideEffect to State | ||
* @param key - Key of SideEffect | ||
* @param key - Key/Name of SideEffect | ||
* @param sideEffect - Callback Function that gets called on every State Value change | ||
@@ -368,3 +421,3 @@ */ | ||
if (!internal_1.isFunction(sideEffect)) { | ||
console.error("Agile: A sideEffect function has to be an function!"); | ||
internal_1.Agile.logger.error("A sideEffect function has to be a function!"); | ||
return this; | ||
@@ -404,5 +457,8 @@ } | ||
* Checks if Value has correct valueType (js) | ||
* Note: If no valueType set, it returns true | ||
* @param value - Value that gets checked for its correct Type | ||
*/ | ||
hasCorrectType(value) { | ||
if (!this.valueType) | ||
return true; | ||
let type = typeof value; | ||
@@ -432,5 +488,5 @@ return type === this.valueType; | ||
getPersistableValue() { | ||
return this.value; | ||
return this._value; | ||
} | ||
} | ||
exports.State = State; |
@@ -1,3 +0,2 @@ | ||
import { Agile, Observer, State, Job, JobConfigInterface, ObserverKey } from "../internal"; | ||
export declare const internalIngestKey = "THIS_IS_AN_INTERNAL_KEY_FOR_INGESTING_INTERNAL_STUFF"; | ||
import { Observer, State, ObserverKey, SubscriptionContainer, IngestConfigInterface, StateRuntimeJob, StateRuntimeJobConfigInterface, RuntimeJobKey } from "../internal"; | ||
export declare class StateObserver<ValueType = any> extends Observer { | ||
@@ -9,10 +8,14 @@ state: () => State<ValueType>; | ||
* State Observer - Handles State changes, dependencies (-> Interface to Runtime) | ||
* @param agileInstance - An instance of Agile | ||
* @param state - State | ||
* @param deps - Initial Dependencies of State Observer | ||
* @param key - Key/Name of State Observer | ||
* @param config - Config | ||
*/ | ||
constructor(agileInstance: Agile, state: State<ValueType>, deps?: Array<Observer>, key?: ObserverKey); | ||
constructor(state: State<ValueType>, config?: CreateStateObserverConfigInterface); | ||
/** | ||
* @internal | ||
* Ingests nextStateValue or computedValue into Runtime and applies it to the State | ||
* @param config - Config | ||
*/ | ||
ingest(config?: StateIngestConfigInterface): void; | ||
/** | ||
* @internal | ||
* Ingests new State Value into Runtime and applies it to the State | ||
@@ -22,22 +25,31 @@ * @param newStateValue - New Value of the State | ||
*/ | ||
ingest(newStateValue?: ValueType | InternalIngestKeyType, config?: StateJobConfigInterface): void; | ||
ingestValue(newStateValue: ValueType, config?: StateIngestConfigInterface): void; | ||
/** | ||
* @internal | ||
* Performs Job from Runtime | ||
* @param job - Job that gets performed | ||
* Performs Job that holds this Observer | ||
* @param job - Job | ||
*/ | ||
perform(job: Job<this>): void; | ||
perform(job: StateRuntimeJob): void; | ||
/** | ||
* @internal | ||
* SideEffects of Perform Function | ||
* @param job - Job whose SideEffects gets executed | ||
* SideEffects of Job | ||
* @param job - Job | ||
*/ | ||
private sideEffects; | ||
sideEffects(job: StateRuntimeJob): void; | ||
} | ||
export declare type InternalIngestKeyType = "THIS_IS_AN_INTERNAL_KEY_FOR_INGESTING_INTERNAL_STUFF"; | ||
/** | ||
* @param forceRerender - Force rerender no matter what happens | ||
* @param deps - Initial Dependencies of State Observer | ||
* @param subs - Initial Subscriptions of State Observer | ||
* @param key - Key/Name of State Observer | ||
*/ | ||
export interface StateJobConfigInterface extends JobConfigInterface { | ||
forceRerender?: boolean; | ||
export interface CreateStateObserverConfigInterface { | ||
deps?: Array<Observer>; | ||
subs?: Array<SubscriptionContainer>; | ||
key?: ObserverKey; | ||
} | ||
/** | ||
* @param key - Key/Name of Job that gets created | ||
*/ | ||
export interface StateIngestConfigInterface extends StateRuntimeJobConfigInterface, IngestConfigInterface { | ||
key?: RuntimeJobKey; | ||
} |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.StateObserver = exports.internalIngestKey = void 0; | ||
exports.StateObserver = void 0; | ||
const internal_1 = require("../internal"); | ||
exports.internalIngestKey = "THIS_IS_AN_INTERNAL_KEY_FOR_INGESTING_INTERNAL_STUFF"; | ||
class StateObserver extends internal_1.Observer { | ||
@@ -10,11 +9,9 @@ /** | ||
* State Observer - Handles State changes, dependencies (-> Interface to Runtime) | ||
* @param agileInstance - An instance of Agile | ||
* @param state - State | ||
* @param deps - Initial Dependencies of State Observer | ||
* @param key - Key/Name of State Observer | ||
* @param config - Config | ||
*/ | ||
constructor(agileInstance, state, deps, key) { | ||
super(agileInstance, deps, key, state.value); | ||
constructor(state, config = {}) { | ||
super(state.agileInstance(), Object.assign(Object.assign({}, config), { value: state._value })); | ||
this.state = () => state; | ||
this.nextStateValue = state.value; | ||
this.nextStateValue = internal_1.copy(state._value); | ||
} | ||
@@ -26,2 +23,19 @@ //========================================================================================================= | ||
* @internal | ||
* Ingests nextStateValue or computedValue into Runtime and applies it to the State | ||
* @param config - Config | ||
*/ | ||
ingest(config = {}) { | ||
const state = this.state(); | ||
let newStateValue; | ||
if (state instanceof internal_1.Computed) | ||
newStateValue = state.computeValue(); | ||
else | ||
newStateValue = state.nextStateValue; | ||
this.ingestValue(newStateValue, config); | ||
} | ||
//========================================================================================================= | ||
// Ingest Value | ||
//========================================================================================================= | ||
/** | ||
* @internal | ||
* Ingests new State Value into Runtime and applies it to the State | ||
@@ -31,3 +45,3 @@ * @param newStateValue - New Value of the State | ||
*/ | ||
ingest(newStateValue = exports.internalIngestKey, config = {}) { | ||
ingestValue(newStateValue, config = {}) { | ||
const state = this.state(); | ||
@@ -38,24 +52,30 @@ config = internal_1.defineConfig(config, { | ||
sideEffects: true, | ||
forceRerender: false, | ||
force: false, | ||
storage: true, | ||
overwrite: false, | ||
}); | ||
// If forceRerender, set background config to false since forceRerender is 'stronger' than background | ||
if (config.forceRerender && config.background) | ||
config.background = false; | ||
// Grab nextState or compute State if internalIngestKey got passed | ||
if (newStateValue === exports.internalIngestKey) { | ||
if (state instanceof internal_1.Computed) | ||
this.nextStateValue = state.computeValue(); | ||
else | ||
this.nextStateValue = state.nextStateValue; | ||
// Force overwriting State because if assigning Value to State, the State shouldn't be a placeholder anymore | ||
if (state.isPlaceholder) { | ||
config.force = true; | ||
config.overwrite = true; | ||
} | ||
else | ||
this.nextStateValue = newStateValue; | ||
// Check if State Value and the new Value are equals (Note: Not checking state.nextStateValue because of the internalIngestKey) | ||
if (internal_1.equal(state.value, this.nextStateValue) && !config.forceRerender) { | ||
if (this.agileInstance().config.logJobs) | ||
console.warn("Agile: Doesn't created job because state values are the same! "); | ||
// Assign next State Value and compute it if necessary | ||
this.nextStateValue = state.computeMethod | ||
? internal_1.copy(state.computeMethod(newStateValue)) | ||
: internal_1.copy(newStateValue); | ||
// Check if State Value and new/next Value are equals | ||
if (internal_1.equal(state._value, this.nextStateValue) && !config.force) | ||
return; | ||
} | ||
this.agileInstance().runtime.ingest(this, config); | ||
// Create Job | ||
const job = new internal_1.StateRuntimeJob(this, { | ||
storage: config.storage, | ||
sideEffects: config.sideEffects, | ||
force: config.force, | ||
background: config.background, | ||
overwrite: config.overwrite, | ||
key: config.key || this._key, | ||
}); | ||
this.agileInstance().runtime.ingest(job, { | ||
perform: config.perform, | ||
}); | ||
} | ||
@@ -67,24 +87,19 @@ //========================================================================================================= | ||
* @internal | ||
* Performs Job from Runtime | ||
* @param job - Job that gets performed | ||
* Performs Job that holds this Observer | ||
* @param job - Job | ||
*/ | ||
perform(job) { | ||
var _a; | ||
const state = job.observer.state(); | ||
// Set Previous State | ||
state.previousStateValue = internal_1.copy(state.value); | ||
// Set new State Value | ||
state._value = internal_1.copy(this.nextStateValue); | ||
state.nextStateValue = internal_1.copy(this.nextStateValue); | ||
// Store State changes in Storage | ||
if (job.config.storage) | ||
(_a = state.persistent) === null || _a === void 0 ? void 0 : _a.updateValue(); | ||
// Set isSet | ||
state.isSet = internal_1.notEqual(this.nextStateValue, state.initialStateValue); | ||
// Reset isPlaceholder since it got an value | ||
if (state.isPlaceholder) | ||
// Assign new State Values | ||
state.previousStateValue = internal_1.copy(state._value); | ||
state._value = internal_1.copy(job.observer.nextStateValue); | ||
state.nextStateValue = internal_1.copy(job.observer.nextStateValue); | ||
job.observer.value = internal_1.copy(job.observer.nextStateValue); | ||
// Overwrite old State Values | ||
if (job.config.overwrite) { | ||
state.initialStateValue = internal_1.copy(state._value); | ||
state.previousStateValue = internal_1.copy(state._value); | ||
state.isPlaceholder = false; | ||
// Update Observer value | ||
this.value = internal_1.copy(this.nextStateValue); | ||
// Perform SideEffects of the Perform Function | ||
} | ||
state.isSet = internal_1.notEqual(state._value, state.initialStateValue); | ||
this.sideEffects(job); | ||
@@ -97,4 +112,4 @@ } | ||
* @internal | ||
* SideEffects of Perform Function | ||
* @param job - Job whose SideEffects gets executed | ||
* SideEffects of Job | ||
* @param job - Job | ||
*/ | ||
@@ -114,6 +129,5 @@ sideEffects(job) { | ||
// Ingest Dependencies of Observer into Runtime | ||
job.observer.deps.forEach((observer) => observer instanceof StateObserver && | ||
observer.ingest(exports.internalIngestKey, { perform: false })); | ||
state.observer.deps.forEach((observer) => observer instanceof StateObserver && observer.ingest({ perform: false })); | ||
} | ||
} | ||
exports.StateObserver = StateObserver; |
@@ -1,3 +0,4 @@ | ||
import { Agile, Persistent, State, StorageKey } from "../internal"; | ||
import { CreatePersistentConfigInterface, Persistent, PersistentKey, State, StorageKey } from "../internal"; | ||
export declare class StatePersistent<ValueType = any> extends Persistent { | ||
static storeValueSideEffectKey: string; | ||
state: () => State; | ||
@@ -7,46 +8,49 @@ /** | ||
* State Persist Manager - Handles permanent storing of State Value | ||
* @param agileInstance - An instance of Agile | ||
* @param state - State that gets stored | ||
* @param key - Key of Storage property | ||
* @param config - Config | ||
*/ | ||
constructor(agileInstance: Agile, state: State<ValueType>, key?: StorageKey, config?: StatePersistentConfigInterface); | ||
set key(value: StorageKey); | ||
get key(): StorageKey; | ||
constructor(state: State<ValueType>, config?: CreatePersistentConfigInterface); | ||
/** | ||
* @public | ||
* Sets Key/Name of Persistent | ||
* @internal | ||
* Updates Key/Name of Persistent | ||
* @param value - New Key/Name of Persistent | ||
*/ | ||
setKey(value: StorageKey): Promise<void>; | ||
setKey(value?: StorageKey): Promise<void>; | ||
/** | ||
* @internal | ||
* Loads Value from Storage | ||
* Loads/Saves Storage Value for the first Time | ||
*/ | ||
initialLoading(): Promise<void>; | ||
/** | ||
* @internal | ||
* Loads State Value from the Storage | ||
* @return Success? | ||
*/ | ||
loadValue(): Promise<boolean>; | ||
loadPersistedValue(key?: PersistentKey): Promise<boolean>; | ||
/** | ||
* @internal | ||
* Saves/Updates Value in Storage | ||
* Sets everything up so that the State gets saved in the Storage on every Value change | ||
* @return Success? | ||
*/ | ||
updateValue(): Promise<boolean>; | ||
persistValue(key?: PersistentKey): Promise<boolean>; | ||
/** | ||
* @internal | ||
* Removes Value form Storage | ||
* Removes State Value form the Storage | ||
* @return Success? | ||
*/ | ||
removeValue(): Promise<boolean>; | ||
removePersistedValue(key?: PersistentKey): Promise<boolean>; | ||
/** | ||
* @internal | ||
* Validates Storage Key | ||
* @param key - Key that gets validated | ||
* Formats Storage Key | ||
* @param key - Key that gets formatted | ||
*/ | ||
validateKey(key?: StorageKey): StorageKey | null; | ||
formatKey(key?: PersistentKey): PersistentKey | undefined; | ||
/** | ||
* @internal | ||
* Rebuilds Storage depending on the State Value (Saves current State Value into the Storage) | ||
* @param state - State that holds the new Value | ||
* @param key - Key/Name of Persistent | ||
* @param config - Config | ||
*/ | ||
rebuildStorageSideEffect(state: State<ValueType>, key: PersistentKey, config?: any): void; | ||
} | ||
/** | ||
* @param instantiate - If Persistent gets instantiated | ||
*/ | ||
export interface StatePersistentConfigInterface { | ||
instantiate?: boolean; | ||
} |
@@ -18,24 +18,22 @@ "use strict"; | ||
* State Persist Manager - Handles permanent storing of State Value | ||
* @param agileInstance - An instance of Agile | ||
* @param state - State that gets stored | ||
* @param key - Key of Storage property | ||
* @param config - Config | ||
*/ | ||
constructor(agileInstance, state, key, config = {}) { | ||
super(agileInstance); | ||
constructor(state, config = {}) { | ||
super(state.agileInstance(), { | ||
instantiate: false, | ||
}); | ||
config = internal_1.defineConfig(config, { | ||
instantiate: true, | ||
storageKeys: [], | ||
}); | ||
this.state = () => state; | ||
if (config.instantiate) | ||
this.instantiatePersistent(key).then((success) => { | ||
state.isPersisted = success; | ||
}); | ||
this.instantiatePersistent({ | ||
key: config.key, | ||
storageKeys: config.storageKeys, | ||
}); | ||
// Load/Store persisted Value for the first Time | ||
if (this.ready && config.instantiate) | ||
this.initialLoading(); | ||
} | ||
set key(value) { | ||
this.setKey(value); | ||
} | ||
get key() { | ||
return this._key; | ||
} | ||
//========================================================================================================= | ||
@@ -45,4 +43,4 @@ // Set Key | ||
/** | ||
* @public | ||
* Sets Key/Name of Persistent | ||
* @internal | ||
* Updates Key/Name of Persistent | ||
* @param value - New Key/Name of Persistent | ||
@@ -52,54 +50,83 @@ */ | ||
return __awaiter(this, void 0, void 0, function* () { | ||
// If persistent isn't ready try to init it with the new Key | ||
if (!this.ready) { | ||
this.instantiatePersistent(value).then((success) => { | ||
this.state().isPersisted = success; | ||
}); | ||
const oldKey = this._key; | ||
const wasReady = this.ready; | ||
// Assign Key | ||
if (value === this._key) | ||
return; | ||
this._key = value || internal_1.Persistent.placeHolderKey; | ||
const isValid = this.validatePersistent(); | ||
// Try to Initial Load Value if persistent wasn't ready and return | ||
if (!wasReady) { | ||
if (isValid) | ||
yield this.initialLoading(); | ||
return; | ||
} | ||
// Check if key has changed | ||
if (value === this._key) | ||
return; | ||
// Remove value with old Key | ||
yield this.removeValue(); | ||
// Update Key | ||
this._key = value; | ||
// Set value with new Key | ||
yield this.updateValue(); | ||
// Remove value at old Key | ||
yield this.removePersistedValue(oldKey); | ||
// Assign Value to new Key | ||
if (isValid) | ||
yield this.persistValue(value); | ||
}); | ||
} | ||
//========================================================================================================= | ||
// Load Value | ||
// Initial Loading | ||
//========================================================================================================= | ||
/** | ||
* @internal | ||
* Loads Value from Storage | ||
* Loads/Saves Storage Value for the first Time | ||
*/ | ||
initialLoading() { | ||
const _super = Object.create(null, { | ||
initialLoading: { get: () => super.initialLoading } | ||
}); | ||
return __awaiter(this, void 0, void 0, function* () { | ||
_super.initialLoading.call(this).then(() => { | ||
this.state().isPersisted = true; | ||
}); | ||
}); | ||
} | ||
//========================================================================================================= | ||
// Load Persisted Value | ||
//========================================================================================================= | ||
/** | ||
* @internal | ||
* Loads State Value from the Storage | ||
* @return Success? | ||
*/ | ||
loadValue() { | ||
loadPersistedValue(key) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
if (!this.ready) | ||
return false; | ||
const loadedValue = yield this.agileInstance().storage.get(this._key); | ||
if (loadedValue) { | ||
this.state().set(loadedValue, { storage: false }); | ||
return true; | ||
} | ||
return false; | ||
const _key = key || this._key; | ||
// Load Value from default Storage | ||
const loadedValue = yield this.agileInstance().storages.get(_key, this.defaultStorageKey); | ||
if (!loadedValue) | ||
return false; | ||
// Assign loaded Value to State | ||
this.state().set(loadedValue, { storage: false }); | ||
// Persist State, so that the Storage Value updates dynamically if the State updates | ||
yield this.persistValue(_key); | ||
return true; | ||
}); | ||
} | ||
//========================================================================================================= | ||
// Set Value | ||
// Persist Value | ||
//========================================================================================================= | ||
/** | ||
* @internal | ||
* Saves/Updates Value in Storage | ||
* Sets everything up so that the State gets saved in the Storage on every Value change | ||
* @return Success? | ||
*/ | ||
updateValue() { | ||
persistValue(key) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
if (!this.ready) | ||
return false; | ||
this.agileInstance().storage.set(this.key, this.state().getPersistableValue()); | ||
this.state().isPersisted = true; | ||
const _key = key || this._key; | ||
// Add SideEffect to State, that updates the saved State Value depending on the current State Value | ||
this.state().addSideEffect(StatePersistent.storeValueSideEffectKey, (config) => { | ||
this.rebuildStorageSideEffect(this.state(), _key, config); | ||
}); | ||
// Rebuild Storage for saving State Value in the Storage | ||
this.rebuildStorageSideEffect(this.state(), _key); | ||
this.isPersisted = true; | ||
return true; | ||
@@ -109,15 +136,19 @@ }); | ||
//========================================================================================================= | ||
// Remove Value | ||
// Remove Persisted Value | ||
//========================================================================================================= | ||
/** | ||
* @internal | ||
* Removes Value form Storage | ||
* Removes State Value form the Storage | ||
* @return Success? | ||
*/ | ||
removeValue() { | ||
removePersistedValue(key) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
if (!this.ready) | ||
return false; | ||
this.agileInstance().storage.remove(this.key); | ||
this.state().isPersisted = false; | ||
const _key = key || this._key; | ||
// Remove SideEffect | ||
this.state().removeSideEffect(StatePersistent.storeValueSideEffectKey); | ||
// Remove Value from Storage | ||
this.agileInstance().storages.remove(_key, this.storageKeys); | ||
this.isPersisted = false; | ||
return true; | ||
@@ -127,23 +158,38 @@ }); | ||
//========================================================================================================= | ||
// Validate Key | ||
// Format Key | ||
//========================================================================================================= | ||
/** | ||
* @internal | ||
* Validates Storage Key | ||
* @param key - Key that gets validated | ||
* Formats Storage Key | ||
* @param key - Key that gets formatted | ||
*/ | ||
validateKey(key) { | ||
formatKey(key) { | ||
const state = this.state(); | ||
// Get key from State | ||
if (!key && state.key) | ||
return state.key; | ||
// Return null if no key found | ||
if (!key && state._key) | ||
return state._key; | ||
if (!key) | ||
return null; | ||
return; | ||
// Set State Key to Storage Key if State has no key | ||
if (!state.key) | ||
state.key = key; | ||
if (!state._key) | ||
state._key = key; | ||
return key; | ||
} | ||
//========================================================================================================= | ||
// Rebuild Storage SideEffect | ||
//========================================================================================================= | ||
/** | ||
* @internal | ||
* Rebuilds Storage depending on the State Value (Saves current State Value into the Storage) | ||
* @param state - State that holds the new Value | ||
* @param key - Key/Name of Persistent | ||
* @param config - Config | ||
*/ | ||
rebuildStorageSideEffect(state, key, config = {}) { | ||
if (config.storage !== undefined && !config.storage) | ||
return; | ||
this.agileInstance().storages.set(key, this.state().getPersistableValue(), this.storageKeys); | ||
} | ||
} | ||
exports.StatePersistent = StatePersistent; | ||
StatePersistent.storeValueSideEffectKey = "rebuildStateStorageValue"; |
@@ -5,6 +5,6 @@ import { Agile } from "./internal"; | ||
* Creates a fresh copy of an Array/Object | ||
* https://www.samanthaming.com/tidbits/70-3-ways-to-clone-objects/ | ||
* @param value - Array/Object that gets copied | ||
*/ | ||
export declare function copy<T = any>(value: T): T; | ||
export declare function copy<T extends Array<T>>(value: T): T[]; | ||
/** | ||
@@ -19,11 +19,21 @@ * @internal | ||
* @internal | ||
* Check if array1 contains all elements of array2 | ||
* @param array1 - Array 1 | ||
* @param array2 - Array 2 | ||
*/ | ||
export declare function includesArray<DataType = any>(array1: Array<DataType>, array2: Array<DataType>): boolean; | ||
/** | ||
* @internal | ||
* Transforms Item/s to an Item Array | ||
* @param items - Item/s that gets transformed to an Array | ||
* @param config - Config | ||
*/ | ||
export declare function normalizeArray<DataType = any>(items?: DataType | Array<DataType>): Array<DataType>; | ||
export declare function normalizeArray<DataType = any>(items?: DataType | Array<DataType>, config?: { | ||
createUndefinedArray?: boolean; | ||
}): Array<DataType>; | ||
/** | ||
* @internal | ||
* Tries to get an AgileInstance from provided instance | ||
* If no agileInstance found it returns the global AgileInstance | ||
* @param instance - Instance that might hold an AgileInstance | ||
* Tries to get an Instance of Agile from provided Instance | ||
* If no agileInstance found it returns the global bound Agile Instance | ||
* @param instance - Instance that might hold an Agile Instance | ||
*/ | ||
@@ -61,4 +71,5 @@ export declare function getAgileInstance(instance: any): Agile | undefined; | ||
* @param defaults - Default values object that gets merged into config object | ||
* @param overwriteUndefinedProperties - If undefined Properties in config gets overwritten by the default value | ||
*/ | ||
export declare function defineConfig<ConfigInterface = Object>(config: ConfigInterface, defaults: Object): ConfigInterface; | ||
export declare function defineConfig<ConfigInterface = Object>(config: ConfigInterface, defaults: Object, overwriteUndefinedProperties?: boolean): ConfigInterface; | ||
/** | ||
@@ -104,10 +115,12 @@ * @internal | ||
*/ | ||
export declare function clone<T>(instance: T): T; | ||
export declare function clone<T = any>(instance: T): T; | ||
/** | ||
* @internal | ||
* Binds Instance Global | ||
* Binds passed Instance globally at passed Key | ||
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/globalThis | ||
* @param key - Key of Instance | ||
* @param instance - Instance which becomes globally accessible (globalThis.key) | ||
* https://blog.logrocket.com/what-is-globalthis-why-use-it/ | ||
* @param key - Key/Name of Instance | ||
* @param instance - Instance | ||
* @param overwrite - If already existing instance at passed Key gets overwritten | ||
*/ | ||
export declare function globalBind(key: string, instance: any): void; | ||
export declare function globalBind(key: string, instance: any, overwrite?: boolean): boolean; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.globalBind = exports.clone = exports.generateId = exports.notEqual = exports.equal = exports.flatMerge = exports.defineConfig = exports.isJsonString = exports.isValidUrl = exports.isAsyncFunction = exports.isFunction = exports.getAgileInstance = exports.normalizeArray = exports.isValidObject = exports.copy = void 0; | ||
exports.globalBind = exports.clone = exports.generateId = exports.notEqual = exports.equal = exports.flatMerge = exports.defineConfig = exports.isJsonString = exports.isValidUrl = exports.isAsyncFunction = exports.isFunction = exports.getAgileInstance = exports.normalizeArray = exports.includesArray = exports.isValidObject = exports.copy = void 0; | ||
const internal_1 = require("./internal"); | ||
//========================================================================================================= | ||
// Copy | ||
//========================================================================================================= | ||
/** | ||
* @internal | ||
* Creates a fresh copy of an Array/Object | ||
* https://www.samanthaming.com/tidbits/70-3-ways-to-clone-objects/ | ||
* @param value - Array/Object that gets copied | ||
*/ | ||
function copy(value) { | ||
if (Array.isArray(value)) | ||
return [...value]; | ||
if (isValidObject(value)) | ||
return Object.assign({}, value); | ||
return value; | ||
// Extra checking '!value' because 'typeof null === object' | ||
if (!value || typeof value !== "object") | ||
return value; | ||
let temp; | ||
let newObject = Array.isArray(value) ? [] : {}; | ||
for (let property in value) { | ||
temp = value[property]; | ||
newObject[property] = typeof temp === "object" ? copy(temp) : temp; | ||
} | ||
return newObject; | ||
} | ||
@@ -41,2 +55,15 @@ exports.copy = copy; | ||
//========================================================================================================= | ||
// Includes Array | ||
//========================================================================================================= | ||
/** | ||
* @internal | ||
* Check if array1 contains all elements of array2 | ||
* @param array1 - Array 1 | ||
* @param array2 - Array 2 | ||
*/ | ||
function includesArray(array1, array2) { | ||
return array2.every((element) => array1.includes(element)); | ||
} | ||
exports.includesArray = includesArray; | ||
//========================================================================================================= | ||
// Normalize Array | ||
@@ -48,5 +75,9 @@ //========================================================================================================= | ||
* @param items - Item/s that gets transformed to an Array | ||
* @param config - Config | ||
*/ | ||
function normalizeArray(items) { | ||
if (!items) | ||
function normalizeArray(items, config = {}) { | ||
config = defineConfig(config, { | ||
createUndefinedArray: false, | ||
}); | ||
if (!items && !config.createUndefinedArray) | ||
return []; | ||
@@ -61,23 +92,21 @@ return Array.isArray(items) ? items : [items]; | ||
* @internal | ||
* Tries to get an AgileInstance from provided instance | ||
* If no agileInstance found it returns the global AgileInstance | ||
* @param instance - Instance that might hold an AgileInstance | ||
* Tries to get an Instance of Agile from provided Instance | ||
* If no agileInstance found it returns the global bound Agile Instance | ||
* @param instance - Instance that might hold an Agile Instance | ||
*/ | ||
function getAgileInstance(instance) { | ||
try { | ||
// Try to find agileInstance in Instance | ||
if (instance instanceof internal_1.State) | ||
return instance.agileInstance(); | ||
if (instance instanceof internal_1.Event) | ||
return instance.agileInstance(); | ||
if (instance instanceof internal_1.Collection) | ||
return instance.agileInstance(); | ||
const _instance = instance["agileInstance"]; | ||
if (_instance) | ||
return instance; | ||
// Return global bound agileInstance (set in first instantiation of Agile) | ||
return globalThis.__agile__; | ||
// Try to get agileInstance from passed Instance | ||
if (instance) { | ||
const _agileInstance = isFunction(instance["agileInstance"]) | ||
? instance["agileInstance"]() | ||
: instance["agileInstance"]; | ||
if (_agileInstance) | ||
return _agileInstance; | ||
} | ||
// Return global bound agileInstance | ||
return globalThis["__agile__"]; | ||
} | ||
catch (e) { | ||
// fail silently | ||
internal_1.Agile.logger.error("Failed to get Agile Instance from ", instance); | ||
} | ||
@@ -108,3 +137,6 @@ return undefined; | ||
function isAsyncFunction(value) { | ||
return isFunction(value) && value.constructor.name === "AsyncFunction"; | ||
const valueString = value.toString(); | ||
return (isFunction(value) && | ||
(value.constructor.name === "AsyncFunction" || | ||
valueString.includes("__awaiter"))); | ||
} | ||
@@ -140,2 +172,4 @@ exports.isAsyncFunction = isAsyncFunction; | ||
function isJsonString(value) { | ||
if (typeof value !== "string") | ||
return false; | ||
try { | ||
@@ -158,4 +192,14 @@ JSON.parse(value); | ||
* @param defaults - Default values object that gets merged into config object | ||
* @param overwriteUndefinedProperties - If undefined Properties in config gets overwritten by the default value | ||
*/ | ||
function defineConfig(config, defaults) { | ||
function defineConfig(config, defaults, overwriteUndefinedProperties) { | ||
if (overwriteUndefinedProperties === undefined) | ||
overwriteUndefinedProperties = true; | ||
if (overwriteUndefinedProperties) { | ||
const finalConfig = Object.assign(Object.assign({}, defaults), config); | ||
for (let key in finalConfig) | ||
if (finalConfig[key] === undefined) | ||
finalConfig[key] = defaults[key]; | ||
return finalConfig; | ||
} | ||
return Object.assign(Object.assign({}, defaults), config); | ||
@@ -209,3 +253,3 @@ } | ||
function notEqual(value1, value2) { | ||
return value1 !== value2 && JSON.stringify(value1) !== JSON.stringify(value2); | ||
return !equal(value1, value2); | ||
} | ||
@@ -242,4 +286,9 @@ exports.notEqual = notEqual; | ||
function clone(instance) { | ||
const copy = Object.create(Object.getPrototypeOf(instance)); | ||
return Object.assign(copy, instance); | ||
// Clone Class | ||
const objectCopy = Object.create(Object.getPrototypeOf(instance)); | ||
const objectClone = Object.assign(objectCopy, instance); | ||
// Copy Properties of Class | ||
for (let key in objectClone) | ||
objectClone[key] = copy(objectClone[key]); | ||
return objectClone; | ||
} | ||
@@ -252,16 +301,25 @@ exports.clone = clone; | ||
* @internal | ||
* Binds Instance Global | ||
* Binds passed Instance globally at passed Key | ||
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/globalThis | ||
* @param key - Key of Instance | ||
* @param instance - Instance which becomes globally accessible (globalThis.key) | ||
* https://blog.logrocket.com/what-is-globalthis-why-use-it/ | ||
* @param key - Key/Name of Instance | ||
* @param instance - Instance | ||
* @param overwrite - If already existing instance at passed Key gets overwritten | ||
*/ | ||
function globalBind(key, instance) { | ||
function globalBind(key, instance, overwrite = false) { | ||
try { | ||
if (!globalThis[key]) | ||
if (overwrite) { | ||
globalThis[key] = instance; | ||
return true; | ||
} | ||
if (!globalThis[key]) { | ||
globalThis[key] = instance; | ||
return true; | ||
} | ||
} | ||
catch (e) { | ||
console.warn(`Agile: Failed to create global Instance called '${name}'`, instance); | ||
internal_1.Agile.logger.error(`Failed to create global Instance called '${key}'`); | ||
} | ||
return false; | ||
} | ||
exports.globalBind = globalBind; |
{ | ||
"name": "@agile-ts/core", | ||
"version": "0.0.5", | ||
"version": "0.0.6", | ||
"author": "BennoDev", | ||
"license": "ISC", | ||
"homepage": "https://agile-ts.org/", | ||
"description": "Global state and logic framework for reactive JavaScript & TypeScript applications.", | ||
@@ -16,15 +17,2 @@ "keywords": [], | ||
}, | ||
"devDependencies": { | ||
"@types/chai": "^4.2.12", | ||
"@types/mocha": "^8.0.2", | ||
"@types/node": "^14.6.0", | ||
"chai": "^4.2.0", | ||
"eslint-config-prettier": "^6.11.0", | ||
"mocha": "^8.1.1", | ||
"prettier": "2.0.5", | ||
"ts-node": "^8.10.2", | ||
"tsc-watch": "^4.1.0", | ||
"tslib": "^2.0.0", | ||
"typescript": "^3.9.7" | ||
}, | ||
"publishConfig": { | ||
@@ -31,0 +19,0 @@ "access": "public" |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Mixed license
License(Experimental) Package contains multiple licenses.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No website
QualityPackage does not have a website.
Found 1 instance in 1 package
280500
0
65
7162
1
1