Skip to content

Commit

Permalink
Refactor popover implementation with react-tiny-popover.
Browse files Browse the repository at this point in the history
  • Loading branch information
ost-ptk committed Jan 13, 2025
1 parent a357369 commit 9a6d96b
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 99 deletions.
16 changes: 16 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@
"react-player": "^2.16.0",
"react-redux": "8.0.5",
"react-router-dom": "6.28.0",
"react-tiny-popover": "^8.1.4",
"react-virtualized": "^9.22.5",
"redux": "4.2.1",
"redux-saga": "1.2.3",
Expand Down
7 changes: 5 additions & 2 deletions src/libs/ui/components/account-list/account-list-item.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { formatNumber } from 'casper-wallet-core';
import { IAccountInfo } from 'casper-wallet-core/src/domain/accountInfo';
import React from 'react';
import React, { useRef } from 'react';
import styled from 'styled-components';

import {
Expand Down Expand Up @@ -72,6 +72,8 @@ export const AccountListItem = ({
accountLiquidBalance,
isLoadingBalance
}: AccountListItemProps) => {
const popoverParentRef = useRef<HTMLDivElement | null>(null);

const accountBalance = accountLiquidBalance
? formatNumber(motesToCSPR(accountLiquidBalance), {
precision: { max: 0 }
Expand All @@ -83,7 +85,7 @@ export const AccountListItem = ({
accountsInfo && accountsInfo[account.accountHash]?.brandingLogo;

return (
<ListItemContainer key={account.name}>
<ListItemContainer key={account.name} ref={popoverParentRef}>
<AlignedSpaceBetweenFlexRow gap={SpacingSize.Small}>
<Avatar
size={38}
Expand Down Expand Up @@ -134,6 +136,7 @@ export const AccountListItem = ({
account={account}
showHideAccountItem={showHideAccountItem}
onClick={closeModal}
popoverParentRef={popoverParentRef}
/>
</AlignedSpaceBetweenFlexRow>
</ListItemContainer>
Expand Down
31 changes: 24 additions & 7 deletions src/libs/ui/components/account-popover/account-popover.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react';
import React, { useState } from 'react';
import { Trans, useTranslation } from 'react-i18next';
import { useSelector } from 'react-redux';
import styled from 'styled-components';

import { getBlockExplorerAccountUrl } from '@src/constants';

Expand All @@ -16,20 +17,33 @@ import {
selectIsAnyAccountConnectedWithActiveOrigin
} from '@background/redux/vault/selectors';

import { FlexColumn } from '@libs/layout';
import { Account } from '@libs/types/account';
import { PopoverLink, SvgIcon, Typography } from '@libs/ui/components';
import { Popover } from '@libs/ui/components/popover/popover';

const PopoverItemsContainer = styled(FlexColumn)`
padding: 8px;
background: ${({ theme }) => theme.color.backgroundPrimary};
box-shadow: ${({ theme }) => theme.shadow.contextMenu};
border-radius: ${({ theme }) => theme.borderRadius.eight}px;
`;

interface AccountActionsMenuPopoverProps {
account: Account;
onClick?: (e: React.MouseEvent) => void;
showHideAccountItem?: boolean;
popoverParentRef: React.MutableRefObject<HTMLDivElement | null>;
}
export const AccountActionsMenuPopover = ({
account,
onClick,
showHideAccountItem
showHideAccountItem,
popoverParentRef
}: AccountActionsMenuPopoverProps) => {
const [isOpen, setIsOpen] = useState(false);

const navigate = useTypedNavigate();
const { t } = useTranslation();
const { disconnectAccountWithEvent: disconnectAccount } = useAccountManager();
Expand All @@ -44,13 +58,16 @@ export const AccountActionsMenuPopover = ({

return (
<Popover
renderMenuItems={({ closePopover }) => (
<>
popoverParentRef={popoverParentRef}
isOpen={isOpen}
setIsOpen={setIsOpen}
content={() => (
<PopoverItemsContainer>
{connectedAccountNames.includes(account.name) ? (
<PopoverLink
variant="contentAction"
onClick={event => {
closePopover(event);
setIsOpen(false);
activeOrigin && disconnectAccount(account.name, activeOrigin);

if (onClick) {
Expand Down Expand Up @@ -176,10 +193,10 @@ export const AccountActionsMenuPopover = ({
<Trans t={t}>Manage</Trans>
</Typography>
</PopoverLink>
</>
</PopoverItemsContainer>
)}
>
<SvgIcon src="assets/icons/more.svg" />
<SvgIcon src="assets/icons/more.svg" onClick={() => setIsOpen(!isOpen)} />
</Popover>
);
};
139 changes: 49 additions & 90 deletions src/libs/ui/components/popover/popover.tsx
Original file line number Diff line number Diff line change
@@ -1,102 +1,61 @@
import React, { MouseEvent, PropsWithChildren, useRef, useState } from 'react';
import styled from 'styled-components';

import { useClickAway } from '@hooks/use-click-away';

import { AlignedFlexRow, FlexColumn, Overlay, SpacingSize } from '@libs/layout';

const popoverOffsetFromChildren = 8;
const contentHeight = 188;

const ChildrenContainer = styled(AlignedFlexRow)`
cursor: pointer;
`;

interface PopoverContainerProps {
domRect?: DOMRect;
}

const PopoverOverlay = styled(Overlay)`
background: inherit;
`;

const PopoverContainer = styled.div<PopoverContainerProps>`
position: absolute;
right: 16px;
top: ${({ domRect }) => {
if (domRect == null) {
return '0px';
}
const { top, bottom, height } = domRect;
if (top && bottom) {
return bottom >= window.innerHeight - contentHeight
? `${top - contentHeight}px`
: `${top + height + popoverOffsetFromChildren}px`;
}
}};
z-index: ${({ theme }) => theme.zIndex.dropdown};
`;

const PopoverItemsContainer = styled(FlexColumn)`
padding: 8px;
background: ${({ theme }) => theme.color.backgroundPrimary};
box-shadow: ${({ theme }) => theme.shadow.contextMenu};
border-radius: ${({ theme }) => theme.borderRadius.eight}px;
`;

type RenderProps = {
closePopover: (e: MouseEvent<HTMLAnchorElement>) => void;
};
import React, { PropsWithChildren, useEffect } from 'react';
import { ContentRenderer, Popover as TinyPopover } from 'react-tiny-popover';

interface PopoverProps {
renderMenuItems: (renderProps: RenderProps) => JSX.Element;
content: ContentRenderer;
popoverParentRef: React.MutableRefObject<HTMLDivElement | null>;
children: JSX.Element;
isOpen: boolean;
setIsOpen: React.Dispatch<React.SetStateAction<boolean>>;
}

export function Popover({
renderMenuItems,
children
content,
children,
popoverParentRef,
isOpen,
setIsOpen
}: PropsWithChildren<PopoverProps>) {
const [isOpen, setIsOpen] = useState(false);
const childrenContainerRef = useRef<HTMLDivElement>(null);

const { ref: clickAwayRef } = useClickAway({
callback: (event: MouseEvent<HTMLAnchorElement>) => {
event.stopPropagation();
isOpen && setIsOpen(false);
useEffect(() => {
// Get the container with class "ms-container"
// (to manage scroll behavior while the popover is open)
const scrollableContainer = document.querySelector('.ms-container');
if (scrollableContainer) {
const style = scrollableContainer.getAttribute('style');
if (isOpen) {
scrollableContainer.setAttribute('style', `${style} overflow: hidden;`);
} else {
scrollableContainer.setAttribute(
'style',
style?.replace('overflow: hidden;', '')!
);
}
}
});

const closePopover = (e: MouseEvent<HTMLAnchorElement>) => {
e.stopPropagation();
setIsOpen(false);
};
return () => {
if (scrollableContainer) {
const style = scrollableContainer.getAttribute('style');
scrollableContainer.setAttribute(
'style',
style?.replace('overflow: hidden;', '')!
);
}
};
}, [isOpen]);

return (
<>
<ChildrenContainer
ref={childrenContainerRef}
data-testid="popover-children-container"
onClick={() => setIsOpen(true)}
>
{children}
</ChildrenContainer>

{isOpen && (
<PopoverOverlay>
<PopoverContainer
ref={clickAwayRef}
domRect={childrenContainerRef.current?.getBoundingClientRect()}
>
<PopoverItemsContainer gap={SpacingSize.Tiny}>
{renderMenuItems({ closePopover })}
</PopoverItemsContainer>
</PopoverContainer>
</PopoverOverlay>
)}
</>
<TinyPopover
isOpen={isOpen}
onClickOutside={() => setIsOpen(false)}
positions={['bottom', 'top']}
containerStyle={{
zIndex: '15'
}}
transform={{ top: 55, left: 135 }}
parentElement={popoverParentRef.current || undefined}
content={content}
>
{children}
</TinyPopover>
);
}

0 comments on commit 9a6d96b

Please sign in to comment.