import * as Prism from 'prismjs' ; import * as React from 'react' ; import * as debounce from 'debounce' ; import { MessageState } from '../../store/defaultState' ; import { AutoScrollCheckBox , AutoScrollLabel , Message , MessageContainer , MessageContents , MessageLogContainer , MessageLogWrapper , MetaContainer , MetaData , MetaType , } from './styles' ; interface Props { messages : MessageState [ ] ; } interface State { autoScroll : boolean ; isHovered : boolean ; } /**
 * MessageLog Component
 */ export default class MessageLog extends React . Component < Props , State > { debouncedScroll : ( container : HTMLDivElement ) => void ; containerRef = React . createRef < HTMLDivElement > ( ) ; constructor param { Props } props constructor ( props ) { super ( props ) ; this . state = { autoScroll : true , isHovered : false , } ; this . debouncedScroll = debounce ( ( container ) => { container . scrollTop = container . scrollHeight ; } , 200 ) ; } param prevProps returns { State } getSnapshotBeforeUpdate ( prevProps ) { const { messages : prevMessages } = prevProps ; const { messages } = this . props ; if ( prevMessages . length !== messages . length ) { return this . state ; } return null ; } param { Props } prevProps param { State } _prevState param { State } snapshot componentDidUpdate ( prevProps , _prevState , snapshot ) { const { messages : prevMessages } = prevProps ; const { messages } = this . props ; const container = this . containerRef . current ; if ( prevMessages . length !== messages . length ) { Prism . highlightAllUnder ( container ) ; } if ( snapshot && snapshot . autoScroll ) { this . debouncedScroll ( container ) ; } } param event handleScroll = ( event ) : void => { const { isHovered } = this . state ; if ( ! isHovered ) { return ; } const container = event . currentTarget ; const { scrollTop , scrollHeight , offsetHeight } = container ; const isScrolledToBottom = scrollTop === scrollHeight - offsetHeight ; this . setState ( { autoScroll : isScrolledToBottom , } ) ; } ; param { MessageState [ ] } returns { React . ReactNode } renderMessages = ( messages ) : React . ReactNode => messages . map ( ( message ) => { const { data , origin , timestamp , type } = message ; return ( < Message key = { timestamp } > < MetaContainer type = { type } > < MetaType > { type } < /MetaType> < MetaData > { origin } < /MetaData> < MetaData > { timestamp } < /MetaData> < /MetaContainer> < MessageContainer type = { type } > < MessageContents type = { type } > < code className = "language-js" > { JSON . stringify ( data , null , 2 ) } < /code> < /MessageContents> < /MessageContainer> < /Message>); } ) ; returns { React . ReactElement < typeof MessageLogWrapper > } render ( ) { const { autoScroll } = this . state ; const { messages } = this . props ; return ( < MessageLogWrapper > < MessageLogContainer ref = { this . containerRef } onScroll = { this . handleScroll } onMouseOver = { ( ) => this . setState ( { isHovered : true } ) } onMouseOut = { ( ) => this . setState ( { isHovered : false } ) } onFocus = { ( ) => { } } onBlur = { ( ) => { } } > { this . renderMessages ( messages ) } < /MessageLogContainer> < AutoScrollLabel htmlFor = "auto-scroll" > < AutoScrollCheckBox id = "auto-scroll" type = "checkbox" checked = { autoScroll } onChange = { ( ) => this . setState ( { autoScroll : ! autoScroll } ) } /> Auto scroll < /AutoScrollLabel> < /MessageLogWrapper>); } }