import { memo, useState } from 'react'; import { useResizeObserver } from 'react-d3-utils'; import { BsArrowsMove } from 'react-icons/bs'; import { useGlobal } from '../../context/GlobalContext.js'; import { usePreferences } from '../../context/PreferencesContext.js'; import type { ActionsButtonsPopoverProps } from '../../elements/ActionsButtonsPopover.js'; import { ActionsButtonsPopover } from '../../elements/ActionsButtonsPopover.js'; import useDraggable from '../../elements/draggable/useDraggable.js'; import { useHighlight } from '../../highlight/index.js'; import { usePeaksLabelSettings } from '../../hooks/usePeaksLabelSettings.js'; import type { Margin } from '../../reducer/Reducer.js'; import { formatNumber } from '../../utility/formatNumber.js'; import { resolve } from '../utilities/intersectionResolver.js'; import { PeakEditionListener } from './PeakEditionManager.js'; import type { PeaksAnnotationsProps, PeaksSource } from './Peaks.js'; import { getHighlightExtraId, getHighlightSource } from './Peaks.js'; const notationWidth = 10; const notationMargin = 2; function usePeaksPosition() { const { viewerRef } = useGlobal(); const { dispatch } = usePreferences(); const { marginTop: originMarginTop } = usePeaksLabelSettings(); const [isDragActive, setIsMoveActive] = useState(false); const [marginTop, setMarginTop] = useState(originMarginTop); const { onPointerDown } = useDraggable({ position: { x: 0, y: marginTop }, onChange: (dragEvent) => { const { action, position } = dragEvent; const yOffset = Math.round(position.y); switch (action) { case 'start': { setMarginTop(yOffset); setIsMoveActive(true); break; } case 'move': { setMarginTop(yOffset); break; } case 'end': dispatch({ type: 'CHANGE_PEAKS_LABEL_POSITION', payload: { marginTop: yOffset, }, }); setIsMoveActive(false); break; default: break; } }, parentElement: viewerRef, }); return { marginTop, isDragActive, onPointerDown }; } interface PeakAnnotationsSpreadModeProps extends Omit< PeaksAnnotationsProps, 'xDomain' > { height: number; margin: Margin; } function PeakAnnotationsSpreadMode(props: PeakAnnotationsSpreadModeProps) { const { peaks, peaksSource, spectrumColor, peakFormat, margin, height, spectrumKey, } = props; const [ref, { height: boxSizeHeight } = { height: 0 }] = useResizeObserver(); const { marginTop, isDragActive, onPointerDown } = usePeaksPosition(); const actionsButtons: ActionsButtonsPopoverProps['buttons'] = [ { icon: , onPointerDown: (event) => { event.stopPropagation(); onPointerDown(event); }, intent: 'none', title: 'Move peaks label vertically', style: { cursor: 'move' }, }, ]; const mapPeaks = resolve(peaks, { key: 'xInPixel', width: notationWidth, margin: notationMargin, groupMargin: 10, }); const roundedBoxHeight = Math.round(boxSizeHeight); let y = roundedBoxHeight + marginTop; if (y + roundedBoxHeight > height - margin.bottom) { y = height - margin.bottom - roundedBoxHeight; } if (marginTop < 0) { y = roundedBoxHeight; } return ( 0 ? 'visible' : 'hidden' }} > {mapPeaks.map((group) => { return ( {group.group.map((item, index) => { const { id, x: value, xInPixel, parentKeys, opacity, } = item; const startX = index * (notationWidth + notationMargin); const x = xInPixel - group.meta.groupStartX; return ( ); })} ); })} ); } interface PeakAnnotationProps { startX: number; x: number; format: string; color: string; opacity: number; id: string; value: number; peakEditionFieldPosition: { x: number; y: number }; peaksSource: PeaksSource; parentKeys: string[]; spectrumKey: string; } function PeakAnnotation(props: PeakAnnotationProps) { const { startX, format, color, id, value, x, peakEditionFieldPosition, peaksSource, parentKeys, spectrumKey, opacity, } = props; const highlight = useHighlight([id], { type: getHighlightSource(peaksSource), extra: { id: getHighlightExtraId(peaksSource, id, parentKeys), spectrumID: spectrumKey, }, }); return ( highlight.show()} onMouseLeave={() => highlight.hide()} opacity={opacity} > {formatNumber(value, format)} ); } export default memo(PeakAnnotationsSpreadMode);