Skip to content

Commit

Permalink
feat(web): add ref node selector
Browse files Browse the repository at this point in the history
  • Loading branch information
ivan-aksamentov committed May 14, 2024
1 parent 4045383 commit 822af4e
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 0 deletions.
104 changes: 104 additions & 0 deletions packages/nextclade-web/src/components/Results/RefNodeSelector.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/* eslint-disable @typescript-eslint/ban-ts-comment */
import { rgba } from 'polished'
import React, { useCallback, useMemo } from 'react'
import type { ActionMeta, GroupBase, OnChangeValue } from 'react-select/dist/declarations/src/types'
import { useRecoilState, useRecoilValue } from 'recoil'
import Select, { OptionProps } from 'react-select'
import { IsMultiValue } from 'src/components/Common/Dropdown'
import { DropdownOption } from 'src/components/Common/DropdownOption'
import { currentRefNodeNameAtom, refNodesAtom } from 'src/state/results.state'
import styled from 'styled-components'
import { SelectComponents } from 'react-select/dist/declarations/src/components'

interface RefNodeSelectorDatum {
value: string
label: string
description?: string
}

const builtinRefs: RefNodeSelectorDatum[] = [
{
value: '_root',
label: 'Reference',
description: 'Reference sequence',
},
{
value: '_parent',
label: 'Parent',
description: 'Nearest node on reference tree',
},
]

export function RefNodeSelector() {
const refNodes = useRecoilValue(refNodesAtom)
const [currentRefNodeName, setCurrentRefNodeName] = useRecoilState(currentRefNodeNameAtom)

const { options, currentOption } = useMemo(() => {
const refs = refNodes.map((node) => ({
value: node.name,
label: node.displayName ?? node.name,
description: node.description,
}))
const options = [...builtinRefs, ...refs]
const currentOption = options.find((o) => o.value === currentRefNodeName)

return { options, currentOption }
}, [currentRefNodeName, refNodes])

const handleChange = useCallback(
(option: OnChangeValue<DropdownOption<string>, IsMultiValue>, _actionMeta: ActionMeta<DropdownOption<string>>) => {
if (option) {
setCurrentRefNodeName(option.value)
}
},
[setCurrentRefNodeName],
)

return (
<Select
className="w-25"
components={COMPONENTS}
options={options}
value={currentOption}
isMulti={false}
onChange={handleChange}
menuPortalTarget={document.body}
/>
)
}

const COMPONENTS: Partial<SelectComponents<RefNodeSelectorDatum, false, GroupBase<RefNodeSelectorDatum>>> = {
Option: RefNodeSelectorOption,
}

function RefNodeSelectorOption({
data,
isDisabled,
isFocused,
isSelected,
innerRef,
innerProps,
}: OptionProps<RefNodeSelectorDatum, false>) {
return (
<OptionBody
// @ts-ignore
ref={innerRef}
aria-disabled={isDisabled}
isSelected={isSelected}
isDisabled={isDisabled}
isFocused={isFocused}
{...innerProps}
>
<div>{data.label}</div>
<div className="small">{data.description}</div>
</OptionBody>
)
}

const OptionBody = styled.div<{ isSelected?: boolean; isFocused?: boolean; isDisabled?: boolean }>`
padding: 0.4rem 0.2rem;
cursor: pointer;
background-color: ${(props) =>
props.isSelected ? props.theme.primary : props.isFocused ? rgba(props.theme.primary, 0.33) : undefined};
color: ${(props) => props.isSelected && 'white'};
`
5 changes: 5 additions & 0 deletions packages/nextclade-web/src/components/Results/ResultsPage.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, { Suspense } from 'react'
import { useRecoilValue } from 'recoil'
import { RefNodeSelector } from 'src/components/Results/RefNodeSelector'
import styled from 'styled-components'
import { resultsTableTotalWidthAtom } from 'src/state/settings.state'
import { Layout } from 'src/components/Layout/Layout'
Expand Down Expand Up @@ -49,6 +50,10 @@ export function ResultsPage() {
<Container>
<WrapperOuter>
<WrapperInner $minWidth={totalWidth}>
<div>
<RefNodeSelector />
</div>

<ResultsFilter />

<MainContent>
Expand Down

0 comments on commit 822af4e

Please sign in to comment.