From fd114df7a4e3e77ccbfa515e8992912844e0f956 Mon Sep 17 00:00:00 2001 From: ivan-aksamentov Date: Mon, 11 Jan 2021 09:35:44 +0100 Subject: [PATCH 1/7] feat: add sequence view pan and zoom, initial attempt --- .../src/components/Results/ResultsTable.tsx | 58 ++++++++++++++++++- .../components/SequenceView/SequenceView.tsx | 16 ++--- packages/web/src/state/ui/ui.actions.ts | 4 ++ packages/web/src/state/ui/ui.reducer.ts | 10 ++++ packages/web/src/state/ui/ui.state.ts | 8 +++ 5 files changed, 87 insertions(+), 9 deletions(-) diff --git a/packages/web/src/components/Results/ResultsTable.tsx b/packages/web/src/components/Results/ResultsTable.tsx index 041e6211f..c567ac0df 100644 --- a/packages/web/src/components/Results/ResultsTable.tsx +++ b/packages/web/src/components/Results/ResultsTable.tsx @@ -1,4 +1,4 @@ -import React, { memo } from 'react' +import React, { ChangeEvent, memo, useCallback } from 'react' import { sum } from 'lodash' import { connect } from 'react-redux' @@ -14,6 +14,7 @@ import type { SequenceAnalysisState } from 'src/state/algorithm/algorithm.state' import type { Sorting } from 'src/helpers/sortResults' import { SortCategory, SortDirection } from 'src/helpers/sortResults' import { resultsSortTrigger } from 'src/state/algorithm/algorithm.actions' +import { setSequenceViewPan, setSequenceViewZoom } from 'src/state/ui/ui.actions' import { SequenceView } from 'src/components/SequenceView/SequenceView' @@ -103,6 +104,7 @@ export const TableRow = styled.div<{ even?: boolean; backgroundColor?: string }> align-items: stretch; background-color: ${(props) => props.backgroundColor}; box-shadow: 1px 2px 2px 2px ${rgba('#212529', 0.25)}; + user-select: none; ` export const TableCell = styled.div<{ basis?: string; grow?: number; shrink?: number }>` @@ -235,6 +237,8 @@ const TableRowMemo = memo(TableRowComponent, areEqual) const mapStateToProps = (state: State) => ({ resultsFiltered: state.algorithm.resultsFiltered, filterPanelCollapsed: state.ui.filterPanelCollapsed, + sequenceViewZoom: state.ui.sequenceView.zoom, + sequenceViewPan: state.ui.sequenceView.pan, }) const mapDispatchToProps = { @@ -263,6 +267,9 @@ const mapDispatchToProps = { sortByTotalGapsAsc: () => resultsSortTrigger({ category: SortCategory.totalGaps, direction: SortDirection.asc }), sortByTotalGapsDesc: () => resultsSortTrigger({ category: SortCategory.totalGaps, direction: SortDirection.desc }), + + setSequenceViewZoom, + setSequenceViewPan, } export const ResultsTable = React.memo(connect(mapStateToProps, mapDispatchToProps)(ResultsTableDisconnected)) @@ -286,6 +293,10 @@ export interface ResultProps { sortByTotalNsDesc(): void sortByTotalGapsAsc(): void sortByTotalGapsDesc(): void + sequenceViewZoom: number + setSequenceViewZoom(zoom: number): void + sequenceViewPan: number + setSequenceViewPan(zoom: number): void } export function ResultsTableDisconnected({ @@ -307,13 +318,56 @@ export function ResultsTableDisconnected({ sortByTotalNsDesc, sortByTotalGapsAsc, sortByTotalGapsDesc, + sequenceViewZoom, + setSequenceViewZoom, + sequenceViewPan, + setSequenceViewPan, }: ResultProps) { const { t } = useTranslation() - const data = resultsFiltered + const handleZoomChange = useCallback( + (event: ChangeEvent) => { + const z = Number.parseInt(event.target.value, 10) / 100 + setSequenceViewZoom(z) + }, + [setSequenceViewZoom], + ) + + const handlePanChange = useCallback( + (event: ChangeEvent) => { + const z = Number.parseInt(event.target.value, 10) / 100 + setSequenceViewPan(z) + }, + [setSequenceViewPan], + ) + return ( <> +
{sequenceViewZoom * 100}
+
+ +
+ +
{sequenceViewPan}
+
+ +
+ diff --git a/packages/web/src/components/SequenceView/SequenceView.tsx b/packages/web/src/components/SequenceView/SequenceView.tsx index 8569f84cb..64fbc0f65 100644 --- a/packages/web/src/components/SequenceView/SequenceView.tsx +++ b/packages/web/src/components/SequenceView/SequenceView.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import React, { useState } from 'react' import { connect } from 'react-redux' import { ReactResizeDetectorDimensions, withResizeDetector } from 'react-resize-detector' @@ -10,6 +10,7 @@ import { SequenceMarkerGap } from './SequenceMarkerGap' import { SequenceMarkerMissing } from './SequenceMarkerMissing' import { SequenceMarkerMutation } from './SequenceMarkerMutation' import { SequenceMarkerMissingEnds } from './SequenceMarkerMissingEnds' +import { BASE_MIN_WIDTH_PX } from 'src/constants' export const SequenceViewWrapper = styled.div` display: flex; @@ -25,25 +26,25 @@ export const SequenceViewWrapper = styled.div` export const SequenceViewSVG = styled.svg` padding: 0; margin: 0; - width: 100%; - height: 100%; - margin: 0; - padding: 0; ` export interface SequenceViewProps extends ReactResizeDetectorDimensions { sequence: AnalysisResult genomeSize: number + zoom: number + pan: number } const mapStateToProps = (state: State) => ({ genomeSize: state.algorithm.params.virus.genomeSize, + zoom: state.ui.sequenceView.zoom, + pan: state.ui.sequenceView.pan, }) const mapDispatchToProps = {} export const SequenceViewUnsized = connect(mapStateToProps, mapDispatchToProps)(SequenceViewUnsizedDisconnected) -export function SequenceViewUnsizedDisconnected({ sequence, width, genomeSize }: SequenceViewProps) { +export function SequenceViewUnsizedDisconnected({ sequence, width, genomeSize, zoom, pan }: SequenceViewProps) { const { seqName, substitutions, missing, deletions, alignmentStart, alignmentEnd } = sequence if (!width) { @@ -55,6 +56,7 @@ export function SequenceViewUnsizedDisconnected({ sequence, width, genomeSize }: } const pixelsPerBase = width / genomeSize + const pixelPan = pan * width const mutationViews = substitutions.map((substitution) => { return ( @@ -100,7 +102,7 @@ export function SequenceViewUnsizedDisconnected({ sequence, width, genomeSize }: return ( - + {missingEndViews} {mutationViews} diff --git a/packages/web/src/state/ui/ui.actions.ts b/packages/web/src/state/ui/ui.actions.ts index e0c5a4e94..484ff6afa 100644 --- a/packages/web/src/state/ui/ui.actions.ts +++ b/packages/web/src/state/ui/ui.actions.ts @@ -13,3 +13,7 @@ export const setTreeFilterPanelCollapsed = action('setTreeFilterPanelCo export const setShowWhatsnew = action('setShowWhatsnew') export const setShowNewRunPopup = action('setShowNewRunPopup') + +export const setSequenceViewZoom = action('setSequenceViewZoom') + +export const setSequenceViewPan = action('setSequenceViewPan') diff --git a/packages/web/src/state/ui/ui.reducer.ts b/packages/web/src/state/ui/ui.reducer.ts index faca5dad3..b9db17713 100644 --- a/packages/web/src/state/ui/ui.reducer.ts +++ b/packages/web/src/state/ui/ui.reducer.ts @@ -4,6 +4,8 @@ import { uiDefaultState } from 'src/state/ui/ui.state' import { setExportFormat, setFilterPanelCollapsed, + setSequenceViewPan, + setSequenceViewZoom, setShowNewRunPopup, setShowWhatsnew, setTreeFilterPanelCollapsed, @@ -29,3 +31,11 @@ export const uiReducer = reducerWithInitialState(uiDefaultState) .icase(setShowNewRunPopup, (draft, showNewRunPopup) => { draft.showNewRunPopup = showNewRunPopup }) + + .icase(setSequenceViewZoom, (draft, zoom) => { + draft.sequenceView.zoom = zoom + }) + + .icase(setSequenceViewPan, (draft, pan) => { + draft.sequenceView.pan = pan + }) diff --git a/packages/web/src/state/ui/ui.state.ts b/packages/web/src/state/ui/ui.state.ts index afa0d4668..be785f686 100644 --- a/packages/web/src/state/ui/ui.state.ts +++ b/packages/web/src/state/ui/ui.state.ts @@ -6,6 +6,10 @@ export enum ExportFormat { } export interface UiState { + sequenceView: { + zoom: number + pan: number + } filterPanelCollapsed: boolean treeFilterPanelCollapsed: boolean exportFormat: ExportFormat @@ -14,6 +18,10 @@ export interface UiState { } export const uiDefaultState: UiState = { + sequenceView: { + zoom: 1, + pan: 0, + }, filterPanelCollapsed: true, treeFilterPanelCollapsed: true, exportFormat: ExportFormat.CSV, From caef3ff67fe4c2e278dfe3579f4b415a0b77b935 Mon Sep 17 00:00:00 2001 From: ivan-aksamentov Date: Tue, 12 Jan 2021 08:35:28 +0100 Subject: [PATCH 2/7] feat: add drag scrolling of sequence view --- .../Common/HorizontalDragScroll.tsx | 36 +++++++++++++++++++ .../components/SequenceView/SequenceView.tsx | 33 +++++++++++++---- 2 files changed, 62 insertions(+), 7 deletions(-) create mode 100644 packages/web/src/components/Common/HorizontalDragScroll.tsx diff --git a/packages/web/src/components/Common/HorizontalDragScroll.tsx b/packages/web/src/components/Common/HorizontalDragScroll.tsx new file mode 100644 index 000000000..0202bbf55 --- /dev/null +++ b/packages/web/src/components/Common/HorizontalDragScroll.tsx @@ -0,0 +1,36 @@ +import React, { PropsWithChildren, useCallback, useState } from 'react' + +export enum MouseState { + up, + down, + moving, +} + +export interface DragScrollProps { + onScroll(delta: number): void +} + +export function HorizontalDragScroll({ children, onScroll, ...restProps }: PropsWithChildren) { + const [mouseState, setMouseState] = useState(MouseState.up) + + const onMouseDown = useCallback(() => setMouseState(MouseState.down), []) + const onMouseUp = useCallback(() => setMouseState(MouseState.up), []) + + const onMouseMove = useCallback( + (e: React.MouseEvent) => { + if (mouseState !== MouseState.up) { + setMouseState(MouseState.moving) + const delta = e.movementX + onScroll(delta) + } + }, + [mouseState, onScroll], + ) + + return ( + // eslint-disable-next-line jsx-a11y/no-static-element-interactions +
+ {children} +
+ ) +} diff --git a/packages/web/src/components/SequenceView/SequenceView.tsx b/packages/web/src/components/SequenceView/SequenceView.tsx index 64fbc0f65..a4b75471a 100644 --- a/packages/web/src/components/SequenceView/SequenceView.tsx +++ b/packages/web/src/components/SequenceView/SequenceView.tsx @@ -1,18 +1,19 @@ -import React, { useState } from 'react' +import React, { useCallback } from 'react' import { connect } from 'react-redux' import { ReactResizeDetectorDimensions, withResizeDetector } from 'react-resize-detector' import styled from 'styled-components' import type { State } from 'src/state/reducer' +import { HorizontalDragScroll } from 'src/components/Common/HorizontalDragScroll' import type { AnalysisResult } from 'src/algorithms/types' import { SequenceMarkerGap } from './SequenceMarkerGap' import { SequenceMarkerMissing } from './SequenceMarkerMissing' import { SequenceMarkerMutation } from './SequenceMarkerMutation' import { SequenceMarkerMissingEnds } from './SequenceMarkerMissingEnds' -import { BASE_MIN_WIDTH_PX } from 'src/constants' +import { setSequenceViewPan } from 'src/state/ui/ui.actions' -export const SequenceViewWrapper = styled.div` +export const SequenceViewWrapper = styled(HorizontalDragScroll)` display: flex; width: 100%; height: 30px; @@ -33,6 +34,7 @@ export interface SequenceViewProps extends ReactResizeDetectorDimensions { genomeSize: number zoom: number pan: number + setSequenceViewPan(pan: number): void } const mapStateToProps = (state: State) => ({ @@ -40,16 +42,33 @@ const mapStateToProps = (state: State) => ({ zoom: state.ui.sequenceView.zoom, pan: state.ui.sequenceView.pan, }) -const mapDispatchToProps = {} + +const mapDispatchToProps = { + setSequenceViewPan, +} export const SequenceViewUnsized = connect(mapStateToProps, mapDispatchToProps)(SequenceViewUnsizedDisconnected) -export function SequenceViewUnsizedDisconnected({ sequence, width, genomeSize, zoom, pan }: SequenceViewProps) { +export function SequenceViewUnsizedDisconnected({ + sequence, + width, + genomeSize, + zoom, + pan, + setSequenceViewPan, +}: SequenceViewProps) { const { seqName, substitutions, missing, deletions, alignmentStart, alignmentEnd } = sequence + const handleScroll = useCallback( + (delta: number) => { + setSequenceViewPan(pan - 0.001 * delta) + }, + [pan, setSequenceViewPan], + ) + if (!width) { return ( - + ) @@ -101,7 +120,7 @@ export function SequenceViewUnsizedDisconnected({ sequence, width, genomeSize, z }) return ( - + {missingEndViews} From ca48ae5234a503190056108b9f8ab5b951796212 Mon Sep 17 00:00:00 2001 From: ivan-aksamentov Date: Tue, 12 Jan 2021 08:58:14 +0100 Subject: [PATCH 3/7] feat: use grabbing cursor when drag-scroll --- .../Common/HorizontalDragScroll.tsx | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/packages/web/src/components/Common/HorizontalDragScroll.tsx b/packages/web/src/components/Common/HorizontalDragScroll.tsx index 0202bbf55..1e33eecd6 100644 --- a/packages/web/src/components/Common/HorizontalDragScroll.tsx +++ b/packages/web/src/components/Common/HorizontalDragScroll.tsx @@ -1,11 +1,18 @@ import React, { PropsWithChildren, useCallback, useState } from 'react' +import styled from 'styled-components' + export enum MouseState { up, down, moving, } +const Draggable = styled.div<{ $dragged: boolean }>` + cursor: ${(props) => props.$dragged && 'grabbing'}; + user-select: ${(props) => props.$dragged && 'none'}; +` + export interface DragScrollProps { onScroll(delta: number): void } @@ -15,6 +22,7 @@ export function HorizontalDragScroll({ children, onScroll, ...restProps }: Props const onMouseDown = useCallback(() => setMouseState(MouseState.down), []) const onMouseUp = useCallback(() => setMouseState(MouseState.up), []) + const dragged = mouseState === MouseState.moving const onMouseMove = useCallback( (e: React.MouseEvent) => { @@ -28,9 +36,14 @@ export function HorizontalDragScroll({ children, onScroll, ...restProps }: Props ) return ( - // eslint-disable-next-line jsx-a11y/no-static-element-interactions -
+ {children} -
+ ) } From 8b7f5573a79ea892f423e7c800d7c12f88ffc81d Mon Sep 17 00:00:00 2001 From: ivan-aksamentov Date: Tue, 12 Jan 2021 10:13:26 +0100 Subject: [PATCH 4/7] feat: add wheel zoom of sequence views --- .../Common/HorizontalDragScroll.tsx | 19 ++++++++++++++++++- .../src/components/Layout/LayoutResults.tsx | 1 + .../components/SequenceView/SequenceView.tsx | 16 +++++++++++++--- 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/packages/web/src/components/Common/HorizontalDragScroll.tsx b/packages/web/src/components/Common/HorizontalDragScroll.tsx index 1e33eecd6..8859bf9c4 100644 --- a/packages/web/src/components/Common/HorizontalDragScroll.tsx +++ b/packages/web/src/components/Common/HorizontalDragScroll.tsx @@ -15,9 +15,15 @@ const Draggable = styled.div<{ $dragged: boolean }>` export interface DragScrollProps { onScroll(delta: number): void + onWheel(delta: number): void } -export function HorizontalDragScroll({ children, onScroll, ...restProps }: PropsWithChildren) { +export function HorizontalDragScroll({ + children, + onScroll, + onWheel, + ...restProps +}: PropsWithChildren) { const [mouseState, setMouseState] = useState(MouseState.up) const onMouseDown = useCallback(() => setMouseState(MouseState.down), []) @@ -35,12 +41,23 @@ export function HorizontalDragScroll({ children, onScroll, ...restProps }: Props [mouseState, onScroll], ) + const handleWheel = useCallback( + (e: React.WheelEvent) => { + if (e.shiftKey) { + const delta = e.deltaY + onWheel(delta) + } + }, + [onWheel], + ) + return ( {children} diff --git a/packages/web/src/components/Layout/LayoutResults.tsx b/packages/web/src/components/Layout/LayoutResults.tsx index eff8902e4..8a58a5377 100644 --- a/packages/web/src/components/Layout/LayoutResults.tsx +++ b/packages/web/src/components/Layout/LayoutResults.tsx @@ -13,6 +13,7 @@ export const LayoutContainer = styled.div` display: flex; flex-direction: column; flex-wrap: nowrap; + overflow: hidden; ` const Header = styled.header` diff --git a/packages/web/src/components/SequenceView/SequenceView.tsx b/packages/web/src/components/SequenceView/SequenceView.tsx index a4b75471a..5c433f08e 100644 --- a/packages/web/src/components/SequenceView/SequenceView.tsx +++ b/packages/web/src/components/SequenceView/SequenceView.tsx @@ -5,13 +5,13 @@ import { ReactResizeDetectorDimensions, withResizeDetector } from 'react-resize- import styled from 'styled-components' import type { State } from 'src/state/reducer' +import { setSequenceViewPan, setSequenceViewZoom } from 'src/state/ui/ui.actions' import { HorizontalDragScroll } from 'src/components/Common/HorizontalDragScroll' import type { AnalysisResult } from 'src/algorithms/types' import { SequenceMarkerGap } from './SequenceMarkerGap' import { SequenceMarkerMissing } from './SequenceMarkerMissing' import { SequenceMarkerMutation } from './SequenceMarkerMutation' import { SequenceMarkerMissingEnds } from './SequenceMarkerMissingEnds' -import { setSequenceViewPan } from 'src/state/ui/ui.actions' export const SequenceViewWrapper = styled(HorizontalDragScroll)` display: flex; @@ -35,6 +35,7 @@ export interface SequenceViewProps extends ReactResizeDetectorDimensions { zoom: number pan: number setSequenceViewPan(pan: number): void + setSequenceViewZoom(pan: number): void } const mapStateToProps = (state: State) => ({ @@ -45,6 +46,7 @@ const mapStateToProps = (state: State) => ({ const mapDispatchToProps = { setSequenceViewPan, + setSequenceViewZoom, } export const SequenceViewUnsized = connect(mapStateToProps, mapDispatchToProps)(SequenceViewUnsizedDisconnected) @@ -56,6 +58,7 @@ export function SequenceViewUnsizedDisconnected({ zoom, pan, setSequenceViewPan, + setSequenceViewZoom, }: SequenceViewProps) { const { seqName, substitutions, missing, deletions, alignmentStart, alignmentEnd } = sequence @@ -66,9 +69,16 @@ export function SequenceViewUnsizedDisconnected({ [pan, setSequenceViewPan], ) + const handleWheel = useCallback( + (delta: number) => { + setSequenceViewZoom(zoom - 0.0005 * delta) + }, + [zoom, setSequenceViewZoom], + ) + if (!width) { return ( - + ) @@ -120,7 +130,7 @@ export function SequenceViewUnsizedDisconnected({ }) return ( - + {missingEndViews} From e8f8868cfc50df63c03cd25633eaf3bfd79b8fe5 Mon Sep 17 00:00:00 2001 From: ivan-aksamentov Date: Mon, 31 Jan 2022 09:40:21 +0100 Subject: [PATCH 5/7] feat: add pan and zoom for peptide view --- .../components/SequenceView/PeptideView.tsx | 54 ++++++++++++++++--- .../components/SequenceView/SequenceView.tsx | 5 +- 2 files changed, 49 insertions(+), 10 deletions(-) diff --git a/packages/web/src/components/SequenceView/PeptideView.tsx b/packages/web/src/components/SequenceView/PeptideView.tsx index 72148e929..161b7ba04 100644 --- a/packages/web/src/components/SequenceView/PeptideView.tsx +++ b/packages/web/src/components/SequenceView/PeptideView.tsx @@ -1,8 +1,9 @@ -import React, { useState } from 'react' +import React, { useCallback, useState } from 'react' import { connect } from 'react-redux' import { ReactResizeDetectorDimensions, withResizeDetector } from 'react-resize-detector' import { Alert as ReactstrapAlert } from 'reactstrap' +import { setSequenceViewPan, setSequenceViewZoom } from 'src/state/ui/ui.actions' import styled from 'styled-components' import type { AnalysisResult, Gene, GeneWarning, Warnings } from 'src/algorithms/types' @@ -76,21 +77,55 @@ export interface PeptideViewProps extends ReactResizeDetectorDimensions { warnings: Warnings geneMap?: Gene[] viewedGene: string + zoom: number + pan: number + setSequenceViewPan(pan: number): void + setSequenceViewZoom(pan: number): void } const mapStateToProps = (state: State) => ({ geneMap: selectGeneMap(state), + zoom: state.ui.sequenceView.zoom, + pan: state.ui.sequenceView.pan, }) -const mapDispatchToProps = {} + +const mapDispatchToProps = { + setSequenceViewPan, + setSequenceViewZoom, +} export const PeptideViewUnsized = connect(mapStateToProps, mapDispatchToProps)(PeptideViewUnsizedDisconnected) -export function PeptideViewUnsizedDisconnected({ width, sequence, warnings, geneMap, viewedGene }: PeptideViewProps) { +export function PeptideViewUnsizedDisconnected({ + sequence, + width, + warnings, + geneMap, + viewedGene, + zoom, + pan, + setSequenceViewPan, + setSequenceViewZoom, +}: PeptideViewProps) { const { t } = useTranslationSafe() + const handleScroll = useCallback( + (delta: number) => { + setSequenceViewPan(pan - 0.001 * delta) + }, + [pan, setSequenceViewPan], + ) + + const handleWheel = useCallback( + (delta: number) => { + setSequenceViewZoom(zoom - 0.0005 * delta) + }, + [zoom, setSequenceViewZoom], + ) + if (!width || !geneMap) { return ( - + ) @@ -99,7 +134,7 @@ export function PeptideViewUnsizedDisconnected({ width, sequence, warnings, gene const gene = geneMap.find((gene) => gene.geneName === viewedGene) if (!gene) { return ( - + {t('Gene {{geneName}} is missing in gene map', { geneName: viewedGene })} ) @@ -108,14 +143,17 @@ export function PeptideViewUnsizedDisconnected({ width, sequence, warnings, gene const warningsForThisGene = warnings.inGenes.filter((warn) => warn.geneName === viewedGene) if (warningsForThisGene.length > 0) { return ( - + ) } const { seqName, unknownAaRanges } = sequence + const zoomedWidth = width * zoom + const pixelPan = pan * width const pixelsPerAa = width / Math.round(gene.length / 3) + const aaSubstitutions = sequence.aaSubstitutions.filter((aaSub) => aaSub.gene === viewedGene) const aaDeletions = sequence.aaDeletions.filter((aaSub) => aaSub.gene === viewedGene) const groups = groupAdjacentAminoacidChanges(aaSubstitutions, aaDeletions) @@ -134,8 +172,8 @@ export function PeptideViewUnsizedDisconnected({ width, sequence, warnings, gene )) return ( - - + + {unknownAaRangesForGene && diff --git a/packages/web/src/components/SequenceView/SequenceView.tsx b/packages/web/src/components/SequenceView/SequenceView.tsx index cf49c3678..1700451c1 100644 --- a/packages/web/src/components/SequenceView/SequenceView.tsx +++ b/packages/web/src/components/SequenceView/SequenceView.tsx @@ -87,8 +87,9 @@ export function SequenceViewUnsizedDisconnected({ ) } - const pixelsPerBase = width / genomeSize + const zoomedWidth = width * zoom const pixelPan = pan * width + const pixelsPerBase = width / genomeSize const mutationViews = substitutions.map((substitution) => { return ( @@ -129,7 +130,7 @@ export function SequenceViewUnsizedDisconnected({ return ( - + Date: Mon, 31 Jan 2022 09:41:13 +0100 Subject: [PATCH 6/7] feat: prettify pan-and-zoom dev controls --- .../src/components/Results/ResultsTable.tsx | 59 +++++++++++-------- 1 file changed, 34 insertions(+), 25 deletions(-) diff --git a/packages/web/src/components/Results/ResultsTable.tsx b/packages/web/src/components/Results/ResultsTable.tsx index e52617ff9..2e97df98a 100644 --- a/packages/web/src/components/Results/ResultsTable.tsx +++ b/packages/web/src/components/Results/ResultsTable.tsx @@ -6,6 +6,7 @@ import { areEqual, FixedSizeList as FixedSizeListBase, FixedSizeListProps, ListC import AutoSizerBase from 'react-virtualized-auto-sizer' import styled from 'styled-components' import { mix, rgba } from 'polished' +import { Col, Container, Label, Row } from 'reactstrap' import { QcStatus } from 'src/algorithms/types' import { ColumnCustomNodeAttr } from 'src/components/Results/ColumnCustomNodeAttr' @@ -17,8 +18,7 @@ import type { SequenceAnalysisState } from 'src/state/algorithm/algorithm.state' import type { State } from 'src/state/reducer' import { SortCategory, SortDirection } from 'src/helpers/sortResults' import { resultsSortByKeyTrigger, resultsSortTrigger } from 'src/state/algorithm/algorithm.actions' -import { setViewedGene } from 'src/state/ui/ui.actions' -import { setSequenceViewPan, setSequenceViewZoom } from 'src/state/ui/ui.actions' +import { setViewedGene, setSequenceViewPan, setSequenceViewZoom } from 'src/state/ui/ui.actions' import { ButtonHelp } from './ButtonHelp' import { ColumnClade } from './ColumnClade' @@ -480,29 +480,38 @@ export function ResultsTableDisconnected({ return ( <> -
{sequenceViewZoom * 100}
-
- -
- -
{sequenceViewPan}
-
- -
+ + +
+ + + + +
From 2e68b58cbc327a7f761adcf063ed44b69aacac3a Mon Sep 17 00:00:00 2001 From: ivan-aksamentov Date: Fri, 24 Jun 2022 23:20:39 +0200 Subject: [PATCH 7/7] wip --- packages/web/src/components/Results/ResultsTable.tsx | 4 ++-- .../web/src/components/SequenceView/SequenceView.tsx | 11 +++++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/packages/web/src/components/Results/ResultsTable.tsx b/packages/web/src/components/Results/ResultsTable.tsx index 2e97df98a..8d06f90a9 100644 --- a/packages/web/src/components/Results/ResultsTable.tsx +++ b/packages/web/src/components/Results/ResultsTable.tsx @@ -501,8 +501,8 @@ export function ResultsTableDisconnected({ setSequenceViewZoom(1)} diff --git a/packages/web/src/components/SequenceView/SequenceView.tsx b/packages/web/src/components/SequenceView/SequenceView.tsx index 1700451c1..affe0fc32 100644 --- a/packages/web/src/components/SequenceView/SequenceView.tsx +++ b/packages/web/src/components/SequenceView/SequenceView.tsx @@ -1,3 +1,4 @@ +import { clamp } from 'lodash' import React, { useCallback } from 'react' import { connect } from 'react-redux' @@ -67,14 +68,16 @@ export function SequenceViewUnsizedDisconnected({ const handleScroll = useCallback( (delta: number) => { - setSequenceViewPan(pan - 0.001 * delta) + const newPan = clamp(pan - 0.001 * delta, -1, 1) + setSequenceViewPan(newPan) }, [pan, setSequenceViewPan], ) const handleWheel = useCallback( (delta: number) => { - setSequenceViewZoom(zoom - 0.0005 * delta) + const newZoom = clamp(zoom - 0.01 * delta, 1, 5) + setSequenceViewZoom(newZoom) }, [zoom, setSequenceViewZoom], ) @@ -87,9 +90,9 @@ export function SequenceViewUnsizedDisconnected({ ) } - const zoomedWidth = width * zoom - const pixelPan = pan * width const pixelsPerBase = width / genomeSize + const zoomedWidth = width * (1 / zoom) + const pixelPan = pan * zoomedWidth * (1 / zoom) const mutationViews = substitutions.map((substitution) => { return (