@kiwicom/orbit-components
Version:
Orbit-components is a React component library which provides developers with the easiest possible way of building Kiwi.com’s products.
198 lines (191 loc) • 7.38 kB
JavaScript
import React, { useRef, useEffect, useContext, useMemo } from "react";
import styled, { css } from "styled-components";
import convertHexToRgba from "@kiwicom/orbit-design-tokens/lib/convertHexToRgba";
import defaultTheme from "../../defaultTheme";
import media from "../../utils/mediaQuery";
import Button from "../../Button";
import resolvePopoverPosition from "../helpers/resolvePopoverPosition";
import resolvePopoverHorizontal from "../helpers/resolvePopoverHorizontal";
import calculatePopoverPosition from "../helpers/calculatePopoverPosition";
import calculateVerticalPosition from "../helpers/calculateVerticalPosition";
import calculateHorizontalPosition from "../helpers/calculateHorizontalPosition";
import useDimensions from "../hooks/useDimensions";
import Translate from "../../Translate";
import transition from "../../utils/transition";
import useClickOutside from "../../hooks/useClickOutside";
import { ModalContext } from "../../Modal/ModalContext";
import boundingClientRect from "../../utils/boundingClientRect";
import getScrollableParent from "../helpers/getScrollableParent";
const mobileTop = theme => theme.orbit.spaceXLarge;
const popoverPadding = theme => theme.orbit.spaceMedium;
const actionsSpace = theme => theme.orbit.spaceMedium;
const allSpacing = theme => parseFloat(popoverPadding(theme)) * 2 + parseFloat(mobileTop(theme)) + parseFloat(actionsSpace(theme));
const StyledContentWrapper = styled.div.withConfig({
displayName: "ContentWrapper__StyledContentWrapper",
componentId: "v01g55-0"
})(["overflow:auto;max-height:", ";", ""], ({
actionsHeight,
theme
}) => // Calculates all the spacing relative to viewport to get space for action box
`calc(100vh - ${allSpacing(theme) + actionsHeight}px)`, media.largeMobile(css(["max-height:100%;"])));
StyledContentWrapper.defaultProps = {
theme: defaultTheme
};
const StyledActions = styled.div.withConfig({
displayName: "ContentWrapper__StyledActions",
componentId: "v01g55-1"
})(["padding:", ";padding-top:0;"], ({
theme
}) => popoverPadding(theme));
StyledActions.defaultProps = {
theme: defaultTheme
};
const StyledPopoverParent = styled.div.withConfig({
displayName: "ContentWrapper__StyledPopoverParent",
componentId: "v01g55-2"
})(["position:fixed;bottom:0;left:0;right:0;width:100%;height:auto;box-sizing:border-box;border-top-left-radius:9px;border-top-right-radius:9px;background-color:", ";box-shadow:", ";z-index:1000;transition:", ";transform:translateY(", ");max-height:", ";&:focus{outline:0;}", ""], ({
theme
}) => theme.orbit.backgroundModal, ({
theme
}) => theme.orbit.boxShadowRaisedReverse, transition(["opacity", "transform"], "fast", "ease-in-out"), ({
shownMobile
}) => shownMobile ? "0%" : "100%", ({
theme
}) => `calc(100% - ${mobileTop(theme)})`, media.largeMobile(css(["z-index:", ";position:", ";left:auto;right:auto;bottom:auto;width:", ";opacity:", ";transform:none;border-radius:", ";box-shadow:", ";max-height:none;", " ", ""], ({
isInsideModal
}) => isInsideModal ? "1000" : "600", ({
fixed
}) => fixed ? "fixed" : "absolute", ({
width
}) => width ? `${width}` : "auto", ({
shown
}) => shown ? "1" : "0", ({
theme
}) => theme.orbit.borderRadiusNormal, ({
theme
}) => theme.orbit.boxShadowRaised, resolvePopoverPosition, resolvePopoverHorizontal)));
StyledPopoverParent.defaultProps = {
theme: defaultTheme
};
const StyledPopoverPadding = styled.div.withConfig({
displayName: "ContentWrapper__StyledPopoverPadding",
componentId: "v01g55-3"
})(["padding:", ";"], ({
noPadding,
theme
}) => noPadding ? 0 : popoverPadding(theme));
StyledPopoverPadding.defaultProps = {
theme: defaultTheme
};
const StyledPopoverContent = styled.div.withConfig({
displayName: "ContentWrapper__StyledPopoverContent",
componentId: "v01g55-4"
})([""]);
const StyledOverlay = styled.div.withConfig({
displayName: "ContentWrapper__StyledOverlay",
componentId: "v01g55-5"
})(["display:block;position:fixed;opacity:", ";top:0;left:0;right:0;width:100%;height:100%;background-color:", ";transition:opacity ", " ease-in-out;z-index:999;", ";"], ({
shown
}) => shown ? "1" : "0", ({
theme
}) => convertHexToRgba(theme.orbit.paletteInkNormal, 60), ({
theme
}) => theme.orbit.durationNormal, media.largeMobile(css(["display:none;"])));
StyledOverlay.defaultProps = {
theme: defaultTheme
};
const StyledPopoverClose = styled.div.withConfig({
displayName: "ContentWrapper__StyledPopoverClose",
componentId: "v01g55-6"
})(["padding:", ";padding-top:0;", ""], ({
theme
}) => popoverPadding(theme), media.largeMobile(css(["display:none;visibility:hidden;padding-bottom:0;"])));
StyledPopoverClose.defaultProps = {
theme: defaultTheme
};
const PopoverContentWrapper = ({
children,
onClose,
width,
dataTest,
preferredPosition,
preferredAlign,
containerRef,
noPadding,
overlapped,
shown,
fixed,
actions
}) => {
const {
isInsideModal
} = useContext(ModalContext);
const popover = useRef(null);
const content = useRef(null);
const overlay = useRef(null);
const actionsRef = useRef(null);
const position = calculatePopoverPosition(preferredPosition, preferredAlign);
const scrollableParent = useMemo(() => getScrollableParent(containerRef.current), [containerRef]);
const dimensions = useDimensions({
containerRef,
popover,
content,
fixed,
scrollableParent
});
const verticalPosition = calculateVerticalPosition(position[0], dimensions);
const horizontalPosition = calculateHorizontalPosition(position[1], dimensions);
const actionsDimensions = useMemo(() => boundingClientRect(actionsRef), [actionsRef.current]);
useEffect(() => {
const timer = setTimeout(() => {
if (popover.current) {
popover.current.focus();
}
}, 100);
return () => clearTimeout(timer);
}, []);
useClickOutside(popover, onClose);
return React.createElement(React.Fragment, null, React.createElement(StyledOverlay, {
ref: overlay,
shown: shown,
isInsideModal: isInsideModal
}), React.createElement(StyledPopoverParent, {
shownMobile: shown,
shown: shown && verticalPosition && horizontalPosition,
anchor: horizontalPosition,
position: verticalPosition,
containerTop: dimensions.containerTop,
containerLeft: dimensions.containerLeft,
containerPureTop: dimensions.containerPureTop,
containerHeight: dimensions.containerHeight,
containerWidth: dimensions.containerWidth,
popoverHeight: dimensions.popoverHeight,
popoverWidth: dimensions.popoverWidth,
width: width,
ref: popover,
tabIndex: "0",
"data-test": dataTest,
noPadding: noPadding,
overlapped: overlapped,
role: "tooltip",
fixed: fixed,
isInsideModal: isInsideModal
}, React.createElement(StyledPopoverContent, {
ref: content
}, React.createElement(StyledContentWrapper, {
actionsHeight: actionsDimensions && actionsDimensions.height
}, React.createElement(StyledPopoverPadding, {
noPadding: noPadding
}, children)), actions ? React.createElement(StyledActions, {
ref: actionsRef
}, actions) : React.createElement(StyledPopoverClose, {
ref: actionsRef
}, React.createElement(Button, {
type: "secondary",
fullWidth: true,
onClick: onClose
}, React.createElement(Translate, {
tKey: "button_close"
}))))));
};
export default PopoverContentWrapper;