/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */ import React from 'react' ; import { t , getNumberFormatter , NumberFormatter , smartDateVerboseFormatter , TimeFormatter , computeMaxFontSize , BRAND_COLOR , styled , } from '@superset-ui/core' ; import { EChartsCoreOption } from 'echarts' ; import Echart from '../components/Echart' ; import { TimeSeriesDatum } from './types' ; const defaultNumberFormatter = getNumberFormatter ( ) ; const PROPORTION = { KICKER : 0.1 , HEADER : 0.3 , SUBHEADER : 0.125 , TRENDLINE : 0.3 , } ; type BigNumberVisProps = { className ? : string ; width : number ; height : number ; bigNumber ? : number | null ; bigNumberFallback ? : TimeSeriesDatum ; headerFormatter : NumberFormatter | TimeFormatter ; formatTime : TimeFormatter ; headerFontSize : number ; kickerFontSize : number ; subheader : string ; subheaderFontSize : number ; showTimestamp ? : boolean ; showTrendLine ? : boolean ; startYAxisAtZero ? : boolean ; timeRangeFixed ? : boolean ; timestamp ? : number ; trendLineData ? : TimeSeriesDatum [ ] ; mainColor : string ; echartOptions : EChartsCoreOption ; } ; class BigNumberVis extends React . PureComponent < BigNumberVisProps > { static defaultProps = { className : '' , headerFormatter : defaultNumberFormatter , formatTime : smartDateVerboseFormatter , headerFontSize : PROPORTION . HEADER , kickerFontSize : PROPORTION . KICKER , mainColor : BRAND_COLOR , showTimestamp : false , showTrendLine : false , startYAxisAtZero : true , subheader : '' , subheaderFontSize : PROPORTION . SUBHEADER , timeRangeFixed : false , } ; getClassName ( ) { const { className , showTrendLine , bigNumberFallback } = this . props ; const names = `superset-legacy-chart-big-number ${ className } ${ bigNumberFallback ? 'is-fallback-value' : '' }` ; if ( showTrendLine ) return names ; return `${ names } no-trendline` ; } createTemporaryContainer ( ) { const container = document . createElement ( 'div' ) ; container . className = this . getClassName ( ) ; container . style . position = 'absolute' ; container . style . opacity = '0' ; return container ; } renderFallbackWarning ( ) { const { bigNumberFallback , formatTime , showTimestamp } = this . props ; if ( ! bigNumberFallback || showTimestamp ) return null ; return ( < span className = "alert alert-warning" role = "alert" title = { t ( `Last available value seen on %s` , formatTime ( bigNumberFallback [ 0 ] ) ) } > { t ( 'Not up to date' ) } < /span>); } renderKicker ( maxHeight ) { const { timestamp , showTimestamp , formatTime , width } = this . props ; if ( ! showTimestamp ) return null ; const text = timestamp === null ? '' : formatTime ( timestamp ) ; const container = this . createTemporaryContainer ( ) ; document . body . append ( container ) ; const fontSize = computeMaxFontSize ( { text , maxWidth : width , maxHeight , className : 'kicker' , container , } ) ; container . remove ( ) ; return ( < div className = "kicker" style = { { fontSize , height : maxHeight , } } > { text } < /div>); } renderHeader ( maxHeight ) { const { bigNumber , headerFormatter , width } = this . props ; const text = bigNumber === null ? t ( 'No data' ) : headerFormatter ( bigNumber ) ; const container = this . createTemporaryContainer ( ) ; document . body . append ( container ) ; const fontSize = computeMaxFontSize ( { text , maxWidth : width , maxHeight , className : 'header-line' , container , } ) ; container . remove ( ) ; return ( < div className = "header-line" style = { { fontSize , height : maxHeight , } } > { text } < /div>); } renderSubheader ( maxHeight ) { const { bigNumber , subheader , width , bigNumberFallback } = this . props ; let fontSize = 0 ; const NO_DATA_OR_HASNT_LANDED = t ( 'No data after filtering or data is NULL for the latest time record' ) ; const NO_DATA = t ( 'Try applying different filters or ensuring your datasource has data' ) ; let text = subheader ; if ( bigNumber === null ) { text = bigNumberFallback ? NO_DATA : NO_DATA_OR_HASNT_LANDED ; } if ( text ) { const container = this . createTemporaryContainer ( ) ; document . body . append ( container ) ; fontSize = computeMaxFontSize ( { text , maxWidth : width , maxHeight , className : 'subheader-line' , container , } ) ; container . remove ( ) ; return ( < div className = "subheader-line" style = { { fontSize , height : maxHeight , } } > { text } < /div>); } return null ; } renderTrendline ( maxHeight ) { const { width , trendLineData , echartOptions } = this . props ; if ( ! trendLineData ?. some ( d => d [ 1 ] !== null ) ) { return null ; } return ( < Echart width = { Math . floor ( width ) } height = { maxHeight } echartOptions = { echartOptions } / > ) ; } render ( ) { const { showTrendLine , height , kickerFontSize , headerFontSize , subheaderFontSize , } = this . props ; const className = this . getClassName ( ) ; if ( showTrendLine ) { const chartHeight = Math . floor ( PROPORTION . TRENDLINE * height ) ; const allTextHeight = height - chartHeight ; return ( < div className = { className } > < div className = "text-container" style = { { height : allTextHeight } } > { this . renderFallbackWarning ( ) } { this . renderKicker ( Math . ceil ( kickerFontSize * ( 1 - PROPORTION . TRENDLINE ) * height ) ) } { this . renderHeader ( Math . ceil ( headerFontSize * ( 1 - PROPORTION . TRENDLINE ) * height ) ) } { this . renderSubheader ( Math . ceil ( subheaderFontSize * ( 1 - PROPORTION . TRENDLINE ) * height ) ) } < /div> { this . renderTrendline ( chartHeight ) } < /div>); } return ( < div className = { className } style = { { height } } > { this . renderFallbackWarning ( ) } { this . renderKicker ( kickerFontSize * height ) } { this . renderHeader ( Math . ceil ( headerFontSize * height ) ) } { this . renderSubheader ( Math . ceil ( subheaderFontSize * height ) ) } < /div>); } } export default styled ( BigNumberVis ) `
  font-family: ${ ( { theme } ) => theme . typography . families . sansSerif };
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: center;

  &.no-trendline .subheader-line {
    padding-bottom: 0.3em;
  }

  .text-container {
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: flex-start;
    .alert {
      font-size: ${ ( { theme } ) => theme . typography . sizes . s };
      margin: -0.5em 0 0.4em;
      line-height: 1;
      padding: 2px 4px 3px;
      border-radius: 3px;
    }
  }

  .kicker {
    font-weight: ${ ( { theme } ) => theme . typography . weights . light };
    line-height: 1em;
    padding-bottom: 2em;
  }

  .header-line {
    font-weight: ${ ( { theme } ) => theme . typography . weights . normal };
    position: relative;
    line-height: 1em;
    span {
      position: absolute;
      bottom: 0;
    }
  }

  .subheader-line {
    font-weight: ${ ( { theme } ) => theme . typography . weights . light };
    line-height: 1em;
    padding-bottom: 0;
  }

  &.is-fallback-value {
    .kicker,
    .header-line,
    .subheader-line {
      opacity: 0.5;
    }
  }

  .superset-data-ui-tooltip {
    z-index: 1000;
    background: #000;
  }
` ;