ElectronicBabylonianLiterature/ebl-frontend

View on GitHub
src/corpus/ui/RowsContext.ts

Summary

Maintainability
A
35 mins
Test Coverage
A
92%
import produce from 'immer'
import React, { Dispatch, useReducer } from 'react'
import flow from 'lodash/fp/flow'
import range from 'lodash/fp/range'
import map from 'lodash/fp/map'
import fromPairs from 'lodash/fp/fromPairs'
import mapValues from 'lodash/fp/mapValues'

export interface RowState {
  readonly score: boolean
  readonly notes: boolean
  readonly parallels: boolean
  readonly oldLineNumbers: boolean
  readonly meter: boolean
  readonly ipa: boolean
}

type State = { readonly [key: number]: RowState }
export type RowsContextService = [State, Dispatch<Action>]

type ToggleAction = {
  type: 'toggle'
  target: 'score' | 'notes' | 'parallels'
  row: number
}

export type Action =
  | ToggleAction
  | {
      type: 'expand' | 'close'
      target: keyof RowState
    }

const RowsContext = React.createContext<RowsContextService>([
  {},
  // eslint-disable-next-line @typescript-eslint/no-empty-function
  (action: Action) => {},
])

function toggle(state: State, row: number, key: keyof RowState): State {
  return produce(state, (draft) => {
    draft[row][key] = !state[row][key]
  })
}

function setAll(state: State, key: keyof RowState, value: boolean): State {
  return mapValues<RowState, RowState>((rowState) => ({
    ...rowState,
    [key]: value,
  }))(state)
}

function reducer(state: State, action: Action): State {
  switch (action.type) {
    case 'toggle':
      return toggle(state, action.row, action.target)
    case 'expand':
      return setAll(state, action.target, true)
    case 'close':
      return setAll(state, action.target, false)
  }
}

export function useRowsContext(
  numberOfRows: number,
  score?: boolean,
  notes?: boolean,
  parallels?: boolean,
  oldLineNumbers?: boolean
): RowsContextService {
  score = score ?? false
  notes = notes ?? false
  parallels = parallels ?? false
  oldLineNumbers = oldLineNumbers ?? false
  return useReducer(
    reducer,
    flow(
      range,
      map((row) => [
        row,
        {
          score: score,
          notes: notes,
          parallels: parallels,
          oldLineNumbers: oldLineNumbers,
        },
      ]),
      fromPairs
    )(0, numberOfRows)
  )
}

export default RowsContext