Skip to content

Commit

Permalink
feat(web): scaffold UI-4
Browse files Browse the repository at this point in the history
  • Loading branch information
ivan-aksamentov committed Sep 29, 2023
1 parent 32fd71f commit 200432d
Show file tree
Hide file tree
Showing 10 changed files with 334 additions and 404 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { isNil } from 'lodash'
import React, { useMemo } from 'react'
import { Button, ButtonProps } from 'reactstrap'
import styled from 'styled-components'
import { useRecoilValue } from 'recoil'
import { useTranslationSafe } from 'src/helpers/useTranslationSafe'
import { datasetCurrentAtom } from 'src/state/dataset.state'

export interface DatasetNoneSectionProps {
toDatasetSelection(): void
}

export function DatasetNoneSection({ toDatasetSelection }: DatasetNoneSectionProps) {
return (
<Container>
<ButtonChangeDataset onClick={toDatasetSelection} />
</Container>
)
}

const Container = styled.div`
display: flex;
flex: 1;
flex-direction: column;
overflow: hidden;
padding: 12px;
border: 1px #ccc9 solid;
border-radius: 5px;
max-width: 100%;
height: 110px;
`

export interface ChangeDatasetButtonProps extends ButtonProps {
onClick(): void
}

export function ButtonChangeDataset({ onClick, ...restProps }: ChangeDatasetButtonProps) {
const { t } = useTranslationSafe()
const dataset = useRecoilValue(datasetCurrentAtom)

const { color, text, tooltip } = useMemo(() => {
const hasDataset = !isNil(dataset)
const text = hasDataset ? t('Change dataset') : t('Select dataset')
return {
color: hasDataset ? 'secondary' : 'primary',
text,
tooltip: text,
}
}, [dataset, t])

return (
<Button className="m-auto" color={color} title={tooltip} onClick={onClick} {...restProps}>
{text}
</Button>
)
}
39 changes: 39 additions & 0 deletions packages_rs/nextclade-web/src/components/Main/ButtonRun.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import React, { useMemo } from 'react'
import { Button, ButtonProps } from 'reactstrap'
import { useRecoilValue } from 'recoil'
import { useTranslationSafe } from 'src/helpers/useTranslationSafe'
import { hasInputErrorsAtom } from 'src/state/error.state'
import { hasRequiredInputsAtom } from 'src/state/inputs.state'
import { canRunAtom } from 'src/state/results.state'
import styled from 'styled-components'

export interface ButtonRunProps extends ButtonProps {
onClick(): void
}

export function ButtonRun({ onClick, ...restProps }: ButtonRunProps) {
const canRun = useRecoilValue(canRunAtom)
const hasRequiredInputs = useRecoilValue(hasRequiredInputsAtom)
const hasInputErrors = useRecoilValue(hasInputErrorsAtom)

const { t } = useTranslationSafe()
const { isDisabled, color, tooltip } = useMemo(() => {
const isDisabled = !(canRun && hasRequiredInputs) || hasInputErrors
return {
isDisabled,
color: isDisabled ? 'secondary' : 'success',
tooltip: isDisabled ? t('Please provide sequence data first') : t('Launch the algorithm!'),
}
}, [canRun, hasInputErrors, hasRequiredInputs, t])

return (
<ButtonStyled disabled={isDisabled} color={color} onClick={onClick} title={tooltip} {...restProps}>
{t('Run')}
</ButtonStyled>
)
}

const ButtonStyled = styled(Button)`
min-width: 150px;
min-height: 45px;
`
36 changes: 8 additions & 28 deletions packages_rs/nextclade-web/src/components/Main/DatasetCurrent.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import React, { useCallback } from 'react'
import { Button, Col, Row } from 'reactstrap'
import { useRecoilValue, useResetRecoilState } from 'recoil'
import { DatasetContentSection } from 'src/components/Main/DatasetContentSection'
import React from 'react'
import { Col, Row } from 'reactstrap'
import { useRecoilValue } from 'recoil'
import styled from 'styled-components'
import { useUpdatedDataset } from 'src/io/fetchDatasets'
import { datasetCurrentAtom } from 'src/state/dataset.state'
import { useTranslationSafe } from 'src/helpers/useTranslationSafe'
import { DatasetInfo } from 'src/components/Main/DatasetInfo'
import { DatasetContentSection } from 'src/components/Main/DatasetContentSection'
import { DatasetCurrentUpdateNotification } from 'src/components/Main/DatasetCurrentUpdateNotification'

