Skip to content
Snippets Groups Projects
Bar.tsx 9.18 KiB
Newer Older
  • Learn to ignore specific revisions
  • Hugo NOUTS's avatar
    Hugo NOUTS committed
    import { ScaleBand, ScaleLinear } from 'd3-scale'
    
    Romain CREY's avatar
    Romain CREY committed
    import { detect } from 'detect-browser'
    
    import { DataloadState, FluidType, TimeStep } from 'enums'
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
    import { DateTime } from 'luxon'
    
    Yoan VALLET's avatar
    Yoan VALLET committed
    import { Dataload } from 'models'
    
    import React, { useEffect, useState } from 'react'
    
    Yoan VALLET's avatar
    Yoan VALLET committed
    import DateChartService from 'services/dateChart.service'
    
    import {
    
      setCurrentDataChartIndex,
    
      setSelectedDate,
    
    } from 'store/chart/chart.slice'
    
    import { useAppDispatch, useAppSelector } from 'store/hooks'
    
    Yoan VALLET's avatar
    Yoan VALLET committed
    
    
    Hugo NOUTS's avatar
    Hugo NOUTS committed
    interface BarProps {
      index: number
    
    Yoan VALLET's avatar
    Yoan VALLET committed
      dataload: Dataload
      compareDataload: Dataload | null
    
      fluidType: FluidType
    
      timeStep: TimeStep
    
    Hugo NOUTS's avatar
    Hugo NOUTS committed
      xScale: ScaleBand<string>
      yScale: ScaleLinear<number, number>
      height: number
    
      isSwitching: boolean
    
    Rémi PAPIN's avatar
    Rémi PAPIN committed
      isDuel?: boolean
    
      isMultiMissingFluid?: boolean
    
    Hugo NOUTS's avatar
    Hugo NOUTS committed
    }
    
    
    const Bar = ({
      index,
      dataload,
      compareDataload,
    
      fluidType,
    
      timeStep,
    
      xScale,
      yScale,
      height,
      isSwitching,
    
    Rémi PAPIN's avatar
    Rémi PAPIN committed
      isDuel,
    
    }: BarProps) => {
    
      const dispatch = useAppDispatch()
      const { selectedDate } = useAppSelector(state => state.ecolyo.chart)
    
    Hugo NOUTS's avatar
    Hugo NOUTS committed
      const [clicked, setClicked] = useState(false)
      const [animationEnded, setAnimationEnded] = useState(false)
      const [compareAnimationEnded, setCompareAnimationEnded] = useState(false)
    
    Romain CREY's avatar
    Romain CREY committed
      const browser = detect()
    
    
      const fluidStyle =
        fluidType === FluidType.MULTIFLUID ? 'MULTIFLUID' : FluidType[fluidType]
    
    Hugo NOUTS's avatar
    Hugo NOUTS committed
      const handleClick = () => {
    
        if (!isSwitching && !isDuel && clickable) {
    
          setClicked(true)
    
          dispatch(setSelectedDate(dataload.date))
    
    Hugo NOUTS's avatar
    Hugo NOUTS committed
      }
    
      const onAnimationEnd = () => {
        setClicked(false)
        setAnimationEnded(true)
      }
    
    Hugo NOUTS's avatar
    Hugo NOUTS committed
      const onCompareAnimationEnd = () => {
        setClicked(false)
        setCompareAnimationEnded(true)
      }
    
    
      let value = dataload.value
      /**
       * Use 10% of average as the placeholder if average is above 1,
       * otherwise use a minimum of 1 as the placeholder.
       */
      if (value === -1 && average) {
        value = average > 1 ? average * 0.1 : 1
      }
      const yScaleValue = yScale(value) ?? 0
      const yScaleCompareValue = yScale(compareDataload?.value ?? 0) ?? 0
    
      const xScaleValue =
        xScale(dataload.date.toLocaleString(DateTime.DATETIME_SHORT)) ?? 0
    
    Yoan VALLET's avatar
    Yoan VALLET committed
      const isSelectedDate = isDuel
        ? false
        : new DateChartService().compareStepDate(
            timeStep,
            selectedDate,
            dataload.date
          )
    
    Romain CREY's avatar
    Romain CREY committed
    
    
      const clickedAnim = clicked ? 'bounce-2 delay' : ''
      const disabled = clickable ? '' : 'disabled'
      const selected = isSelectedDate ? 'selected' : ''
    
    Hugo NOUTS's avatar
    Hugo NOUTS committed
    
    
      const getBarClass = () => {
    
        const upcoming = dataload.value === -1 ? 'bar-UNCOMING' : ''
        const edgeBrowser = browser && browser.name !== 'edge'
        const bounce = edgeBrowser ? '1' : '3'
    
        const baseStyles = `bar-${fluidStyle} ${upcoming} ${weekdays} ${selected} ${disabled}`
    
        if (clicked) {
    
          return `${baseStyles} ${clickedAnim}`
    
        if (animationEnded) {
          return baseStyles
        }
        return `${baseStyles} bounce-${bounce} delay--${index}`
    
      }
    
      const getCompareBarClass = () => {
    
        const animate = `bounce-2 delay--${clicked ? 0 : index}`
        const animationClass = compareAnimationEnded ? '' : animate
    
    Hugo NOUTS's avatar
    Hugo NOUTS committed
    
    
        return `bar-compare-${fluidStyle} ${selected} ${animationClass} ${clickedAnim}`
    
      }
    
      const barBackgroundClass = isSelectedDate
    
    Hugo NOUTS's avatar
    Hugo NOUTS committed
    
      const getBandWidth = (): number => {
    
        return compare ? xScale.bandwidth() / 2 : xScale.bandwidth()
    
    Hugo NOUTS's avatar
    Hugo NOUTS committed
      }
    
      const topRoundedRect = (
    
        x: number,
        y: number,
        width: number,
        height: number
    
      ): string => {
    
        const radius = height > 4 ? 4 : height / 4
    
        return `
          M${x},${y + radius}
          a${radius},${radius} 0 0 1 ${radius},${-radius}
          h${width - 2 * radius}
          a${radius},${radius} 0 0 1 ${radius},${radius}
          v${height - radius}
          h${-width}
          z`
    
    Hugo NOUTS's avatar
    Hugo NOUTS committed
      }
    
      useEffect(() => {
    
    Yoan VALLET's avatar
    Yoan VALLET committed
        if (isSelectedDate && !isDuel) {
    
    Hugo NOUTS's avatar
    Hugo NOUTS committed
          setClicked(true)
    
          dispatch(setCurrentDataChartIndex(index))
    
    Hugo NOUTS's avatar
    Hugo NOUTS committed
        }
    
    Yoan VALLET's avatar
    Yoan VALLET committed
      }, [dispatch, isSelectedDate, isDuel, index])
    
    Hugo NOUTS's avatar
    Hugo NOUTS committed
    
      return (
        <g>
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
          {height > 0 && (
    
            <g
              transform={`translate(${xScaleValue}, -40)`}
    
              className={`barContainer ${disabled}`}
    
    Hugo NOUTS's avatar
    Hugo NOUTS committed
              <rect
    
                onClick={!weekdays ? handleClick : () => undefined}
    
    Hugo NOUTS's avatar
    Hugo NOUTS committed
                x="0"
                y="0"
    
                width={compare ? getBandWidth() * 2 : getBandWidth()}
    
    Hugo NOUTS's avatar
    Hugo NOUTS committed
                height={height + 40}
                className={`background-${barBackgroundClass}`}
    
    Romain CREY's avatar
    Romain CREY committed
                fill="#E0E0E0"
    
    Hugo NOUTS's avatar
    Hugo NOUTS committed
              />
            </g>
    
          {/* placeholder bar for upcoming values */}
          {height > 0 &&
            dataload.state === DataloadState.COMING &&
            dataload.date < DateTime.local() && (
              <g
                transform={`translate(${xScaleValue}, ${yScaleValue})`}
                className="barFill"
              >
                <defs>
                  <linearGradient id="gradient" x1="0" x2="0" y1="0" y2="1">
                    <stop id="stop-color-1" offset="0%" />
                    <stop id="stop-color-2" offset="100%" />
                  </linearGradient>
                </defs>
                <path
                  d={topRoundedRect(
                    compare ? getBandWidth() : 0,
                    0,
                    weekdays ? 3 : getBandWidth(),
                    height - yScaleValue
                  )}
                  className={getBarClass()}
                  onClick={!weekdays ? handleClick : () => undefined}
                  onAnimationEnd={onAnimationEnd}
                />
              </g>
            )}
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
          {height > 0 && dataload.value >= 0 && isMultiMissingFluid ? (
    
            <g
              transform={`translate(${xScaleValue}, ${yScaleValue})`}
              fill="#00000"
    
              className="barFill"
    
              <defs>
                <linearGradient
                  id="gradient"
    
                  x1="0"
                  x2="0"
                  y1="0"
                  y2="1"
                >
                  <stop id="stop-color-1" offset="0%" />
                  <stop id="stop-color-2" offset="100%" />
                </linearGradient>
                <pattern
                  id="diagonalHatch"
                  patternUnits="userSpaceOnUse"
    
    Guilhem CARRON's avatar
    Guilhem CARRON committed
                  width="8"
                  height="8"
    
                  <rect x="0" y="0" width="100%" height="100%" fill="#121212" />
    
    Guilhem CARRON's avatar
    Guilhem CARRON committed
                  <path d="M0 8h20z" strokeWidth="3" stroke="#71612E" fill="none" />
    
                </pattern>
                <pattern
                  id="diagonalHatchSelected"
                  patternUnits="userSpaceOnUse"
    
    Guilhem CARRON's avatar
    Guilhem CARRON committed
                  width="8"
                  height="8"
    
                  <rect x="0" y="0" width="100%" height="100%" fill="#121212" />
    
    Guilhem CARRON's avatar
    Guilhem CARRON committed
                  <path d="M0 8h20z" strokeWidth="3" stroke="#E3B82A" fill="none" />
    
                </pattern>
              </defs>
              <path
                d={topRoundedRect(
    
                  compare ? getBandWidth() : 0,
    
                  0,
                  getBandWidth(),
                  height - yScaleValue
                )}
    
                stroke={!isSelectedDate ? '#71612E' : '#e3b82a'}
    
                fill={
                  isSelectedDate
                    ? 'url(#diagonalHatchSelected)'
                    : 'url(#diagonalHatch)'
                }
    
                onClick={!weekdays ? handleClick : () => undefined}
    
                onAnimationEnd={onAnimationEnd}
              />
            </g>
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
          ) : (
            height > 0 &&
            dataload.value &&
            dataload.value >= 0 && (
              <g transform={`translate(${xScaleValue}, ${yScaleValue})`}>
                <defs>
                  <linearGradient
                    id="gradient"
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
                    x1="0"
                    x2="0"
                    y1="0"
                    y2="1"
                  >
                    <stop id="stop-color-1" offset="0%" />
                    <stop id="stop-color-2" offset="100%" />
                  </linearGradient>
                </defs>
                <path
                  d={topRoundedRect(
    
                    compare ? getBandWidth() : 0,
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
                    0,
                    weekdays ? 3 : getBandWidth(),
                    height - yScaleValue
                  )}
    
                  className={isDuel ? 'bar-duel' : getBarClass()}
    
                  onClick={!weekdays ? handleClick : () => undefined}
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
                  onAnimationEnd={onAnimationEnd}
                />
              </g>
            )
          )}
    
    Hugo NOUTS's avatar
    Hugo NOUTS committed
    
    
          {compare && compareDataload && compareDataload.value >= 0 && (
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
            <g transform={`translate(${xScaleValue}, ${yScaleCompareValue})`}>
              <defs>
                <linearGradient
                  id="gradient-compare"
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
                  x1="0"
                  x2="0"
                  y1="0"
                  y2="1"
                >
                  <stop id="stop-compare-color-1" offset="0%" />
                  <stop id="stop-compare-color-2" offset="100%" />
                </linearGradient>
              </defs>
              <path
                d={topRoundedRect(
                  0,
                  0,
                  getBandWidth(),
                  height - yScaleCompareValue
                )}
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
                onClick={handleClick}
                onAnimationEnd={onCompareAnimationEnd}
              />
            </g>
          )}
    
    Hugo NOUTS's avatar
    Hugo NOUTS committed
        </g>
      )
    }
    
    export default Bar