packageDocumentation module import { Primitives , PropertyRecord , PropertyValueFormat , UiError } from "@itwin/appui-abstract" ; import { SortDirection } from "@itwin/core-react" ; import { TypeConverterManager } from "../converters/TypeConverterManager" ; import { UiComponents } from "../UiComponents" ; import { CompositeFilterDescriptorCollection , DistinctValueCollection , TableDistinctValue } from "./columnfiltering/ColumnFiltering" ; import { ColumnDescription , MutableTableDataProvider , RowItem , TableDataChangeEvent } from "./TableDataProvider" ; public export class SimpleTableDataProvider implements MutableTableDataProvider { private _items : RowItem [ ] ; private _rowItemIndices : number [ ] ; private _columns : ColumnDescription [ ] ; private _secondarySortColumnStack ? : number [ ] ; private _sortDirection : SortDirection = SortDirection . NoSort ; private _sortColumnIndex : number = - 1 ; private _filterDescriptors ? : CompositeFilterDescriptorCollection ; /** Event emitted by the data provider when column data changes */ public onColumnsChanged = new TableDataChangeEvent ( ) ; /** Event emitted by the data provider when row data changes */ public onRowsChanged = new TableDataChangeEvent ( ) ; constructor ( columns ) { this . _columns = columns ; this . _items = [ ] ; this . _rowItemIndices = [ ] ; this . _secondarySortColumnStack = [ ] ; } /** Sets the row items based on an array */ public setItems ( items ) { this . _items = items ; this . _rowItemIndices = [ ] ; this . resetRowIndices ( ) ; this . onRowsChanged . raiseEvent ( ) ; } private resetRowIndices ( ) { this . _rowItemIndices . splice ( 0 ) ; for ( let rowIndex = 0 ; rowIndex < this . _items . length ; rowIndex ++ ) { this . _rowItemIndices . push ( rowIndex ) ; } } /** Retrieves the column descriptions */ public async getColumns ( ) { return this . _columns ; } /** Retrieves the row count */ public async getRowsCount ( ) { return this . _rowItemIndices . length ; } /** Retrieves a specific row by index */ public async getRow ( rowIndex , unfiltered ? ) { let realRowIndex = - 1 ; if ( unfiltered ) { if ( 0 <= rowIndex && rowIndex < this . _items . length ) realRowIndex = rowIndex ; } else { if ( 0 <= rowIndex && rowIndex < this . _rowItemIndices . length ) realRowIndex = this . getRowIndexFromFilteredRowIndex ( rowIndex ) ; } if ( 0 <= realRowIndex && realRowIndex < this . _items . length ) return this . _items [ realRowIndex ] ; throw new UiError ( UiComponents . loggerCategory ( this ) , `getRow: Invalid row index - ${ rowIndex }` ) ; } private getRowIndexFromFilteredRowIndex ( filteredRowIndex ) { if ( filteredRowIndex === undefined ) return filteredRowIndex ; if ( filteredRowIndex < 0 || filteredRowIndex >= this . _rowItemIndices . length ) return - 1 ; return this . _rowItemIndices [ filteredRowIndex ] ; } /** Sorts the rows based on the value in a specific column */ public async sort ( columnIndex , sortDirection ) { if ( columnIndex < 0 || columnIndex >= this . _columns . length ) return ; this . _sortColumnIndex = columnIndex ; this . _sortDirection = sortDirection ; if ( sortDirection === SortDirection . NoSort ) { if ( this . _filterDescriptors ) await this . applyFilterDescriptors ( this . _filterDescriptors ) ; else this . resetRowIndices ( ) ; return ; } this . _secondarySortColumnStack = [ ] ; this . _secondarySortColumnStack . push ( columnIndex ) ; const dataProvider = this ; this . _rowItemIndices . sort ( ( a , b ) => { return dataProvider . sortDispatcher ( dataProvider . _items [ a ] , dataProvider . _items [ b ] , columnIndex , sortDirection ) ; } ) ; delete this . _secondarySortColumnStack ; return ; } private sortDispatcher ( a , b , columnIndex , sortDirection ) { let result = this . sortRows ( a , b , columnIndex , sortDirection ) ; if ( result !== 0 ) return result ; const column = this . _columns [ columnIndex ] ; if ( this . _secondarySortColumnStack !== undefined && column . secondarySortColumn !== undefined && column . secondarySortColumn !== columnIndex && this . _secondarySortColumnStack . indexOf ( column . secondarySortColumn ) < 0 ) { this . _secondarySortColumnStack . push ( column . secondarySortColumn ) ; result = this . sortDispatcher ( a , b , column . secondarySortColumn , sortDirection ) ; this . _secondarySortColumnStack . pop ( ) ; } return result ; } private getPrimitiveValue ( record ) { let primitiveValue ; const recordValue = record . value ; if ( recordValue . valueFormat === PropertyValueFormat . Primitive ) primitiveValue = recordValue . value ; return primitiveValue ; } private sortRows ( a , b , columnIndex , sortDirection ) { const aCell = a . cells [ columnIndex ] ; const bCell = b . cells [ columnIndex ] ; let result = 0 ; const column = this . _columns [ columnIndex ] ; const propertyDescription = column . propertyDescription ; const valueA = this . getPrimitiveValue ( aCell . record as PropertyRecord ) ; const valueB = this . getPrimitiveValue ( bCell . record as PropertyRecord ) ; if ( valueA !== undefined && valueB !== undefined && propertyDescription ) result = TypeConverterManager . getConverter ( propertyDescription . typename , propertyDescription . converter ?. name ) . sortCompare ( valueA , valueB , column . sortIgnoreCase ) ; if ( sortDirection === SortDirection . Descending ) result *= - 1 ; return result ; } public public async getDistinctValues ( columnKey , maximumValueCount ? ) { const distinctValues = new DistinctValueCollection ( ) ; const columnIndex = this . _columns . findIndex ( ( description ) => description . key === columnKey ) ; if ( columnIndex < 0 || columnIndex >= this . _columns . length ) return distinctValues ; const columnDescription = this . _columns [ columnIndex ] ; const propertyDescription = columnDescription . propertyDescription ; const uniqueKeysMap = { } ; const uniqueValues = [ ] ; if ( ! propertyDescription ) return distinctValues ; for ( const row of this . _items ) { const cell = row . cells [ columnIndex ] ; const record = cell . record ; if ( record === undefined ) continue ; const value = this . getPrimitiveValue ( record ) ; const displayValue = await TypeConverterManager . getConverter ( propertyDescription . typename , propertyDescription . converter ?. name ) . convertPropertyToString ( propertyDescription , value ) ; if ( value === undefined || typeof displayValue !== "string" ) continue ; const valueKey = value . toString ( ) ; if ( ! uniqueKeysMap . hasOwnProperty ( valueKey ) ) { const propertyValue = { value , label : displayValue } ; uniqueKeysMap [ valueKey ] = propertyValue ; uniqueValues . push ( propertyValue ) ; } } distinctValues . values = uniqueValues ; distinctValues . values . sort ( ( a , b ) => { if ( a . value && b . value ) return TypeConverterManager . getConverter ( propertyDescription . typename , propertyDescription . converter ?. name ) . sortCompare ( a . value , b . value ) ; return 0 ; } ) ; if ( maximumValueCount !== undefined && distinctValues . values . length > maximumValueCount ) distinctValues . values . splice ( maximumValueCount ) ; return distinctValues ; } public public async applyFilterDescriptors ( filterDescriptors ) { this . _filterDescriptors = filterDescriptors ; this . _rowItemIndices . splice ( 0 ) ; this . _items . forEach ( ( row , i ) => { if ( filterDescriptors . evaluateRow ( row ) ) this . _rowItemIndices . push ( i ) ; } ) ; if ( this . _sortDirection !== SortDirection . NoSort ) { await this . sort ( this . _sortColumnIndex , this . _sortDirection ) ; } } /** Adds a row to the end */ public addRow ( rowItem ) { return this . insertRow ( rowItem , - 1 ) ; } /** Inserts a row at a given row index */ public insertRow ( rowItem , index ) { let newIdx = index ; if ( index < 0 || index >= this . _items . length ) newIdx = this . _items . push ( rowItem ) - 1 ; else this . _items . splice ( newIdx , 0 , rowItem ) ; this . resetRowIndices ( ) ; this . onRowsChanged . raiseEvent ( ) ; return newIdx ; } /** Deletes a row */ public deleteRow ( rowItem , raiseRowsChangedEvent = true ) { const rowIndex = this . findRowIndex ( rowItem ) ; if ( rowIndex >= 0 ) { this . _items . splice ( rowIndex , 1 ) ; this . resetRowIndices ( ) ; if ( raiseRowsChangedEvent ) this . onRowsChanged . raiseEvent ( ) ; } } /** Moves a row to a new row index */ public moveRow ( rowItem , newIndex ) { const oldIndex = this . findRowIndex ( rowItem ) ; if ( oldIndex >= 0 ) { this . deleteRow ( rowItem , false ) ; if ( newIndex > oldIndex ) newIndex -- ; return this . insertRow ( rowItem , newIndex ) ; } return - 1 ; } private findRowIndex ( item ) { for ( let i = 0 ; i < this . _items . length ; i ++ ) { if ( item === this . _items [ i ] ) return i ; } return - 1 ; } }