mozilla/metrics-graphics

View on GitHub
lib/src/components/legend.ts

Summary

Maintainability
A
0 mins
Test Coverage
import { select } from 'd3'
import constants from '../misc/constants'
import { LegendSymbol } from '../misc/typings'

export interface ILegend {
  /** array of descriptive legend strings */
  legend: Array<string>

  /** colors used for the legend -- will be darkened for better visibility */
  colorScheme: Array<string>

  /** symbol used in the legend */
  symbolType: LegendSymbol
}

export default class Legend {
  legend: Array<string>
  colorScheme: Array<string>
  symbolType: LegendSymbol

  constructor({ legend, colorScheme, symbolType }: ILegend) {
    this.legend = legend
    this.colorScheme = colorScheme
    this.symbolType = symbolType
  }

  /**
   * Darken a given color by a given amount.
   *
   * @see https://css-tricks.com/snippets/javascript/lighten-darken-color/
   * @param color hex color specifier
   * @param amount how much to darken the color
   * @returns darkened color in hex representation.
   */
  darkenColor(color: string, amount: number): string {
    // remove hash
    color = color.slice(1)

    const num = parseInt(color, 16)

    const r = this.clamp((num >> 16) + amount)
    const b = this.clamp(((num >> 8) & 0x00ff) + amount)
    const g = this.clamp((num & 0x0000ff) + amount)

    return '#' + (g | (b << 8) | (r << 16)).toString(16)
  }

  /**
   * Clamp a number between 0 and 255.
   *
   * @param number number to be clamped.
   * @returns clamped number.
   */
  clamp(number: number): number {
    return number > 255 ? 255 : number < 0 ? 0 : number
  }

  /**
   * Mount the legend to the given node.
   *
   * @param node d3 specifier or d3 node to mount the legend to.
   */
  mountTo(node: any) {
    const symbol = constants.symbol[this.symbolType]

    // create d3 selection if necessary
    const target = typeof node === 'string' ? select(node).append('div') : node.append('div')
    target.classed('mg-legend', true)

    this.legend.forEach((item, index) => {
      target
        .append('span')
        .classed('text-legend', true)
        .style('color', this.darkenColor(this.colorScheme[index], -10))
        .text(`${symbol} ${item}`)
    })
  }
}