/**
 * Copyright (c) 2018 Dell Inc., or its subsidiaries. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 */ import * as React from "react" ; import { classNames } from "../utils" ; import { ClassNames } from "./ClassNames" ; import { Button } from "../forms/button" ; import { DataGridRow } from "./DataGrid" ; import { DebounceUtils } from "../forms/common/DebounceUtils" ; param { style } CSS param { className } CSS param { datagridRef } Refrence param { columnName } columnName param { placeholder } placeholder param { onFilter } Custom param { filterType } Type param { disabled } boolean param { debounce } boolean param { debounceTime } number param { position } position param { defaultValue } defaultValue export type DataGridFilterProps = { style ? : any ; className ? : string ; datagridRef : any ; columnName : string ; placeholder ? : string ; onFilter : ( rows : DataGridRow [ ] , columnValue : any , columnName : string ) => Promise < DataGridFilterResult > ; filterType ? : FilterType ; showFilter ? : boolean ; disabled ? : boolean ; debounce ? : boolean ; debounceTime ? : number ; position ? : FilterPosition ; defaultValue ? : any ; } ; param { rows } datagrid param { totalItems } total export type DataGridFilterResult = { rows : DataGridRow [ ] ; totalItems : number ; } ; param { STR } to param { CUSTOM } to export enum FilterType { STR = "String" , CUSTOM = "Custom" } export enum FilterPosition { LEFT = "left" , CENTER = "center" , RIGHT = "right" } param { isOpen } check param { transformVal } value param { isFiltered } boolean type DataGridFilterState = { isOpen : boolean ; transformVal : string ; isFiltered : boolean ; } ; param { OnChange } function param { filterValue } value export type DataGridCustomFilterProps = { onChange ? : Function ; filterValue ? : any ; } ; /**
 * General component description :
 * DataGridFilter :
 * Render filter box for datagrid
 */ export class DataGridFilter extends React . PureComponent < DataGridFilterProps , DataGridFilterState > { private refParent = React . createRef < HTMLDivElement > ( ) ; private refChild = React . createRef < HTMLDivElement > ( ) ; private filterValue : any ; private debounceHandleChange : DebounceUtils = new DebounceUtils ( ) ; static defaultProps = { filterType : FilterType . STR , className : "" , style : { } , customFilter : null , showFilter : true , disabled : false , } ; private handleResizeInterval = ( prevChildWidth ) => { const interval = setInterval ( ( ) => { let nextChildWidth = ( this . refChild && this . refChild . current && this . refChild . current . getClientRects ( ) [ 0 ] && this . refChild . current . getClientRects ( ) [ 0 ] . width ) || 0 ; if ( prevChildWidth === nextChildWidth ) { clearInterval ( interval ) ; this . updateFilterPosition ( nextChildWidth ) ; } else { prevChildWidth = nextChildWidth ; } } , 10 ) ; } ; private updateFilterPosition = ( childWidth ) => { if ( this . refParent && this . refParent . current && this . refParent . current . getClientRects ( ) [ 0 ] ) { const filterBoxTop = this . refParent . current . getClientRects ( ) [ 0 ] . top + 15 ; const filterBoxLeft = this . refParent . current . getClientRects ( ) [ 0 ] . left - childWidth + 20 ; const transformVal = "translateX(" + filterBoxLeft + "px) " + "translateY(" + filterBoxTop + "px)" ; this . setState ( { transformVal : transformVal } ) ; } } ; constructor ( props ) { super ( props ) ; let initialStateData = { isOpen : false , transformVal : "translateX(0px) translateY(0px)" , isFiltered : false , } ; const { defaultValue } = props ; if ( defaultValue ) { initialStateData . isFiltered = true ; this . filterValue = defaultValue ; } else { this . filterValue = undefined ; } this . state = { ... initialStateData } ; } componentWillMount ( ) { window . addEventListener ( "resize" , this . resize as any , true ) ; } componentWillUnmount ( ) { window . removeEventListener ( "resize" , this . resize as any , true ) ; } componentDidUpdate ( ) { const { isOpen , transformVal } = this . state ; if ( isOpen ) { let prevChildWidth = ( this . refChild && this . refChild . current && this . refChild . current . getClientRects ( ) [ 0 ] && this . refChild . current . getClientRects ( ) [ 0 ] . width ) || 0 ; if ( transformVal === "translateX(0px) translateY(0px)" ) { this . updateFilterPosition ( prevChildWidth ) ; } else { this . handleResizeInterval ( prevChildWidth ) ; } } } private handleOnChange = ( evt ) => { const value = evt . target . value ; this . updateFilter ( value ) ; } ; public updateFilter = ( value ) => { const { columnName , datagridRef , onFilter } = this . props ; datagridRef . current ! . showLoader ( ) ; const rows = datagridRef . current ! . getAllRows ( ) ; if ( onFilter && datagridRef ) { this . filterValue = value ; this . setState ( { isFiltered : String ( value ) . length !== 0 } ) ; onFilter ( rows , value , columnName ) . then ( data => { const { rows , totalItems } = data ; datagridRef . current ! . updateRows ( rows , totalItems ) ; datagridRef . current ! . closeDetailPane ( ) ; datagridRef . current ! . hideLoader ( ) ; } ) ; } } ; private handleButtonClick = ( ) => { this . toggle ( ) ; } ; private toggle ( ) { this . setState ( prevState => ( { isOpen : ! prevState . isOpen , } ) , this . afterToggle ) ; } private afterToggle = ( ) => { if ( this . state . isOpen ) { window . addEventListener ( "click" , this . handleDocumentClick as any , true ) ; } else { window . removeEventListener ( "click" , this . handleDocumentClick as any , true ) ; } } ; private resize = ( ) => { if ( this . state . isOpen ) { this . toggle ( ) ; } } ; private handleDocumentClick = ( evt ) => { if ( this . state . isOpen ) { const target = ( evt . target as any ) as HTMLElement ; const el = this . refChild . current ; if ( el && typeof el !== "string" && ! el . contains ( target ) ) { this . toggle ( ) ; } } } ; private openFilter ( ) { const { filterValue } = this ; const { transformVal } = this . state ; const { style , className , filterType , placeholder , columnName , children , disabled , debounce , debounceTime , position , } = this . props ; const childrenWithProps = React . Children . map ( children , child => { if ( React . isValidElement ( child ) ) { return React . cloneElement ( child , { onChange : this . updateFilter , filterValue : filterValue } ) ; } return child ; } ) ; let alignment ; switch ( position ) { case FilterPosition . RIGHT : alignment = "195px" ; break ; case FilterPosition . CENTER : alignment = "97px" ; break ; default : alignment = "0px" ; break ; } return ( < div > < span className = { ClassNames . OFFSCREEN_FOCUS_REBOUNDER } / > < div ref = { this . refChild } className = { classNames ( [ ClassNames . DATARID_FILTER , ClassNames . CLR_POPOVER_CONTENT , className ] ) } style = { { top : 0 , bottom : "auto" , right : "auto" , height : "90px" , left : alignment , transform : transformVal , ... style , } } > < div className = { ClassNames . DATAGRID_FILTER_WRAPPER } > < Button className = { ClassNames . DATAGRID_FILTER_POPUP_CLOSE } defaultBtn = { false } onClick = { this . handleButtonClick } icon = { { shape : "close" , } } /> < /div> { filterType === FilterType . STR ? ( < input className = { ClassNames . CLR_INPUT } type = "search" name = { `name-${ columnName }` } placeholder = { placeholder } defaultValue = { filterValue } disabled = { disabled || false } onChange = { ( evt ) => this . debounceHandleChange . debounce ( evt , this . handleOnChange , debounce , debounceTime ) } />) : (filterType === FilterType.CUSTOM && childrenWithProps)} < /div> < span className = { ClassNames . OFFSCREEN_FOCUS_REBOUNDER } / > < /div>); } render ( ) { const { isOpen , isFiltered } = this . state ; const { showFilter } = this . props ; const FilterBtnClasses = classNames ( [ ClassNames . DATAGRID_FILTER_BUTTON , isFiltered && ClassNames . DATAGRID_FILTERED , ] ) ; return ( < div ref = { this . refParent } className = { classNames ( [ ClassNames . CLR_FILTER ] ) } style = { { position : "relative" } } > { showFilter && ( < Button defaultBtn = { false } className = { FilterBtnClasses } onClick = { this . handleButtonClick } icon = { { shape : isFiltered ? "filter-grid-circle" : "filter-grid" , className : ClassNames . ICON_SOLID , } } />)} { showFilter && isOpen && this . openFilter ( ) } < /div>); } }