UNPKG

@equinor/eds-core-react

Version:

The React implementation of the Equinor Design System

194 lines (191 loc) 5.82 kB
import { forwardRef, useRef, useMemo, useEffect } from 'react'; import styled, { css, ThemeProvider } from 'styled-components'; import { typographyTemplate, bordersTemplate, outlineTemplate, mergeRefs, useToken } from '@equinor/eds-utils'; import { popover } from './Popover.tokens.js'; import { useFloating, offset, flip, shift, arrow, autoUpdate, useInteractions, useDismiss, FloatingFocusManager } from '@floating-ui/react'; import { jsx, jsxs, Fragment } from 'react/jsx-runtime'; import { Paper } from '../Paper/Paper.js'; import { useEds } from '../EdsProvider/eds.context.js'; const PopoverPaper = styled(Paper).withConfig({ displayName: "Popover__PopoverPaper", componentId: "sc-b7p1is-0" })(({ theme }) => { const { entities: { paper } } = theme; return css(["position:relative;", " background:", ";", " &:focus-visible{", "}"], typographyTemplate(theme.typography), theme.background, bordersTemplate(theme.border), outlineTemplate(paper.states.focus.outline)); }); const StyledPopover = styled('div').withConfig({ shouldForwardProp: () => true //workaround to avoid warning until popover gets added to react types }).withConfig({ displayName: "Popover__StyledPopover", componentId: "sc-b7p1is-1" })(["inset:unset;border:0;padding:0;margin:0;overflow:visible;background-color:transparent;&::backdrop{background-color:transparent;}"]); const ArrowWrapper = styled.div.withConfig({ displayName: "Popover__ArrowWrapper", componentId: "sc-b7p1is-2" })(({ theme }) => { return css(["position:absolute;width:", ";height:", ";"], theme.entities.arrow.width, theme.entities.arrow.height); }); const InnerWrapper = styled.div.withConfig({ displayName: "Popover__InnerWrapper", componentId: "sc-b7p1is-3" })(({ theme }) => { return css(["display:grid;grid-gap:", ";max-height:", ";width:max-content;max-width:", ";overflow:auto;"], theme.spacings.bottom, theme.maxHeight, theme.maxWidth); }); const PopoverArrow = styled.svg.withConfig({ displayName: "Popover__PopoverArrow", componentId: "sc-b7p1is-4" })(({ theme }) => { return css(["width:", ";height:", ";position:absolute;fill:", ";filter:drop-shadow(-4px 0px 2px rgba(0,0,0,0.2));"], theme.entities.arrow.width, theme.entities.arrow.height, theme.background); }); const Popover = /*#__PURE__*/forwardRef(function Popover({ children, placement = 'bottom', anchorEl, style, open, onClose, withinPortal, trapFocus, ...rest }, ref) { if (withinPortal) { console.warn('Popover "withinPortal" prop has been deprecated. Popover now uses the native popover api'); } const arrowRef = useRef(null); const { x, y, refs, strategy, context, elements, middlewareData: { arrow: { x: arrowX, y: arrowY } = {} }, placement: finalPlacement } = useFloating({ elements: { reference: anchorEl }, placement, open, onOpenChange: onClose, middleware: [offset(14), flip(), shift({ padding: 8 }), arrow({ element: arrowRef })], whileElementsMounted: autoUpdate }); const popoverRef = useMemo(() => mergeRefs(refs.setFloating, ref), [refs.setFloating, ref]); const { getFloatingProps } = useInteractions([useDismiss(context)]); useEffect(() => { if (!elements.floating) return; if (open) { if (elements.floating.isConnected) { elements.floating.showPopover(); } } else { elements.floating.hidePopover(); } }, [open, elements.floating]); useEffect(() => { if (arrowRef.current) { const staticSide = { top: 'bottom', right: 'left', bottom: 'top', left: 'right' }[finalPlacement.split('-')[0]]; let arrowTransform = 'none'; switch (staticSide) { case 'right': arrowTransform = 'rotateY(180deg)'; break; case 'left': arrowTransform = 'none'; break; case 'top': arrowTransform = 'rotate(90deg)'; break; case 'bottom': arrowTransform = 'rotate(-90deg)'; break; } Object.assign(arrowRef.current.style, { left: arrowX != null ? `${arrowX}px` : '', top: arrowY != null ? `${arrowY}px` : '', right: '', bottom: '', [staticSide]: '-6px', transform: arrowTransform }); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [arrowRef.current, arrowX, arrowY, finalPlacement]); const props = { ...rest }; const { density } = useEds(); const token = useToken({ density }, popover); const popover$1 = /*#__PURE__*/jsx(ThemeProvider, { theme: token, children: /*#__PURE__*/jsx(StyledPopover, { popover: "manual", ...getFloatingProps({ ref: popoverRef, style: { ...style, position: strategy, top: y || 0, left: x || 0 } }), children: /*#__PURE__*/jsxs(PopoverPaper, { elevation: "overlay", ...props, children: [/*#__PURE__*/jsx(ArrowWrapper, { ref: arrowRef, className: "arrow", children: /*#__PURE__*/jsx(PopoverArrow, { className: "arrowSvg", children: /*#__PURE__*/jsx("path", { d: "M0.504838 4.86885C-0.168399 4.48524 -0.168399 3.51476 0.504838 3.13115L6 8.59227e-08L6 8L0.504838 4.86885Z" }) }) }), /*#__PURE__*/jsx(InnerWrapper, { children: children })] }) }) }); return /*#__PURE__*/jsx(Fragment, { children: trapFocus ? open && /*#__PURE__*/jsx(FloatingFocusManager, { context: context, modal: true, children: popover$1 }) : open && popover$1 }); }); export { Popover };