Skip to content

Commit

Permalink
Merge pull request #1483 from nextstrain/feat/web-seq-markers
Browse files Browse the repository at this point in the history
  • Loading branch information
ivan-aksamentov authored Jun 19, 2024
2 parents eef7019 + f206800 commit 5f6b431
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { Tooltip } from 'src/components/Results/Tooltip'
import { BASE_MIN_WIDTH_PX } from 'src/constants'
import { formatRange, formatRangeMaybeEmpty } from 'src/helpers/formatRange'
import { getSafeId } from 'src/helpers/getSafeId'
import { SeqMarkerFrameShiftState, seqMarkerFrameShiftStateAtom } from 'src/state/seqViewSettings.state'
import { SeqMarkerState, seqMarkerFrameShiftStateAtom } from 'src/state/seqViewSettings.state'
import { cdsAtom } from 'src/state/results.state'

const frameShiftColor = '#eb0d2a'
Expand Down Expand Up @@ -86,7 +86,7 @@ function SequenceMarkerFrameShiftSegment({

const seqMarkerFrameShiftState = useRecoilValue(seqMarkerFrameShiftStateAtom)
const cds = useRecoilValue(cdsAtom(geneName))
if (!cds || seqMarkerFrameShiftState === SeqMarkerFrameShiftState.Off) {
if (!cds || seqMarkerFrameShiftState === SeqMarkerState.Off) {
return null
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import React, { SVGProps, useCallback, useMemo, useState } from 'react'
import { useRecoilValue } from 'recoil'
import { seqMarkerInsertionStateAtom, SeqMarkerState } from 'src/state/seqViewSettings.state'
import { useTheme } from 'styled-components'

import { BASE_MIN_WIDTH_PX } from 'src/constants'
Expand Down Expand Up @@ -39,6 +41,11 @@ function SequenceMarkerInsertionUnmemoed({ index, seqName, insertion, pixelsPerB
const pointsMain = useMemo(() => `${x} 10, ${x + 5} 19, ${x - 5} 19`, [x])
const pointsOutline = useMemo(() => `${x} 7, ${x + 7} 22, ${x - 7} 22`, [x])

const seqMarkerInsertionState = useRecoilValue(seqMarkerInsertionStateAtom)
if (seqMarkerInsertionState === SeqMarkerState.Off) {
return null
}

return (
<g id={id} onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave}>
<polygon points={pointsOutline} fill={outline} {...rest} />
Expand Down
66 changes: 56 additions & 10 deletions packages/nextclade-web/src/components/Settings/SeqViewSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,21 @@ import { useTranslationSafe } from 'src/helpers/useTranslationSafe'
import { useRecoilStateDeferred } from 'src/hooks/useRecoilStateDeferred'
import {
SEQ_MARKER_HEIGHT_STATES,
SEQ_MARKER_STATES,
SeqMarkerHeightState,
SeqMarkerState,
maxNucMarkersAtom,
seqMarkerAmbiguousHeightStateAtom,
seqMarkerFrameShiftStateAtom,
seqMarkerGapHeightStateAtom,
seqMarkerHeightStateFromString,
seqMarkerMissingHeightStateAtom,
seqMarkerHeightStateToString,
seqMarkerGapHeightStateAtom,
seqMarkerInsertionStateAtom,
seqMarkerMissingHeightStateAtom,
seqMarkerMutationHeightStateAtom,
seqMarkerStateFromString,
seqMarkerStateToString,
seqMarkerUnsequencedHeightStateAtom,
maxNucMarkersAtom,
seqMarkerAmbiguousHeightStateAtom,
} from 'src/state/seqViewSettings.state'

/** Adapts Recoil state `enum` to `string` */
Expand All @@ -41,33 +47,41 @@ export function useEnumRecoilState<T>(
return [stringValue, setStringValue]
}

export function useSeqMarkerState(state: RecoilState<SeqMarkerHeightState>) {
export function useSeqMarkerHeightState(state: RecoilState<SeqMarkerHeightState>) {
return useEnumRecoilState(state, seqMarkerHeightStateToString, seqMarkerHeightStateFromString)
}

export function useSeqMarkerState(state: RecoilState<SeqMarkerState>) {
return useEnumRecoilState(state, seqMarkerStateToString, seqMarkerStateFromString)
}

export function SeqViewSettings() {
const { t } = useTranslationSafe()

const [maxNucMarkers, setMaxNucMarkers] = useRecoilStateDeferred(maxNucMarkersAtom)

const [seqMarkerMissingHeightState, setSeqMarkerMissingHeightState] = useSeqMarkerState(
const [seqMarkerMissingHeightState, setSeqMarkerMissingHeightState] = useSeqMarkerHeightState(
seqMarkerMissingHeightStateAtom,
)

const [seqMarkerAmbiguousHeightState, setSeqMarkerAmbiguousHeightState] = useSeqMarkerState(
const [seqMarkerAmbiguousHeightState, setSeqMarkerAmbiguousHeightState] = useSeqMarkerHeightState(
seqMarkerAmbiguousHeightStateAtom,
)

const [seqMarkerGapHeightState, setSeqMarkerGapHeightState] = useSeqMarkerState(seqMarkerGapHeightStateAtom)
const [seqMarkerGapHeightState, setSeqMarkerGapHeightState] = useSeqMarkerHeightState(seqMarkerGapHeightStateAtom)

const [seqMarkerMutationHeightState, setSeqMarkerMutationHeightState] = useSeqMarkerState(
const [seqMarkerMutationHeightState, setSeqMarkerMutationHeightState] = useSeqMarkerHeightState(
seqMarkerMutationHeightStateAtom,
)

const [seqMarkerUnsequencedHeightState, setSeqMarkerUnsequencedHeightState] = useSeqMarkerState(
const [seqMarkerUnsequencedHeightState, setSeqMarkerUnsequencedHeightState] = useSeqMarkerHeightState(
seqMarkerUnsequencedHeightStateAtom,
)

const [seqMarkerInsertionState, setSeqMarkerInsertionState] = useSeqMarkerState(seqMarkerInsertionStateAtom)

const [seqMarkerFrameShiftState, setSeqMarkerFrameShiftState] = useSeqMarkerState(seqMarkerFrameShiftStateAtom)

const labels: Record<SeqMarkerHeightState, string> = useMemo(
() => ({
[SeqMarkerHeightState.Off]: t('off'),
Expand All @@ -78,6 +92,14 @@ export function SeqViewSettings() {
[t],
)

const labelsSpecial: Record<SeqMarkerState, string> = useMemo(
() => ({
[SeqMarkerState.Off]: t('off'),
[SeqMarkerState.On]: t('on'),
}),
[t],
)

return (
<Form>
<NumericField
Expand Down Expand Up @@ -151,6 +173,30 @@ export function SeqViewSettings() {
/>
</Label>
</FormGroup>

<FormGroup>
<Label className="pointer-events-none" title={t('Toggle markers for insertions')}>
{t('Insertions')}
<Multitoggle
values={SEQ_MARKER_STATES}
labels={labelsSpecial}
currentValue={seqMarkerInsertionState}
onChange={setSeqMarkerInsertionState}
/>
</Label>
</FormGroup>

<FormGroup>
<Label className="pointer-events-none" title={t('Toggle markers for insertions')}>
{t('Frame shifts')}
<Multitoggle
values={SEQ_MARKER_STATES}
labels={labelsSpecial}
currentValue={seqMarkerFrameShiftState}
onChange={setSeqMarkerFrameShiftState}
/>
</Label>
</FormGroup>
</Form>
)
}
29 changes: 26 additions & 3 deletions packages/nextclade-web/src/state/seqViewSettings.state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,14 +76,37 @@ export const seqMarkerUnsequencedHeightStateAtom = atom<SeqMarkerHeightState>({
effects: [persistAtom],
})

export enum SeqMarkerFrameShiftState {
export enum SeqMarkerState {
Off = 'Off',
On = 'On',
}

export const seqMarkerFrameShiftStateAtom = atom<SeqMarkerFrameShiftState>({
export const SEQ_MARKER_STATES = Object.keys(SeqMarkerState)

export function seqMarkerStateToString(val: SeqMarkerState) {
return val.toString()
}

export function seqMarkerStateFromString(key: string) {
// prettier-ignore
switch (key) {
case 'On':
return SeqMarkerState.On
case 'Off':
return SeqMarkerState.Off
}
throw new ErrorInternal(`When converting string to 'SeqMarkerState': Unknown variant'${key}'`)
}

export const seqMarkerInsertionStateAtom = atom<SeqMarkerState>({
key: 'seqMarkerInsertionStateAtom',
default: SeqMarkerState.On,
effects: [persistAtom],
})

export const seqMarkerFrameShiftStateAtom = atom<SeqMarkerState>({
key: 'seqMarkerFrameShiftState',
default: SeqMarkerFrameShiftState.On,
default: SeqMarkerState.On,
effects: [persistAtom],
})

Expand Down

0 comments on commit 5f6b431

Please sign in to comment.