Skip to content

Commit

Permalink
feat: change floating molecule position from pixels to percentage
Browse files Browse the repository at this point in the history
  • Loading branch information
hamed-musallam committed Jan 30, 2025
1 parent e3e1926 commit 4abb43b
Show file tree
Hide file tree
Showing 6 changed files with 133 additions and 64 deletions.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import styled from '@emotion/styled';
import type { Ranges, Zones } from 'nmr-processing';
import OCL from 'openchemlib/full';
import { useEffect, useState } from 'react';
import { ResponsiveChart } from 'react-d3-utils';
import { BsArrowsMove } from 'react-icons/bs';
import { FaTimes } from 'react-icons/fa';
import OCLnmr from 'react-ocl-nmr';
import { Rnd } from 'react-rnd';

Expand All @@ -12,14 +15,15 @@ import type {
} from '../../../../data/molecules/Molecule.js';
import { useDispatch } from '../../../context/DispatchContext.js';
import { useGlobal } from '../../../context/GlobalContext.js';
import { ActionsButtonsPopover } from '../../../elements/ActionsButtonsPopover.js';
import type { ActionsButtonsPopoverProps } from '../../../elements/ActionsButtonsPopover.js';
import { useHighlightColor } from '../../../hooks/useHighlightColor.js';
import { useSVGUnitConverter } from '../../../hooks/useSVGUnitConverter.js';
import { useCheckExportStatus } from '../../../hooks/useViewportSize.js';
import { useMoleculeEditor } from '../../../modal/MoleculeStructureEditorModal.js';
import useAtomAssignment from '../../../panels/MoleculesPanel/useAtomAssignment.js';
import type { DisplayerMode } from '../../../reducer/Reducer.js';

import ActionsButton from './ActionsButton.js';

