Skip to content
This repository was archived by the owner on Oct 16, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 44 additions & 15 deletions packages/core/src/collection/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ export class Collection<DataType = DefaultItem> {
*/
public Group(
initialItems?: Array<ItemKey>,
config?: GroupConfigInterface
config: GroupConfigInterface = {}
): Group<DataType> {
if (this.isInstantiated) {
const key = config?.key || generateId();
Expand Down Expand Up @@ -150,7 +150,7 @@ export class Collection<DataType = DefaultItem> {
*/
public Selector(
initialKey: ItemKey,
config?: SelectorConfigInterface
config: SelectorConfigInterface = {}
): Selector<DataType> {
if (this.isInstantiated) {
const key = config?.key || generateId();
Expand Down Expand Up @@ -305,7 +305,7 @@ export class Collection<DataType = DefaultItem> {
const item = this.getItem(itemKey, { notExisting: true });
const primaryKey = this.config.primaryKey;
config = defineConfig(config, {
addNewProperties: true,
patch: true,
background: false,
});

Expand All @@ -326,20 +326,45 @@ export class Collection<DataType = DefaultItem> {
const newItemKey = changes[primaryKey] || oldItemKey;
const updateItemKey = oldItemKey !== newItemKey;

// Delete primaryKey from 'changes' because if it has changed, it gets properly updated in 'updateItemKey' (below)
if (changes[primaryKey]) delete changes[primaryKey];

// Update ItemKey
if (updateItemKey)
this.updateItemKey(oldItemKey, newItemKey, {
background: config.background,
});

// Apply changes to Item
item.patch(changes as any, {
background: config.background,
addNewProperties: config.addNewProperties,
});
// Patch changes into Item
if (config.patch) {
// Delete primaryKey from 'changes' because if it has changed, it gets properly updated in 'updateItemKey' (see above)
if (changes[primaryKey]) delete changes[primaryKey];

let patchConfig: { addNewProperties?: boolean } =
typeof config.patch === 'object' ? config.patch : {};
patchConfig = defineConfig(patchConfig, {
addNewProperties: true,
});

// Apply changes to Item
item.patch(changes as any, {
background: config.background,
addNewProperties: patchConfig.addNewProperties,
});
}

// Set changes into Item
if (!config.patch) {
// To make sure that the primaryKey doesn't differ from the changes object primaryKey
if (changes[this.config.primaryKey] !== itemKey) {
changes[this.config.primaryKey] = itemKey;
Agile.logger.warn(
`By overwriting the whole Item don't forget passing the correct primaryKey!`, changes
);
}

// Apply changes to Item
item.set(changes as any, {
background: config.background,
});
}

return item;
}
Expand Down Expand Up @@ -847,7 +872,7 @@ export class Collection<DataType = DefaultItem> {
* @public
* Resets this Collection
*/
public reset() {
public reset(): this {
// Reset Data
this.data = {};
this.size = 0;
Expand All @@ -857,6 +882,8 @@ export class Collection<DataType = DefaultItem> {

// Reset Selectors
for (const key in this.selectors) this.getSelector(key)?.reset();

return this;
}

//=========================================================================================================
Expand All @@ -873,14 +900,16 @@ export class Collection<DataType = DefaultItem> {
itemKeys: ItemKey | Array<ItemKey>,
groupKeys: GroupKey | Array<GroupKey>,
config: GroupAddConfig = {}
) {
): this {
const _itemKeys = normalizeArray(itemKeys);
const _groupKeys = normalizeArray(groupKeys);

// Add ItemKeys to Groups
_groupKeys.forEach((groupKey) => {
this.getGroup(groupKey)?.add(_itemKeys, config);
});

return this;
}

//=========================================================================================================
Expand Down Expand Up @@ -1205,11 +1234,11 @@ export interface CollectConfigInterface<DataType = any> {
}

/**
* @param addNewProperties - If properties that doesn't exist in base ItemData get added
* @param patch - If Data gets merged into the current Data
* @param background - If updating an Item happens in the background (-> not causing any rerender)
*/
export interface UpdateConfigInterface {
addNewProperties?: boolean;
patch?: boolean | { addNewProperties?: boolean };
background?: boolean;
}

Expand Down
6 changes: 3 additions & 3 deletions packages/core/src/computed/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,13 +109,13 @@ export class Computed<ComputedValueType = any> extends State<
}

//=========================================================================================================
// Compute Values
// Compute
//=========================================================================================================
/**
* @internal
* Computes Value and adds missing Dependencies to Computed
* Recomputes value and adds missing dependencies to Computed
*/
public computeValue(): ComputedValueType {
public compute(): ComputedValueType {
// Auto track Observers the computeFunction might depend on
ComputedTracker.track();
const computedValue = this.computeFunction();
Expand Down
42 changes: 34 additions & 8 deletions packages/core/src/state/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import {
PersistentKey,
ComputedTracker,
StateIngestConfigInterface,
StateRuntimeJobConfigInterface,
} from '../internal';

export class State<ValueType = any> {
Expand All @@ -34,7 +33,8 @@ export class State<ValueType = any> {
public sideEffects: {
[key: string]: SideEffectInterface<State<ValueType>>;
} = {}; // SideEffects of State (will be executed in Runtime)
public computeMethod?: ComputeMethod<ValueType>;
public computeValueMethod?: ComputeValueMethod<ValueType>;
public computeExistsMethod: ComputeExistsMethod<ValueType>;

public isPersisted = false; // If State can be stored in Agile Storage (-> successfully integrated persistent)
public persistent: StatePersistent | undefined; // Manages storing State Value into Storage
Expand Down Expand Up @@ -68,6 +68,9 @@ export class State<ValueType = any> {
this.previousStateValue = copy(initialValue);
this.nextStateValue = copy(initialValue);
this.isPlaceholder = true;
this.computeExistsMethod = (v) => {
return v != null;
};

// Initial Set
if (!config.isPlaceholder) this.set(initialValue, { overwrite: true });
Expand Down Expand Up @@ -476,7 +479,25 @@ export class State<ValueType = any> {
* Checks if State exists
*/
public get exists(): boolean {
return !this.isPlaceholder;
return !this.isPlaceholder && this.computeExistsMethod(this.value);
}

//=========================================================================================================
// Compute Exists
//=========================================================================================================
/**
* @public
* Function that computes the exists status of the State
* @param method - Computed Function
*/
public computeExists(method: ComputeExistsMethod<ValueType>): this {
if (!isFunction(method)) {
Agile.logger.error(`A 'computeExistsMethod' has to be a function!`);
return this;
}
this.computeExistsMethod = method;

return this;
}

//=========================================================================================================
Expand Down Expand Up @@ -521,19 +542,23 @@ export class State<ValueType = any> {
}

//=========================================================================================================
// Compute
// Compute Value
//=========================================================================================================
/**
* @public
* Function that recomputes State Value if it changes
* @param method - Computed Function
*/
public compute(method: ComputeMethod<ValueType>): this {
public computeValue(method: ComputeValueMethod<ValueType>): this {
if (!isFunction(method)) {
Agile.logger.error('A computeMethod has to be a function!');
Agile.logger.error(`A 'computeValueMethod' has to be a function!`);
return this;
}
this.computeMethod = method;
this.computeValueMethod = method;

// Initial compute
this.set(method(this.nextStateValue));

return this;
}

Expand Down Expand Up @@ -662,7 +687,8 @@ export interface StatePersistentConfigInterface {
}

export type StateWatcherCallback<T = any> = (value: T, key: string) => void;
export type ComputeMethod<T = any> = (value: T) => T;
export type ComputeValueMethod<T = any> = (value: T) => T;
export type ComputeExistsMethod<T = any> = (value: T) => boolean;

export type SideEffectFunctionType<Instance extends State<any>> = (
instance: Instance,
Expand Down
6 changes: 3 additions & 3 deletions packages/core/src/state/state.observer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export class StateObserver<ValueType = any> extends Observer {
const state = this.state();
let newStateValue: ValueType;

if (state instanceof Computed) newStateValue = state.computeValue();
if (state instanceof Computed) newStateValue = state.compute();
else newStateValue = state.nextStateValue;

this.ingestValue(newStateValue, config);
Expand Down Expand Up @@ -86,8 +86,8 @@ export class StateObserver<ValueType = any> extends Observer {
}

// Assign next State Value and compute it if necessary
this.nextStateValue = state.computeMethod
? copy(state.computeMethod(newStateValue))
this.nextStateValue = state.computeValueMethod
? copy(state.computeValueMethod(newStateValue))
: copy(newStateValue);

// Check if State Value and new/next Value are equals
Expand Down
Loading