react-popper#usePopper TypeScript Examples
The following examples show how to use
react-popper#usePopper.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: index.tsx From cuiswap with GNU General Public License v3.0 | 6 votes |
export default function Popover({ content, show, children, placement = 'auto' }: PopoverProps) {
const [referenceElement, setReferenceElement] = useState<HTMLDivElement>(null)
const [popperElement, setPopperElement] = useState<HTMLDivElement>(null)
const [arrowElement, setArrowElement] = useState<HTMLDivElement>(null)
const { styles, update, attributes } = usePopper(referenceElement, popperElement, {
placement,
strategy: 'fixed',
modifiers: [
{ name: 'offset', options: { offset: [8, 8] } },
{ name: 'arrow', options: { element: arrowElement } }
]
})
useInterval(update, show ? 100 : null)
return (
<>
<ReferenceElement ref={setReferenceElement}>{children}</ReferenceElement>
<Portal>
<PopoverContainer show={show} ref={setPopperElement} style={styles.popper} {...attributes.popper}>
{content}
<Arrow
className={`arrow-${attributes.popper?.['data-popper-placement'] ?? ''}`}
ref={setArrowElement}
style={styles.arrow}
{...attributes.arrow}
/>
</PopoverContainer>
</Portal>
</>
)
}
Example #2
Source File: usePop.ts From gio-design with Apache License 2.0 | 6 votes |
usePop = ({ referenceElement, popperElement, placement, modifiers, strategy }: UsePopProps) => {
const { styles, attributes, ...popperProps } = usePopper(referenceElement, popperElement, {
placement: placements[placement],
modifiers,
strategy,
});
if (popperElement) {
const three = clear3D(styles?.popper?.transform as string);
if (Array.isArray(three)) {
const [x, y, z] = three;
const divHeight = popperElement.offsetHeight;
// const pageHeight = getMaxHeight(referenceElement);
const winHeight = window.innerHeight;
if (styles?.popper?.bottom === 'auto') {
let yField = y;
// if bottom === auto, so the top === 0
// that means the pop will display the bottm of trigger.
// so if we don't think about the limit of bottom of page.
// we should show the pop in window
const bottomTrigger = getBottomOfElement(referenceElement);
if (bottomTrigger > winHeight) {
yField = bottomTrigger - divHeight;
} else {
yField = yField + divHeight > winHeight ? winHeight - divHeight : yField;
}
styles.popper.transform = `translate3d(${x}px, ${yField}px, ${z || 0}px)`;
} else if (styles?.popper?.bottom === '0') {
// const maxYField = pageHeight - (window.pageYOffset + window.innerHeight);
// const yField = y + divHeight > maxYField ? maxYField : y;
styles.popper.transform = `translate3d(${x}px, ${y}px, ${z || 0}px)`;
}
}
}
return { styles, attributes, ...popperProps };
}
Example #3
Source File: index.tsx From goose-frontend-amm with GNU General Public License v3.0 | 6 votes |
export default function Popover({ content, show, children, placement = 'auto' }: PopoverProps) {
const [referenceElement, setReferenceElement] = useState<HTMLDivElement | null>(null)
const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null)
const [arrowElement, setArrowElement] = useState<HTMLDivElement | null>(null)
const { styles, update, attributes } = usePopper(referenceElement, popperElement, {
placement,
strategy: 'fixed',
modifiers: [
{ name: 'offset', options: { offset: [8, 8] } },
{ name: 'arrow', options: { element: arrowElement } },
],
})
const updateCallback = useCallback(() => {
if (update) {
update()
}
}, [update])
useInterval(updateCallback, show ? 100 : null)
return (
<>
<ReferenceElement ref={setReferenceElement as any}>{children}</ReferenceElement>
<Portal>
<PopoverContainer show={show} ref={setPopperElement as any} style={styles.popper} {...attributes.popper}>
{content}
<Arrow
className={`arrow-${attributes.popper?.['data-popper-placement'] ?? ''}`}
ref={setArrowElement as any}
style={styles.arrow}
{...attributes.arrow}
/>
</PopoverContainer>
</Portal>
</>
)
}
Example #4
Source File: index.tsx From sybil-interface with GNU General Public License v3.0 | 6 votes |
export default function Popover({ content, show, children, placement = 'auto' }: PopoverProps) {
const [referenceElement, setReferenceElement] = useState<HTMLDivElement | null>(null)
const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null)
const [arrowElement, setArrowElement] = useState<HTMLDivElement | null>(null)
const { styles, update, attributes } = usePopper(referenceElement, popperElement, {
placement,
strategy: 'fixed',
modifiers: [
{ name: 'offset', options: { offset: [8, 8] } },
{ name: 'arrow', options: { element: arrowElement } },
],
})
const updateCallback = useCallback(() => {
update && update()
}, [update])
useInterval(updateCallback, show ? 100 : null)
return (
<>
<ReferenceElement ref={setReferenceElement as any}>{children}</ReferenceElement>
<Portal>
<PopoverContainer show={show} ref={setPopperElement as any} style={styles.popper} {...attributes.popper}>
{content}
<Arrow
className={`arrow-${attributes.popper?.['data-popper-placement'] ?? ''}`}
ref={setArrowElement as any}
style={styles.arrow}
{...attributes.arrow}
/>
</PopoverContainer>
</Portal>
</>
)
}
Example #5
Source File: index.tsx From cheeseswap-interface with GNU General Public License v3.0 | 6 votes |
export default function Popover({ content, show, children, placement = 'auto' }: PopoverProps) {
const [referenceElement, setReferenceElement] = useState<HTMLDivElement | null>(null)
const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null)
const [arrowElement, setArrowElement] = useState<HTMLDivElement | null>(null)
const { styles, update, attributes } = usePopper(referenceElement, popperElement, {
placement,
strategy: 'fixed',
modifiers: [
{ name: 'offset', options: { offset: [8, 8] } },
{ name: 'arrow', options: { element: arrowElement } }
]
})
const updateCallback = useCallback(() => {
update && update()
}, [update])
useInterval(updateCallback, show ? 100 : null)
return (
<>
<ReferenceElement ref={setReferenceElement as any}>{children}</ReferenceElement>
<Portal>
<PopoverContainer show={show} ref={setPopperElement as any} style={styles.popper} {...attributes.popper}>
{content}
<Arrow
className={`arrow-${attributes.popper?.['data-popper-placement'] ?? ''}`}
ref={setArrowElement as any}
style={styles.arrow}
{...attributes.arrow}
/>
</PopoverContainer>
</Portal>
</>
)
}
Example #6
Source File: index.tsx From dyp with Do What The F*ck You Want To Public License | 6 votes |
export default function Popover({ content, show, children, placement = 'auto' }: PopoverProps) {
const [referenceElement, setReferenceElement] = useState<HTMLDivElement | null>(null)
const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null)
const [arrowElement, setArrowElement] = useState<HTMLDivElement | null>(null)
const { styles, update, attributes } = usePopper(referenceElement, popperElement, {
placement,
strategy: 'fixed',
modifiers: [
{ name: 'offset', options: { offset: [8, 8] } },
{ name: 'arrow', options: { element: arrowElement } }
]
})
const updateCallback = useCallback(() => {
update && update()
}, [update])
useInterval(updateCallback, show ? 100 : null)
return (
<>
<ReferenceElement ref={setReferenceElement as any}>{children}</ReferenceElement>
<Portal>
<PopoverContainer show={show} ref={setPopperElement as any} style={styles.popper} {...attributes.popper}>
{content}
<Arrow
className={`arrow-${attributes.popper?.['data-popper-placement'] ?? ''}`}
ref={setArrowElement as any}
style={styles.arrow}
{...attributes.arrow}
/>
</PopoverContainer>
</Portal>
</>
)
}
Example #7
Source File: manufacturer.tsx From the-researcher-covid-tracker with GNU General Public License v3.0 | 5 votes |
ShareChart = (props) => {
const manufacturers = ["AstraZeneca", "Sinovac", "Sinopharm", "Pfizer", "J&J"]
const brand_colors = ['#F29F05', '#ff5722', 'green', '#00AFF0', '#D71500']
const [isHover, setHover] = useState(false)
const [referenceElement, setReferenceElement] = useState(null);
const [popperElement, setPopperElement] = useState(null);
const [arrowElement, setArrowElement] = useState(null);
const { styles, attributes } = usePopper(referenceElement, popperElement, {
placement: 'left',
strategy: 'fixed',
modifiers: [{
name: 'arrow',
options: {
element: arrowElement
}
}],
});
return (
<td onMouseEnter={() => setHover(true)}
onMouseLeave={() => setHover(false)}>
<div >
<div
className="manufacturers_bar d-flex w-100" style={{ height: 10 }} ref={setReferenceElement}
>
{props.shares.map((mf, index) => {
return (
<div key={index} style={{ width: `${mf * 100}%`, backgroundColor: brand_colors[index] }} />
)
})}
</div>
{isHover &&
<div
className='bg-white container shadow rounded text-dark py-2 mr-3'
style={{ ...styles.popper, maxWidth: 180, minWidth: 150 }}
ref={setPopperElement} {...attributes.popper}>
{props.shares.map((mf, index) => {
return (
<div className='row' key={index}>
<div className='col-4 text-left'>
<b>{manufacturers[index]}</b>
</div>
<div className='col-8'>
{(mf * 100).toFixed(1)}%
</div>
</div>
)
})}
</div>
}
</div>
</td>
)
}
Example #8
Source File: QnaAcceptPopper.tsx From WEB_CodeSquare_AmongUs with MIT License | 5 votes |
QnaAcceptPopper: React.FC<QnaAcceptPopperProps> = ({
anchorEl,
show,
}) => {
const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(
null,
);
const [arrowElement, setArrowElement] = useState<HTMLDivElement | null>(null);
const { styles, attributes } = usePopper(anchorEl, popperElement, {
placement: "left",
modifiers: [
{ name: "arrow", options: { element: arrowElement } },
{
name: "offset",
options: {
offset: [0, 10],
},
},
],
});
return (
<PopperContainer
ref={setPopperElement}
style={styles.popper}
show={show}
{...attributes.popper}
>
<div ref={setArrowElement} style={styles.arrow} className="arrow" />
<div>
<div>답변이 도움이 되셨다면,</div>
<div>
<AcceptIcon
disabled
css={css`
width: 14px;
height: 11px;
`}
/>
를 눌러 채택해 주세요!
</div>
</div>
</PopperContainer>
);
}
Example #9
Source File: BaseMenu.tsx From pancake-toolkit with GNU General Public License v3.0 | 5 votes |
BaseMenu: React.FC<BaseMenuProps> = ({ component, options, children, isOpen = false }) => {
const [targetElement, setTargetElement] = useState<HTMLElement | null>(null);
const [menuElement, setMenuElement] = useState<HTMLElement | null>(null);
const placement = options?.placement ?? "bottom";
const offset = options?.offset ?? [0, 10];
const padding = options?.padding ?? { left: 16, right: 16 };
const [isMenuOpen, setIsMenuOpen] = useState(isOpen);
const toggle = () => {
setIsMenuOpen((prev) => !prev);
};
const open = () => {
setIsMenuOpen(true);
};
const close = () => {
setIsMenuOpen(false);
};
// Allow for component to be controlled
useEffect(() => {
setIsMenuOpen(isOpen);
}, [isOpen, setIsMenuOpen]);
useEffect(() => {
const handleClickOutside = ({ target }: Event) => {
if (target instanceof Node) {
if (
menuElement !== null &&
targetElement !== null &&
!menuElement.contains(target) &&
!targetElement.contains(target)
) {
setIsMenuOpen(false);
}
}
};
if (menuElement !== null) {
document.addEventListener("click", handleClickOutside);
}
return () => {
document.removeEventListener("click", handleClickOutside);
};
}, [menuElement, targetElement]);
const { styles, attributes } = usePopper(targetElement, menuElement, {
placement,
modifiers: [
{ name: "offset", options: { offset } },
{ name: "preventOverflow", options: { padding } },
],
});
const menu = (
<div ref={setMenuElement} style={styles.popper} {...attributes.popper}>
{typeof children === "function" ? children({ toggle, open, close }) : children}
</div>
);
const portal = getPortalRoot();
const renderMenu = portal ? createPortal(menu, portal) : menu;
return (
<>
<ClickableElementContainer ref={setTargetElement} onClick={toggle}>
{component}
</ClickableElementContainer>
{isMenuOpen && renderMenu}
</>
);
}
Example #10
Source File: tooltips.tsx From vscode-lean4 with Apache License 2.0 | 5 votes |
Tooltip = forwardAndUseRef<HTMLDivElement,
React.HTMLProps<HTMLDivElement> &
{ reference: HTMLElement | null,
mkTooltipContent: MkTooltipContentFn,
placement?: Popper.Placement,
onFirstUpdate?: (_: Partial<Popper.State>) => void
}>((props_, divRef, setDivRef) => {
const {reference, mkTooltipContent, placement: preferPlacement, onFirstUpdate, ...props} = props_
// We remember the global trend in placement (as `globalPlacement`) so tooltip chains can bounce
// off the top and continue downwards or vice versa and initialize to that, but then update
// the trend (as `ourPlacement`).
const globalPlacement = React.useContext(TooltipPlacementContext)
const placement = preferPlacement ? preferPlacement : globalPlacement
const [ourPlacement, setOurPlacement] = React.useState<Popper.Placement>(placement)
// https://popper.js.org/react-popper/v2/faq/#why-i-get-render-loop-whenever-i-put-a-function-inside-the-popper-configuration
const onFirstUpdate_ = React.useCallback((state: Partial<Popper.State>) => {
if (state.placement) setOurPlacement(state.placement)
if (onFirstUpdate) onFirstUpdate(state)
}, [onFirstUpdate])
const [arrowElement, setArrowElement] = React.useState<HTMLDivElement | null>(null)
const { styles, attributes, update } = usePopper(reference, divRef.current, {
modifiers: [
{ name: 'arrow', options: { element: arrowElement } },
{ name: 'offset', options: { offset: [0, 8] } },
],
placement,
onFirstUpdate: onFirstUpdate_
})
const update_ = React.useCallback(() => update?.(), [update])
const logicalDom = React.useContext(LogicalDomContext)
const popper = <div
ref={node => {
setDivRef(node)
logicalDom.registerDescendant(node)
}}
style={styles.popper}
className='tooltip'
{...props}
{...attributes.popper}
>
<TooltipPlacementContext.Provider value={ourPlacement}>
{mkTooltipContent(update_)}
</TooltipPlacementContext.Provider>
<div ref={setArrowElement}
style={styles.arrow}
className='tooltip-arrow'
/>
</div>
// Append the tooltip to the end of document body to avoid layout issues.
// (https://github.com/leanprover/vscode-lean4/issues/51)
return ReactDOM.createPortal(popper, document.body)
})
Example #11
Source File: index.tsx From pancake-toolkit with GNU General Public License v3.0 | 5 votes |
UserMenu: React.FC<UserMenuProps> = ({
account,
text,
avatarSrc,
variant = variants.DEFAULT,
children,
...props
}) => {
const [isOpen, setIsOpen] = useState(false);
const [targetRef, setTargetRef] = useState<HTMLDivElement | null>(null);
const [tooltipRef, setTooltipRef] = useState<HTMLDivElement | null>(null);
const accountEllipsis = account ? `${account.substring(0, 2)}...${account.substring(account.length - 4)}` : null;
const { styles, attributes } = usePopper(targetRef, tooltipRef, {
strategy: "fixed",
placement: "bottom-end",
modifiers: [{ name: "offset", options: { offset: [0, 0] } }],
});
useEffect(() => {
const showDropdownMenu = () => {
setIsOpen(true);
};
const hideDropdownMenu = (evt: MouseEvent | TouchEvent) => {
const target = evt.target as Node;
if (target && !tooltipRef?.contains(target)) {
setIsOpen(false);
evt.stopPropagation();
}
};
targetRef?.addEventListener("mouseenter", showDropdownMenu);
targetRef?.addEventListener("mouseleave", hideDropdownMenu);
return () => {
targetRef?.removeEventListener("mouseenter", showDropdownMenu);
targetRef?.removeEventListener("mouseleave", hideDropdownMenu);
};
}, [targetRef, tooltipRef, setIsOpen]);
return (
<Flex alignItems="center" height="100%" ref={setTargetRef} {...props}>
<StyledUserMenu
onTouchStart={() => {
setIsOpen((s) => !s);
}}
>
<MenuIcon avatarSrc={avatarSrc} variant={variant} />
<LabelText title={text || account}>{text || accountEllipsis}</LabelText>
<ChevronDownIcon color="text" width="24px" />
</StyledUserMenu>
<Menu style={styles.popper} ref={setTooltipRef} {...attributes.popper} isOpen={isOpen}>
<Box onClick={() => setIsOpen(false)}>{children}</Box>
</Menu>
</Flex>
);
}
Example #12
Source File: index.tsx From limit-orders-lib with GNU General Public License v3.0 | 5 votes |
export default function Popover({
content,
show,
children,
placement = "auto",
}: PopoverProps) {
const [
referenceElement,
setReferenceElement,
] = useState<HTMLDivElement | null>(null);
const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(
null
);
const [arrowElement, setArrowElement] = useState<HTMLDivElement | null>(null);
const { styles, update, attributes } = usePopper(
referenceElement,
popperElement,
{
placement,
strategy: "fixed",
modifiers: [
{ name: "offset", options: { offset: [8, 8] } },
{ name: "arrow", options: { element: arrowElement } },
],
}
);
const updateCallback = useCallback(() => {
update && update();
}, [update]);
useInterval(updateCallback, show ? 100 : null);
return (
<Fragment>
<ReferenceElement ref={setReferenceElement as any}>
{children}
</ReferenceElement>
<Portal>
<PopoverContainer
show={show}
ref={setPopperElement as any}
style={styles.popper}
{...attributes.popper}
>
{content}
<Arrow
className={`arrow-${
attributes.popper?.["data-popper-placement"] ?? ""
}`}
ref={setArrowElement as any}
style={styles.arrow}
{...attributes.arrow}
/>
</PopoverContainer>
</Portal>
</Fragment>
);
}
Example #13
Source File: RangeSelector.tsx From crypto-fees with MIT License | 5 votes |
RangeSelector: React.FC<RangeSelectorProps> = ({ value, onChange, maxDate }) => {
const [show, setShow] = useState(false);
const button = useRef(null);
const popup = useRef(null);
const { styles, attributes } = usePopper(button.current, popup.current, {
placement: 'bottom-start',
});
useEffect(() => {
if (show) {
const listener = () => setShow(false);
document.addEventListener('click', listener);
return () => document.removeEventListener('click', listener);
}
}, [show]);
return (
<div>
<Button ref={button} onClick={() => setShow(!show)}>
{formatDate(value.start, '/')} - {formatDate(value.end, '/')}
</Button>
<div
ref={popup}
style={{ ...styles.popper, display: show ? 'block' : 'none' }}
className="popover"
{...attributes.popper}
onClick={(e: any) => {
e.stopPropagation();
e.nativeEvent.stopImmediatePropagation();
}}
>
<div className="picker-container">
<DatePicker
selected={value.start}
startDate={value.start}
endDate={value.end}
onChange={(start: Date) => onChange({ ...value, start })}
selectsStart
inline
maxDate={maxDate}
/>
<DatePicker
selected={value.end}
startDate={value.start}
endDate={value.end}
onChange={(end: Date) => onChange({ ...value, end })}
selectsEnd
inline
maxDate={maxDate}
/>
</div>
</div>
<style jsx>{`
.popover {
z-index: 100;
}
.picker-container {
display: flex;
background: #ffffff;
border: 1px solid #aeaeae;
border-radius: 2px;
}
.picker-container :global(.react-datepicker) {
border: none;
border-radius: 0;
}
`}</style>
</div>
);
}
Example #14
Source File: font-family-select.tsx From utopia with MIT License | 5 votes |
FontFamilySelect = React.memo(() => {
const [referenceElement, setReferenceElement] = React.useState<HTMLDivElement | null>(null)
const [popperElement, setPopperElement] = React.useState<HTMLDivElement | null>(null)
const { styles, attributes } = usePopper(referenceElement, popperElement, {
modifiers: [
{
name: 'offset',
options: {
offset: [0, 8],
},
},
],
})
const [popupIsOpen, setPopupIsOpen] = React.useState(false)
const togglePopup = React.useCallback(() => setPopupIsOpen((v) => !v), [])
const closePopup = React.useCallback(() => setPopupIsOpen(false), [])
const onMouseDown = React.useCallback(
(e: React.MouseEvent) => {
togglePopup()
e.stopPropagation()
},
[togglePopup],
)
const { value, useSubmitValueFactory, onUnsetValues, controlStyles } = useInspectorInfo(
['fontFamily', 'fontStyle', 'fontWeight'],
identity,
identity,
stylePropPathMappingFn,
)
const fontFamilyContextMenuItems = utils.stripNulls([
controlStyles.unsettable ? addOnUnsetValues(['fontFamily'], onUnsetValues) : null,
])
return (
<PropertyRow>
<InspectorContextMenuWrapper
id='fontFamily-context-menu'
items={fontFamilyContextMenuItems}
data={null}
style={{ marginBottom: 8, gridColumn: '1 / span 6' }}
>
{popupIsOpen ? (
<FontFamilySelectPopup
{...attributes.popper}
style={styles.popper}
ref={setPopperElement}
value={value}
onUnsetValues={onUnsetValues}
controlStyles={controlStyles}
closePopup={closePopup}
useSubmitFontVariantFactory={useSubmitValueFactory}
/>
) : null}
<FlexRow
ref={setReferenceElement}
onMouseDown={onMouseDown}
style={{
background: controlStyles.backgroundColor,
color: controlStyles.mainColor,
boxShadow: `0 0 0 1px ${controlStyles.borderColor} inset`,
padding: 4,
fontSize: 14,
height: 30,
borderRadius: UtopiaTheme.inputBorderRadius,
}}
>
<div style={{ flexGrow: 1 }}>{value.fontFamily[0]}</div>
<Icons.ExpansionArrowDown />
</FlexRow>
</InspectorContextMenuWrapper>
</PropertyRow>
)
})
Example #15
Source File: BaseMenu.tsx From vvs-ui with GNU General Public License v3.0 | 5 votes |
BaseMenu: React.FC<BaseMenuProps> = ({ component, options, children, isOpen = false }) => {
const [targetElement, setTargetElement] = useState<HTMLElement | null>(null);
const [menuElement, setMenuElement] = useState<HTMLElement | null>(null);
const placement = options?.placement ?? "bottom";
const offset = options?.offset ?? [0, 10];
const padding = options?.padding ?? { left: 16, right: 16 };
const [isMenuOpen, setIsMenuOpen] = useState(isOpen);
const toggle = () => {
setIsMenuOpen((prev) => !prev);
};
const open = () => {
setIsMenuOpen(true);
};
const close = () => {
setIsMenuOpen(false);
};
// Allow for component to be controlled
useEffect(() => {
setIsMenuOpen(isOpen);
}, [isOpen, setIsMenuOpen]);
useEffect(() => {
const handleClickOutside = ({ target }: Event) => {
if (target instanceof Node) {
if (
menuElement !== null &&
targetElement !== null &&
!menuElement.contains(target) &&
!targetElement.contains(target)
) {
setIsMenuOpen(false);
}
}
};
if (menuElement !== null) {
document.addEventListener("click", handleClickOutside);
}
return () => {
document.removeEventListener("click", handleClickOutside);
};
}, [menuElement, targetElement]);
const { styles, attributes } = usePopper(targetElement, menuElement, {
placement,
modifiers: [
{ name: "offset", options: { offset } },
{ name: "preventOverflow", options: { padding } },
],
});
const menu = (
<div ref={setMenuElement} style={styles.popper} {...attributes.popper}>
{typeof children === "function" ? children({ toggle, open, close }) : children}
</div>
);
const renderMenu = portalRoot ? createPortal(menu, portalRoot) : menu;
return (
<>
<ClickableElementContainer ref={setTargetElement} onClick={toggle}>
{component}
</ClickableElementContainer>
{isMenuOpen && renderMenu}
</>
);
}
Example #16
Source File: Tooltip.tsx From UUI with MIT License | 5 votes |
Tooltip = UUIFunctionComponent({
name: 'Tooltip',
nodes: {
Root: 'div',
Tip: 'div',
Arrow: 'div',
},
propTypes: TooltipPropTypes,
}, (props: TooltipFeatureProps, { nodes }) => {
const { Root, Tip, Arrow } = nodes
/**
* handle optional props default value
*/
const finalProps = {
placement: props.placement || 'bottom',
strategy: props.strategy || 'fixed',
}
const [referenceElement, setReferenceElement] = useState<any>(null);
const [popperElement, setPopperElement] = useState<any>(null);
const { styles, attributes } = usePopper(referenceElement, popperElement, {
placement: finalProps.placement,
strategy: finalProps.strategy,
modifiers: [
{ name: 'offset', options: { offset: [0, 8] } },
]
});
return (
<Root ref={(ref) => { setReferenceElement(ref); }}>
{props.children}
<Tip
role="tooltip"
ref={(ref) => { setPopperElement(ref); }}
style={{...styles.popper}} {...attributes.popper}
>
{props.label}
<Arrow style={{...styles.arrow}} data-popper-arrow></Arrow>
</Tip>
</Root>
)
})
Example #17
Source File: HelpDialog.tsx From pybricks-code with MIT License | 4 votes |
HelpDialog: React.FunctionComponent<HelpDialogProps> = ({
title,
isOpen,
openButton,
onClose,
onAnimationEnd,
children,
}) => {
const i18n = useI18n();
// this is the dialog element and the popper element
const ref = useRef<HTMLDivElement>(null);
const { overlayProps, underlayProps } = useOverlay(
{ isOpen, onClose, isDismissable: true },
ref,
);
const descId = useId();
const { modalProps } = useModal();
const { dialogProps } = useDialog(
{ 'aria-label': title, 'aria-describedby': descId },
ref,
);
const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null);
const [arrow, setArrow] = useState<HTMLDivElement | null>(null);
const { styles, attributes, state } = usePopper(
openButton,
popperElement,
useMemo(
() => ({
placement: 'right',
modifiers: [
{
name: 'arrow',
options: {
element: arrow,
},
},
{
name: 'offset',
options: {
offset: [0, POPOVER_ARROW_SVG_SIZE / 2],
},
},
],
}),
[arrow],
),
);
// HACK: workaround https://github.com/palantir/blueprint/pull/5302
useEffect(() => {
arrow?.setAttribute('aria-hidden', 'true');
}, [arrow]);
// HACK: since there are not keyboard focusable elements for autoFocus
// we have to manually focus the only focusable element in the dialog,
// otherwise the FocusScope doesn't work.
const dismissId = useId();
useEffect(() => {
document.getElementById(dismissId)?.focus();
}, [dismissId]);
return (
<div
className={classNames(Classes.OVERLAY, { [Classes.OVERLAY_OPEN]: isOpen })}
>
<div className={Classes.OVERLAY_BACKDROP} {...underlayProps}>
<div
className={classNames(
Classes2.POPOVER2_TRANSITION_CONTAINER,
Classes.OVERLAY_CONTENT,
)}
ref={setPopperElement}
style={styles.popper}
{...attributes.popper}
>
<div
className={classNames(
Classes2.POPOVER2,
`${Classes2.POPOVER2}-${isOpen ? 'open' : 'close'}`,
`${Classes2.POPOVER2_CONTENT_PLACEMENT}-right`,
Classes2.POPOVER2_CONTENT_SIZING,
)}
onAnimationEnd={onAnimationEnd}
>
<Popover2Arrow
arrowProps={{ ref: setArrow, style: styles.arrow }}
placement={state?.placement ?? 'auto'}
/>
<FocusScope contain restoreFocus autoFocus>
<div
aria-modal
className="pb-help-dialog"
ref={ref}
{...mergeProps(overlayProps, dialogProps, modalProps)}
// dialog itself should not be focusable
tabIndex={undefined}
// clicking anywhere in the dialog closes it
onClick={onClose}
>
<div id={descId} className={Classes2.POPOVER2_CONTENT}>
{children}
</div>
<DismissButton
id={dismissId}
aria-label={i18n.translate(
I18nId.HelpDialogCloseButtonLabel,
)}
onDismiss={onClose}
/>
</div>
</FocusScope>
</div>
</div>
</div>
</div>
);
}
Example #18
Source File: index.tsx From exevo-pan with The Unlicense | 4 votes |
Popover = ({
className,
children,
content,
placement = 'top',
trigger = 'hover',
visible,
offset = [0, 0],
...props
}: PopoverProps) => {
const {
translations: { common },
} = useTranslations()
const [isVisible, setVisible] = useState<boolean>(visible ?? false)
const derivedVisibility =
trigger === 'none' ? visible ?? isVisible : isVisible
const [referenceElement, setReferenceElement] =
useState<PopperReferenceElement>(null)
const [popperElement, setPopperElement] =
useState<PopperReferenceElement>(null)
const modifiers: Partial<Modifier<string, Record<string, unknown>>>[] =
useMemo(
() => [
{
name: 'flip',
enabled: true,
options: {
allowedAutoPlacements: ['top', 'bottom'],
},
},
{
name: 'offset',
options: {
offset,
},
},
],
[offset],
)
const { styles, attributes } = usePopper(referenceElement, popperElement, {
placement,
modifiers,
})
const triggers = useMemo(() => {
switch (trigger) {
case 'click':
return {
tabIndex: 0,
onClick: () => setVisible((prev) => !prev),
onKeyPress: (event: React.KeyboardEvent) => {
if (checkKeyboardTrigger(event.code)) {
event.preventDefault()
setVisible((prev) => !prev)
}
},
}
case 'hover':
return {
tabIndex: 0,
onMouseEnter: () => setVisible(true),
onMouseLeave: () => setVisible(false),
onFocus: () => setVisible(true),
onBlur: () => setVisible(false),
}
case 'none':
default:
return {}
}
}, [trigger])
const increaseHoverArea = trigger === 'hover' && derivedVisibility
return (
<>
<div
ref={setReferenceElement}
className="child:z-1 child:relative relative inline-block cursor-pointer"
{...triggers}
>
{increaseHoverArea && (
<div
role="none"
className="top-1/2 left-1/2"
style={{
position: 'absolute',
transform: 'translate(-50%, -50%)',
width: `calc(100% + ${offset[0] + 8}px)`,
height: `calc(100% + ${offset[1] + 8}px)`,
}}
/>
)}
{children}
</div>
{derivedVisibility && (
<div
ref={setPopperElement}
style={styles.popper}
{...attributes.popper}
className={clsx(
'z-51 animate-fadeIn',
className,
attributes.popper?.className,
)}
{...props}
{...(trigger === 'hover' ? { ...triggers, tabIndex: undefined } : {})}
>
{Children.map(content, (contentChild) => {
if (!isValidElement(contentChild)) return contentChild
if (typeof contentChild.type === 'string') return contentChild
return cloneElement(contentChild, {
'aria-hidden': false,
disabled: false,
hidden: false,
})
})}
</div>
)}
{trigger === 'click' && derivedVisibility && (
<button
type="button"
className="bg-backdrop animate-fadeIn fixed top-0 left-0 z-50 h-screen w-screen"
aria-label={common.PopoverCloseLabel}
onClick={() => setVisible(false)}
/>
)}
</>
)
}
Example #19
Source File: ManageLists.tsx From forward.swaps with GNU General Public License v3.0 | 4 votes |
ListRow = memo(function ListRow({ listUrl }: { listUrl: string }) {
const listsByUrl = useSelector<AppState, AppState['lists']['byUrl']>(state => state.lists.byUrl)
const dispatch = useDispatch<AppDispatch>()
const { current: list, pendingUpdate: pending } = listsByUrl[listUrl]
const theme = useTheme()
const listColor = useListColor(list?.logoURI)
const isActive = useIsListActive(listUrl)
const [open, toggle] = useToggle(false)
const node = useRef<HTMLDivElement>()
const [referenceElement, setReferenceElement] = useState<HTMLDivElement>()
const [popperElement, setPopperElement] = useState<HTMLDivElement>()
const { styles, attributes } = usePopper(referenceElement, popperElement, {
placement: 'auto',
strategy: 'fixed',
modifiers: [{ name: 'offset', options: { offset: [8, 8] } }]
})
useOnClickOutside(node, open ? toggle : undefined)
const handleAcceptListUpdate = useCallback(() => {
if (!pending) return
ReactGA.event({
category: 'Lists',
action: 'Update List from List Select',
label: listUrl
})
dispatch(acceptListUpdate(listUrl))
}, [dispatch, listUrl, pending])
const handleRemoveList = useCallback(() => {
ReactGA.event({
category: 'Lists',
action: 'Start Remove List',
label: listUrl
})
if (window.prompt(`Please confirm you would like to remove this list by typing REMOVE`) === `REMOVE`) {
ReactGA.event({
category: 'Lists',
action: 'Confirm Remove List',
label: listUrl
})
dispatch(removeList(listUrl))
}
}, [dispatch, listUrl])
const handleEnableList = useCallback(() => {
ReactGA.event({
category: 'Lists',
action: 'Enable List',
label: listUrl
})
dispatch(enableList(listUrl))
}, [dispatch, listUrl])
const handleDisableList = useCallback(() => {
ReactGA.event({
category: 'Lists',
action: 'Disable List',
label: listUrl
})
dispatch(disableList(listUrl))
}, [dispatch, listUrl])
if (!list) return null
return (
<RowWrapper active={isActive} bgColor={listColor} key={listUrl} id={listUrlRowHTMLId(listUrl)}>
{list.logoURI ? (
<ListLogo size="40px" style={{ marginRight: '1rem' }} logoURI={list.logoURI} alt={`${list.name} list logo`} />
) : (
<div style={{ width: '24px', height: '24px', marginRight: '1rem' }} />
)}
<Column style={{ flex: '1' }}>
<Row>
<StyledTitleText active={isActive}>{list.name}</StyledTitleText>
</Row>
<RowFixed mt="4px">
<StyledListUrlText active={isActive} mr="6px">
{list.tokens.length} tokens
</StyledListUrlText>
<StyledMenu ref={node as any}>
<ButtonEmpty onClick={toggle} ref={setReferenceElement} padding="0">
<Settings stroke={isActive ? theme.bg1 : theme.text1} size={12} />
</ButtonEmpty>
{open && (
<PopoverContainer show={true} ref={setPopperElement as any} style={styles.popper} {...attributes.popper}>
<div>{list && listVersionLabel(list.version)}</div>
<SeparatorDark />
<ExternalLink href={`https://tokenlists.org/token-list?url=${listUrl}`}>View list</ExternalLink>
<UnpaddedLinkStyledButton onClick={handleRemoveList} disabled={Object.keys(listsByUrl).length === 1}>
Remove list
</UnpaddedLinkStyledButton>
{pending && (
<UnpaddedLinkStyledButton onClick={handleAcceptListUpdate}>Update list</UnpaddedLinkStyledButton>
)}
</PopoverContainer>
)}
</StyledMenu>
</RowFixed>
</Column>
<ListToggle
isActive={isActive}
bgColor={listColor}
toggle={() => {
isActive ? handleDisableList() : handleEnableList()
}}
/>
</RowWrapper>
)
})
Example #20
Source File: ListSelect.tsx From panther-frontend-dex with GNU General Public License v3.0 | 4 votes |
ListRow = memo(function ListRow({ listUrl, onBack }: { listUrl: string; onBack: () => void }) {
const listsByUrl = useSelector<AppState, AppState['lists']['byUrl']>((state) => state.lists.byUrl)
const selectedListUrl = useSelectedListUrl()
const dispatch = useDispatch<AppDispatch>()
const { current: list, pendingUpdate: pending } = listsByUrl[listUrl]
const isSelected = listUrl === selectedListUrl
const [open, toggle] = useToggle(false)
const node = useRef<HTMLDivElement>()
const [referenceElement, setReferenceElement] = useState<HTMLDivElement | null>()
const [popperElement, setPopperElement] = useState<HTMLDivElement>()
const { styles, attributes } = usePopper(referenceElement, popperElement, {
placement: 'auto',
strategy: 'fixed',
modifiers: [{ name: 'offset', options: { offset: [8, 8] } }],
})
useOnClickOutside(node, open ? toggle : undefined)
const selectThisList = useCallback(() => {
if (isSelected) return
dispatch(selectList(listUrl))
onBack()
}, [dispatch, isSelected, listUrl, onBack])
const handleAcceptListUpdate = useCallback(() => {
if (!pending) return
dispatch(acceptListUpdate(listUrl))
}, [dispatch, listUrl, pending])
const handleRemoveList = useCallback(() => {
if (window.prompt(`Please confirm you would like to remove this list by typing REMOVE`) === `REMOVE`) {
dispatch(removeList(listUrl))
}
}, [dispatch, listUrl])
if (!list) return null
return (
<Row key={listUrl} align="center" padding="16px" id={listUrlRowHTMLId(listUrl)}>
{list.logoURI ? (
<ListLogo style={{ marginRight: '1rem' }} logoURI={list.logoURI} alt={`${list.name} list logo`} />
) : (
<div style={{ width: '24px', height: '24px', marginRight: '1rem' }} />
)}
<Column style={{ flex: '1' }}>
<Row>
<Text bold={isSelected} fontSize="16px" style={{ overflow: 'hidden', textOverflow: 'ellipsis' }}>
{list.name}
</Text>
</Row>
<Row
style={{
marginTop: '4px',
}}
>
<StyledListUrlText title={listUrl}>
<ListOrigin listUrl={listUrl} />
</StyledListUrlText>
</Row>
</Column>
<StyledMenu ref={node as any}>
<div style={{ display: 'inline-block' }} ref={setReferenceElement}>
<Button
style={{
width: '32px',
marginRight: '8px',
}}
onClick={toggle}
variant="secondary"
>
<ChevronDownIcon />
</Button>
</div>
{open && (
<PopoverContainer show ref={setPopperElement as any} style={styles.popper} {...attributes.popper}>
<div>{list && listVersionLabel(list.version)}</div>
<SeparatorDark />
<ExternalLink href={`https://tokenlists.org/token-list?url=${listUrl}`}>View list</ExternalLink>
<UnpaddedLinkStyledButton onClick={handleRemoveList} disabled={Object.keys(listsByUrl).length === 1}>
Remove list
</UnpaddedLinkStyledButton>
{pending && (
<UnpaddedLinkStyledButton onClick={handleAcceptListUpdate}>Update list</UnpaddedLinkStyledButton>
)}
</PopoverContainer>
)}
</StyledMenu>
{isSelected ? (
<Button disabled style={{ width: '5rem', minWidth: '5rem' }}>
Selected
</Button>
) : (
<>
<Button
className="select-button"
style={{
width: '5rem',
minWidth: '4.5rem',
}}
onClick={selectThisList}
>
Select
</Button>
</>
)}
</Row>
)
})
Example #21
Source File: ListSelect.tsx From sushiswap-exchange with GNU General Public License v3.0 | 4 votes |
ListRow = memo(function ListRow({ listUrl, onBack }: { listUrl: string; onBack: () => void }) {
const listsByUrl = useSelector<AppState, AppState['lists']['byUrl']>(state => state.lists.byUrl)
const selectedListUrl = useSelectedListUrl()
const dispatch = useDispatch<AppDispatch>()
const { current: list, pendingUpdate: pending } = listsByUrl[listUrl]
const isSelected = listUrl === selectedListUrl
const [open, toggle] = useToggle(false)
const node = useRef<HTMLDivElement>()
const [referenceElement, setReferenceElement] = useState<HTMLDivElement>()
const [popperElement, setPopperElement] = useState<HTMLDivElement>()
const { styles, attributes } = usePopper(referenceElement, popperElement, {
placement: 'auto',
strategy: 'fixed',
modifiers: [{ name: 'offset', options: { offset: [8, 8] } }]
})
useOnClickOutside(node, open ? toggle : undefined)
const selectThisList = useCallback(() => {
if (isSelected) return
ReactGA.event({
category: 'Lists',
action: 'Select List',
label: listUrl
})
dispatch(selectList(listUrl))
onBack()
}, [dispatch, isSelected, listUrl, onBack])
const handleAcceptListUpdate = useCallback(() => {
if (!pending) return
ReactGA.event({
category: 'Lists',
action: 'Update List from List Select',
label: listUrl
})
dispatch(acceptListUpdate(listUrl))
}, [dispatch, listUrl, pending])
const handleRemoveList = useCallback(() => {
ReactGA.event({
category: 'Lists',
action: 'Start Remove List',
label: listUrl
})
if (window.prompt(`Please confirm you would like to remove this list by typing REMOVE`) === `REMOVE`) {
ReactGA.event({
category: 'Lists',
action: 'Confirm Remove List',
label: listUrl
})
dispatch(removeList(listUrl))
}
}, [dispatch, listUrl])
if (!list) return null
return (
<Row key={listUrl} align="center" padding="16px" id={listUrlRowHTMLId(listUrl)}>
{list.logoURI ? (
<ListLogo style={{ marginRight: '1rem' }} logoURI={list.logoURI} alt={`${list.name} list logo`} />
) : (
<div style={{ width: '24px', height: '24px', marginRight: '1rem' }} />
)}
<Column style={{ flex: '1' }}>
<Row>
<Text
fontWeight={isSelected ? 500 : 400}
fontSize={16}
style={{ overflow: 'hidden', textOverflow: 'ellipsis' }}
>
{list.name}
</Text>
</Row>
<Row
style={{
marginTop: '4px'
}}
>
<StyledListUrlText title={listUrl}>
<ListOrigin listUrl={listUrl} />
</StyledListUrlText>
</Row>
</Column>
<StyledMenu ref={node as any}>
<ButtonOutlined
style={{
width: '2rem',
padding: '.8rem .35rem',
borderRadius: '12px',
fontSize: '14px',
marginRight: '0.5rem'
}}
onClick={toggle}
ref={setReferenceElement}
>
<DropDown />
</ButtonOutlined>
{open && (
<PopoverContainer show={true} ref={setPopperElement as any} style={styles.popper} {...attributes.popper}>
<div>{list && listVersionLabel(list.version)}</div>
<SeparatorDark />
<ExternalLink href={`https://tokenlists.org/token-list?url=${listUrl}`}>View list</ExternalLink>
<UnpaddedLinkStyledButton onClick={handleRemoveList} disabled={Object.keys(listsByUrl).length === 1}>
Remove list
</UnpaddedLinkStyledButton>
{pending && (
<UnpaddedLinkStyledButton onClick={handleAcceptListUpdate}>Update list</UnpaddedLinkStyledButton>
)}
</PopoverContainer>
)}
</StyledMenu>
{isSelected ? (
<ButtonPrimary
disabled={true}
className="select-button"
style={{ width: '5rem', minWidth: '5rem', padding: '0.5rem .35rem', borderRadius: '12px', fontSize: '14px' }}
>
Selected
</ButtonPrimary>
) : (
<>
<ButtonPrimary
className="select-button"
style={{
width: '5rem',
minWidth: '4.5rem',
padding: '0.5rem .35rem',
borderRadius: '12px',
fontSize: '14px'
}}
onClick={selectThisList}
>
Select
</ButtonPrimary>
</>
)}
</Row>
)
})
Example #22
Source File: ListSelect.tsx From luaswap-interface with GNU General Public License v3.0 | 4 votes |
ListRow = memo(function ListRow({ listUrl, onBack }: { listUrl: string; onBack: () => void }) {
const listsByUrl = useSelector<AppState, AppState['lists']['byUrl']>(state => state.lists.byUrl)
const selectedListUrl = useSelectedListUrl()
const dispatch = useDispatch<AppDispatch>()
const { current: list, pendingUpdate: pending } = listsByUrl[listUrl]
const isSelected = listUrl === selectedListUrl
const [open, toggle] = useToggle(false)
const node = useRef<HTMLDivElement>()
const [referenceElement, setReferenceElement] = useState<HTMLDivElement>()
const [popperElement, setPopperElement] = useState<HTMLDivElement>()
const { styles, attributes } = usePopper(referenceElement, popperElement, {
placement: 'auto',
strategy: 'fixed',
modifiers: [{ name: 'offset', options: { offset: [8, 8] } }]
})
useOnClickOutside(node, open ? toggle : undefined)
const selectThisList = useCallback(() => {
if (isSelected) return
ReactGA.event({
category: 'Lists',
action: 'Select List',
label: listUrl
})
dispatch(selectList(listUrl))
onBack()
}, [dispatch, isSelected, listUrl, onBack])
const handleAcceptListUpdate = useCallback(() => {
if (!pending) return
ReactGA.event({
category: 'Lists',
action: 'Update List from List Select',
label: listUrl
})
dispatch(acceptListUpdate(listUrl))
}, [dispatch, listUrl, pending])
const handleRemoveList = useCallback(() => {
ReactGA.event({
category: 'Lists',
action: 'Start Remove List',
label: listUrl
})
if (window.prompt(`Please confirm you would like to remove this list by typing REMOVE`) === `REMOVE`) {
ReactGA.event({
category: 'Lists',
action: 'Confirm Remove List',
label: listUrl
})
dispatch(removeList(listUrl))
}
}, [dispatch, listUrl])
if (!list) return null
return (
<Row key={listUrl} align="center" padding="16px" id={listUrlRowHTMLId(listUrl)}>
{list.logoURI ? (
<ListLogo style={{ marginRight: '1rem' }} logoURI={list.logoURI} alt={`${list.name} list logo`} />
) : (
<div style={{ width: '24px', height: '24px', marginRight: '1rem' }} />
)}
<Column style={{ flex: '1' }}>
<Row>
<Text
fontWeight={isSelected ? 500 : 400}
fontSize={16}
style={{ overflow: 'hidden', textOverflow: 'ellipsis' }}
>
{list.name}
</Text>
</Row>
<Row
style={{
marginTop: '4px'
}}
>
<StyledListUrlText title={listUrl}>
<ListOrigin listUrl={listUrl} />
</StyledListUrlText>
</Row>
</Column>
<StyledMenu ref={node as any}>
<ButtonOutlined
style={{
width: '2rem',
padding: '.8rem .35rem',
borderRadius: '12px',
fontSize: '14px',
marginRight: '0.5rem'
}}
onClick={toggle}
ref={setReferenceElement}
>
<DropDown />
</ButtonOutlined>
{open && (
<PopoverContainer show={true} ref={setPopperElement as any} style={styles.popper} {...attributes.popper}>
<div>{list && listVersionLabel(list.version)}</div>
<SeparatorDark />
<ExternalLink href={`https://tokenlists.org/token-list?url=${listUrl}`}>View list</ExternalLink>
<UnpaddedLinkStyledButton onClick={handleRemoveList} disabled={Object.keys(listsByUrl).length === 1}>
Remove list
</UnpaddedLinkStyledButton>
{pending && (
<UnpaddedLinkStyledButton onClick={handleAcceptListUpdate}>Update list</UnpaddedLinkStyledButton>
)}
</PopoverContainer>
)}
</StyledMenu>
{isSelected ? (
<ButtonPrimary
disabled={true}
className="select-button"
style={{ width: '5rem', minWidth: '5rem', padding: '0.5rem .35rem', borderRadius: '12px', fontSize: '14px' }}
>
Selected
</ButtonPrimary>
) : (
<>
<ButtonPrimary
className="select-button"
style={{
width: '5rem',
minWidth: '4.5rem',
padding: '0.5rem .35rem',
borderRadius: '12px',
fontSize: '14px'
}}
onClick={selectThisList}
>
Select
</ButtonPrimary>
</>
)}
</Row>
)
})
Example #23
Source File: useTooltip.tsx From pancake-toolkit with GNU General Public License v3.0 | 4 votes |
useTooltip = (content: React.ReactNode, options: TooltipOptions): TooltipRefs => {
const {
placement = "auto",
trigger = "hover",
arrowPadding = 16,
tooltipPadding = { left: 16, right: 16 },
tooltipOffset = [0, 10],
} = options;
const [targetElement, setTargetElement] = useState<HTMLElement | null>(null);
const [tooltipElement, setTooltipElement] = useState<HTMLElement | null>(null);
const [arrowElement, setArrowElement] = useState<HTMLElement | null>(null);
const [visible, setVisible] = useState(false);
const isHoveringOverTooltip = useRef(false);
const hideTimeout = useRef<number>();
const hideTooltip = useCallback(
(e: Event) => {
const hide = () => {
e.stopPropagation();
e.preventDefault();
setVisible(false);
};
if (trigger === "hover") {
if (hideTimeout.current) {
window.clearTimeout(hideTimeout.current);
}
if (e.target === tooltipElement) {
isHoveringOverTooltip.current = false;
}
if (!isHoveringOverTooltip.current) {
hideTimeout.current = window.setTimeout(() => {
if (!isHoveringOverTooltip.current) {
hide();
}
}, 100);
}
} else {
hide();
}
},
[tooltipElement, trigger]
);
const showTooltip = useCallback(
(e: Event) => {
e.stopPropagation();
e.preventDefault();
setVisible(true);
if (trigger === "hover") {
if (e.target === targetElement) {
// If we were about to close the tooltip and got back to it
// then prevent closing it.
clearTimeout(hideTimeout.current);
}
if (e.target === tooltipElement) {
isHoveringOverTooltip.current = true;
}
}
},
[tooltipElement, targetElement, trigger]
);
const toggleTooltip = useCallback(
(e: Event) => {
e.stopPropagation();
setVisible(!visible);
},
[visible]
);
// Trigger = hover
useEffect(() => {
if (targetElement === null || trigger !== "hover") return undefined;
if (isTouchDevice()) {
targetElement.addEventListener("touchstart", showTooltip);
targetElement.addEventListener("touchend", hideTooltip);
} else {
targetElement.addEventListener("mouseenter", showTooltip);
targetElement.addEventListener("mouseleave", hideTooltip);
}
return () => {
targetElement.removeEventListener("touchstart", showTooltip);
targetElement.removeEventListener("touchend", hideTooltip);
targetElement.removeEventListener("mouseenter", showTooltip);
targetElement.removeEventListener("mouseleave", showTooltip);
};
}, [trigger, targetElement, hideTooltip, showTooltip]);
// Keep tooltip open when cursor moves from the targetElement to the tooltip
useEffect(() => {
if (tooltipElement === null || trigger !== "hover") return undefined;
tooltipElement.addEventListener("mouseenter", showTooltip);
tooltipElement.addEventListener("mouseleave", hideTooltip);
return () => {
tooltipElement.removeEventListener("mouseenter", showTooltip);
tooltipElement.removeEventListener("mouseleave", hideTooltip);
};
}, [trigger, tooltipElement, hideTooltip, showTooltip]);
// Trigger = click
useEffect(() => {
if (targetElement === null || trigger !== "click") return undefined;
targetElement.addEventListener("click", toggleTooltip);
return () => targetElement.removeEventListener("click", toggleTooltip);
}, [trigger, targetElement, visible, toggleTooltip]);
// Handle click outside
useEffect(() => {
if (trigger !== "click") return undefined;
const handleClickOutside = ({ target }: Event) => {
if (target instanceof Node) {
if (
tooltipElement != null &&
targetElement != null &&
!tooltipElement.contains(target) &&
!targetElement.contains(target)
) {
setVisible(false);
}
}
};
document.addEventListener("mousedown", handleClickOutside);
return () => document.removeEventListener("mousedown", handleClickOutside);
}, [trigger, targetElement, tooltipElement]);
// Trigger = focus
useEffect(() => {
if (targetElement === null || trigger !== "focus") return undefined;
targetElement.addEventListener("focus", showTooltip);
targetElement.addEventListener("blur", hideTooltip);
return () => {
targetElement.removeEventListener("focus", showTooltip);
targetElement.removeEventListener("blur", hideTooltip);
};
}, [trigger, targetElement, showTooltip, hideTooltip]);
// On small screens Popper.js tries to squeeze the tooltip to available space without overflowing beyound the edge
// of the screen. While it works fine when the element is in the middle of the screen it does not handle well the
// cases when the target element is very close to the edge of the screen - no margin is applied between the tooltip
// and the screen edge.
// preventOverflow mitigates this behaviour, default 16px paddings on left and right solve the problem for all screen sizes
// that we support.
// Note that in the farm page where there are tooltips very close to the edge of the screen this padding works perfectly
// even on the iPhone 5 screen (320px wide), BUT in the storybook with the contrived example ScreenEdges example
// iPhone 5 behaves differently overflowing beyound the edge. All paddings are identical so I have no idea why it is,
// and fixing that seems like a very bad use of time.
const { styles, attributes } = usePopper(targetElement, tooltipElement, {
placement,
modifiers: [
{
name: "arrow",
options: { element: arrowElement, padding: arrowPadding },
},
{ name: "offset", options: { offset: tooltipOffset } },
{ name: "preventOverflow", options: { padding: tooltipPadding } },
],
});
const tooltip = (
<StyledTooltip ref={setTooltipElement} style={styles.popper} {...attributes.popper}>
<ThemeProvider theme={invertTheme}>{content}</ThemeProvider>
<Arrow ref={setArrowElement} style={styles.arrow} />
</StyledTooltip>
);
const portal = getPortalRoot();
const tooltipInPortal = portal ? createPortal(tooltip, portal) : null;
return {
targetRef: setTargetElement,
tooltip: tooltipInPortal ?? tooltip,
tooltipVisible: visible,
};
}
Example #24
Source File: DropdownMenu.tsx From vvs-ui with GNU General Public License v3.0 | 4 votes |
DropdownMenu: React.FC<DropdownMenuProps> = ({
children,
isBottomNav = false,
showItemsOnMobile = false,
activeItem = "",
items = [],
openMenuTimeout = 0,
...props
}) => {
const [isOpen, setIsOpen] = useState(false);
const [targetRef, setTargetRef] = useState<HTMLDivElement | null>(null);
const [tooltipRef, setTooltipRef] = useState<HTMLDivElement | null>(null);
const hideTimeout = useRef<number>();
const isHoveringOverTooltip = useRef(false);
const hasItems = items.length > 0;
const clickTimeRef = useRef(openMenuTimeout);
const { styles, attributes } = usePopper(targetRef, tooltipRef, {
placement: isBottomNav ? "top" : "bottom-start",
modifiers: [{ name: "offset", options: { offset: [0, isBottomNav ? 6 : 0] } }],
});
/**
* See "useTooltip"
*/
useEffect(() => {
const showTooltip = (evt: MouseEvent | TouchEvent) => {
setIsOpen(true);
if (evt.target === targetRef) {
clearTimeout(hideTimeout.current);
}
if (evt.target === tooltipRef) {
isHoveringOverTooltip.current = true;
}
};
const hideTooltip = (evt: MouseEvent | TouchEvent) => {
const target = evt.target as Node;
return target && !tooltipRef?.contains(target) && setIsOpen(false);
};
const toggleTouch = (evt: TouchEvent) => {
const target = evt.target as Node;
const isTouchingTargetRef = target && targetRef?.contains(target);
const isTouchingTooltipRef = target && tooltipRef?.contains(target);
if (isTouchingTargetRef) {
if (isOpen || openMenuTimeout === 0) {
setIsOpen((prevOpen) => !prevOpen);
}
} else if (isTouchingTooltipRef) {
// Don't close the menu immediately so it catches the event
setTimeout(() => {
setIsOpen(false);
}, 500);
} else {
setIsOpen(false);
}
};
const handlePointerDown = (e: PointerEvent) => {
const target = e.target as Node;
const isTouchingTargetRef = target && targetRef?.contains(target);
if (isTouchingTargetRef) {
clickTimeRef.current = e.timeStamp;
setTimeout(() => {
if (clickTimeRef.current > 0) setIsOpen(true);
}, openMenuTimeout);
}
};
const handlePointerUp = () => {
clickTimeRef.current = 0;
};
if (isTouchDevice()) {
document.addEventListener("touchstart", toggleTouch);
if (openMenuTimeout > 0) {
document.addEventListener("pointerdown", handlePointerDown);
document.addEventListener("pointerup", handlePointerUp);
}
} else {
targetRef?.addEventListener("mouseenter", showTooltip);
targetRef?.addEventListener("mouseleave", hideTooltip);
tooltipRef?.addEventListener("mouseenter", showTooltip);
tooltipRef?.addEventListener("mouseleave", hideTooltip);
}
return () => {
if (isTouchDevice()) {
document.removeEventListener("touchstart", toggleTouch);
if (openMenuTimeout > 0) {
document.removeEventListener("pointerdown", handlePointerDown);
document.removeEventListener("pointerup", handlePointerUp);
}
} else {
targetRef?.removeEventListener("mouseenter", showTooltip);
targetRef?.removeEventListener("mouseleave", hideTooltip);
tooltipRef?.removeEventListener("mouseenter", showTooltip);
tooltipRef?.removeEventListener("mouseleave", hideTooltip);
}
};
}, [targetRef, tooltipRef, hideTimeout, isHoveringOverTooltip, setIsOpen, openMenuTimeout, isOpen, isBottomNav]);
return (
<Box ref={isBottomNav ? null : setTargetRef} {...props}>
<Box ref={isBottomNav ? setTargetRef : null}>{children}</Box>
{isBottomNav && isOpen && showItemsOnMobile && <StyledOverlay />}
{hasItems && (
<StyledDropdownMenu
style={styles.popper}
ref={setTooltipRef}
{...attributes.popper}
$isBottomNav={isBottomNav}
$isOpen={isOpen && ((isBottomNav && showItemsOnMobile) || !isBottomNav)}
>
{items.map(
({ type = DropdownMenuItemType.INTERNAL_LINK, label, href = "/", status, ...itemProps }, index) => {
const MenuItemContent = (
<>
{label}
{status && (
<LinkStatus color={status.color} fontSize="14px">
{status.text}
</LinkStatus>
)}
</>
);
const isActive = href === activeItem;
return (
<StyledDropdownMenuItemContainer key={index}>
{type === DropdownMenuItemType.BUTTON && (
<DropdownMenuItem $isActive={isActive} type="button" {...itemProps}>
{MenuItemContent}
</DropdownMenuItem>
)}
{type === DropdownMenuItemType.INTERNAL_LINK && (
<DropdownMenuItem $isActive={isActive} as={Link} to={href} {...itemProps}>
{MenuItemContent}
</DropdownMenuItem>
)}
{type === DropdownMenuItemType.EXTERNAL_LINK && (
<DropdownMenuItem $isActive={isActive} as="a" href={href} target="_blank" {...itemProps}>
<Flex alignItems="center" justifyContent="space-between" width="100%">
{label}
{/* <IconComponent iconName="Logout" /> */}
</Flex>
</DropdownMenuItem>
)}
{type === DropdownMenuItemType.DIVIDER && <DropdownMenuDivider />}
</StyledDropdownMenuItemContainer>
);
}
)}
</StyledDropdownMenu>
)}
</Box>
);
}
Example #25
Source File: useTooltip.tsx From vvs-ui with GNU General Public License v3.0 | 4 votes |
useTooltip = (content: React.ReactNode, options: TooltipOptions): TooltipRefs => {
const {
placement = "auto",
trigger = "hover",
arrowPadding = 16,
tooltipPadding = { left: 16, right: 16 },
tooltipOffset = [0, 10],
} = options;
const [targetElement, setTargetElement] = useState<HTMLElement | null>(null);
const [tooltipElement, setTooltipElement] = useState<HTMLElement | null>(null);
const [arrowElement, setArrowElement] = useState<HTMLElement | null>(null);
const [visible, setVisible] = useState(false);
const isHoveringOverTooltip = useRef(false);
const hideTimeout = useRef<number>();
const hideTooltip = useCallback(
(e: Event) => {
const hide = () => {
e.stopPropagation();
e.preventDefault();
setVisible(false);
};
if (trigger === "hover") {
if (hideTimeout.current) {
window.clearTimeout(hideTimeout.current);
}
if (e.target === tooltipElement) {
isHoveringOverTooltip.current = false;
}
if (!isHoveringOverTooltip.current) {
hideTimeout.current = window.setTimeout(() => {
if (!isHoveringOverTooltip.current) {
hide();
}
}, 100);
}
} else {
hide();
}
},
[tooltipElement, trigger]
);
const showTooltip = useCallback(
(e: Event) => {
e.stopPropagation();
e.preventDefault();
setVisible(true);
if (trigger === "hover") {
if (e.target === targetElement) {
// If we were about to close the tooltip and got back to it
// then prevent closing it.
clearTimeout(hideTimeout.current);
}
if (e.target === tooltipElement) {
isHoveringOverTooltip.current = true;
}
}
},
[tooltipElement, targetElement, trigger]
);
const toggleTooltip = useCallback(
(e: Event) => {
e.stopPropagation();
setVisible(!visible);
},
[visible]
);
// Trigger = hover
useEffect(() => {
if (targetElement === null || trigger !== "hover") return undefined;
if (isTouchDevice()) {
targetElement.addEventListener("touchstart", showTooltip);
targetElement.addEventListener("touchend", hideTooltip);
} else {
targetElement.addEventListener("mouseenter", showTooltip);
targetElement.addEventListener("mouseleave", hideTooltip);
}
return () => {
targetElement.removeEventListener("touchstart", showTooltip);
targetElement.removeEventListener("touchend", hideTooltip);
targetElement.removeEventListener("mouseenter", showTooltip);
targetElement.removeEventListener("mouseleave", showTooltip);
};
}, [trigger, targetElement, hideTooltip, showTooltip]);
// Keep tooltip open when cursor moves from the targetElement to the tooltip
useEffect(() => {
if (tooltipElement === null || trigger !== "hover") return undefined;
tooltipElement.addEventListener("mouseenter", showTooltip);
tooltipElement.addEventListener("mouseleave", hideTooltip);
return () => {
tooltipElement.removeEventListener("mouseenter", showTooltip);
tooltipElement.removeEventListener("mouseleave", hideTooltip);
};
}, [trigger, tooltipElement, hideTooltip, showTooltip]);
// Trigger = click
useEffect(() => {
if (targetElement === null || trigger !== "click") return undefined;
targetElement.addEventListener("click", toggleTooltip);
return () => targetElement.removeEventListener("click", toggleTooltip);
}, [trigger, targetElement, visible, toggleTooltip]);
// Handle click outside
useEffect(() => {
if (trigger !== "click") return undefined;
const handleClickOutside = ({ target }: Event) => {
if (target instanceof Node) {
if (
tooltipElement != null &&
targetElement != null &&
!tooltipElement.contains(target) &&
!targetElement.contains(target)
) {
setVisible(false);
}
}
};
document.addEventListener("mousedown", handleClickOutside);
return () => document.removeEventListener("mousedown", handleClickOutside);
}, [trigger, targetElement, tooltipElement]);
// Trigger = focus
useEffect(() => {
if (targetElement === null || trigger !== "focus") return undefined;
targetElement.addEventListener("focus", showTooltip);
targetElement.addEventListener("blur", hideTooltip);
return () => {
targetElement.removeEventListener("focus", showTooltip);
targetElement.removeEventListener("blur", hideTooltip);
};
}, [trigger, targetElement, showTooltip, hideTooltip]);
// On small screens Popper.js tries to squeeze the tooltip to available space without overflowing beyound the edge
// of the screen. While it works fine when the element is in the middle of the screen it does not handle well the
// cases when the target element is very close to the edge of the screen - no margin is applied between the tooltip
// and the screen edge.
// preventOverflow mitigates this behaviour, default 16px paddings on left and right solve the problem for all screen sizes
// that we support.
// Note that in the farm page where there are tooltips very close to the edge of the screen this padding works perfectly
// even on the iPhone 5 screen (320px wide), BUT in the storybook with the contrived example ScreenEdges example
// iPhone 5 behaves differently overflowing beyound the edge. All paddings are identical so I have no idea why it is,
// and fixing that seems like a very bad use of time.
const { styles, attributes } = usePopper(targetElement, tooltipElement, {
placement,
modifiers: [
{
name: "arrow",
options: { element: arrowElement, padding: arrowPadding },
},
{ name: "offset", options: { offset: tooltipOffset } },
{ name: "preventOverflow", options: { padding: tooltipPadding } },
],
});
const tooltip = (
<StyledTooltip ref={setTooltipElement} style={styles.popper} {...attributes.popper}>
<ThemeProvider theme={invertTheme}>{content}</ThemeProvider>
<Arrow ref={setArrowElement} style={styles.arrow} />
</StyledTooltip>
);
const tooltipInPortal = portalRoot ? createPortal(tooltip, portalRoot) : null;
return {
targetRef: setTargetElement,
tooltip: tooltipInPortal ?? tooltip,
tooltipVisible: visible,
};
}
Example #26
Source File: DropdownMenu.tsx From pancake-toolkit with GNU General Public License v3.0 | 4 votes |
DropdownMenu: React.FC<DropdownMenuProps> = ({
children,
isBottomNav = false,
showItemsOnMobile = false,
activeItem = "",
items = [],
index,
setMenuOpenByIndex,
...props
}) => {
const { linkComponent } = useContext(MenuContext);
const [isOpen, setIsOpen] = useState(false);
const [targetRef, setTargetRef] = useState<HTMLDivElement | null>(null);
const [tooltipRef, setTooltipRef] = useState<HTMLDivElement | null>(null);
const hasItems = items.length > 0;
const { styles, attributes } = usePopper(targetRef, tooltipRef, {
strategy: isBottomNav ? "absolute" : "fixed",
placement: isBottomNav ? "top" : "bottom-start",
modifiers: [{ name: "offset", options: { offset: [0, isBottomNav ? 6 : 0] } }],
});
const isMenuShow = isOpen && ((isBottomNav && showItemsOnMobile) || !isBottomNav);
useEffect(() => {
const showDropdownMenu = () => {
setIsOpen(true);
};
const hideDropdownMenu = (evt: MouseEvent | TouchEvent) => {
const target = evt.target as Node;
return target && !tooltipRef?.contains(target) && setIsOpen(false);
};
targetRef?.addEventListener("mouseenter", showDropdownMenu);
targetRef?.addEventListener("mouseleave", hideDropdownMenu);
return () => {
targetRef?.removeEventListener("mouseenter", showDropdownMenu);
targetRef?.removeEventListener("mouseleave", hideDropdownMenu);
};
}, [targetRef, tooltipRef, setIsOpen, isBottomNav]);
useEffect(() => {
if (setMenuOpenByIndex && index !== undefined) {
setMenuOpenByIndex((prevValue) => ({ ...prevValue, [index]: isMenuShow }));
}
}, [isMenuShow, setMenuOpenByIndex, index]);
useOnClickOutside(
{
current: targetRef,
},
() => {
setIsOpen(false);
}
);
return (
<Box ref={setTargetRef} {...props}>
<Box
onPointerDown={() => {
setIsOpen((s) => !s);
}}
>
{children}
</Box>
{hasItems && (
<StyledDropdownMenu
style={styles.popper}
ref={setTooltipRef}
{...attributes.popper}
$isBottomNav={isBottomNav}
$isOpen={isMenuShow}
>
{items
.filter((item) => !item.isMobileOnly)
.map(({ type = DropdownMenuItemType.INTERNAL_LINK, label, href = "/", status, ...itemProps }, itemItem) => {
const MenuItemContent = (
<>
{label}
{status && (
<LinkStatus color={status.color} fontSize="14px">
{status.text}
</LinkStatus>
)}
</>
);
const isActive = href === activeItem;
return (
<StyledDropdownMenuItemContainer key={itemItem}>
{type === DropdownMenuItemType.BUTTON && (
<DropdownMenuItem $isActive={isActive} type="button" {...itemProps}>
{MenuItemContent}
</DropdownMenuItem>
)}
{type === DropdownMenuItemType.INTERNAL_LINK && (
<DropdownMenuItem
$isActive={isActive}
as={linkComponent}
href={href}
onClick={() => {
setIsOpen(false);
}}
{...itemProps}
>
{MenuItemContent}
</DropdownMenuItem>
)}
{type === DropdownMenuItemType.EXTERNAL_LINK && (
<DropdownMenuItem
$isActive={isActive}
as="a"
href={href}
target="_blank"
onClick={() => {
setIsOpen(false);
}}
{...itemProps}
>
<Flex alignItems="center" justifyContent="space-between" width="100%">
{label}
<IconComponent iconName="Logout" />
</Flex>
</DropdownMenuItem>
)}
{type === DropdownMenuItemType.DIVIDER && <DropdownMenuDivider />}
</StyledDropdownMenuItemContainer>
);
})}
</StyledDropdownMenu>
)}
</Box>
);
}
Example #27
Source File: index.tsx From vvs-ui with GNU General Public License v3.0 | 4 votes |
UserMenu: React.FC<UserMenuProps> = ({
account,
text,
avatarSrc,
variant = variants.DEFAULT,
children,
...props
}) => {
const [isOpen, setIsOpen] = useState(false);
const [targetRef, setTargetRef] = useState<HTMLDivElement | null>(null);
const [tooltipRef, setTooltipRef] = useState<HTMLDivElement | null>(null);
const hideTimeout = useRef<number>();
const isHoveringOverTooltip = useRef(false);
const accountEllipsis = account ? `${account.substring(0, 2)}...${account.substring(account.length - 4)}` : null;
const { styles, attributes } = usePopper(targetRef, tooltipRef, {
placement: "bottom-end",
modifiers: [{ name: "offset", options: { offset: [0, 12] } }],
});
/**
* See "useTooltip"
*/
useEffect(() => {
const showTooltip = (evt: MouseEvent | TouchEvent) => {
setIsOpen(true);
if (evt.target === targetRef) {
clearTimeout(hideTimeout.current);
}
if (evt.target === tooltipRef) {
isHoveringOverTooltip.current = true;
}
};
const hideTooltip = (evt: MouseEvent | TouchEvent) => {
if (hideTimeout.current) {
window.clearTimeout(hideTimeout.current);
}
if (evt.target === tooltipRef) {
isHoveringOverTooltip.current = false;
}
if (!isHoveringOverTooltip.current) {
hideTimeout.current = window.setTimeout(() => {
if (!isHoveringOverTooltip.current) {
setIsOpen(false);
}
}, 150);
}
};
const toggleTouch = (evt: TouchEvent) => {
const target = evt.target as Node;
const isTouchingTargetRef = target && targetRef?.contains(target);
const isTouchingTooltipRef = target && tooltipRef?.contains(target);
if (isTouchingTargetRef) {
setIsOpen((prevOpen) => !prevOpen);
} else if (isTouchingTooltipRef) {
// Don't close the menu immediately so it catches the event
setTimeout(() => {
setIsOpen(false);
}, 100);
} else {
setIsOpen(false);
}
};
if (isTouchDevice()) {
document.addEventListener("touchstart", toggleTouch);
} else {
targetRef?.addEventListener("mouseenter", showTooltip);
targetRef?.addEventListener("mouseleave", hideTooltip);
tooltipRef?.addEventListener("mouseenter", showTooltip);
tooltipRef?.addEventListener("mouseleave", hideTooltip);
}
return () => {
if (isTouchDevice()) {
document.removeEventListener("touchstart", toggleTouch);
} else {
targetRef?.removeEventListener("mouseenter", showTooltip);
targetRef?.removeEventListener("mouseleave", hideTooltip);
tooltipRef?.removeEventListener("mouseenter", showTooltip);
tooltipRef?.removeEventListener("mouseleave", hideTooltip);
}
};
}, [targetRef, tooltipRef, hideTimeout, isHoveringOverTooltip, setIsOpen]);
return (
<>
<StyledUserMenu ref={setTargetRef} {...props}>
<MenuIcon avatarSrc={avatarSrc} variant={variant} />
<LabelText title={text || account}>{text || accountEllipsis}</LabelText>
<ChevronDownIcon color="text" width="24px" />
</StyledUserMenu>
<Menu style={styles.popper} ref={setTooltipRef} {...attributes.popper} isOpen={isOpen}>
{children}
</Menu>
</>
);
}
Example #28
Source File: ListSelect.tsx From pancake-swap-testnet with MIT License | 4 votes |
ListRow = memo(function ListRow({ listUrl, onBack }: { listUrl: string; onBack: () => void }) {
const listsByUrl = useSelector<AppState, AppState['lists']['byUrl']>((state) => state.lists.byUrl)
const selectedListUrl = useSelectedListUrl()
const dispatch = useDispatch<AppDispatch>()
const { current: list, pendingUpdate: pending } = listsByUrl[listUrl]
const isSelected = listUrl === selectedListUrl
const [open, toggle] = useToggle(false)
const node = useRef<HTMLDivElement>()
const [referenceElement, setReferenceElement] = useState<HTMLDivElement | null>()
const [popperElement, setPopperElement] = useState<HTMLDivElement>()
const { styles, attributes } = usePopper(referenceElement, popperElement, {
placement: 'auto',
strategy: 'fixed',
modifiers: [{ name: 'offset', options: { offset: [8, 8] } }],
})
useOnClickOutside(node, open ? toggle : undefined)
const selectThisList = useCallback(() => {
if (isSelected) return
dispatch(selectList(listUrl))
onBack()
}, [dispatch, isSelected, listUrl, onBack])
const handleAcceptListUpdate = useCallback(() => {
if (!pending) return
dispatch(acceptListUpdate(listUrl))
}, [dispatch, listUrl, pending])
const handleRemoveList = useCallback(() => {
if (window.prompt(`Please confirm you would like to remove this list by typing REMOVE`) === `REMOVE`) {
dispatch(removeList(listUrl))
}
}, [dispatch, listUrl])
const TranslateString = useI18n()
if (!list) return null
return (
<Row key={listUrl} align="center" padding="16px" id={listUrlRowHTMLId(listUrl)}>
{list.logoURI ? (
<ListLogo style={{ marginRight: '1rem' }} logoURI={list.logoURI} alt={`${list.name} list logo`} />
) : (
<div style={{ width: '24px', height: '24px', marginRight: '1rem' }} />
)}
<Column style={{ flex: '1' }}>
<Row>
<Text bold={isSelected} fontSize="16px" style={{ overflow: 'hidden', textOverflow: 'ellipsis' }}>
{list.name}
</Text>
</Row>
<Row
style={{
marginTop: '4px',
}}
>
<StyledListUrlText title={listUrl}>
<ListOrigin listUrl={listUrl} />
</StyledListUrlText>
</Row>
</Column>
<StyledMenu ref={node as any}>
<div style={{ display: 'inline-block' }} ref={setReferenceElement}>
<Button
style={{
width: '32px',
marginRight: '8px',
}}
onClick={toggle}
variant="secondary"
>
<ChevronDownIcon />
</Button>
</div>
{open && (
<PopoverContainer show ref={setPopperElement as any} style={styles.popper} {...attributes.popper}>
<div>{list && listVersionLabel(list.version)}</div>
<SeparatorDark />
<ExternalLink href={`https://tokenlists.org/token-list?url=${listUrl}`}>
{TranslateString(1206, 'View list')}
</ExternalLink>
<UnpaddedLinkStyledButton onClick={handleRemoveList} disabled={Object.keys(listsByUrl).length === 1}>
Remove list
</UnpaddedLinkStyledButton>
{pending && (
<UnpaddedLinkStyledButton onClick={handleAcceptListUpdate}>Update list</UnpaddedLinkStyledButton>
)}
</PopoverContainer>
)}
</StyledMenu>
{isSelected ? (
<Button disabled style={{ width: '5rem', minWidth: '5rem' }}>
Selected
</Button>
) : (
<>
<Button
className="select-button"
style={{
width: '5rem',
minWidth: '4.5rem',
}}
onClick={selectThisList}
>
Select
</Button>
</>
)}
</Row>
)
})
Example #29
Source File: UserListItem.tsx From ide with Mozilla Public License 2.0 | 4 votes |
UserListItem = ({ user }: { user: User }): JSX.Element | null => {
const userRef = useAtomValue(authenticatedUserRefAtom);
const firebaseRef = useAtomValue(authenticatedFirebaseRefAtom);
const [permission] = useAtom(actualUserPermissionAtom);
const [defaultPermission] = useAtom(defaultPermissionAtom);
const [referenceElement, setReferenceElement] = useState<HTMLElement | null>(
null
);
const [popperElement, setPopperElement] = useState<HTMLElement | null>(null);
const { styles, attributes } = usePopper(referenceElement, popperElement, {
placement: 'bottom-end',
});
if (!permission || !defaultPermission) return null;
type PermissionUpdate = 'OWNER' | 'READ_WRITE' | 'READ' | 'DEFAULT';
const handleUpdateUserPermission = (
user: User,
permission: PermissionUpdate
): void => {
if (!firebaseRef) {
alert("Firebase hasn't loaded yet, please wait");
return;
}
if (permission === 'DEFAULT') {
firebaseRef.child('users').child(user.id).child('permission').remove();
} else {
firebaseRef.child('users').child(user.id).update({
permission,
});
}
};
return (
<li key={user.id} className="py-2 flex">
<span className="relative h-5 w-5">
<span
className="inline-block h-5 w-5 rounded"
style={{ backgroundColor: user.color }}
/>
<span className="absolute bottom-0 right-0 transform translate-y-1/3 translate-x-1/3 block rounded-full">
<span
className={`block h-3 w-3 rounded-full ${
isUserOnline(user) ? 'bg-green-400' : 'bg-gray-400'
}`}
/>
</span>
</span>
<div className="ml-3 flex-1">
<p
className={`text-sm font-medium ${
isUserOnline(user) ? 'text-gray-300' : 'text-gray-400'
}`}
>
{user.name}
{user.id === userRef?.key ? ' (Me)' : ''}
</p>
<p
className={`text-sm ${
isUserOnline(user) ? 'text-gray-400' : 'text-gray-500'
}`}
>
{user.permission
? permissionLabels[user.permission]
: `Default (${permissionLabels[defaultPermission]})`}
</p>
</div>
{user.id !== userRef?.key && permission === 'OWNER' && (
<Menu>
{({ open }) => (
<>
<div>
<Menu.Button
className="bg-[#1E1E1E] rounded-full flex items-center text-gray-500 hover:text-gray-400 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-[#1E1E1E] focus:ring-indigo-500"
ref={setReferenceElement}
>
<span className="sr-only">Open options</span>
<DotsVerticalIcon className="h-5 w-5" aria-hidden="true" />
</Menu.Button>
</div>
<Transition show={open}>
{ReactDOM.createPortal(
<Menu.Items as="div" static>
<div
ref={setPopperElement}
style={styles.popper}
{...attributes.popper}
className="relative"
>
<Transition.Child
as={Fragment}
enter="transition ease-out duration-100"
enterFrom="transform opacity-0 scale-95"
enterTo="transform opacity-100 scale-100"
leave="transition ease-in duration-75"
leaveFrom="transform opacity-100 scale-100"
leaveTo="transform opacity-0 scale-95"
>
<div className="origin-top-right absolute right-0 bg-gray-800 rounded-md mt-2 w-48 shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none">
<div className="py-1">
{([
['OWNER', 'Owner'],
['READ_WRITE', 'Read & Write'],
['READ', 'View Only'],
[
'DEFAULT',
`Default (${permissionLabels[defaultPermission]})`,
],
['PRIVATE', 'Blocked'],
] as [PermissionUpdate, string][]).map(option => (
<Menu.Item key={option[0]}>
{({ active }) => (
<button
className={classNames(
active
? 'bg-gray-700 text-gray-100'
: 'text-gray-200',
'w-full text-left block px-4 py-2 text-sm focus:outline-none'
)}
onClick={() =>
handleUpdateUserPermission(
user,
option[0]
)
}
>
{option[1]}
</button>
)}
</Menu.Item>
))}
</div>
</div>
</Transition.Child>
</div>
</Menu.Items>,
document.body
)}
</Transition>
</>
)}
</Menu>
)}
</li>
);
}