@equinor/eds-core-react
Version:
The React implementation of the Equinor Design System
194 lines (191 loc) • 5.82 kB
JavaScript
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 };