From 08026e318f53fbc1f0636b7f66fe72bffda648e7 Mon Sep 17 00:00:00 2001 From: hamed-musallam <35760236+hamed-musallam@users.noreply.github.com> Date: Thu, 27 Oct 2022 14:47:23 +0200 Subject: [PATCH] refactor: change the integration value position and shape (#1840) * refactor: change the integration value position and shape close: #1835 * test: fix integral test by select the first path element * style: fix overlap between line and text for the integration * refactor: integrals * refactor: use the same integral indicator in ranges * refactor: remove useless useCallback hook * chore: fix prettier * refcator: dim the range if its kind is not signal --- src/component/1d/Chart1D.tsx | 2 +- src/component/1d/IntegralResizable.tsx | 140 --------------- src/component/1d/{ => integral}/Integral.tsx | 4 +- .../1d/integral/IntegralIndicator.tsx | 48 +++++ .../1d/integral/IntegralResizable.tsx | 130 ++++++++++++++ .../1d/{ => integral}/IntegralsSeries.tsx | 6 +- src/component/1d/ranges/Range.tsx | 164 +++++++----------- src/component/1d/utilities/scale.ts | 4 +- src/component/reducer/Reducer.ts | 2 +- src/component/reducer/core/Constants.ts | 2 +- test-e2e/panels/integral.test.ts | 4 +- 11 files changed, 253 insertions(+), 253 deletions(-) delete mode 100644 src/component/1d/IntegralResizable.tsx rename src/component/1d/{ => integral}/Integral.tsx (86%) create mode 100644 src/component/1d/integral/IntegralIndicator.tsx create mode 100644 src/component/1d/integral/IntegralResizable.tsx rename src/component/1d/{ => integral}/IntegralsSeries.tsx (85%) diff --git a/src/component/1d/Chart1D.tsx b/src/component/1d/Chart1D.tsx index c5793a5f4..0ab20edbc 100644 --- a/src/component/1d/Chart1D.tsx +++ b/src/component/1d/Chart1D.tsx @@ -2,10 +2,10 @@ import FloatMoleculeStructures from '../tool/FloatMoleculeStructures'; import ApdoizationLine from './ApodizationLine'; import ExclusionZonesAnnotations from './ExclusionZonesAnnotations'; -import IntegralsSeries from './IntegralsSeries'; import LinesSeries from './LinesSeries'; import XAxis from './XAxis'; import DatabaseElements from './database/DatabaseElements'; +import IntegralsSeries from './integral/IntegralsSeries'; import JGraph from './jCouplingGraph/JGraph'; import MultiAnalysisRanges from './multiAnalysis/MultiAnalysisRanges'; import PeakAnnotations from './peaks/PeakAnnotations'; diff --git a/src/component/1d/IntegralResizable.tsx b/src/component/1d/IntegralResizable.tsx deleted file mode 100644 index 2c355aa94..000000000 --- a/src/component/1d/IntegralResizable.tsx +++ /dev/null @@ -1,140 +0,0 @@ -/** @jsxImportSource @emotion/react */ -import { css } from '@emotion/react'; -import { useCallback } from 'react'; - -import { useChartData } from '../context/ChartContext'; -import { useDispatch } from '../context/DispatchContext'; -import { useGlobal } from '../context/GlobalContext'; -import { useScaleChecked } from '../context/ScaleContext'; -import Resizer from '../elements/resizer/Resizer'; -import { HighlightEventSource, useHighlight } from '../highlight/index'; -import { RESIZE_INTEGRAL } from '../reducer/types/Types'; -import { options } from '../toolbar/ToolTypes'; -import { formatNumber } from '../utility/formatNumber'; - -const stylesOnHover = css` - pointer-events: bounding-box; - @-moz-document url-prefix() { - pointer-events: fill; - } - .highlight { - fill: transparent; - } - .target { - visibility: hidden; - } -`; - -const stylesHighlighted = css` - pointer-events: bounding-box; - - @-moz-document url-prefix() { - pointer-events: fill; - } - fill: #ff6f0057; - - .target { - visibility: visible; - } -`; - -interface IntegralResizableProps { - integralData: { - id: string; - from: number; - to: number; - integral?: number; - }; - integralFormat: string; -} - -function IntegralResizable({ - integralData, - integralFormat, -}: IntegralResizableProps) { - const { - height, - margin, - toolOptions: { selectedTool }, - } = useChartData(); - const { viewerRef } = useGlobal(); - const { scaleX } = useScaleChecked(); - const dispatch = useDispatch(); - const { id, integral } = integralData; - const highlight = useHighlight([id], { - type: HighlightEventSource.INTEGRAL, - extra: { id }, - }); - - const handleOnStopResizing = useCallback( - (position) => { - dispatch({ - type: RESIZE_INTEGRAL, - payload: { - data: { - ...integralData, - from: scaleX().invert(position.x2), - to: scaleX().invert(position.x1), - }, - }, - }); - }, - [dispatch, integralData, scaleX], - ); - - const handleOnEnterNotation = useCallback(() => { - highlight.show(); - }, [highlight]); - - const handleOnMouseLeaveNotation = useCallback(() => { - highlight.hide(); - }, [highlight]); - - const from = integralData.from ? scaleX()(integralData.from) : 0; - const to = integralData.to ? scaleX()(integralData.to) : 0; - - return ( - - - {({ x1, x2 }, isActive) => ( - - - - {integral !== undefined - ? formatNumber(integral, integralFormat) - : ''} - - - )} - - - ); -} - -export default IntegralResizable; diff --git a/src/component/1d/Integral.tsx b/src/component/1d/integral/Integral.tsx similarity index 86% rename from src/component/1d/Integral.tsx rename to src/component/1d/integral/Integral.tsx index 4038fc61d..e42271f5d 100644 --- a/src/component/1d/Integral.tsx +++ b/src/component/1d/integral/Integral.tsx @@ -1,5 +1,5 @@ -import useIntegralPath from '../hooks/useIntegralPath'; -import { usePanelPreferences } from '../hooks/usePanelPreferences'; +import useIntegralPath from '../../hooks/useIntegralPath'; +import { usePanelPreferences } from '../../hooks/usePanelPreferences'; import IntegralResizable from './IntegralResizable'; diff --git a/src/component/1d/integral/IntegralIndicator.tsx b/src/component/1d/integral/IntegralIndicator.tsx new file mode 100644 index 000000000..e46deaf1e --- /dev/null +++ b/src/component/1d/integral/IntegralIndicator.tsx @@ -0,0 +1,48 @@ +import { CSSProperties } from 'react'; + +import { useChartData } from '../../context/ChartContext'; +import { formatNumber } from '../../utility/formatNumber'; + +interface IntegralIndicatorProps { + value: number | undefined; + format: string; + width: number; + opacity?: number; +} + +const styles: Record<'text' | 'path', CSSProperties> = { + text: { + fontSize: '11px', + textAnchor: 'middle', + dominantBaseline: 'middle', + writingMode: 'vertical-rl', + fill: 'black', + }, + path: { + fill: 'none', + strokeWidth: '1px', + shapeRendering: 'crispEdges', + stroke: 'black', + }, +}; + +export function IntegralIndicator(props: IntegralIndicatorProps) { + const { value, width, format, opacity = 1 } = props; + const { height, margin } = useChartData(); + + const bottom = height - margin.bottom; + + return ( + + + {value ? formatNumber(value, format) : ''} + + + + ); +} diff --git a/src/component/1d/integral/IntegralResizable.tsx b/src/component/1d/integral/IntegralResizable.tsx new file mode 100644 index 000000000..ecc11a703 --- /dev/null +++ b/src/component/1d/integral/IntegralResizable.tsx @@ -0,0 +1,130 @@ +/** @jsxImportSource @emotion/react */ +import { css } from '@emotion/react'; + +import { useChartData } from '../../context/ChartContext'; +import { useDispatch } from '../../context/DispatchContext'; +import { useGlobal } from '../../context/GlobalContext'; +import { useScaleChecked } from '../../context/ScaleContext'; +import Resizer from '../../elements/resizer/Resizer'; +import { HighlightEventSource, useHighlight } from '../../highlight/index'; +import { RESIZE_INTEGRAL } from '../../reducer/types/Types'; +import { options } from '../../toolbar/ToolTypes'; + +import { IntegralIndicator } from './IntegralIndicator'; + +const stylesOnHover = css` + pointer-events: bounding-box; + @-moz-document url-prefix() { + pointer-events: fill; + } + .highlight { + fill: transparent; + } + .target { + visibility: hidden; + } +`; + +const stylesHighlighted = css` + pointer-events: bounding-box; + + @-moz-document url-prefix() { + pointer-events: fill; + } + fill: #ff6f0057; + + .target { + visibility: visible; + } +`; + +interface IntegralResizableProps { + integralData: { + id: string; + from: number; + to: number; + integral?: number; + }; + integralFormat: string; +} + +function IntegralResizable({ + integralData, + integralFormat, +}: IntegralResizableProps) { + const { + height, + margin, + toolOptions: { selectedTool }, + } = useChartData(); + const { viewerRef } = useGlobal(); + const { scaleX } = useScaleChecked(); + const dispatch = useDispatch(); + const { id, integral } = integralData; + const highlight = useHighlight([id], { + type: HighlightEventSource.INTEGRAL, + extra: { id }, + }); + + function handleOnStopResizing(position) { + dispatch({ + type: RESIZE_INTEGRAL, + payload: { + data: { + ...integralData, + from: scaleX().invert(position.x2), + to: scaleX().invert(position.x1), + }, + }, + }); + } + + const from = integralData.from ? scaleX()(integralData.from) : 0; + const to = integralData.to ? scaleX()(integralData.to) : 0; + + const bottom = height - margin.bottom; + + return ( + highlight.show()} + onMouseLeave={() => highlight.hide()} + > + + {({ x1, x2 }, isActive) => { + const width = x2 - x1; + + return ( + + + + + ); + }} + + + ); +} + +export default IntegralResizable; diff --git a/src/component/1d/IntegralsSeries.tsx b/src/component/1d/integral/IntegralsSeries.tsx similarity index 85% rename from src/component/1d/IntegralsSeries.tsx rename to src/component/1d/integral/IntegralsSeries.tsx index b6daec0b5..68bf05985 100644 --- a/src/component/1d/IntegralsSeries.tsx +++ b/src/component/1d/integral/IntegralsSeries.tsx @@ -1,8 +1,8 @@ import { useMemo } from 'react'; -import { isSpectrum1D } from '../../data/data1d/Spectrum1D'; -import { useChartData } from '../context/ChartContext'; -import { useActiveSpectrum } from '../reducer/Reducer'; +import { isSpectrum1D } from '../../../data/data1d/Spectrum1D'; +import { useChartData } from '../../context/ChartContext'; +import { useActiveSpectrum } from '../../reducer/Reducer'; import Integral from './Integral'; diff --git a/src/component/1d/ranges/Range.tsx b/src/component/1d/ranges/Range.tsx index 3408d0c9c..be8229674 100644 --- a/src/component/1d/ranges/Range.tsx +++ b/src/component/1d/ranges/Range.tsx @@ -1,6 +1,5 @@ /** @jsxImportSource @emotion/react */ import { css } from '@emotion/react'; -import { useCallback, useState, useEffect } from 'react'; import { Signal1D } from '../../../data/types/data1d'; import { checkRangeKind } from '../../../data/utilities/RangeUtilities'; @@ -16,7 +15,7 @@ import Resizer from '../../elements/resizer/Resizer'; import { HighlightEventSource, useHighlight } from '../../highlight'; import { RESIZE_RANGE } from '../../reducer/types/Types'; import { options } from '../../toolbar/ToolTypes'; -import { formatNumber } from '../../utility/formatNumber'; +import { IntegralIndicator } from '../integral/IntegralIndicator'; import MultiplicityTree from '../multiplicityTree/MultiplicityTree'; const stylesOnHover = css` @@ -24,11 +23,10 @@ const stylesOnHover = css` @-moz-document url-prefix() { pointer-events: fill; } - user-select: 'none'; - -webkit-user-select: none; /* Chrome all / Safari all */ - -moz-user-select: none; /* Firefox all */ - - .delete-button { + .highlight { + fill: transparent; + } + .target { visibility: hidden; } `; @@ -39,13 +37,10 @@ const stylesHighlighted = css` @-moz-document url-prefix() { pointer-events: fill; } - .range-area { - height: 100%; - fill: #ff6f0057; - } - .delete-button { + fill: #ff6f0057; + + .target { visibility: visible; - cursor: pointer; } `; @@ -87,59 +82,44 @@ function Range({ const { scaleX } = useScaleChecked(); const dispatch = useDispatch(); - const [reduceOpacity, setReduceOpacity] = useState(false); - const [isBlockedByEditing, setIsBlockedByEditing] = useState(false); - - useEffect(() => { - if (selectedTool && selectedTool === options.editRange.id) { - setIsBlockedByEditing(true); - } else { - setIsBlockedByEditing(false); - } - }, [selectedTool]); - - useEffect(() => { - setReduceOpacity(!checkRangeKind(rangeData)); - }, [rangeData]); - - const handleOnStopResizing = useCallback( - (position) => { - dispatch({ - type: RESIZE_RANGE, - data: { - ...rangeData, - from: scaleX().invert(position.x2), - to: scaleX().invert(position.x1), - }, - }); - }, - [dispatch, rangeData, scaleX], - ); + const isBlockedByEditing = + selectedTool && selectedTool === options.editRange.id; + + function handleOnStopResizing(position) { + dispatch({ + type: RESIZE_RANGE, + data: { + ...rangeData, + from: scaleX().invert(position.x2), + to: scaleX().invert(position.x1), + }, + }); + } - const mouseEnterHandler = useCallback(() => { + function mouseEnterHandler() { assignmentRange.show('x'); highlightRange.show(); - }, [assignmentRange, highlightRange]); + } - const mouseLeaveHandler = useCallback(() => { + function mouseLeaveHandler() { assignmentRange.hide(); highlightRange.hide(); - }, [assignmentRange, highlightRange]); - - const assignHandler = useCallback( - (e) => { - if ( - selectedTool === options.rangePicking.id && - e.shiftKey && - !isBlockedByEditing - ) { - assignmentRange.setActive('x'); - } - }, - [assignmentRange, isBlockedByEditing, selectedTool], - ); + } + + function assignHandler(e) { + if ( + selectedTool === options.rangePicking.id && + e.shiftKey && + !isBlockedByEditing + ) { + assignmentRange.setActive('x'); + } + } const from = scaleX()(rangeData.from); const to = scaleX()(rangeData.to); + + const isNotSignal = !checkRangeKind(rangeData); + return ( - {({ x1, x2 }, isActive) => ( - - - { + const width = x2 - x1; + return ( + - {integration !== undefined - ? formatNumber(integration, relativeFormat) - : ''} - - - )} + + + + ); + }} {showMultiplicityTrees && diff --git a/src/component/1d/utilities/scale.ts b/src/component/1d/utilities/scale.ts index a55dedc42..f927e01a3 100644 --- a/src/component/1d/utilities/scale.ts +++ b/src/component/1d/utilities/scale.ts @@ -13,8 +13,8 @@ function getYScale(state, spectrumId: number | null | string = null) { const { height, margin, verticalAlign, yDomain, yDomains } = state; const _height = verticalAlign.align === 'center' - ? (height - 30) / 2 - : height - margin.bottom - 30; + ? (height - 40) / 2 + : height - margin.bottom - 40; let domainY: [number, number] | [] = []; if (spectrumId === null || yDomains[spectrumId] === undefined) { domainY = [0, yDomain[1]]; diff --git a/src/component/reducer/Reducer.ts b/src/component/reducer/Reducer.ts index 145204524..2f2c0888d 100644 --- a/src/component/reducer/Reducer.ts +++ b/src/component/reducer/Reducer.ts @@ -153,7 +153,7 @@ export const getInitialState = (): State => ({ margin: { top: 10, right: 20, - bottom: 70, + bottom: 50, left: 0, }, mode: 'RTL', diff --git a/src/component/reducer/core/Constants.ts b/src/component/reducer/core/Constants.ts index 424355163..a66189c4a 100644 --- a/src/component/reducer/core/Constants.ts +++ b/src/component/reducer/core/Constants.ts @@ -10,7 +10,7 @@ export const MARGIN = { '1D': { top: 10, right: 10, - bottom: 70, + bottom: 50, left: 10, }, }; diff --git a/test-e2e/panels/integral.test.ts b/test-e2e/panels/integral.test.ts index 05dbd7cf6..5e9a6dbf1 100644 --- a/test-e2e/panels/integral.test.ts +++ b/test-e2e/panels/integral.test.ts @@ -17,7 +17,7 @@ async function addIntegral( // Should have integral with at least 1000 points const path = (await nmrium.page.getAttribute( - `_react=Integral >> nth=${childIndex} >> path`, + `_react=Integral >> nth=${childIndex} >> path >> nth=0`, 'd', )) as string; expect(path.length).toBeGreaterThan(1000); @@ -46,7 +46,7 @@ async function resizeIntegral(nmrium: NmriumPage) { await nmrium.page.mouse.up(); const path = (await nmrium.page.getAttribute( - '_react=Integral >> nth=0 >> path', + '_react=Integral >> nth=0 >> path >> nth=0', 'd', )) as string;