github.com/uchennaokeke444/nomad@v0.11.8/website/components/featured-slider/index.jsx (about)

     1  import React, { Component } from 'react'
     2  import StatusBar from './StatusBar'
     3  import marked from 'marked'
     4  import Button from '@hashicorp/react-button'
     5  import Image from '@hashicorp/react-image'
     6  
     7  class FeaturedSlider extends Component {
     8    constructor(props) {
     9      super(props)
    10      const timing = this.props.timing ? parseInt(this.props.timing) : 10
    11      this.state = {
    12        active: 0,
    13        timing: timing,
    14        numFrames: this.props.features.length,
    15        measure: true,
    16        containerWidth: 0
    17      }
    18  
    19      this.frames = []
    20  
    21      this.handleClick = this.handleClick.bind(this)
    22      this.throttledResize = this.throttledResize.bind(this)
    23      this.measureFrameSize = this.measureFrameSize.bind(this)
    24      this.resetTimer = this.resetTimer.bind(this)
    25      this.resizeTimeout = null
    26    }
    27  
    28    componentDidMount() {
    29      if (this.state.numFrames > 1) {
    30        this.timer = setInterval(() => this.tick(), this.state.timing * 1000)
    31        this.measureFrameSize()
    32      }
    33      window.addEventListener('resize', this.throttledResize, false)
    34    }
    35  
    36    componentWillUnmount() {
    37      clearInterval(this.timer)
    38      window.removeEventListener('resize', this.throttledResize)
    39    }
    40  
    41    componentDidUpdate(prevProps, prevState) {
    42      if (this.props.features !== prevProps.features) {
    43        if (this.props.features.length != prevState.numFrames) {
    44          this.setState(
    45            {
    46              numFrames: this.props.features.length,
    47              measure: true
    48            },
    49            () => {
    50              if (this.props.features.length === 1) {
    51                clearInterval(this.timer)
    52                window.removeEventListener('resize', this.throttledResize)
    53              }
    54            }
    55          )
    56        }
    57        if (prevState.active > this.props.features.length - 1) {
    58          this.setState({ active: 0 })
    59        }
    60      }
    61  
    62      if (this.props.timing && parseInt(this.props.timing) != prevState.timing) {
    63        this.setState(
    64          {
    65            timing: parseInt(this.props.timing),
    66            active: 0
    67          },
    68          this.resetTimer
    69        )
    70      }
    71      // If we're measuring on this update get the width
    72      if (!prevState.measure && this.state.measure && this.state.numFrames > 1) {
    73        this.measureFrameSize()
    74      }
    75    }
    76  
    77    resetTimer() {
    78      clearInterval(this.timer)
    79      this.timer = setInterval(() => this.tick(), this.state.timing * 1000)
    80    }
    81  
    82    throttledResize() {
    83      this.resizeTimeout && clearTimeout(this.resizeTimeout)
    84      this.resizeTimeout = setTimeout(() => {
    85        this.resizeTimeout = null
    86        this.setState({ measure: true })
    87      }, 250)
    88    }
    89  
    90    tick() {
    91      const nextSlide =
    92        this.state.active === this.state.numFrames - 1 ? 0 : this.state.active + 1
    93      this.setState({ active: nextSlide })
    94    }
    95  
    96    handleClick(i) {
    97      if (i === this.state.active) return
    98      this.setState({ active: i }, this.resetTimer)
    99    }
   100  
   101    measureFrameSize() {
   102      // All frames are the same size, so we measure the first one
   103      if (this.frames[0]) {
   104        const { width } = this.frames[0].getBoundingClientRect()
   105        this.setState({
   106          frameSize: width,
   107          containerWidth: width * this.state.numFrames,
   108          measure: false
   109        })
   110      }
   111    }
   112  
   113    render() {
   114      // Clear our frames array so we don't keep old refs around
   115      this.frames = []
   116      const { theme, brand, features } = this.props
   117      const { measure, active, timing, numFrames, containerWidth } = this.state
   118  
   119      const single = numFrames === 1
   120  
   121      // Create inline styling for slide container
   122      // If we're measuring, or have a single slide then no inline styles should be applied
   123      const containerStyle =
   124        measure || single
   125          ? {}
   126          : {
   127              width: `${containerWidth}px`,
   128              transform: `translateX(-${(containerWidth / numFrames) * active}px`
   129            }
   130  
   131      // Create inline styling to apply to each frame
   132      // If we're measuring or have a single slide then no inline styles should be applied
   133      const frameStyle =
   134        measure || single ? {} : { float: 'left', width: `${100 / numFrames}%` }
   135  
   136      return (
   137        <div className="g-featured-slider">
   138          {!single && (
   139            <div
   140              className={`logo-bar-container${numFrames === 2 ? ' double' : ''}`}
   141            >
   142              {features.map((feature, i) => (
   143                <div
   144                  className="logo-bar"
   145                  onClick={() => this.handleClick(i)}
   146                  key={feature.logo.url}
   147                >
   148                  <div className="logo-container">
   149                    <Image url={feature.logo.url} alt={feature.logo.alt} />
   150                  </div>
   151                  <StatusBar
   152                    theme={theme}
   153                    active={active === i}
   154                    timing={timing}
   155                    brand={brand}
   156                  />
   157                </div>
   158              ))}
   159            </div>
   160          )}
   161          <div className="feature-container">
   162            <div className="slider-container" style={containerStyle}>
   163              {/* React pushes a null ref the first time, so we're filtering those out. */}
   164              {/* see https://reactjs.org/docs/refs-and-the-dom.html#caveats-with-callback-refs */}
   165              {features.map(feature => (
   166                <div
   167                  className={`slider-frame${single ? ' single' : ''}`}
   168                  style={frameStyle}
   169                  ref={el => el && this.frames.push(el)}
   170                  key={feature.heading}
   171                >
   172                  <div className="feature">
   173                    <div className="feature-image">
   174                      <a href={feature.link.url}>
   175                        <Image
   176                          url={feature.image.url}
   177                          alt={feature.image.alt}
   178                          aspectRatio={single ? [16, 10, 500] : [16, 9, 500]}
   179                        />
   180                      </a>
   181                    </div>
   182                    <div className="feature-content g-type-body">
   183                      {single && (
   184                        <div className="single-logo">
   185                          <Image url={feature.logo.url} alt={feature.logo.alt} />
   186                        </div>
   187                      )}
   188                      <h3
   189                        className="g-type-display-4"
   190                        dangerouslySetInnerHTML={{
   191                          __html: marked.inlineLexer(feature.heading, [])
   192                        }}
   193                      />
   194                      <p
   195                        className="g-type-body"
   196                        dangerouslySetInnerHTML={{
   197                          __html: marked.inlineLexer(feature.content, [])
   198                        }}
   199                      />
   200                      <Button
   201                        theme={{
   202                          brand,
   203                          variant: 'secondary',
   204                          background: theme
   205                        }}
   206                        linkType={feature.link.type}
   207                        title={feature.link.text}
   208                        url={feature.link.url}
   209                      />
   210                    </div>
   211                  </div>
   212                </div>
   213              ))}
   214            </div>
   215          </div>
   216        </div>
   217      )
   218    }
   219  }
   220  
   221  export default FeaturedSlider