packageDocumentation module import { PropertyRecord , PropertyValue , PropertyValueFormat } from "@itwin/appui-abstract" ; import { IDisposable } from "@itwin/core-bentley" ; import { HighlightInfo } from "../../common/HighlightingComponentProps" ; import { CategoryRecordsDict } from "../internal/flat-items/MutableGridCategory" ; import { IPropertyDataProvider , PropertyCategory , PropertyData , PropertyDataChangeEvent } from "../PropertyDataProvider" ; import { FilteredType , IPropertyDataFilterer , PropertyDataFilterResult } from "./filterers/PropertyDataFiltererBase" ; interface FilteredRecords { filteredRecords : PropertyRecord [ ] ; shouldExpandNodeParents : boolean ; matchesCount : number ; activeMatch ? : HighlightInfo ; filteredResultMatches : { id : string ; matchesCount : number ; } [ ] ; filteredTypes : FilteredType [ ] ; } public export interface FilteredPropertyData extends PropertyData { /**
    * Shows how many matches were found when filtering data.
    * `undefined` when filterer is not active
    */ matchesCount ? : number ; /**
    * Function used for getting [[HighlightInfo]] by index from all the filtered matches.
    * `undefined` when filterer is not active
    */ getMatchByIndex ? : ( index : number ) => HighlightInfo | undefined ; /** Types of objects that were used for filtering. */ filteredTypes ? : FilteredType [ ] ; } public export class FilteringPropertyDataProvider implements IPropertyDataProvider , IDisposable { public onDataChanged = new PropertyDataChangeEvent ( ) ; private _filteredPropertyData : Promise < FilteredPropertyData > | undefined = undefined ; private _disposeFilterChangedListener : Function ; private _disposeDataChangedListener : Function ; public constructor ( private _dataProvider , private _filterer ) { this . _disposeFilterChangedListener = this . _filterer . onFilterChanged . addListener ( ( ) => { this . _filteredPropertyData = undefined ; this . onDataChanged . raiseEvent ( ) ; } ) ; this . _disposeDataChangedListener = this . _dataProvider . onDataChanged . addListener ( ( ) => { this . _filteredPropertyData = undefined ; this . onDataChanged . raiseEvent ( ) ; } ) ; } public dispose ( ) { this . _disposeDataChangedListener ( ) ; this . _disposeFilterChangedListener ( ) ; } public async getData ( ) { if ( this . _filteredPropertyData ) { return this . _filteredPropertyData ; } this . _filteredPropertyData = this . _dataProvider . getData ( ) . then ( async ( propertyData ) => { return ( ( this . _filterer . isActive ) ? this . filterPropertyData ( propertyData ) : propertyData ) ; } ) ; return this . _filteredPropertyData ; } private async filterPropertyData ( propertyData ) { const { categories , records , label , description } = { ... propertyData } ; const { filteredCategories , filteredRecords , matchesCount , filteredResultMatches , filteredTypes } = await matchHierarchy ( this . _filterer , categories , records , undefined ) ; const uniqueTypes = [ ... new Set ( filteredTypes ) ] ; const getMatchByIndex = ( index ) => { let activeMatch ; if ( index <= 0 ) return undefined ; let i = 1 ; for ( const record of filteredResultMatches ) { const fullMatchesCount = record . matchesCount ; if ( index < i + fullMatchesCount ) { activeMatch = { highlightedItemIdentifier : record . id , highlightIndex : index - i } ; break ; } i += fullMatchesCount ; } return activeMatch ; } ; return { label , description , categories : filteredCategories , records : filteredRecords , reusePropertyDataState : false , matchesCount , getMatchByIndex , filteredTypes : uniqueTypes } ; } } async function matchHierarchy ( filterer , categories , records , newRecords ? , parentMatchInfo ? ) { const newCategories = [ ] ; newRecords = newRecords ?? { } ; let matchesCount = 0 ; let allFilteredResultMatches = [ ] ; let allFilteredTypes = [ ] ; for ( const category of categories ) { const matchInfo = await filterer . categoryMatchesFilter ( category , [ ] ) ; if ( matchInfo . matchesFilter ) { allFilteredResultMatches . push ( { id : category . name , matchesCount : matchInfo . matchesCount ?? 0 } ) ; allFilteredTypes = allFilteredTypes . concat ( matchInfo . filteredTypes ?? [ ] ) ; matchesCount += matchInfo . matchesCount ?? 0 ; } const childRecords = records [ category . name ] ?? [ ] ; const { filteredRecords , matchesCount : count , filteredResultMatches , filteredTypes } = await matchRecordHierarchy ( filterer , childRecords , [ ] ) ; allFilteredResultMatches = allFilteredResultMatches . concat ( filteredResultMatches ) ; allFilteredTypes = allFilteredTypes . concat ( filteredTypes ) ; matchesCount += count ; const parentInfo = parentMatchInfo ?. matchesFilter ? parentMatchInfo : matchInfo ; const childCategories = category . childCategories ?? [ ] ; const { filteredCategories , matchesCount : childCount , filteredResultMatches : childFilteredResultMatches , filteredTypes : childFilteredTypes } = await matchHierarchy ( filterer , childCategories , records , newRecords , parentInfo ) ; allFilteredResultMatches = allFilteredResultMatches . concat ( childFilteredResultMatches ) ; allFilteredTypes = allFilteredTypes . concat ( childFilteredTypes ) ; matchesCount += childCount ; let expand = ! ( count === 0 && childCount === 0 ) ; if ( matchInfo . matchesFilter || ( parentMatchInfo ?. matchesFilter && parentMatchInfo . shouldForceIncludeDescendants ) ) { newRecords [ category . name ] = records [ category . name ] ; if ( matchInfo . matchesFilter ) { expand = true ; } const newCategory = copyPropertyCategory ( category , filteredCategories , expand ) ; newCategories . push ( newCategory ) ; } if ( ( filteredRecords . length !== 0 || filteredCategories . length !== 0 ) && ! matchInfo . matchesFilter && ! ( parentMatchInfo ?. shouldExpandNodeParents && parentMatchInfo . matchesFilter ) ) { const newCategory = copyPropertyCategory ( category , filteredCategories , expand ) ; newRecords [ newCategory . name ] = filteredRecords ; newCategories . push ( newCategory ) ; } } return { filteredCategories : newCategories , filteredRecords : newRecords , matchesCount , filteredResultMatches : allFilteredResultMatches , filteredTypes : allFilteredTypes } ; } async function matchRecordHierarchy ( filterer , records , parents , forceIncludeItem = false ) { const newMatchedRecords = [ ] ; let expandParent = false ; let matchesCount = 0 ; let filteredResultMatches = [ ] ; let filteredTypes = [ ] ; for ( const record of records ) { const matchInfo = await filterer . recordMatchesFilter ( record , parents ) ; const shouldForceIncludeDescendants = forceIncludeItem || matchInfo . shouldForceIncludeDescendants ; const { filteredRecords : filteredChildren , shouldExpandNodeParents , matchesCount : childrenMatchesCount , filteredResultMatches : childrenResultMatches , filteredTypes : childrenFilteredTypes } = await matchRecordHierarchy ( filterer , record . getChildrenRecords ( ) , [ ... parents , record ] , shouldForceIncludeDescendants ) ; filteredResultMatches = filteredResultMatches . concat ( childrenResultMatches ) ; filteredTypes = [ ... filteredTypes , ... childrenFilteredTypes ] ; matchesCount += childrenMatchesCount ; expandParent = expandParent || shouldExpandNodeParents ; let newRecord ; if ( matchInfo . matchesFilter ) { filteredTypes = [ ... filteredTypes , ... ( matchInfo . filteredTypes ?? [ ] ) ] ; const recordMatchesCount = matchInfo . matchesCount ?? 0 ; matchesCount += recordMatchesCount ; expandParent = expandParent || ! ! matchInfo . shouldExpandNodeParents ; newRecord = copyPropertyRecord ( record , filteredChildren , shouldExpandNodeParents ) ; filteredResultMatches . push ( { id : newRecord . property . name , matchesCount : recordMatchesCount } ) ; } else if ( filteredChildren . length !== 0 || forceIncludeItem ) { newRecord = copyPropertyRecord ( record , filteredChildren , shouldExpandNodeParents ) ; } if ( newRecord ) newMatchedRecords . push ( newRecord ) ; } return { filteredRecords : newMatchedRecords , shouldExpandNodeParents : expandParent , matchesCount , filteredResultMatches , filteredTypes } ; } function copyPropertyCategory ( propertyCategory , newChildren , expand ) { return { ... propertyCategory , childCategories : newChildren , expand } ; } function copyPropertyRecord ( record , newChildren , autoExpand ) { const r = record . copyWithNewValue ( createNewPropertyValue ( record . value , newChildren ) ) ; r . autoExpand = autoExpand ; return r ; } function createNewPropertyValue ( value , newChildren ) { switch ( value . valueFormat ) { case PropertyValueFormat . Primitive : return { ... value } ; case PropertyValueFormat . Array : return { valueFormat : PropertyValueFormat . Array , itemsTypeName : value . itemsTypeName , items : newChildren } ; case PropertyValueFormat . Struct : const members = { } ; newChildren . forEach ( ( child ) => members [ child . property . name ] = child ) ; return { valueFormat : PropertyValueFormat . Struct , members } ; } }