Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@
const [isMoveActive, setIsMoveActive] = useState(false);

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

Check warning on line 63 in src/component/1d-2d/components/FloatMoleculeStructures/DraggableStructure.tsx

View workflow job for this annotation

GitHub Actions / nodejs / lint-eslint

Avoid storing derived state. Compute "bounding" directly during render, optionally with `useMemo` if it's expensive
}, [moleculeView.floating.bounding]);

function floatMoleculeHandler() {
Expand Down Expand Up @@ -187,7 +187,6 @@
buttons={actionsButtons}
fill
positioningStrategy="fixed"
position="top-left"
direction="row"
targetProps={{ style: { width: '100%', height: '100%' } }}
space={2}
Expand Down
1 change: 0 additions & 1 deletion src/component/1d-2d/components/SpectrumInfoBlock.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,6 @@ function SpectrumInfoBlock() {
targetTagName="g"
buttons={actionsButtons}
positioningStrategy="fixed"
position="top-left"
direction="row"
space={2}
{...(isMoveActive && { isOpen: true })}
Expand Down
1 change: 0 additions & 1 deletion src/component/1d/FloatPublicationString.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@
}));
}

function handleDrag(internalBounding: Pick<BoundingBox, 'x' | 'y'>) {

Check warning on line 159 in src/component/1d/FloatPublicationString.tsx

View workflow job for this annotation

GitHub Actions / nodejs / lint-eslint

Avoid storing derived state. Compute "bounding" directly during render, optionally with `useMemo` if it's expensive
setBounding((prevBounding) => ({
...prevBounding,
...convertToPercent(internalBounding),
Expand Down Expand Up @@ -289,7 +289,6 @@
buttons={actionButtons}
fill
positioningStrategy="fixed"
position="top-left"
direction="row"
targetProps={{ style: { width: '100%', height: '100%' } }}
space={2}
Expand Down
1 change: 0 additions & 1 deletion src/component/1d/FloatingRanges.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@
const isExportProcessStart = useCheckExportStatus();

useEffect(() => {
setBounding({ ...externalBounding });

Check warning on line 176 in src/component/1d/FloatingRanges.tsx

View workflow job for this annotation

GitHub Actions / nodejs / lint-eslint

Avoid storing derived state. Compute "bounding" directly during render, optionally with `useMemo` if it's expensive
}, [externalBounding]);

function handleResize(
Expand Down Expand Up @@ -321,7 +321,6 @@
buttons={actionButtons}
fill
positioningStrategy="fixed"
position="top-left"
direction="row"
targetProps={{ style: { width: '100%', height: '100%' } }}
space={2}
Expand Down
1 change: 0 additions & 1 deletion src/component/1d/inset/DraggableInset.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
const isExportProcessStart = useCheckExportStatus();

useEffect(() => {
setBounding({ ...externalBounding });

Check warning on line 72 in src/component/1d/inset/DraggableInset.tsx

View workflow job for this annotation

GitHub Actions / nodejs / lint-eslint

Avoid storing derived state. Compute "bounding" directly during render, optionally with `useMemo` if it's expensive
}, [externalBounding]);

function handleResize(
Expand Down Expand Up @@ -209,7 +209,6 @@
buttons={actionButtons}
fill
positioningStrategy="fixed"
position="top-left"
direction="row"
targetProps={{ style: { width: '100%', height: '100%' } }}
space={2}
Expand Down
4 changes: 2 additions & 2 deletions src/component/1d/peaks/PeakAnnotationsSpreadMode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,8 @@ function PeakAnnotationsSpreadMode(props: PeakAnnotationsSpreadModeProps) {
direction="row"
{...(isDragActive && { isOpen: true })}
y={y}
offsetY={-20}
offsetXMode="cursor"
anchorTo="cursor-x"
anchorPlacement="start"
autoFlip={false}
>
<g className="peaks">
Expand Down
2 changes: 1 addition & 1 deletion src/component/1d/ranges/Range.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ function Range(options: RangeProps) {
onClosed={() => {
isAssignBtnTrigged.current = false;
}}
offsetYMode="cursor"
anchorTo="cursor-y"
>
<g
data-testid="range"
Expand Down
2 changes: 1 addition & 1 deletion src/component/2d/zones/SignalsGuideLines.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ function IndicationLine(props: IndicationLineProps) {
targetTagName="g"
buttons={actionsButtons}
space={2}
position={isOverXAxis ? 'right-top' : 'top-left'}
anchorTo={isOverXAxis ? 'cursor-y' : 'cursor-x'}
onClosed={() => {
isAssignBtnTrigged.current = false;
}}
Expand Down
104 changes: 80 additions & 24 deletions src/component/elements/ActionsButtonsPopover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,21 +51,30 @@ function isSeparator(

type Direction = 'column' | 'row';

export interface ActionsButtonsPopoverProps extends Omit<
PopoverProps,
'interactionKind' | 'content' | 'modifiers' | 'renderTarget'
> {
type AnchorTo = 'element' | 'cursor-x' | 'cursor-y' | 'cursor';

type AnchorPlacement = 'start' | 'end';

interface Position {
x: number;
y: number;
}
export interface ActionsButtonsPopoverProps
extends
Omit<
PopoverProps,
'interactionKind' | 'content' | 'modifiers' | 'renderTarget'
>,
Partial<Position> {
buttons: ActionButtonProps[];
contentStyle?: CSSProperties;
direction?: Direction;
space?: number;
offsetX?: number;
offsetY?: number;
offsetYMode?: 'fixed' | 'cursor';
offsetXMode?: 'fixed' | 'cursor';
x?: number;
y?: number;
anchorTo?: AnchorTo;
autoFlip?: boolean;
anchorPlacement?: AnchorPlacement;
}

function ActionButton(props: ButtonProps) {
Expand All @@ -90,6 +99,52 @@ function filterButtons(buttons: ActionButtonProps[]) {
return { visibleButtons, disablePopover };
}

interface OffsetOptions {
offsetX?: number;
offsetY?: number;
anchorTo?: AnchorTo;
cursorPosition: Position;
}

function getOffset(options: OffsetOptions): [number, number] {
const { anchorTo, cursorPosition, offsetX = 0, offsetY = 0 } = options;
const isTrackX = anchorTo === 'cursor-x' || anchorTo === 'cursor';
const isTrackY = anchorTo === 'cursor-y' || anchorTo === 'cursor';

const x = isTrackX ? cursorPosition.x : 0;
const y = isTrackY ? cursorPosition.y : 0;

if (anchorTo === 'cursor-x') {
return [x + offsetX, -offsetY];
}

if (anchorTo === 'cursor-y') {
return [y + offsetY, offsetX];
}

if (anchorTo === 'cursor') {
return [x + offsetX, -(y + offsetY)];
}

return [offsetX, -offsetY];
}

function getPlacement(
anchorTo: AnchorTo,
anchorPlacement: AnchorPlacement,
): PopoverProps['placement'] {
switch (anchorTo) {
case 'cursor-x':
return `${anchorPlacement === 'start' ? 'top' : 'bottom'}-start`;
case 'cursor-y':
return `${anchorPlacement === 'start' ? 'left' : 'right'}-start`;
case 'cursor':
return 'top-start';
default:
return 'top-start';
}
}

export function ActionsButtonsPopover(props: ActionsButtonsPopoverProps) {
const {
targetTagName = 'div',
Expand All @@ -99,40 +154,42 @@ export function ActionsButtonsPopover(props: ActionsButtonsPopoverProps) {
space,
direction = 'column',
contentStyle = {},
offsetX: externalOffsetX = 0,
offsetY: externalOffsetY = 0,
offsetX = 0,
offsetY = 0,
x,
y,
offsetYMode = 'fixed',
offsetXMode = 'fixed',
autoFlip = true,
anchorTo = 'element',
autoFlip = false,
disabled,
anchorPlacement = 'end',
...otherProps
} = props;

const [cursor, setCursor] = useState({ x: 0, y: 0 });
const [cursorPosition, setCursorPosition] = useState<Position>({
x: 0,
y: 0,
});
const Wrapper = targetTagName as any;

const { visibleButtons, disablePopover } = filterButtons(buttons);

const offsetY = offsetYMode === 'fixed' ? externalOffsetY : cursor.y;
const offsetX = offsetXMode === 'fixed' ? externalOffsetX : cursor.x;

function handleMouseEnter(event: any) {
const { clientX, clientY, currentTarget } = event;
if (!(currentTarget instanceof Element)) return;
const rect = currentTarget.getBoundingClientRect();

setCursor((prev) => ({
x: offsetXMode === 'cursor' ? clientX - rect.left : prev.x,
y: offsetYMode === 'cursor' ? clientY - rect.top : prev.y,
}));
setCursorPosition({
x: clientX - rect.left,
y: clientY - rect.top,
});
}

const placement = getPlacement(anchorTo, anchorPlacement);
const offset = getOffset({ anchorTo, offsetX, offsetY, cursorPosition });
return (
<Popover
minimal
position="auto-start"
placement={placement}
popoverClassName="actions-buttons-popover"
interactionKind="hover"
enforceFocus={false}
Expand All @@ -155,8 +212,7 @@ export function ActionsButtonsPopover(props: ActionsButtonsPopoverProps) {
enabled: true,
data: { x, y },
options: {
offset:
direction === 'column' ? [offsetY, offsetX] : [offsetX, offsetY],
offset,
},
},

Expand Down
Loading