export const CurrentDatasetInfoContainer = styled.div`
Expand Down Expand Up @@ -62,26 +62,14 @@ export const FlexLeft = styled.div`

export const FlexRight = styled.div``

const ChangeButton = styled(Button)`
flex: 0 0 auto;
height: 2.1rem;
min-width: 100px;
margin-left: auto;
`

export function DatasetCurrent() {
// Periodically checks if there's local update for the current dataset
useUpdatedDataset()

const { t } = useTranslationSafe()
const datasetCurrent = useRecoilValue(datasetCurrentAtom)
const resetDatasetCurrent = useResetRecoilState(datasetCurrentAtom)

const onChangeClicked = useCallback(() => {
resetDatasetCurrent()
}, [resetDatasetCurrent])
const dataset = useRecoilValue(datasetCurrentAtom)

if (!datasetCurrent) {
if (!dataset) {
return null
}

Expand All @@ -96,16 +84,8 @@ export function DatasetCurrent() {
<DatasetCurrentUpdateNotification />

<Row noGutters className="w-100">
<Col className="d-flex w-100">
<FlexLeft>
<DatasetInfo dataset={datasetCurrent} />
</FlexLeft>

<FlexRight>
<ChangeButton type="button" color="secondary" onClick={onChangeClicked}>
{t('Change')}
</ChangeButton>
</FlexRight>
<Col className="d-flex">
<DatasetInfo dataset={dataset} />
</Col>
</Row>
</CurrentDatasetInfoBody>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import React from 'react'
import { Col, Row } from 'reactstrap'
import { useRecoilValue } from 'recoil'
import { ButtonChangeDataset } from 'src/components/Main/ButtonChangeDataset'
import { ButtonRun } from 'src/components/Main/ButtonRun'
import { useRunAnalysis } from 'src/hooks/useRunAnalysis'
import { useUpdatedDataset } from 'src/io/fetchDatasets'
import { datasetCurrentAtom } from 'src/state/dataset.state'
import { DatasetCurrentUpdateNotification } from 'src/components/Main/DatasetCurrentUpdateNotification'
import { DatasetInfo } from 'src/components/Main/DatasetInfo'
import styled from 'styled-components'

export interface DatasetCurrentSummaryProps {
toDatasetSelection(): void
}

export function DatasetCurrentSummary({ toDatasetSelection }: DatasetCurrentSummaryProps) {
// Periodically checks if there's local update for the current dataset
useUpdatedDataset()

const dataset = useRecoilValue(datasetCurrentAtom)
const run = useRunAnalysis()

if (!dataset) {
return null
}

return (
<Container>
<DatasetCurrentUpdateNotification />

<Row noGutters className="w-100">
<Col className="d-flex">
<FlexLeft>
<DatasetInfo dataset={dataset} />
</FlexLeft>

<FlexRight>
<div className="d-flex flex-column">
<ButtonChangeDataset className="mb-2" onClick={toDatasetSelection} />
<ButtonRun onClick={run} />
</div>
</FlexRight>
</Col>
</Row>
</Container>
)
}

const Container = styled.div`
display: flex;
flex-direction: column;
padding: 12px;
border: 1px #ccc9 solid;
border-radius: 5px;
margin: 0.5rem auto;
`

const FlexLeft = styled.div`
flex: 1;
`

const FlexRight = styled.div`
margin-left: 1rem;
`
90 changes: 17 additions & 73 deletions packages_rs/nextclade-web/src/components/Main/DatasetSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import React, { useEffect, useMemo, useState } from 'react'
import { useRecoilState, useRecoilValue } from 'recoil'
import { Container as ContainerBase } from 'reactstrap'
import { DatasetSelectorListImpl } from 'src/components/Main/DatasetSelectorListImpl'
import { PROJECT_NAME } from 'src/constants'
import {
autodetectResultsAtom,
AutodetectRunState,
Expand All @@ -16,21 +15,6 @@ import { useTranslationSafe } from 'src/helpers/useTranslationSafe'
import { datasetsAtom } from 'src/state/dataset.state'
import { SearchBox } from 'src/components/Common/SearchBox'

// HACK: dataset entry for 'autodetect' option. This is not a real dataset.
export const DATASET_AUTODETECT: Dataset = {
path: 'autodetect',
enabled: true,
official: true,
attributes: {
name: { value: 'autodetect', valueFriendly: 'Autodetect' },
reference: { value: 'autodetect', valueFriendly: 'Autodetect' },
},
files: {
reference: '',
pathogenJson: '',
},
}

export interface DatasetSelectorProps {
datasetHighlighted?: Dataset
onDatasetHighlighted?(dataset?: Dataset): void
Expand All @@ -39,13 +23,9 @@ export interface DatasetSelectorProps {
export function DatasetSelector({ datasetHighlighted, onDatasetHighlighted }: DatasetSelectorProps) {
const { datasets } = useRecoilValue(datasetsAtom)

const datasetsActive = useMemo(() => {
return [DATASET_AUTODETECT, ...datasets]
}, [datasets])

return (
<DatasetSelectorImpl
datasetsActive={datasetsActive}
datasetsActive={datasets}
datasetHighlighted={datasetHighlighted}
onDatasetHighlighted={onDatasetHighlighted}
/>
Expand All @@ -58,9 +38,9 @@ export function DatasetAutosuggestionResultsList({ datasetHighlighted, onDataset
const autodetectResults = useRecoilValue(autodetectResultsAtom)
const [autodetectRunState, setAutodetectRunState] = useRecoilState(autodetectRunStateAtom)

const autodetectResult = useMemo(() => {
const result = useMemo(() => {
if (isNil(autodetectResults) || autodetectResults.length === 0) {
return undefined
return { itemsStartWith: [], itemsInclude: datasets, itemsNotInclude: [] }
}

const recordsByDataset = groupByDatasets(autodetectResults)
Expand All @@ -77,71 +57,40 @@ export function DatasetAutosuggestionResultsList({ datasetHighlighted, onDataset
}, [autodetectResults, datasets])

const datasetsActive = useMemo(() => {
if (!autodetectResult) {
if (!result) {
return []
}
const { itemsStartWith, itemsInclude } = autodetectResult
const { itemsStartWith, itemsInclude } = result
return [...itemsStartWith, ...itemsInclude]
}, [autodetectResult])
}, [result])

const datasetsInactive = useMemo(() => {
if (!result) {
return []
}
const { itemsNotInclude } = result
return itemsNotInclude
}, [result])

useEffect(() => {
const topSuggestion = autodetectResult?.itemsInclude[0]
const topSuggestion = result?.itemsInclude[0]
if (autodetectRunState === AutodetectRunState.Done) {
onDatasetHighlighted?.(topSuggestion)
setAutodetectRunState(AutodetectRunState.Idle)
}
}, [autodetectRunState, autodetectResult?.itemsInclude, onDatasetHighlighted, setAutodetectRunState])

if (!autodetectResults) {
return <DatasetAutosuggestionInstructions />
}
}, [autodetectRunState, result?.itemsInclude, onDatasetHighlighted, setAutodetectRunState])

return (
<DatasetSelectorImpl
datasetsActive={datasetsActive}
datasetsInactive={datasetsInactive}
datasetHighlighted={datasetHighlighted}
onDatasetHighlighted={onDatasetHighlighted}
showSuggestions
/>
)
}

function DatasetAutosuggestionInstructions() {
const { t } = useTranslationSafe()
return (
<div className="d-flex flex-column">
<Heading>{t('Dataset autosuggestion')}</Heading>
<Wrapper>
<div className="flex-1 text-center">
<p className="mx-auto">
{t('{{projectName}} will try to guess dataset from data and will present its suggestions here.', {
projectName: PROJECT_NAME,
})}
</p>
<p className="mx-auto">{t('Please provide sequences to start.')}</p>
</div>
</Wrapper>
</div>
)
}

const Heading = styled.h4`
padding-top: 12px;
margin-bottom: 0;
margin-left: 7px;
width: 100%;
`

const Wrapper = styled.div`
display: flex;
height: 100%;
width: 100%;
padding: 10px;
border: 1px #ccc9 solid;
border-radius: 5px;
margin: 7px;
`

export interface DatasetSelectorImplProps {
datasetsActive: Dataset[]
datasetsInactive?: Dataset[]
Expand Down Expand Up @@ -205,11 +154,6 @@ const Main = styled.div`
overflow: hidden;
`

// const Footer = styled.div`
// display: flex;
// flex: 0;
// `

const Title = styled.h4`
flex: 1;
margin: auto 0;
Expand Down
Loading

0 comments on commit 200432d

Please sign in to comment.