outsystems-ui
Version:
OutSystems UI Framework
208 lines (186 loc) • 7.19 kB
text/typescript
/// <reference path="AbstractPattern.ts" />
// eslint-disable-next-line @typescript-eslint/no-unused-vars
namespace OSFramework.OSUI.Patterns {
/**
* Defines the Default props and methods for Patterns that will have other Patterns as its childs
*
* @export
* @abstract
* @class AbstractParent
* @implements {Interface.IParent}
* @template {C extends AbstractConfiguration, CT extends Interface.IChild}
*/
export abstract class AbstractParent<C extends AbstractConfiguration, CT extends Interface.IChild>
extends AbstractPattern<C>
implements Interface.IParent
{
// Store a collection about all the child items <childId, childType> in the parent scope
private _childIdsByType: Map<string, string> = new Map<string, string>();
// Associative Array that will contain the childType as keyName and a map with all childs of same type!
private _childItemsByType = {};
/**
* Get the child reference based on given Id
*
* @protected
* @param childId Child Id of the element to be found
* @returns Child Reference
* @memberof OSFramework.Patterns.AbstractParent
*/
protected getChild(childId: string): CT {
// Get ChildType
const childType = this._childIdsByType.get(childId);
// Check if there are childs of the given ChildId type!
if (this._childItemsByType[childType]) {
return this._childItemsByType[childType].get(childId);
} else {
return undefined;
}
}
/**
* Get a child reference by a given Index position
*
* @protected
* @param index Index of the item to be returned
* @param childType To be based on childType childs collection, otherwise will assume parent only contains one child type!
* @returns Child Reference
* @memberof OSFramework.Patterns.AbstractParent
*/
protected getChildByIndex(index: number, childType?: string): CT {
// If childType undefined, get the first key object name!
if (childType === undefined) {
childType = Object.keys(this._childItemsByType)[0];
}
// Get the correct map based on the given childType
const childsMap = this._childItemsByType[childType];
return childsMap ? this.getChild(Helper.MapOperation.ExportKeys(childsMap)[index]) : undefined;
}
/**
* Get a child index from a given child Id
*
* @protected
* @param childId Child id where index will be found
* @returns Index value
* @memberof OSFramework.Patterns.AbstractParent
*/
protected getChildIndex(childId: string): number {
// Get ChildType
const childType = this._childIdsByType.get(childId);
// Get the correct map based on the given childType
const childsMap = this._childItemsByType[childType];
return Helper.MapOperation.ExportKeys(childsMap).indexOf(childId);
}
/**
* Method that is used to set a given child as a Parent child.
*
* @protected
* @param childId Id that should be added
* @param childItem Reference to be added
* @memberof OSFramework.Patterns.AbstractParent
*/
protected setChild(childItem: CT): void {
const childType = childItem.constructor.name;
if (this._childItemsByType[childType] === undefined) {
this._childItemsByType[childType] = new Map<string, CT>();
}
// Check if we have no items yet
if (this._childItemsByType[childType].size === 0) {
// Set item as First and Last child
childItem.isFirstChild = true;
childItem.isLastChild = true;
} else {
// Since we've more than one child, get the previous and unset it as lastChild
this.getChildByIndex(this._childItemsByType[childType].size - 1, childType).isLastChild = false;
// Set the current one as lastChild
childItem.isLastChild = true;
}
// Set the child into the global _childIdsByType
this._childIdsByType.set(childItem.uniqueId, childType);
// Set the given child into the map with the same type
this._childItemsByType[childType].set(childItem.uniqueId, childItem);
}
/**
* Method that will remove Child by given Id
*
* @protected
* @param childId Id of the item that will be removed
* @memberof OSFramework.Patterns.AbstractParent
*/
protected unsetChild(childId: string): void {
// Get ChildType
const childType = this._childIdsByType.get(childId);
if (childType === undefined) {
// Was not able to get type!
throw new Error(
`${ErrorCodes.AbstractParent.FailTypeNotFound}:Child Type of Child ('${childId}') was not found!`
);
}
// Get the correct map based on the given childType
const childsMap = this._childItemsByType[childType];
if (childsMap === undefined) {
// Was not able to get childs of type!
throw new Error(
`${ErrorCodes.AbstractParent.FailChildsNotFound}:Childs of Type ('${childType}') were not found!`
);
}
// Get the child reference
const childItem = this.getChild(childId);
if (childItem === undefined) {
// Was not able to get child by id!
throw new Error(
`${ErrorCodes.AbstractParent.FailChildNotFound}:Child of Id ('${childId}') was not found!`
);
}
// Check if it's the firstChild
if (childItem.isFirstChild) {
// unSet it as firstChild
childItem.isFirstChild = false;
// Set the 2nd child as firsChild since it will turns into the first one!
const nextSibling = this.getChildByIndex(1, childType);
if (nextSibling) {
nextSibling.isFirstChild = true;
}
}
// Check if it's the lastChild
if (childItem.isLastChild) {
// unSet it as lastChild
childItem.isLastChild = false;
// Set the previous child as lastChild since it will turns into the last one!
// The current last child (childsMap.size - 1), the prevSibling will be the child before (-2)
const prevSibling = this.getChildByIndex(childsMap.size - 2, childType);
if (prevSibling) {
prevSibling.isLastChild = true;
}
}
// Remove child from it's Map of all child items global reference!
this._childIdsByType.delete(childId);
// Remove child from it's Map of items with the same type!
childsMap.delete(childId);
}
/**
* Getter that allows to obtain all the childs when parent only contains childs of one type,
* otherwise a type must be passed in order to return all child of the given type!
*
* @param type To be based on childType childs collection, otherwise will assume parent only contains one child type!
* @returns
* @memberof OSFramework.Patterns.AbstractParent
*/
public getChildItems(type?: string): Array<CT> {
// If type undefined, get the first key object name!
if (type === undefined) {
type = Object.keys(this._childItemsByType)[0];
}
// Get the correct map based on the given type
const childsMap = this._childItemsByType[type];
return childsMap === undefined ? [] : [...childsMap.values()];
}
/**
* Method used to be notified by a given ChildId about a given action and act accordingly
*
* @abstract
* @param {string} childItem Child Item
* @param {string} notifiedTo Notification name (Should be based on an Enum)
* @memberof OSFramework.Patterns.AbstractParent
*/
public abstract beNotifiedByChild(childItem: CT, notifiedTo: string): void;
}
}