jeansaad/hotel

View on GitHub
src/app/components/Content/index.tsx

Summary

Maintainability
B
5 hrs
Test Coverage
import { observer } from 'mobx-react'
import * as React from 'react'
import ArrowDownwardIcon from '@material-ui/icons/ArrowDownward'
import ClearAllIcon from '@material-ui/icons/ClearAll'
import Link from '../Link'

import Store, { RUNNING } from '../../Store'
import './index.css'

export interface IProps {
  store: Store
}

@observer
class Content extends React.Component<IProps, {}> {
  private el: HTMLDivElement | null = null
  private atBottom: boolean = true
  private keydownListener: ((event: KeyboardEvent) => void) | null = null

  public getKeydownListener(store: Store) {
    if (this.keydownListener == null) {
      this.keydownListener = (event: KeyboardEvent) => {
        const selectedMonitor = store.monitors.get(store.selectedMonitorId);
        const isSelectedMonitorRunning = selectedMonitor?.status === RUNNING
        // shift + alt to avoid system shortcuts
        if (event.shiftKey && event.altKey) {
          switch (event.code) {
            case 'KeyK':
              store.clearOutput(store.selectedMonitorId)
              break
            case 'KeyS':
              this.scrollToBottom()
              break
            case 'Comma':
              if (!isSelectedMonitorRunning) {
                this.scrollToBottom()
              }
              store.toggleMonitor(store.selectedMonitorId)
              break
            case 'Period':
              if (!isSelectedMonitorRunning) {
                store.clearOutput(store.selectedMonitorId)
              }
              store.toggleMonitor(store.selectedMonitorId)
              break
          }
        }
      }
    }
    return this.keydownListener
  }

  public componentDidMount() {
    document.addEventListener(
      'keydown',
      this.getKeydownListener(this.props.store),
    )
  }

  public componentWillUnmount() {
    document.removeEventListener(
      'keydown',
      this.getKeydownListener(this.props.store),
    )
  }

  public componentWillUpdate() {
    if (this.el) {
      this.atBottom = this.isAtBottom()
    }
  }

  public componentDidUpdate() {
    if (this.atBottom) {
      this.scrollToBottom()
    }
  }

  public isAtBottom() {
    if (this.el) {
      const { scrollHeight, scrollTop, clientHeight } = this.el
      return scrollHeight - scrollTop === clientHeight
    } else {
      return true
    }
  }

  public scrollToBottom() {
    if (this.el) {
      this.el.scrollTop = this.el.scrollHeight
    }
  }

  public onScroll() {
    this.atBottom = this.isAtBottom()
  }

  public render() {
    const { store } = this.props
    const monitor = store.monitors.get(store.selectedMonitorId)
    return (
      <div
        className="content"
        onScroll={() => this.onScroll()}
        ref={el => {
          this.el = el
        }}
      >
        <div className="content-bar">
          <span>
            <Link id={store.selectedMonitorId} />
          </span>
          <span>
            <button
              title="Clear output (shift + alt + K)"
              onClick={() => store.clearOutput(store.selectedMonitorId)}
            >
              <ClearAllIcon />
            </button>
            <button
              title="Scroll to bottom (shift + alt + S)"
              onClick={() => this.scrollToBottom()}
            >
              <ArrowDownwardIcon />
            </button>
          </span>
        </div>
        <pre>
          {monitor &&
            monitor.output.map(line => (
              <div
                key={line.id}
                dangerouslySetInnerHTML={{ __html: line.html }}
              />
            ))}
        </pre>
      </div>
    )
  }
}

export default Content