interface DraggableMoleculeProps extends DraggableStructureProps {
width: number;
height: number;
Expand All @@ -35,15 +39,9 @@ interface DraggableStructureProps {
molecule: StateMoleculeExtended;
}

const AUTO_CROP_MARGIN = 30;

const ReactRnd = styled(Rnd)`
border: 1px solid transparent;
button {
visibility: hidden;
}
.content {
width: 100%;
height: 100%;
Expand All @@ -52,10 +50,6 @@ const ReactRnd = styled(Rnd)`
&:hover {
border: 1px solid #ebecf1;
background-color: white;
button {
visibility: visible;
}
}
`;

Expand All @@ -65,6 +59,15 @@ export function DraggableStructure(props: DraggableStructureProps) {
const isExportProcessStart = useCheckExportStatus();
const dispatch = useDispatch();
const { modal, openMoleculeEditor } = useMoleculeEditor();
const { percentToPixel, pixelToPercent } = useSVGUnitConverter();
const [bounding, setBounding] = useState<MoleculeBoundingRect>(
moleculeView.floating.bounding,
);
const [isMoveActive, setIsMoveActive] = useState(false);

useEffect(() => {
setBounding({ ...moleculeView.floating.bounding });
}, [moleculeView.floating.bounding]);

function floatMoleculeHandler() {
dispatch({
Expand All @@ -88,10 +91,45 @@ export function DraggableStructure(props: DraggableStructureProps) {
});
}

function handleDrag(internalBounding: Pick<MoleculeBoundingRect, 'x' | 'y'>) {
setBounding((prevBounding) => ({
...prevBounding,
...convertPositionToPercent(internalBounding),
}));
}

function convertPositionToPercent({
x,
y,
}: Pick<MoleculeBoundingRect, 'x' | 'y'>) {
return { x: pixelToPercent(x, 'x'), y: pixelToPercent(y, 'y') };
}

if (!viewerRef) return null;

const actionsButtons: ActionsButtonsPopoverProps['buttons'] = [
{
icon: <BsArrowsMove />,

intent: 'none',
title: 'Move molecule',
style: { cursor: 'move' },
className: 'handle',
},
{
icon: <FaTimes />,
intent: 'danger',
title: 'Hide molecule',
onClick: floatMoleculeHandler,
},
];

const { x: xInPercent, y: yInPercent, width, height } = bounding;

const x = percentToPixel(xInPercent, 'x');
const y = percentToPixel(yInPercent, 'y');

if (isExportProcessStart) {
const { width, height, x, y } = moleculeView.floating.bounding;
return (
<g transform={`translate(${x} ${y})`}>
<DraggableMolecule {...{ width, height }} {...props} />
Expand All @@ -101,33 +139,55 @@ export function DraggableStructure(props: DraggableStructureProps) {

return (
<ReactRnd
default={moleculeView.floating.bounding}
minWidth={90 + AUTO_CROP_MARGIN * 2}
minHeight={100 + AUTO_CROP_MARGIN * 2}
default={{ x, y, width, height }}
position={{ x, y }}
minWidth={90}
minHeight={100}
dragHandleClassName="handle"
enableUserSelectHack={false}
bounds={viewerRef}
style={{ zIndex: 1 }}
className="draggable-molecule"
onDragStart={() => setIsMoveActive(true)}
onResizeStop={(e, dir, eRef, { width, height }) =>
dragFloatMoleculeHandler({ width, height })
}
onDrag={(e, { x, y }) => {
handleDrag({ x, y });
}}
onDragStop={(e, { x, y }) => {
dragFloatMoleculeHandler({ x, y });
dragFloatMoleculeHandler(convertPositionToPercent({ x, y }));
setIsMoveActive(false);
}}
resizeHandleWrapperStyle={{ backgroundColor: 'white' }}
>
<div
className="content"
onDoubleClick={() => openMoleculeEditor(molecule)}
<ActionsButtonsPopover
buttons={actionsButtons}
fill
positioningStrategy="fixed"
position="top-left"
direction="row"
targetProps={{ style: { width: '100%', height: '100%' } }}
space={2}
{...(isMoveActive && { isOpen: true })}
modifiers={{
offset: {
data: { x, y },
},
}}
>
<ActionsButton onFloatBtnClick={floatMoleculeHandler} />
<ResponsiveChart>
{({ width, height }) => {
return <DraggableMolecule {...{ width, height }} {...props} />;
}}
</ResponsiveChart>
</div>
<div
className="content"
onDoubleClick={() => openMoleculeEditor(molecule)}
>
<ResponsiveChart>
{({ width, height }) => {
return <DraggableMolecule {...{ width, height }} {...props} />;
}}
</ResponsiveChart>
</div>
</ActionsButtonsPopover>

{modal}
</ReactRnd>
);
Expand Down Expand Up @@ -159,9 +219,8 @@ function DraggableMolecule(props: DraggableMoleculeProps) {
OCL={OCL}
id={`molSVG${index || ''}`}
autoCrop
autoCropMargin={AUTO_CROP_MARGIN}
height={height - AUTO_CROP_MARGIN * 2}
width={width - AUTO_CROP_MARGIN * 2}
height={height}
width={width}
label={molecule.label}
labelFontSize={15}
labelColor="rgb(0,0,0)"
Expand Down
8 changes: 7 additions & 1 deletion src/component/elements/ActionsButtonsPopover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,13 @@ export interface ActionsButtonsPopoverProps
buttons: Array<
Pick<
ButtonProps,
'icon' | 'onClick' | 'intent' | 'disabled' | 'onPointerDown' | 'style'
| 'icon'
| 'onClick'
| 'intent'
| 'disabled'
| 'onPointerDown'
| 'style'
| 'className'
> & {
title?: string;
visible?: boolean;
Expand Down
28 changes: 28 additions & 0 deletions src/component/hooks/useSVGUnitConverter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { useChartData } from '../context/ChartContext.js';

function truncate(value, numberOfDigits = 0) {
const power = 10 ** numberOfDigits;
return Math.trunc(value * power) / power;
}

export function convertPixelToPercent(value: number, baseValue: number) {
return truncate((value / baseValue) * 100, 3);
}
export function convertPercentToPixel(value: number, baseValue: number) {
return Math.round((value / 100) * baseValue);
}

export function useSVGUnitConverter() {
const { width, height } = useChartData();

function pixelToPercent(value: number, axis: 'x' | 'y') {
const size = axis === 'x' ? width : height;
return convertPixelToPercent(value, size);
}
function percentToPixel(value: number, axis: 'x' | 'y') {
const size = axis === 'x' ? width : height;
return convertPercentToPixel(value, size);
}

return { pixelToPercent, percentToPixel };
}
6 changes: 5 additions & 1 deletion src/component/reducer/actions/MoleculeActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { DRAGGABLE_STRUCTURE_INITIAL_BOUNDING_REACT } from '../../../data/molecu
import * as MoleculeManager from '../../../data/molecules/MoleculeManager.js';
import { generateColor } from '../../../data/utilities/generateColor.js';
import type { AssignmentContext } from '../../assignment/AssignmentsContext.js';
import { convertPixelToPercent } from '../../hooks/useSVGUnitConverter.js';
import type { State } from '../Reducer.js';
import { MARGIN } from '../core/Constants.js';
import type { ActionType } from '../types/ActionType.js';
Expand Down Expand Up @@ -346,7 +347,10 @@ function getFloatingMoleculeInitialPosition(id: string, draft: Draft<State>) {
}
const x = ((index + 1) % columns) * (width / columns) + left;
const y = Math.floor(moleculeKeys.length / columns) * (height / rows) + top;
return { x, y };
return {
x: convertPixelToPercent(x, displayerWidth),
y: convertPixelToPercent(y, displayerHeight),
};
}

function floatMoleculeOverSpectrum(
Expand Down
8 changes: 4 additions & 4 deletions src/data/molecules/Molecule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ export interface MoleculeBoundingRect {

export const DRAGGABLE_STRUCTURE_INITIAL_BOUNDING_REACT: MoleculeBoundingRect =
{
x: 100,
y: 50,
width: 190,
height: 200,
x: 10,
y: 10,
width: 130,
height: 120,
};

export type MoleculesView = Record<string, MoleculeView>;
Expand Down

0 comments on commit 4abb43b

Please sign in to comment.