import { Palette , PropertyExt } from "@hpcc-js/common" ; import { hashSum } from "@hpcc-js/util" ; import { format as d3Format } from "d3-format" ; import { select as d3Select } from "d3-selection" ; import { Common } from "./Common" ; import { CellFormatter , CellRenderer , ColumnType , RowType } from "./RowFormatter" ; export class ColumnFormat extends PropertyExt { _owner : Table ; constructor ( ) { super ( ) ; } owner ( ) ; owner ( _ ) ; owner ( _ ? ) { if ( ! arguments . length ) return this . _owner ; this . _owner = _ ; return this ; } valid ( ) { return ! ! this . column ( ) ; } formatterFunc ( ) { const defaultFormatter = this . _owner . formatterFunc ( ) ; if ( this . valid ( ) && this . format ( ) ) { const numberFormatter = d3Format ( this . format ( ) ) ; return function ( this , cell , row ) : string { if ( typeof cell === "number" ) return numberFormatter ( cell ) ; return defaultFormatter . call ( this , cell , row ) ; } ; } return defaultFormatter ; } renderCellFunc ( ) { const defaultRenderCell = this . _owner . renderCellFunc ( ) ; const defaultFormatter = this . formatterFunc ( ) ; if ( this . valid ( ) && this . paletteID ( ) ) { const columns = this . _owner . columns ( ) ; const palette = Palette . rainbow ( this . paletteID ( ) ) ; const min = this . min ( ) ; const max = this . max ( ) ; const valueColIdx = this . valueColumn ( ) ? columns . indexOf ( this . valueColumn ( ) ) : undefined ; return function ( this , row , cell , cellElement ) : HTMLElement | void { if ( defaultRenderCell ) { defaultRenderCell . call ( this , row , cell , cellElement ) ; } const value = valueColIdx ? row . __origRow [ valueColIdx ] : cell ; const background = palette ( value , min , max ) ; const cellText = defaultFormatter . call ( this , cell , row ) ; d3Select ( cellElement ) . style ( "background" , background ) . style ( "color" , Palette . textColor ( background ) ) . text ( cellText . html ? cellText . html : cell ) ; } ; } return defaultRenderCell ; } } ColumnFormat . prototype . _class += " dgrid_Table.ColumnFormat" ; export interface ColumnFormat { column ( ) : string ; column ( _ ) : this ; width ( ) : number ; width ( _ ) : this ; format ( ) : string ; format ( _ ) : this ; paletteID ( ) : string ; paletteID ( _ ) : this ; min ( ) : number ; min ( _ ) : this ; max ( ) : number ; max ( _ ) : this ; valueColumn ( ) : string ; valueColumn ( _ ) : this ; } ColumnFormat . prototype . publish ( "column" , null , "set" , "Column" , function ( this ) { return this . _owner . columns ( ) ; } , { optional : true } ) ; ColumnFormat . prototype . publish ( "width" , null , "number" , "Width" , null , { optional : true } ) ; ColumnFormat . prototype . publish ( "format" , null , "string" , "Format (d3-format)" , null , { optional : true } ) ; ColumnFormat . prototype . publish ( "paletteID" , null , "set" , "Color palette for this widget" , [ "" , ... Palette . rainbow ( "default" ) . switch ( ) ] , { optional : true } ) ; ColumnFormat . prototype . publish ( "min" , 0 , "number" , "Min Value" , null , { disable : ( cf ) => ! cf . paletteID ( ) } ) ; ColumnFormat . prototype . publish ( "max" , 100 , "number" , "Max Value" , null , { disable : ( cf ) => ! cf . paletteID ( ) } ) ; ColumnFormat . prototype . publish ( "valueColumn" , null , "set" , "Column" , function ( this ) { return this . _owner . columns ( ) ; } , { optional : true , disable : ( cf ) => ! cf . paletteID ( ) } ) ; export class Table extends Common { private _prevColsHash ; private _prevFieldsHash ; _colsRefresh = false ; _dataRefresh = false ; constructor ( ) { super ( ) ; } fields ( _ ? ) { const retVal = super . fields . apply ( this , arguments ) ; if ( arguments . length ) { const hash = hashSum ( { _ } ) ; if ( this . _prevFieldsHash !== hash ) { this . _prevFieldsHash = hash ; this . _colsRefresh = true ; } } return retVal ; } columns ( _ ? ) { const retVal = super . columns . apply ( this , arguments ) ; if ( arguments . length ) { const hash = hashSum ( { _ } ) ; if ( this . _prevColsHash !== hash ) { this . _prevColsHash = hash ; this . _colsRefresh = true ; } } return retVal ; } data ( _ ? ) { const retVal = super . data . apply ( this , arguments ) ; if ( arguments . length ) { this . _dataRefresh = true ; } return retVal ; } enter ( domNode , element ) { super . enter ( domNode , element ) ; } guessWidth ( columns , data ) { const sortablePadding = this . sortable ( ) ? 12 : 0 ; for ( const column of columns ) { if ( column . children ) { let sampleData = [ ] ; for ( let i = 0 ; i < Math . min ( 3 , data . length ) ; ++ i ) { sampleData = sampleData . concat ( data [ i ] [ column . idx ] ) ; } this . guessWidth ( column . children , sampleData ) ; } else { column . width = data . reduce ( ( prevVal , row ) => { const cell = ( "" + row [ column . idx ] ) . trim ( ) ; return Math . max ( prevVal , this . textSize ( cell ) . width ) ; } , this . textSize ( "" + column . label , undefined , undefined , true ) . width + sortablePadding ) + 8 ; } } } _prevHash ; update ( domNode , element ) { super . update ( domNode , element ) ; const hash = this . hashSum ( ) ; if ( this . _prevHash !== hash ) { this . _prevHash = hash ; this . _colsRefresh = true ; } if ( this . _colsRefresh ) { this . _columns = this . _store . columns ( this . sortable ( ) , this . formatterFunc ( ) , this . renderCellFunc ( ) ) ; switch ( this . columnWidth ( ) ) { case "auto" : const tenRows = this . data ( ) . filter ( ( row , idx ) => idx < 10 ) ; this . guessWidth ( this . _columns , tenRows ) ; break ; } const columns = this . columns ( ) ; for ( const columnFormat of this . columnFormats ( ) ) { if ( columnFormat . valid ( ) ) { const colIdx = columns . indexOf ( columnFormat . column ( ) ) ; if ( this . _columns [ colIdx ] ) { this . _columns [ colIdx ] . hidden = columnFormat . width ( ) === 0 ; this . _columns [ colIdx ] . width = columnFormat . width ( ) || this . _columns [ colIdx ] . width ; this . _columns [ colIdx ] . formatter = columnFormat . formatterFunc ( ) ; this . _columns [ colIdx ] . renderCell = columnFormat . renderCellFunc ( ) ; } } } this . _dgrid . set ( "columns" , this . _columns ) ; this . _colsRefresh = false ; } if ( this . _colsRefresh || this . _dataRefresh ) { if ( this . _colsRefresh ) { this . _dgrid . refresh ( { } ) ; } else { this . _dgrid . refresh ( ) ; } this . _colsRefresh = false ; this . _dataRefresh = false ; } } exit ( domNode , element ) { delete this . _prevColsHash ; delete this . _prevFieldsHash ; super . exit ( domNode , element ) ; } formatterFunc ( ) { return function ( this , cell , row ) : string | any { switch ( typeof cell ) { case "string" : return { html : cell . replace ( /\t/g , "&nbsp;&nbsp;&nbsp;&nbsp;" ) . trim ( ) } ; case "undefined" : return "" ; } return cell ; } ; } renderCellFunc ( ) { return undefined ; } click ( row , col , sel ) { } } Table . prototype . _class += " dgrid_Table" ; export interface Table { columnWidth ( ) : "auto" | "none" ; columnWidth ( _ ) : this ; columnFormats ( ) : ColumnFormat [ ] ; columnFormats ( _ ) : this ; } Table . prototype . publish ( "columnWidth" , "auto" , "set" , "Default column width" , [ "auto" , "none" ] ) ; Table . prototype . publish ( "columnFormats" , [ ] , "propertyArray" , "Source Columns" , null , { autoExpand : ColumnFormat } ) ;