import { RecordNode, RecordMap, RecordMapGeneric, idAndRecord, idOrAddress, rAndP, ClipboardData, RecordEntry } 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 * *! JSON Structure: * * { //RecordNode -> A recordNode doesn't know what it's id is. The id is defined one level above * name: "string" //optional record name * type: "project" * order: "integer" //the order of the record in the list * props: {"key1": "value1", "key2", "value2"} //All properties in a record * records: { * "scene": { //Scene RecordMap * "7878789089": { //record id 1 * //Sub RecordNode 1 * }, * "7878789777": { //record id 2 * //Sub RecordNode 2 * } * } * "variable": {} //Varaible RecordMap * } * } * *! PROPS * json / getName -> return full json / return name * getProps/getAllPossibleProps/getAllPropValues -> what this json has/what this json can have (based on type) * get/set/reset/delete/getValueOrDefault/getDefault -> prop's values * changePropertyName -> used during migrations * *! RECORDS * getRecordTypes -> list of subrecord types ["scene", "variable"] * getRecord / getRecordWithPath / getIdAndRecord / getDeepRecord -> get subrecord of any type (level 1), faster if type is passed / deepRecord using idOrAddress * getDeepIdAndRecord -> works with idOrAddress * getRecordMap / getRecordEntries / getRecords / getRecordIds -> recordMap of one type (all types when type isn't passed) - lvl 1 records * getDeepRecordMap / getDeepRecordEntries / RecordUtils.getDeepRecordAndParentArray * getDeepRecordByName -> Gets record(s) by name. Name can be duplicated, so returning an array of idAndRecord * *! SORTED RECORDS (FOR UI) * Sorting only makes sense in a single type (eg: you wont sort variables & scenes) * getSortedRecordEntries / getSortedRecordIds / getSortedRecords * getSortedDFSRecordEntriesInternal * *! ADDRESS RELATED * getAddress / getDeepAddress -> Get address of a subnode (with optional property suffix) * getPropertyAtAddress / updatePropertyAtAddress * $ private getRecordAndParentWithId / getRecordAndParentWithAddress * getRecordAndParent -> Find record and its parent with either id or address * $ private getBreadCrumbsWithAddress / getBreadCrumbsWithId * getBreadCrumbs -> Returns an array of all sub-records leading to the given idOrAddress * *! RECORD RELATED * getLinkedSubRecords -> Get records that are linked to another record via any prop * cycleAllSubRecordIds / changeRecordIdInProperties -> Updates ids and their references in the tree * $ private getNewOrders / initializeRecordMap * addRecord / addBlankRecord * duplicateRecord / deleteRecord / changeRecordName * duplicateDeepRecord / deleteDeepRecord / changeDeepRecordName * duplicateDeepRecords / deleteDeepRecords * *! MOVE/COPY/CLIPBOARD * reorderRecords / copyDeepRecords / moveDeepRecords * copyToClipboard / pasteFromClipboard */ export declare class RecordFactory { protected readonly _json: RecordNode; protected readonly _type: RT; constructor(json: RecordNode); json(): RecordNode; getName(): string | undefined; /** A list of recordNode.props' keys in the json */ getProps(): string[]; /** * A list of props this RecordType is supposed to have. * This is different from getProps - * getProps tells you what the json has * getAllPossibleProps tell you what the json can have */ getAllPossibleProps(): string[]; /** In case a property isn't defined in the json, this method returns "undefined" */ get(property: RTP[T]): unknown; set(property: RTP[T], value: unknown): RecordFactory; reset(property: RTP[T]): RecordFactory; delete(property: string): RecordFactory; /** * 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(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(property: RTP[T]): unknown; /** Used mostly for migrations. And so this fn doesn't do type check on property name. */ changePropertyName(propertyName: string, newPropertyName: string): RecordFactory; /** A list of Records this json has (eg: project might have scene, variable, menu) */ getRecordTypes(): RT[]; /** * Returns all sub records (one-lvl deep) of a either a single type or all types (if type is not passed) */ getRecordMap(type?: N): RecordMap; getRecordEntries(type?: N): RecordEntry[]; getRecords(type?: N): RecordNode[]; getRecordIds(type?: N): number[]; private getSortedDFSRecordEntriesInternal; /** * A Pure DFS of the tree * An array that goes depth first, then breadth */ getSortedDFSRecordEntries(type?: N): RecordEntry[]; private getDFSRecordEntriesInternal; getDeepRecordEntries(type?: N): RecordEntry[]; /** * A flattened record map of all ids and records in a tree, of all types * If there are two records with the same id, this function will fail * Uses DFS * The optional argument recordMap exists only for its internal functioning */ getDeepRecordMap(type?: N): RecordMapGeneric; getDeepRecordsByName(name: string, type?: RT): idAndRecord[] | undefined; getIdAndRecord(id: number, type?: RT): idAndRecord | undefined; /** Sending type also makes getRecord faster */ getRecord(id: number, type?: RT): RecordNode | undefined; /** Accepts an array of ids to get deeper and get a record */ getRecordWithPath(path: Array): RecordNode | undefined; getDeepIdAndRecord(idOrAddress: idOrAddress, type?: RT): idAndRecord | undefined; /** * This function works faster if type is passed */ private getDeepRecordWithIdInternal; /** if idOrAddress is "", it refers to the current node */ getDeepRecord(idOrAddress: idOrAddress, type?: RT): RecordNode | undefined; /** ORDERED entries of id, records. Returns ids as strings. */ getSortedRecordEntries(type: N): RecordEntry[]; /** ORDERED ids */ getSortedRecordIds(type: N): number[]; /** ORDERED records */ getSortedRecords(type: N): RecordNode[]; /** * Generate an address like scene:1|element:2 for any child RecordNode * RFC: https://docs.google.com/document/d/1DVM_i_Go5iX5-EShV5FikfI29k8YEC9cAjzeAY49blc/edit# * Eg 1. scene:1|element:2!opacity * Eg 2. scene:1|element:2!wh>1 * If property is given, it adds property suffix * If selfAddr is given (from root), it prefixes that to the address, otherwise returns a partial child address * * property needs to a prop of the be same RT.type as "type" argument */ getAddress({ id, type, selfAddr, property, index }: { id: number; type?: RT; selfAddr?: string; property?: RTP[T]; index?: number; }): string; /** * property can be a prop of any RT.type * (That's why we use RTP[N] and not RTP[T] (T is the type used in this class, N is any type within RT)) */ getDeepAddress({ id, selfAddr, property, index }: { id: number; selfAddr?: string; property?: RTP[N]; index?: number; }): string; /** Find a record and its parent given any record id */ private getRecordAndParentWithId; /** * Find the record and its parent with a given address. Searches only in child record nodes. * Examples * 1. scene:1 * 2. scene:1|element:2 * 3. scene:1|element:2!opacity * 4. scene:1|element:2!wh>1 */ private getRecordAndParentWithAddress; getRecordAndParent(idOrAddress: idOrAddress): rAndP | undefined; private getBreadCrumbsWithId; private getBreadCrumbsWithAddress; getBreadCrumbs(idOrAddress: idOrAddress): idAndRecord[] | undefined; /** * Given an address like scene:1|element:2!wh>1, get its value * If it doesn't find a value, return undefined */ getPropertyAtAddress(addr: string): unknown; /** * Update the value of a property at an address * 1. scene:1|element:2!opacity * 2. scene:1|element:2!wh>1 */ updatePropertyAtAddress(addr: string, value: unknown): boolean; /** * We assume that the element names are unique. If there are 2 elements (at different depths), we update only the 1st one. * Apply any property level overrides * propertiesReplacementMap: * { * "scene intro|character john!brain_slug": "BRAIN_SLUG" * } * * externalValueMap: * { * "BRAIN_SLUG": "gmetri" * } */ applyPropertiesReplacementMap(propertiesReplacementMap: Record, externalValueMap: Record): void; getLinkedSubRecords(matchingId: number, type?: N): idAndRecord[]; /** * When we cycle record ids, all their references in the props of other records also need * to be updated. This function takes a replacementMap, and using that ensure that for "this" * record all props are updated */ changeRecordIdInProperties(replacementMap: { [oldId: number]: number; }): boolean; getAllPropValues(): Array; /** * Change all sub-record ids (of sub-records) in this RecordNode * Used for copy pasting record nodes - to ensure none of the subrecords end up having a duplicate id */ cycleAllSubRecordIds(): void; replaceAllSubRecordIds(replacementMap: { [oldId: number]: number; }): void; replaceAllSubRecordIdsInProperties(replacementMap: { [oldId: number]: number; }): void; private initializeRecordMap; /** * Definition of "position" - the [gap] to insert into: [0] 0, [1] 1, [2] 2, [3] 3, [4] 4 [5] * If position is undefined, it gets inserted at the end * All ids in the tree need to be unique. * So we first get all existing sub-ids. * And all sub-ids in this new record. * And make sure none overlap. */ addRecord({ record, position, id, dontCycleSubRecordIds, dontPosition, parentIdOrAddress }: { record: RecordNode; position?: number; id?: number; dontCycleSubRecordIds?: boolean; dontPosition?: boolean; parentIdOrAddress?: idOrAddress; }): idAndRecord | undefined; addBlankRecord({ type, position, id, parentIdOrAddress }: { type: N; position?: number; id?: number; parentIdOrAddress?: idOrAddress; }): idAndRecord | undefined; duplicateRecord(type: N, id: number): idAndRecord | undefined; duplicateDeepRecord(idOrAddress: idOrAddress): rAndP | undefined; duplicateDeepRecords(idOrAddresses: idOrAddress[]): Array; deleteRecord(id: number, type?: N): idAndRecord | undefined; deleteDeepRecord(idOrAddress: idOrAddress): idAndRecord | undefined; deleteDeepRecords(idOrAddresses: idOrAddress[]): Array | undefined>; changeRecordName(id: number, newName?: string, type?: N): idAndRecord | undefined; changeDeepRecordName(idOrAddress: idOrAddress, newName?: string): idAndRecord | undefined; /** * Used in drag-drop operations * When you remove the ids, you also impact the position. So first come up with the new order numbers * Allows moving multiple items at the same time * Changes order in place * * Input: [ 1, 2, 3, 4, 5, 6 ] * Positions: [0, 1, 2, 3, 4, 5, 6] * Operation: nodeIds: [2,4], position: 5 * Output: [1, 3, 5, 2, 4, 6] * * Initial Ord: 1, 2, 3, 4, 5, 6 ] * Final Ord: 1, 5.3, 3, 5.6, 5, 6 ] */ reorderRecords(type: RT, ids: number[], position: number): void; /** * Keeps ids intact * To check destPosition logic check reorderRecords */ moveDeepRecords(source: idOrAddress[], dest: idOrAddress, destPosition?: number): boolean; /** Changes ids */ copyDeepRecords(source: idOrAddress[], dest: idOrAddress, destPosition?: number): boolean; /** * Function to create what gets copied when you type ctrl+c */ copyToClipboard(selectedIdOrAddrs: idOrAddress[]): ClipboardData; pasteFromClipboard(parentIdOrAddr: idOrAddress, clipboardData: ClipboardData, positionInPlace?: number): void; }