@gmetrixr/rjson
Version:
(R)ecursive Json
225 lines (224 loc) • 11.4 kB
TypeScript
import { RecordNode, ROM, RecordMap, ClipboardR } from "./RecordNode";
import { RT, RTP } from "./RecordTypes";
/**
* A convenient Factory class to maninpulate a RecordNode object of any type
* This class can be extended to provide any recordType specific funcitonality
*
* Using arrow functions in Classes has a runtime performance cost. The constructor bloats up.
* Solution: https://www.typescriptlang.org/docs/handbook/2/classes.html#this-parameters
*/
export declare class RecordFactory<T extends RT> {
protected readonly _json: RecordNode<T>;
protected readonly _type: RT;
constructor(json: RecordNode<T>);
/**
* Returns the value of a property, or it default in case the value isn't defined.
* In case there is no default defined, it returns "undefined"
*/
getValueOrDefault(this: RecordFactory<T>, property: RTP[T]): unknown;
/**
* Returns a clone default value of a property. If no default is found, returns undefined
* Note: the returned object is a cloned value to avoid reuse of references across r objects
*/
getDefault(this: RecordFactory<T>, property: RTP[T]): unknown;
json(this: RecordFactory<T>): RecordNode<T>;
getId(this: RecordFactory<T>): number;
getName(this: RecordFactory<T>): string | undefined;
/** A list of recordNode.props' keys in the json */
getProps(this: RecordFactory<T>): string[];
/** A list of Records this json has (eg: project might have scene, variable, menu) */
getROMTypes(this: RecordFactory<T>): RT[];
/** A list of props this RecordType is supposed to have */
getRecordTypeProps(this: RecordFactory<T>): string[];
/** In case a property isn't defined in the json, this method returns "undefined" */
get(this: RecordFactory<T>, property: RTP[T]): unknown;
set(this: RecordFactory<T>, property: RTP[T], value: unknown): RecordFactory<T>;
delete(this: RecordFactory<T>, property: string): RecordFactory<T>;
/** get RecordOrderedMap for a particular sub record, of the shape {map: {}, order: []} - */
getROM<N extends RT>(this: RecordFactory<T>, type: N): ROM<N> | undefined;
getRecordMap<N extends RT>(this: RecordFactory<T>, type: N): RecordMap<N>;
getRecordOrder(this: RecordFactory<T>, type: RT): number[];
getRecord<N extends RT>(this: RecordFactory<T>, type: N, id: number): RecordNode<N> | undefined;
getRecords<N extends RT>(this: RecordFactory<T>, type: N): RecordNode<N>[];
changeRecordId<N extends RT>(this: RecordFactory<T>, type: N, id: number, newId?: number): RecordNode<N> | undefined;
changeRecordName<N extends RT>(this: RecordFactory<T>, type: N, id: number, newName?: string): RecordNode<N> | undefined;
changeDeepRecordName<N extends RT>(this: RecordFactory<T>, type: N, id: number, newName?: string): RecordNode<N> | undefined;
changePropertyName(this: RecordFactory<T>, propertyName: string, newPropertyName: string): RecordFactory<T>;
deleteProperty(this: RecordFactory<T>, propertyName: string): RecordFactory<T>;
addBlankRecord<N extends RT>(this: RecordFactory<T>, type: N, position?: number): RecordNode<N>;
addRecord<N extends RT>(this: RecordFactory<T>, record: RecordNode<N>, position?: number): RecordNode<N> | undefined;
duplicateRecord<N extends RT>(this: RecordFactory<T>, type: N, id: number): RecordNode<N> | undefined;
duplicateDeepRecord<N extends RT>(this: RecordFactory<T>, type: N, id: number): RecordNode<N> | undefined;
deleteRecord<N extends RT>(this: RecordFactory<T>, type: N, id: number): RecordNode<N> | undefined;
deleteDeepRecord<N extends RT>(this: RecordFactory<T>, type: N, id: number): RecordNode<N> | undefined;
/**
* Used in drag-drop operations
* Allows moving multiple items at the same time
* Changes order in place
*
* Input: [1, 2, 3, 4, 5, 6]
* Operation: nodeIds: [2,4], position: 5
* Output: [1, 3, 5, 6, 2, 4]
*/
transposeRecords(this: RecordFactory<T>, type: RT, ids: number[], position: number): undefined;
getDeepChildAndParent<N extends RT>(this: RecordFactory<T>, type: N, id: number): {
c: RecordNode<N>;
p: RecordNode<RT>;
} | undefined;
getAllDeepChildrenIds<N extends RT>(this: RecordFactory<T>, type: N): number[];
getAllDeepChildren<N extends RT>(this: RecordFactory<T>, type: N): RecordNode<N>[];
/**
* Documentation for filter predicate: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter#description
*/
getAllDeepChildrenIdsWithFilter<N extends RT>(this: RecordFactory<T>, type: N, predicate: (value: RecordNode<N>, index?: number, array?: RecordNode<N>[]) => boolean): number[];
getAllDeepChildrenWithFilter<N extends RT>(this: RecordFactory<T>, type: N, predicate: (value: RecordNode<N>, index?: number, array?: RecordNode<N>[]) => boolean): RecordNode<N>[];
/**
* Returns the object that needs to be stringified before sending to clipboard
* Needs to be overriden at Project/Scene level to ensure that
* 1) Vars are copied correctly (in case of scene copy). Vars pasting will be a bit different - no point changing ids.
* 2) element ids don't conflict at any depth while pasting
* 3) To ensure Ctrl+V of scene works even within another scene (ie project's paste gets used)
*
* * For clipboard operation, this function can only be called at the parent level
* * For scene copy, call to r.project()
* * For element copy, call to r.scene()
*
* Scene copy logic:
* In scene rules, vars can be referenced via ids or via variable names (in templates used in elements)
* Copy all variables referenced.
*
* Scene paste logic:
* For EACH variable which was copied
* - If the variable id and name doesn't exist - paste it
* - If the variable id exists, but name doesn't - ignore it. (To make this work, we need to go to each string template used in rules
* and elements in the new scene, and change the templates to use the new name)
* - If the variable id doesn't exist, but name does - replace the variable id in the new scene being pasted (in all rules)
* with the new variable id
* - If both the variable id and name exist - ignore it
*/
copyToClipboardObject(this: RecordFactory<T>, ids: number[]): ClipboardR;
/**
* Once the clipboard has been converted into ClipboardR, this function can be used to merge into parent RecordNode
*/
pasteFromClipboardObject(this: RecordFactory<T>, { obj, position }: {
obj: ClipboardR;
position?: number;
}): void;
/**
* Add moveRecords in RecordFactory. ProjectFactory will override this to add rules logic.
* moveRecords(selectIds: [{id: , path: [recordId, parentRecordId, parent2RecordId....]}], destiation: {id: ,path: []}, destinationPosition)
*/
/**
* RFC: https://docs.google.com/document/d/1DVM_i_Go5iX5-EShV5FikfI29k8YEC9cAjzeAY49blc/edit#
* get the address of the RecordNode. In case parentAddr is not passed (root levels) then self address is returned
*/
getSelfRecordAddress(this: RecordFactory<T>, parentAddr?: string): string;
/**
* get the address of a reacord node property. incase when index is passed, return address to indexed property
* 1. project:1|scene:1|element:2!opacity
* 2. project:1|scene:1|element:2!wh>1
*/
getPropertyAddress(this: RecordFactory<T>, recordAddress: string, property: RTP[T], index?: number): string;
/**
* Find the record at a given address. Searches only in child record nodes.
* Assumes that 1st entry in addr is self
*
* examples for ref
* 1. project:1|scene:1
* 2. project:1|scene:1|element:2
* 3. project:1|scene:1|element:2!opacity
* 4. project:1|scene:1|element:2!wh>1
*
*/
getRecordAtAddress(this: RecordFactory<T>, addr: string): RecordNode<RT> | null;
/**
* Find the record at a given address. Searches only in child record nodes.
* Returns both self and parent
* Assumes that 1st entry in addr is self
*
* examples for ref
* 1. project:1|scene:1
* 2. project:1|scene:1|element:2
* 3. project:1|scene:1|element:2!opacity
* 4. project:1|scene:1|element:2!wh>1
*
*/
getRecordAndParentAtAddress(this: RecordFactory<T>, addr: string): {
p?: RecordNode<RT>;
c: RecordNode<RT>;
} | null;
/**
* Update the value of a property at an address
* examples for ref
* 1. project:1|scene:1|element:2!opacity
* 2. project:1|scene:1|element:2!wh>1
*
* First find the RecordNode using getRecordAtAddress method
* if the property address contains an index, check if the property is an array type
* 1. if yes, udpate the value at index
* 2. else return false
* else update property value directly in the RecordNode
*
*/
updatePropertyAtAddress(this: RecordFactory<T>, addr: string, value: unknown): boolean;
/**
* This allows re-parenting records between different depths inside a tree using their addresses.
* This needs to be called for the record where both sourceParentRecord and destParentRecord are child records of a common super parent record
* For almost all operations, we will use a top down approach here for re-parenting, i.e re-parenting process needs to start from the top most parent
* in this case, it is almost always project
* Example1: moving elements from within a group to 1 level above.
*
* Scene1
* Element1 <-------|
* Element2 |
* Element3 (group) |
* Element31 -----|
* Element32
*
* In above scenario we want to move Element31 below Element1
* sourceRecordAddr = [{parentAddr: Scene:1|Element:3, recordAddr: Scene:1|Element:3|Element:31}]
* destParentAddr = Scene:1
* destPosition = 0 (we insert at x+1 index)
*
* Example2: moving elements from one scene to another
*
* Scene1
* Element1
* Element2
* Element3 (group)
* Element31
* Element32 <-|
* Scene2 |
* Element4 ------|
* Element5
*
* In above scenario we want to move Element4 from Scene2 to Element3(Group)
* sourceRecordAddr = [{parentAddr: Scene:2, recordAddr: Scene:2|Element:4}]
* destParentAddr = Scene:1|Element:3
* destPosition = 1 (we insert at x+1 index)
*
*/
reParentRecordsWithAddress(destParentAddr: string, sourceRecordAddr: {
parentAddr: string;
recordAddr: string;
}[], destPosition?: number): [RecordNode<RT>[], RecordNode<RT>[]];
/**
* This returns all the nodes in the path from a parent to a leaf
*/
getBreadcrumbs(this: RecordFactory<T>, id: number, type: RT): RecordNode<RT>[];
/**
* Get address for a deep record
*/
getDeepRecordAddress(this: RecordFactory<T>, { id, type, parentAddr }: {
id: number;
type: RT;
parentAddr?: string;
}): string;
/**
* Get record + parent address for a given record
*/
getDeepChildAndParentAddress(this: RecordFactory<T>, id: number, type: RT): string[] | undefined;
}
export declare class RecordUtils {
static getDefaultValues: <T extends RT>(type: T) => Record<string, unknown>;
}