import * as React from 'react' ; import { BaseWebComponent , IDataFilterInfo , IDataFilterValueInfo , IDataFilterInternal , ExtensibilityConstants , FilterComparisonOperator } from '@pnp/modern-search-extensibility' ; import * as ReactDOM from 'react-dom' ; import { ITheme , ChoiceGroup , IChoiceGroupOption , MessageBar , MessageBarType } from 'office-ui-fabric-react' ; import * as strings from 'CommonStrings' ; import { IReadonlyTheme } from '@microsoft/sp-component-base' ; import { DateHelper } from '../../helpers/DateHelper' ; export enum DateFilterInterval { AnyTime , Past24 , PastWeek , PastMonth , Past3Months , PastYear , OlderThanAYear } export interface IFilterDateIntervalComponentProps { /**
     * The current selected filters. Because we can select values outside of values retrieved from results, we need this information to display the default date picker values correctly after the user selection
     */ filter : IDataFilterInternal ; /**
     * The current theme settings
     */ themeVariant ? : IReadonlyTheme ; /**
     * Handler when the date is updated
     */ onUpdate : ( filterValues : IDataFilterValueInfo [ ] ) => void ; /**
     * The moment.js library reference
     */ moment : any ; } export interface IFilterDateIntervalComponentState { options : IChoiceGroupOption [ ] ; } export class FilterDateIntervalComponent extends React . Component < IFilterDateIntervalComponentProps , IFilterDateIntervalComponentState > { private _allOptions = [ { key : DateFilterInterval . AnyTime . toString ( ) , text : strings . General . DateIntervalStrings . AnyTime } , { key : DateFilterInterval . Past24 . toString ( ) , text : strings . General . DateIntervalStrings . PastDay } , { key : DateFilterInterval . PastWeek . toString ( ) , text : strings . General . DateIntervalStrings . PastWeek , } , { key : DateFilterInterval . PastMonth . toString ( ) , text : strings . General . DateIntervalStrings . PastMonth } , { key : DateFilterInterval . Past3Months . toString ( ) , text : strings . General . DateIntervalStrings . Past3Months } , { key : DateFilterInterval . PastYear . toString ( ) , text : strings . General . DateIntervalStrings . PastYear } , { key : DateFilterInterval . OlderThanAYear . toString ( ) , text : strings . General . DateIntervalStrings . Older } ] ; public constructor ( props ) { super ( props ) ; this . state = { options : [ ] } ; this . _onChange = this . _onChange . bind ( this ) ; } public render ( ) { let dateAsString = undefined ; const values = this . props . filter . values . filter ( value => value . selected ) . sort ( ( a , b ) => { return new Date ( a . value ) . getTime ( ) - new Date ( b . value ) . getTime ( ) ; } ) ; if ( values . length >= 1 ) { dateAsString = values [ 0 ] . value ; } return < div > < ChoiceGroup theme = { this . props . themeVariant as ITheme } options = { this . state . options } selectedKey = { this . _getIntervalKeyForValue ( dateAsString ) } onChange = { this . _onChange } styles = { { root : { paddingLeft : 10 , backgroundColor : 'inherit' } , flexContainer : { selectors : { label : { color : this . props . themeVariant . semanticColors . bodyText } } } } } /> < /div>; } public componentDidMount ( ) { const intervals = { } ; if ( this . props . filter . values . length > 0 ) { this . props . filter . values . filter ( v => ! v . selected ) . forEach ( value => { if ( value . count > 0 ) { const interval = this . _getIntervalKeyForValue ( value . value ) ; if ( interval ) { if ( Object . keys ( intervals ) . indexOf ( interval ) === - 1 ) { intervals [ interval ] = value . count ; } else { intervals [ interval ] = ( intervals [ interval ] += value . count ) ; } } } } ) ; const availableOptions = this . _allOptions . map ( option => { if ( Object . keys ( intervals ) . indexOf ( option . key ) !== - 1 ) { option . text = this . props . filter . showCount ? `${ option . text } (${ intervals [ option . key ] })` : option . text ; return option ; } else if ( option . key === DateFilterInterval . AnyTime . toString ( ) ) { return option ; } } ) . filter ( o => o ) ; this . setState ( { options : availableOptions } ) ; } } public _onChange ( ev ? , option ? ) { let updatedValues = [ ] ; switch ( option . key ) { case String ( DateFilterInterval . OlderThanAYear ) : updatedValues . push ( { name : strings . General . DateIntervalStrings . Older , value : this . props . moment ( new Date ( ) ) . subtract ( 1 , 'years' ) . subtract ( 'minutes' , 1 ) . toISOString ( ) , selected : true , operator : FilterComparisonOperator . Lt } ) ; break ; case String ( DateFilterInterval . Past24 ) : updatedValues . push ( { name : strings . General . DateIntervalStrings . PastDay , value : new Date ( ) . toISOString ( ) , selected : true , operator : FilterComparisonOperator . Leq } , { name : strings . General . DateIntervalStrings . PastDay , value : this . props . moment ( new Date ( ) ) . subtract ( 24 , 'hours' ) . toISOString ( ) , selected : true , operator : FilterComparisonOperator . Geq } ) ; break ; case String ( DateFilterInterval . Past3Months ) : updatedValues . push ( { name : strings . General . DateIntervalStrings . Past3Months , value : this . props . moment ( new Date ( ) ) . subtract ( 1 , 'months' ) . toISOString ( ) , selected : true , operator : FilterComparisonOperator . Leq } , { name : strings . General . DateIntervalStrings . Past3Months , value : this . props . moment ( new Date ( ) ) . subtract ( 3 , 'months' ) . toISOString ( ) , selected : true , operator : FilterComparisonOperator . Geq } ) ; break ; case String ( DateFilterInterval . PastMonth ) : updatedValues . push ( { name : strings . General . DateIntervalStrings . PastMonth , value : this . props . moment ( new Date ( ) ) . subtract ( 1 , 'week' ) . toISOString ( ) , selected : true , operator : FilterComparisonOperator . Leq } , { name : strings . General . DateIntervalStrings . PastMonth , value : this . props . moment ( new Date ( ) ) . subtract ( 1 , 'months' ) . toISOString ( ) , selected : true , operator : FilterComparisonOperator . Geq } ) ; break ; case String ( DateFilterInterval . PastWeek ) : updatedValues . push ( { name : strings . General . DateIntervalStrings . PastWeek , value : this . props . moment ( new Date ( ) ) . subtract ( 24 , 'hours' ) . toISOString ( ) , selected : true , operator : FilterComparisonOperator . Leq } , { name : strings . General . DateIntervalStrings . PastWeek , value : this . props . moment ( new Date ( ) ) . subtract ( 1 , 'week' ) . toISOString ( ) , selected : true , operator : FilterComparisonOperator . Geq } ) ; break ; case String ( DateFilterInterval . PastYear ) : updatedValues . push ( { name : strings . General . DateIntervalStrings . PastYear , value : this . props . moment ( new Date ( ) ) . subtract ( 3 , 'months' ) . toISOString ( ) , selected : true , operator : FilterComparisonOperator . Leq } , { name : strings . General . DateIntervalStrings . PastYear , value : this . props . moment ( new Date ( ) ) . subtract ( 1 , 'years' ) . toISOString ( ) , selected : true , operator : FilterComparisonOperator . Geq } ) ; break ; } this . props . onUpdate ( updatedValues ) ; } private _getIntervalDate ( unit , count ) { return this . _getIntervalDateFromStartDate ( new Date ( ) , unit , count ) ; } private _getIntervalDateFromStartDate ( startDate , unit , count ) { return this . props . moment ( startDate ) . subtract ( count , unit ) . toDate ( ) ; } private _getIntervalKeyForValue ( dateAsString ) { if ( dateAsString ) { if ( dateAsString . indexOf ( 'range(' ) !== - 1 ) { const matches = dateAsString . match ( /(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)((-(\d{2}):(\d{2})|Z)?)/gi ) ; if ( matches ) { dateAsString = matches [ matches . length - 1 ] ; } } const selectedStartDate = new Date ( dateAsString ) ; const pastYearDate = this . _getIntervalDateFromStartDate ( this . _getIntervalDate ( "years" , 1 ) , 'minutes' , 1 ) ; if ( selectedStartDate < pastYearDate ) { return DateFilterInterval . OlderThanAYear . toString ( ) ; } else { const past24Date = this . _getIntervalDateFromStartDate ( this . _getIntervalDate ( "days" , 1 ) , 'minutes' , 1 ) ; const pastWeekDate = this . _getIntervalDateFromStartDate ( this . _getIntervalDate ( "weeks" , 1 ) , 'minutes' , 1 ) ; const pastMonthDate = this . _getIntervalDateFromStartDate ( this . _getIntervalDate ( "months" , 1 ) , 'minutes' , 1 ) ; const past3MonthsDate = this . _getIntervalDateFromStartDate ( this . _getIntervalDate ( "months" , 3 ) , 'minutes' , 1 ) ; if ( selectedStartDate >= past24Date ) { return DateFilterInterval . Past24 . toString ( ) ; } else if ( selectedStartDate >= pastWeekDate ) { return DateFilterInterval . PastWeek . toString ( ) ; } else if ( selectedStartDate >= pastMonthDate ) { return DateFilterInterval . PastMonth . toString ( ) ; } else if ( selectedStartDate >= past3MonthsDate ) { return DateFilterInterval . Past3Months . toString ( ) ; } else if ( selectedStartDate >= pastYearDate ) { return DateFilterInterval . PastYear . toString ( ) ; } } } else { return DateFilterInterval . AnyTime . toString ( ) ; } } } export class FilterDateIntervalWebComponent extends BaseWebComponent { public constructor ( ) { super ( ) ; } public async connectedCallback ( ) { const dateHelper = this . _serviceScope . consume < DateHelper > ( DateHelper . ServiceKey ) ; const moment = await dateHelper . moment ( ) ; let props = this . resolveAttributes ( ) ; let renderDateRange = null ; if ( props . filter ) { const filter = props . filter as IDataFilterInternal ; renderDateRange = < FilterDateIntervalComponent { ... props } moment = { moment } filter = { filter } onUpdate = { ( ( filterValues ) => { const updatedValues = filter . values . map ( value => { if ( filterValues . filter ( filterValue => { return filterValue . value === value . value ; } ) . length === 0 ) { return { name : value . name , selected : false , value : value . value , operator : value . operator } as IDataFilterValueInfo ; } } ) ; this . dispatchEvent ( new CustomEvent ( ExtensibilityConstants . EVENT_FILTER_UPDATED , { detail : { filterName : filter . filterName , filterValues : filterValues . concat ( updatedValues . filter ( v => v ) ) , instanceId : props . instanceId } as IDataFilterInfo , bubbles : true , cancelable : true } ) ) ; } ) . bind ( this ) } />; } else { renderDateRange = < MessageBar messageBarType = { MessageBarType . warning } isMultiline = { false } > { `Component <pnp-date-interval> misconfigured. The HTML attribute 'filter' is missing.` } < /MessageBar>; } ReactDOM . render ( renderDateRange , this ) ; } }