UNPKG

@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
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;