packageDocumentation module import { assert , Id64String } from "@itwin/core-bentley" ; import { ClassInfo , ClassInfoJSON , CompressedClassInfoJSON , PropertyInfo , PropertyInfoJSON , RelatedClassInfo , RelationshipPath , RelationshipPathJSON , StrippedRelationshipPath , } from "../EC" ; import { PresentationError , PresentationStatus } from "../Error" ; import { RelationshipMeaning } from "../rules/content/modifiers/RelatedPropertiesSpecification" ; import { CategoryDescription } from "./Category" ; import { EditorDescription } from "./Editor" ; import { Property , PropertyJSON } from "./Property" ; import { RendererDescription } from "./Renderer" ; import { TypeDescription } from "./TypeDescription" ; public export interface BaseFieldJSON { category : string ; name : string ; label : string ; type : TypeDescription ; isReadonly : boolean ; priority : number ; renderer ? : RendererDescription ; editor ? : EditorDescription ; } public export interface PropertiesFieldJSON < TClassInfoJSON = ClassInfoJSON > extends BaseFieldJSON { properties : PropertyJSON < TClassInfoJSON > [ ] ; } public export interface NestedContentFieldJSON < TClassInfoJSON = ClassInfoJSON > extends BaseFieldJSON { contentClassInfo : TClassInfoJSON ; pathToPrimaryClass : RelationshipPathJSON < TClassInfoJSON > ; alpha relationshipMeaning ? : RelationshipMeaning ; alpha actualPrimaryClassIds ? : Id64String [ ] ; autoExpand ? : boolean ; nestedFields : FieldJSON < TClassInfoJSON > [ ] ; } public export type FieldJSON < TClassInfoJSON = ClassInfoJSON > = BaseFieldJSON | PropertiesFieldJSON < TClassInfoJSON > | NestedContentFieldJSON < TClassInfoJSON > ; /** Is supplied field a properties field. */ const isPropertiesField = ( field ) : field is PropertiesFieldJSON < any > | PropertiesField => { return ! ! ( field as any ) . properties ; } ; /** Is supplied field a nested content field. */ const isNestedContentField = ( field ) : field is NestedContentFieldJSON < any > | NestedContentField => { return ! ! ( field as any ) . nestedFields ; } ; public export class Field { /** Category information */ public category : CategoryDescription ; /** Unique name */ public name : string ; /** Display label */ public label : string ; /** Description of this field's values data type */ public type : TypeDescription ; /** Are values in this field read-only */ public isReadonly : boolean ; /** Priority of the field. Higher priority fields should appear first in the UI */ public priority : number ; /** Property renderer used to render values of this field */ public renderer ? : RendererDescription ; /** Property editor used to edit values of this field */ public editor ? : EditorDescription ; /** Parent field */ private _parent ? : NestedContentField ; param category param name param label param type param isReadonly param priority param editor param renderer public constructor ( category , name , label , type , isReadonly , priority , editor ? , renderer ? ) { this . category = category ; this . name = name ; this . label = label ; this . type = type ; this . isReadonly = isReadonly ; this . priority = priority ; this . editor = editor ; this . renderer = renderer ; } /**
     * Is this a [[PropertiesField]]
     */ public isPropertiesField ( ) { return isPropertiesField ( this ) ; } /**
     * Is this a [[NestedContentField]]
     */ public isNestedContentField ( ) { return isNestedContentField ( this ) ; } /**
     * Get parent
     */ public get parent ( ) : NestedContentField | undefined { return this . _parent ; } public clone ( ) { const clone = new Field ( this . category , this . name , this . label , this . type , this . isReadonly , this . priority , this . editor , this . renderer ) ; clone . rebuildParentship ( this . parent ) ; return clone ; } /** Serialize this object to JSON */ public toJSON ( ) { return { category : this . category . name , name : this . name , label : this . label , type : this . type , isReadonly : this . isReadonly , priority : this . priority , renderer : this . renderer , editor : this . editor } ; } /** Serialize this object to compressed JSON */ public toCompressedJSON ( classesMap ) { if ( this . isPropertiesField ( ) ) return { ... this . toJSON ( ) , properties : this . properties . map ( ( property ) => Property . toCompressedJSON ( property , classesMap ) ) } ; if ( this . isNestedContentField ( ) ) { const { id , ... leftOverInfo } = this . contentClassInfo ; classesMap [ id ] = leftOverInfo ; return { ... this . toJSON ( ) , contentClassInfo : id , pathToPrimaryClass : this . pathToPrimaryClass . map ( ( classInfo ) => RelatedClassInfo . toCompressedJSON ( classInfo , classesMap ) ) , nestedFields : this . nestedFields . map ( ( field ) => field . toCompressedJSON ( classesMap ) ) } ; } return this . toJSON ( ) ; } /** Deserialize [[Field]] from JSON */ public static fromJSON ( json , categories ) { if ( ! json ) return undefined ; if ( isPropertiesField ( json ) ) return PropertiesField . fromJSON ( json , categories ) ; if ( isNestedContentField ( json ) ) return NestedContentField . fromJSON ( json , categories ) ; const field = Object . create ( Field . prototype ) ; return Object . assign ( field , json , { category : Field . getCategoryFromFieldJson ( json , categories ) } ) ; } public public static fromCompressedJSON ( json , classesMap , categories ) { if ( ! json ) return undefined ; if ( isPropertiesField ( json ) ) return PropertiesField . fromCompressedJSON ( json , classesMap , categories ) ; if ( isNestedContentField ( json ) ) return NestedContentField . fromCompressedJSON ( json , classesMap , categories ) ; const field = Object . create ( Field . prototype ) ; return Object . assign ( field , json , { category : Field . getCategoryFromFieldJson ( json , categories ) } ) ; } protected static getCategoryFromFieldJson ( fieldJson , categories ) { const category = categories . find ( ( c ) => c . name === fieldJson . category ) ; if ( ! category ) throw new PresentationError ( PresentationStatus . InvalidArgument , `Invalid content field category` ) ; return category ; } internal public resetParentship ( ) { this . _parent = undefined ; } internal public rebuildParentship ( parentField ? ) { this . _parent = parentField ; } public public getFieldDescriptor ( ) { return { type : FieldDescriptorType . Name , fieldName : this . name } as NamedFieldDescriptor ; } } public export class PropertiesField extends Field { /** A list of properties this field is created from */ public properties : Property [ ] ; param category param name param label param type param isReadonly param priority param properties param editor param renderer public constructor ( category , name , label , description , isReadonly , priority , properties , editor ? , renderer ? ) { super ( category , name , label , description , isReadonly , priority , editor , renderer ) ; this . properties = properties ; } public override clone ( ) { const clone = new PropertiesField ( this . category , this . name , this . label , this . type , this . isReadonly , this . priority , this . properties , this . editor , this . renderer ) ; clone . rebuildParentship ( this . parent ) ; return clone ; } /** Serialize this object to JSON */ public override toJSON ( ) { return { ... super . toJSON ( ) , properties : this . properties . map ( ( p ) => Property . toJSON ( p ) ) } ; } /** Deserialize [[PropertiesField]] from JSON */ public static override fromJSON ( json , categories ) { if ( ! json ) return undefined ; const field = Object . create ( PropertiesField . prototype ) ; return Object . assign ( field , json , { category : this . getCategoryFromFieldJson ( json , categories ) , properties : json . properties . map ( Property . fromJSON ) } ) ; } public public static override fromCompressedJSON ( json , classesMap , categories ) { const field = Object . create ( PropertiesField . prototype ) ; return Object . assign ( field , json , { category : this . getCategoryFromFieldJson ( json , categories ) , properties : json . properties . map ( ( propertyJson ) => fromCompressedPropertyJSON ( propertyJson , classesMap ) ) } ) ; } public public override getFieldDescriptor ( ) { const pathFromPropertyToSelectClass = new Array < RelatedClassInfo > ( ) ; let currAncestor = this . parent ; while ( currAncestor ) { pathFromPropertyToSelectClass . push ( ... currAncestor . pathToPrimaryClass ) ; currAncestor = currAncestor . parent ; } return { type : FieldDescriptorType . Properties , pathFromSelectToPropertyClass : RelationshipPath . strip ( RelationshipPath . reverse ( pathFromPropertyToSelectClass ) ) , properties : this . properties . map ( ( p ) => ( { class : p . property . classInfo . name , name : p . property . name } ) ) } as PropertiesFieldDescriptor ; } } public export class NestedContentField extends Field { /** Information about an ECClass whose properties are nested inside this field */ public contentClassInfo : ClassInfo ; /** Relationship path to [Primary class]($docs/presentation/Content/Terminology#primary-class) */ public pathToPrimaryClass : RelationshipPath ; alpha public relationshipMeaning : RelationshipMeaning ; alpha public actualPrimaryClassIds : Id64String [ ] ; /** Contained nested fields */ public nestedFields : Field [ ] ; /** Flag specifying whether field should be expanded */ public autoExpand ? : boolean ; param category param name param label param type param isReadonly param priority param contentClassInfo param pathToPrimaryClass param nestedFields param editor param autoExpand param relationshipMeaning param renderer public constructor ( category , name , label , description , isReadonly , priority , contentClassInfo , pathToPrimaryClass , nestedFields , editor ? , autoExpand ? , renderer ? ) { super ( category , name , label , description , isReadonly , priority , editor , renderer ) ; this . contentClassInfo = contentClassInfo ; this . pathToPrimaryClass = pathToPrimaryClass ; this . relationshipMeaning = RelationshipMeaning . RelatedInstance ; this . nestedFields = nestedFields ; this . autoExpand = autoExpand ; this . actualPrimaryClassIds = [ ] ; } public override clone ( ) { const clone = new NestedContentField ( this . category , this . name , this . label , this . type , this . isReadonly , this . priority , this . contentClassInfo , this . pathToPrimaryClass , this . nestedFields . map ( ( n ) => n . clone ( ) ) , this . editor , this . autoExpand , this . renderer ) ; clone . actualPrimaryClassIds = this . actualPrimaryClassIds ; clone . relationshipMeaning = this . relationshipMeaning ; clone . rebuildParentship ( this . parent ) ; return clone ; } param name param recurse public getFieldByName ( name , recurse ? ) { return getFieldByName ( this . nestedFields , name , recurse ) ; } /** Serialize this object to JSON */ public override toJSON ( ) { return { ... super . toJSON ( ) , contentClassInfo : this . contentClassInfo , pathToPrimaryClass : this . pathToPrimaryClass , relationshipMeaning : this . relationshipMeaning , actualPrimaryClassIds : this . actualPrimaryClassIds , nestedFields : this . nestedFields . map ( ( field ) => field . toJSON ( ) ) , autoExpand : this . autoExpand } ; } /** Deserialize [[NestedContentField]] from JSON */ public static override fromJSON ( json , categories ) { if ( ! json ) return undefined ; const field = Object . create ( NestedContentField . prototype ) ; return Object . assign ( field , json , this . fromCommonJSON ( json , categories ) , { nestedFields : json . nestedFields . map ( ( nestedFieldJson ) => Field . fromJSON ( nestedFieldJson , categories ) ) . filter ( ( nestedField ) : nestedField is Field => ! ! nestedField ) , contentClassInfo : ClassInfo . fromJSON ( json . contentClassInfo ) , pathToPrimaryClass : json . pathToPrimaryClass . map ( RelatedClassInfo . fromJSON ) } ) ; } public public static override fromCompressedJSON ( json , classesMap , categories ) { assert ( classesMap . hasOwnProperty ( json . contentClassInfo ) ) ; const field = Object . create ( NestedContentField . prototype ) ; return Object . assign ( field , json , this . fromCommonJSON ( json , categories ) , { category : this . getCategoryFromFieldJson ( json , categories ) , nestedFields : json . nestedFields . map ( ( nestedFieldJson ) => Field . fromCompressedJSON ( nestedFieldJson , classesMap , categories ) ) . filter ( ( nestedField ) : nestedField is Field => ! ! nestedField ) , contentClassInfo : ClassInfo . fromJSON ( { id : json . contentClassInfo , ... classesMap [ json . contentClassInfo ] } ) , pathToPrimaryClass : json . pathToPrimaryClass . map ( ( stepJson ) => RelatedClassInfo . fromCompressedJSON ( stepJson , classesMap ) ) } ) ; } private static fromCommonJSON ( json , categories ) { return { category : this . getCategoryFromFieldJson ( json , categories ) , relationshipMeaning : json . relationshipMeaning ?? RelationshipMeaning . RelatedInstance , actualPrimaryClassIds : json . actualPrimaryClassIds ?? [ ] , autoExpand : json . autoExpand } ; } internal public override resetParentship ( ) { super . resetParentship ( ) ; for ( const nestedField of this . nestedFields ) nestedField . resetParentship ( ) ; } internal public override rebuildParentship ( parentField ? ) { super . rebuildParentship ( parentField ) ; for ( const nestedField of this . nestedFields ) nestedField . rebuildParentship ( this ) ; } } internal export const getFieldByName = ( fields , name , recurse ? ) : Field | undefined => { if ( name ) { for ( const field of fields ) { if ( field . name === name ) return field ; if ( recurse && field . isNestedContentField ( ) ) { const nested = getFieldByName ( field . nestedFields , name , recurse ) ; if ( nested ) return nested ; } } } return undefined ; } ; public export enum FieldDescriptorType { Name = "name" , Properties = "properties" } public export interface FieldDescriptorBase { type : FieldDescriptorType ; } public export type FieldDescriptor = NamedFieldDescriptor | PropertiesFieldDescriptor ; public export namespace FieldDescriptor { /** Is this a named field descriptor */ export function isNamed ( d ) { return d . type === FieldDescriptorType . Name ; } /** Is this a properties field descriptor */ export function isProperties ( d ) { return d . type === FieldDescriptorType . Properties ; } } public export interface NamedFieldDescriptor extends FieldDescriptorBase { type : FieldDescriptorType . Name ; fieldName : string ; } public export interface PropertiesFieldDescriptor extends FieldDescriptorBase { type : FieldDescriptorType . Properties ; pathFromSelectToPropertyClass : StrippedRelationshipPath ; /**
     * A list of properties that describe the field. At least one property in the list must
     * match at least one property in the field for the descriptor to be considered matching.
     */ properties : Array < { /** Full class name */ class : string ; /** Property name */ name : string ; } > ; } function fromCompressedPropertyJSON ( compressedPropertyJSON , classesMap ) { return { property : fromCompressedPropertyInfoJSON ( compressedPropertyJSON . property , classesMap ) } ; } function fromCompressedPropertyInfoJSON ( compressedPropertyJSON , classesMap ) { assert ( classesMap . hasOwnProperty ( compressedPropertyJSON . classInfo ) ) ; return { ... compressedPropertyJSON , classInfo : { id : compressedPropertyJSON . classInfo , ... classesMap [ compressedPropertyJSON . classInfo ] } } ; }