2017-03-13 06:35:20 -04:00
import * as React from 'react' ;
import * as ReactDom from 'react-dom' ;
import { Version } from '@microsoft/sp-core-library' ;
import {
BaseClientSideWebPart ,
IPropertyPaneConfiguration ,
PropertyPaneTextField ,
IWebPartContext ,
PropertyPaneSlider ,
PropertyPaneDropdown ,
IPropertyPaneDropdownOption ,
PropertyPaneButton
} from '@microsoft/sp-webpart-base' ;
import * as strings from 'modernChartsStrings' ;
import ModernCharts from './components/ModernCharts' ;
import { IModernChartsProps } from './IModernChartsWebPartProps' ;
import { MChart } from './IModernChartsWebPartProps' ;
import { IModernChartsWebPartProps } from './IModernChartsWebPartProps' ;
import { ChartConfiguration } from './IModernChartsWebPartProps' ;
import ChartOptions from './ChartOptions' ;
import {
SPHttpClient ,
SPHttpClientResponse
} from '@microsoft/sp-http' ;
export interface ISPLists {
value : ISPList [ ] ;
}
export interface ISPList {
Title : string ;
Id : string ;
}
export default class ModernChartsWebPart extends BaseClientSideWebPart < IModernChartsWebPartProps > {
private reactCharts : React.ReactElement < IModernChartsProps > ;
2018-04-27 11:26:24 -04:00
// Columns Enable / Disable
2017-03-13 06:35:20 -04:00
private colsDisabled : boolean = true ;
private listDisabled : boolean = true ;
private urlTextDisabled : boolean = true ;
//Property Dropdown Options
private _chartThemeOptions : IPropertyPaneDropdownOption [ ] = [ ] ;
private _siteOptions : IPropertyPaneDropdownOption [ ] = [ ] ;
private _dropdownOptions : IPropertyPaneDropdownOption [ ] = [ ] ;
private _columnOptions : IPropertyPaneDropdownOption [ ] = [ ] ;
private _ListColumnOptions : Array < IPropertyPaneDropdownOption > = [ ] ;
private _chartSizeOptions : IPropertyPaneDropdownOption [ ] = [
{ key : 3 , text : 'Small' } ,
{ key : 6 , text : 'Medium' } ,
{ key : 9 , text : 'Medium-Large' } ,
{ key : 12 , text : 'Large' }
2018-04-27 11:26:24 -04:00
] ;
2017-03-13 06:35:20 -04:00
private _chartTypeOptions : IPropertyPaneDropdownOption [ ] = [
{ key : 'bar' , text : 'Bar' } ,
{ key : 'horizontalbar' , text : 'Horizontal Bar' } ,
{ key : 'doughnut' , text : 'Doughnut' } ,
{ key : 'line' , text : 'Line' } ,
{ key : 'pie' , text : 'Pie' } ,
{ key : 'polar' , text : 'Polar' } ,
{ key : 'radar' , text : 'Radar' }
] ;
private _chartColActions : IPropertyPaneDropdownOption [ ] = [
{ key : 'average' , text : 'Average' } ,
{ key : 'count' , text : 'Count' } ,
{ key : 'sum' , text : 'Sum' }
] ;
public constructor ( context : IWebPartContext ) {
super ( ) ;
}
private ChartThemes : ChartOptions ;
private defaultOptions : Object = {
legend : {
display : false ,
layout : {
padding : 10
} ,
position : 'bottom' ,
labels : {
fontColor : 'rgba(100, 100, 100, 1.0)'
}
}
} ;
public defaultChartConfig ( chartDesc : string ) : ChartConfiguration {
var defConfig = {
title : 'Chart Title' ,
description : chartDesc ,
type : 'doughnut' ,
list : null ,
dataurl : this.context.pageContext.web.absoluteUrl ,
url : this.context.pageContext.web.absoluteUrl ,
other : true ,
dataUrlDisabled : true ,
colsDisabled : true ,
listsDisabled : false ,
col1 : '' ,
col2 : '' ,
act : '' ,
unique : '' ,
size : 12 ,
options : ChartOptions.Options ( ) ,
columns : [ ] ,
lists : this.properties.listOptions ,
theme : "Random" ,
bgColors : ChartOptions.RandomColors ( ) [ 'bgColors' ] ,
hoverColors : ChartOptions.RandomColors ( ) [ 'bgColors' ]
} ;
2018-04-27 11:26:24 -04:00
2017-03-13 06:35:20 -04:00
return defConfig ;
}
public render ( ) : void {
//Initialize first demo chart
if ( ! this . properties . state ) {
this . properties . state = true ;
this . properties . firstLoad = true ;
this . properties . numCharts = 1 ;
this . properties . chartConfig = [ ] ;
const firstChartConfig = this . defaultChartConfig ;
this . properties . chartConfig . push ( this . defaultChartConfig ( 'Demo Chart, Edit Web Part to Customize' ) ) ;
}
this . getChartData ( ) ;
}
protected get dataVersion ( ) : Version {
return Version . parse ( '1.0' ) ;
}
private getChartData ( ) : void {
const _chartData : Array < MChart > = [ ] ;
var _count = 0 ;
this . properties . chartConfig . forEach ( ( cfg , i ) = > {
if ( cfg . list != null ) {
this . getData ( cfg ) . then ( ( response ) = > {
const chart = this . calculateData ( response . value , cfg ) ;
const _chart : MChart = { data :chart [ 'data' ] , labels :chart [ 'labels' ] , config :cfg , key : i } ;
_chartData [ i ] = _chart ;
if ( ++ _count == this . properties . chartConfig . length ) { this . getCharts ( _chartData ) ; }
} ) ;
} else {
const _chart : MChart = { data :ChartOptions._sampleData , labels :ChartOptions._sampleCols , config :cfg , key : i } ;
_chartData [ i ] = _chart ;
2018-04-27 11:26:24 -04:00
if ( ++ _count == this . properties . chartConfig . length ) { this . getCharts ( _chartData ) ; }
2017-03-13 06:35:20 -04:00
}
} ) ;
}
private getCharts ( charts : Array < MChart > ) : void {
const chartArea : React.ReactElement < IModernChartsProps > = React . createElement ( ModernCharts , {
description : this.properties.description ,
title : this.properties.description ,
state : this.properties.state ,
config : { } ,
context : this.context ,
data : { } ,
charts : charts
} ) ;
ReactDom . render ( chartArea , this . domElement ) ;
}
private calculateData ( data : Array < Object > , config :ChartConfiguration ) : Object {
var values : Array < number > = [ ] ;
var labels : Object = this . getUnique ( data , config ) ;
var dataVal : Array < Array < any > > = this . getValues ( data , labels [ 'unique' ] , config ) ;
switch ( config . act ) {
case 'sum' :
dataVal . forEach ( ( vals , i ) = > {
values [ i ] = 0 ;
vals . forEach ( ( val ) = > {
values [ i ] += parseFloat ( val ) ;
} ) ;
} ) ;
break ;
case 'average' :
dataVal . forEach ( ( vals , i ) = > {
values [ i ] = 0 ;
vals . forEach ( ( val ) = > {
values [ i ] += parseFloat ( val ) ;
} ) ;
if ( values [ i ] != 0 ) {
values [ i ] = values [ i ] / vals . length ;
}
} ) ;
break ;
case 'count' :
dataVal . forEach ( ( vals ) = > {
values . push ( vals . length ) ;
} ) ;
break ;
default :
values = [ 100 , 250 , 90 , 300 ] ;
break ;
}
return { data :values , labels :labels [ 'labels' ] } ;
}
private getUnique ( data : Array < Object > , config :ChartConfiguration ) : Object {
const chLabels : Object = { unique : [ ] , labels : [ ] } ;
data . forEach ( ( item ) = > {
if ( chLabels [ 'unique' ] . indexOf ( item [ config . unique ] ) == - 1 && item [ config . unique ] != null && item [ config . unique ] != "" ) {
chLabels [ 'unique' ] . push ( item [ config . unique ] ) ;
chLabels [ 'labels' ] . push ( item [ config . col1 ] ) ;
}
} ) ;
return chLabels ;
}
private getValues ( data : Array < Object > , unique : Array < string > , config : ChartConfiguration ) : Array < Array < any > > {
2018-04-27 11:26:24 -04:00
2017-03-13 06:35:20 -04:00
const values : Object = { } ;
const vals : Array < Array < any > > = [ [ ] ] ;
unique . forEach ( ( col , i ) = > {
values [ col ] = [ ] ;
vals [ i ] = [ ] ;
data . forEach ( ( item , _i ) = > {
if ( item [ config . unique ] == col ) {
vals [ i ] . push ( item [ config . col2 ] ) ;
}
} ) ;
} ) ;
return vals ;
}
protected onPropertyPaneFieldChanged ( propertyPath : string , oldValue : any , newValue : any ) : void {
var pPath = propertyPath ;
var pPathInd = propertyPath [ 12 ] ;
if ( pPath === 'numCharts' && oldValue != newValue ) {
if ( this . properties . chartConfig . length < newValue ) {
while ( this . properties . chartConfig . length < newValue ) {
this . properties . chartConfig . push ( this . defaultChartConfig ( 'Chart Description' ) ) ;
2018-04-27 11:26:24 -04:00
}
2017-03-13 06:35:20 -04:00
} else if ( this . properties . chartConfig . length > newValue ) {
2018-04-27 11:26:24 -04:00
while ( newValue < this . properties . chartConfig . length ) {
this . properties . chartConfig . pop ( ) ;
2017-03-13 06:35:20 -04:00
}
}
}
2018-04-27 11:26:24 -04:00
if ( propertyPath . indexOf ( '[' ) != - 1 ) {
pPath = propertyPath . substring ( 16 ) . replace ( '\"]' , '' ) ;
2017-03-13 06:35:20 -04:00
}
if ( pPath === 'url' && newValue == 'other' && ( oldValue != newValue ) ) {
this . urlTextDisabled = false ;
this . properties . chartConfig [ pPathInd ] . dataUrlDisabled = false ;
this . properties . chartConfig [ pPathInd ] . dataurl = oldValue ;
}
if ( pPath === 'url' && newValue != 'other' && ( oldValue != newValue ) ) {
this . properties . chartConfig [ pPathInd ] . dataurl = newValue ;
this . properties . chartConfig [ pPathInd ] . dataUrlDisabled = true ;
}
if ( ( pPath === 'url' && ( newValue != oldValue ) ) || ( pPath === 'dataurl' && ( newValue != oldValue ) ) ) {
this . _updateListTitles ( newValue , this . properties . chartConfig [ pPathInd ] ) ;
this . properties . chartConfig [ pPathInd ] [ 'other' ] = true ;
this . properties . state = true ;
}
if ( pPath === 'list' && ( newValue != oldValue ) ) {
var siteUrl = this . properties . chartConfig [ pPathInd ] [ 'dataurl' ] ;
this . _updateListColumns ( siteUrl , newValue , this . properties . chartConfig [ pPathInd ] ) ;
this . properties . chartConfig [ pPathInd ] . colsDisabled = false ;
}
if ( pPath === 'theme' && ( newValue != oldValue ) ) {
const newTheme = ChartOptions . RandomColors ( ) ;
this . properties . chartConfig [ pPathInd ] . bgColors = newTheme [ 'bgColors' ] ;
this . properties . chartConfig [ pPathInd ] . hoverColors = newTheme [ 'hoverColors' ] ;
}
this . context . propertyPane . refresh ( ) ;
this . render ( ) ;
}
protected onPropertyPaneConfigurationStart ( ) : void {
if ( this . properties . firstLoad || this . properties . chartConfig [ 0 ] . columns . length <= 0 || this . properties . chartConfig [ 0 ] . list . length <= 0 ) {
this . context . statusRenderer . displayLoadingIndicator ( this . domElement , 'First Time Configuration' ) ;
this . properties . firstLoad = false ;
this . _getSiteRootWeb ( )
. then ( ( response0 ) = > {
this . _getSites ( response0 [ 'Url' ] )
. then ( ( response ) = > {
var sites : IPropertyPaneDropdownOption [ ] = [ ] ;
sites . push ( { key :this.context.pageContext.web.absoluteUrl , text : 'This Site' } ) ;
sites . push ( { key : 'other' , text : 'Other Site (Specify Url)' } ) ;
for ( var _key in response . value ) {
if ( this . context . pageContext . web . absoluteUrl != response . value [ _key ] [ 'Url' ] ) {
sites . push ( { key : response.value [ _key ] [ 'Url' ] , text : response.value [ _key ] [ 'Title' ] } ) ;
}
}
this . _siteOptions = sites ;
this . properties . siteOptions = sites ;
this . _getListTitles ( this . properties . chartConfig [ 0 ] . dataurl )
. then ( ( response2 ) = > {
this . properties . listOptions = response2 . value . map ( ( list : ISPList ) = > {
return {
key : list.Title ,
text : list.Title
} ;
} ) ;
this . properties . chartConfig [ 0 ] . lists = this . properties . listOptions ;
this . _getListColumns ( this . properties . chartConfig [ 0 ] . list , this . properties . chartConfig [ 0 ] . url )
. then ( ( response3 ) = > {
var col : IPropertyPaneDropdownOption [ ] = [ ] ;
for ( var __key in response3 . value ) {
col . push ( { key : response3.value [ __key ] [ 'InternalName' ] , text : response3.value [ __key ] [ 'Title' ] } ) ;
}
this . _columnOptions = col ;
this . colsDisabled = false ;
this . listDisabled = false ;
this . context . propertyPane . refresh ( ) ;
this . context . statusRenderer . clearLoadingIndicator ( this . domElement ) ;
this . render ( ) ;
} ) ;
} ) ;
} ) ;
} ) ;
}
2018-04-27 11:26:24 -04:00
}
2017-03-13 06:35:20 -04:00
protected getPropertyPaneConfiguration ( ) : IPropertyPaneConfiguration {
let columnPropertyOptions : any ;
columnPropertyOptions = [
{
groupName : 'General Options' ,
groupFields : [
PropertyPaneTextField ( 'description' , {
label : 'Web Part Title'
} ) ,
PropertyPaneSlider ( 'numCharts' , {
label : 'Number of Charts' ,
min : 1 ,
max : 10
} ) ,
PropertyPaneSlider ( 'maxResults' , {
label : 'Max # of list items' ,
min : 1 ,
max : 1000
2018-04-27 11:26:24 -04:00
} )
2017-03-13 06:35:20 -04:00
]
}
] ;
for ( var _i = 0 ; _i < this . properties . numCharts ; _i ++ ) {
columnPropertyOptions . push (
{
groupName : "Chart " + ( _i + 1 ) + " Configuration" ,
groupFields : [
PropertyPaneTextField ( 'chartConfig[' + _i + ']["title"]' , {
label : "Chart Title"
} ) ,
PropertyPaneTextField ( 'chartConfig[' + _i + ']["description"]' , {
label : "Description "
} ) ,
PropertyPaneDropdown ( 'chartConfig[' + _i + ']["type"]' , {
label : 'Chart Type' ,
options : this._chartTypeOptions
} ) ,
PropertyPaneDropdown ( 'chartConfig[' + _i + ']["size"]' , {
label : 'Chart Size' ,
options : this._chartSizeOptions
2018-04-27 11:26:24 -04:00
} ) ,
2017-03-13 06:35:20 -04:00
PropertyPaneButton ( 'chartConfig[' + _i + ']["theme"]' , {
buttonType : 0 ,
text : 'Generate Theme' ,
icon : 'Color' ,
onClick : ( ( val ) = > {
return new Date ( ) . valueOf ( ) ;
} )
} ) ,
PropertyPaneDropdown ( 'chartConfig[' + _i + ']["url"]' , {
label : 'Chart Data Source' ,
options : this.properties.siteOptions
} ) ,
PropertyPaneTextField ( 'chartConfig[' + _i + ']["dataurl"]' , {
label : 'Chart Site Url (i.e. https://contoso.sharepoint.com/path)' ,
disabled : this.properties.chartConfig [ _i ] . dataUrlDisabled
} ) ,
PropertyPaneDropdown ( 'chartConfig[' + _i + ']["list"]' , {
label : 'List Data Source' ,
options : this.properties.chartConfig [ _i ] . lists ,
disabled : this.properties.chartConfig [ _i ] . listsDisabled
} ) ,
PropertyPaneDropdown ( 'chartConfig[' + _i + ']["col1"]' , {
label : 'Label Column' ,
options : this.properties.chartConfig [ _i ] . columns ,
disabled : this.properties.chartConfig [ _i ] . colsDisabled
} ) ,
PropertyPaneDropdown ( 'chartConfig[' + _i + ']["col2"]' , {
label : 'Data Column' ,
options : this.properties.chartConfig [ _i ] . columns ,
disabled : this.properties.chartConfig [ _i ] . colsDisabled
} ) ,
PropertyPaneDropdown ( 'chartConfig[' + _i + ']["unique"]' , {
label : 'Unique Identifier' ,
options : this.properties.chartConfig [ _i ] . columns ,
disabled : this.properties.chartConfig [ _i ] . colsDisabled
} ) ,
PropertyPaneDropdown ( 'chartConfig[' + _i + ']["act"]' , {
label : 'Operation' ,
options : this._chartColActions ,
disabled : this.properties.chartConfig [ _i ] . colsDisabled
} )
]
} ) ;
}
return {
pages : [
{
header : {
description : strings.PropertyPaneDescription
} ,
groups : columnPropertyOptions
}
]
} ;
}
protected onPropertyPaneConfigurationComplete ( ) {
this . render ( ) ;
}
private _getSiteRootWeb ( ) : Promise < string [ ] > {
return this . context . spHttpClient . get ( this . context . pageContext . web . absoluteUrl + ` /_api/Site/RootWeb? $ select=Title,Url ` , SPHttpClient . configurations . v1 )
. then ( ( response : SPHttpClientResponse ) = > {
return response . json ( ) ;
} ) ;
}
private _getSites ( rootWebUrl : string ) : Promise < ISPLists > {
return this . context . spHttpClient . get ( rootWebUrl + ` /_api/web/webs? $ select=Title,Url ` , SPHttpClient . configurations . v1 )
. then ( ( response : SPHttpClientResponse ) = > {
return response . json ( ) ;
} ) ;
}
private _getListTitles ( site : string ) : Promise < ISPLists > {
return this . context . spHttpClient . get ( site + ` /_api/web/lists? $ filter=Hidden eq false and BaseType eq 0 ` , SPHttpClient . configurations . v1 )
. then ( ( response : SPHttpClientResponse ) = > {
return response . json ( ) ;
} ) ;
}
private _getListColumns ( listName : string , listsite : string ) : Promise < any > {
return this . context . spHttpClient . get ( listsite + ` /_api/web/lists/GetByTitle(' ${ listName } ')/Fields? $ filter=Hidden eq false and ReadOnlyField eq false and TypeAsString ne 'User' and TypeAsString ne 'Lookup' ` , SPHttpClient . configurations . v1 )
. then ( ( response : SPHttpClientResponse ) = > {
return response . json ( ) ;
} ) ;
}
public getData ( chartConfig : Object ) {
return this . context . spHttpClient . get ( chartConfig [ 'dataurl' ] + ` /_api/web/lists/GetByTitle( \ ' ${ chartConfig [ 'list' ] } \ ')/items? $ orderby=Id desc& $ limit=10& $ top= ${ this . properties . maxResults } ` , SPHttpClient . configurations . v1 )
. then ( ( response : SPHttpClientResponse ) = > {
return response . json ( ) ;
} ) ;
2018-04-27 11:26:24 -04:00
}
2017-03-13 06:35:20 -04:00
private _updateListTitles ( siteUrl : string , _chartConfig : ChartConfiguration ) : void {
this . _getListTitles ( siteUrl ) . then ( ( response ) = > {
var respLists : IPropertyPaneDropdownOption [ ] = [ ] ;
for ( var _key in response . value ) {
respLists . push ( { key : response.value [ _key ] [ 'Title' ] , text : response.value [ _key ] [ 'Title' ] } ) ;
}
this . _dropdownOptions = respLists ;
_chartConfig . lists = respLists ;
this . context . propertyPane . refresh ( ) ;
} ) . catch ( ( err ) = > {
this . context . statusRenderer . clearLoadingIndicator ( this . domElement ) ;
this . context . statusRenderer . renderError ( this . domElement , "There was an error loading your list, please verify the selected list has Calendar Events or choose a new list." ) ;
} ) ;
}
private _updateListColumns ( siteUrl : string , listName : string , _chartConfig : ChartConfiguration ) : void {
this . _getListColumns ( listName , siteUrl ) . then ( ( response ) = > {
var respLists : IPropertyPaneDropdownOption [ ] = [ ] ;
console . log ( response . value ) ;
for ( var _key in response . value ) {
respLists . push ( { key : response.value [ _key ] [ 'InternalName' ] , text : response.value [ _key ] [ 'Title' ] } ) ;
}
this . _columnOptions = respLists ;
_chartConfig . columns = respLists ;
this . context . propertyPane . refresh ( ) ;
} ) . catch ( ( err ) = > {
this . context . statusRenderer . clearLoadingIndicator ( this . domElement ) ;
this . context . statusRenderer . renderError ( this . domElement , "There was an error loading your list, please verify the selected list has Calendar Events or choose a new list." ) ;
} ) ;
}
}