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.

300 lines (277 loc) 9.06 kB
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } import * as React from "react"; import styled, { css } from "styled-components"; import defaultTokens from "../defaultTokens"; import ButtonLink, { StyledButtonLink } from "../ButtonLink"; import Close from "../icons/Close"; import SIZES from "./consts"; import media from "../utils/media"; import { StyledModalFooter } from "./ModalFooter"; import { MobileHeader } from "./ModalHeader"; import { StyledModalSection } from "./ModalSection"; import { StyledHeading } from "../Heading"; const getSizeToken = () => ({ size }) => { const tokens = { // TODO: create tokens widthModalSmall,... [SIZES.SMALL]: "540px", [SIZES.NORMAL]: "740px", [SIZES.LARGE]: "1280px" }; return tokens[size]; }; const ModalBody = styled.div.withConfig({ displayName: "Modal__ModalBody", componentId: "sc-1f0srsl-0" })(["width:100%;height:100%;position:fixed;top:0;right:0;bottom:0;left:0;z-index:", ";box-sizing:border-box;outline:none;overflow-x:hidden;background-color:rgba(0,0,0,0.5);font-family:", ";", ";"], ({ theme }) => theme.orbit.zIndexModalOverlay, ({ theme }) => theme.orbit.fontfamily, media.desktop` overflow-y: auto; padding: ${({ theme }) => theme.orbit.spaceXXLarge}; `); ModalBody.defaultProps = { theme: defaultTokens }; const ModalWrapper = styled.div.withConfig({ displayName: "Modal__ModalWrapper", componentId: "sc-1f0srsl-1" })(["box-sizing:border-box;min-height:100%;display:flex;align-items:flex-start;margin:0 auto;position:fixed;width:100%;border-top-left-radius:", ";border-top-right-radius:", ";transition:top ", " ease-in-out;top:", ";", ";"], ({ theme }) => theme.orbit.borderRadiusNormal, ({ theme }) => theme.orbit.borderRadiusNormal, ({ theme }) => theme.orbit.durationNormal, ({ loaded }) => loaded ? "32px" : "100%", media.desktop` position: relative; top: 0; max-width: ${getSizeToken}; align-items: center; `); ModalWrapper.defaultProps = { theme: defaultTokens }; const CloseContainer = styled.div.withConfig({ displayName: "Modal__CloseContainer", componentId: "sc-1f0srsl-2" })(["display:flex;position:absolute;top:0;right:0;z-index:1;justify-content:flex-end;align-items:center;box-sizing:border-box;height:52px;width:100%;transition:box-shadow ", " ease-in-out,background-color ", " ease-in-out;border-top-left-radius:", ";border-top-right-radius:", ";", " & + ", ":first-of-type{padding-top:52px;border-top:0;margin:0;}", "{margin-right:4px;& svg{transition:color ", " ease-in-out;color:", ";}&:hover svg{color:", ";}&:active svg{color:", ";}}"], ({ theme }) => theme.orbit.durationNormal, ({ theme }) => theme.orbit.durationNormal, ({ theme }) => theme.orbit.borderRadiusNormal, ({ theme }) => theme.orbit.borderRadiusNormal, media.desktop` box-shadow: none!important; background: transparent!important; `, StyledModalSection, StyledButtonLink, ({ theme }) => theme.orbit.durationFast, ({ theme }) => theme.orbit.paletteInkLight, ({ theme }) => theme.orbit.paletteInkLightHover, ({ theme }) => theme.orbit.paletteInkLightActive); CloseContainer.defaultProps = { theme: defaultTokens }; const ModalWrapperContent = styled.div.withConfig({ displayName: "Modal__ModalWrapperContent", componentId: "sc-1f0srsl-3" })(["position:absolute;box-sizing:border-box;border-top-left-radius:", ";border-top-right-radius:", ";background-color:", ";font-family:", ";width:100%;height:calc( 100% - ", " - ", " );box-shadow:", ";overflow-y:auto;overflow-x:hidden;", ":last-of-type{padding-bottom:", ";margin-bottom:", ";}", ";", "{top:", ";opacity:", ";}", "{top:", ";position:", ";box-shadow:", ";background-color:", ";}", ";"], ({ theme }) => theme.orbit.borderRadiusNormal, ({ theme }) => theme.orbit.borderRadiusNormal, ({ theme }) => theme.orbit.backgroundModal, ({ theme }) => theme.orbit.fontFamily, ({ theme }) => theme.orbit.spaceXLarge, ({ fixedFooter }) => fixedFooter ? "76px" : "0px", ({ theme }) => theme.orbit.boxShadowModal, StyledModalSection, ({ theme, fixedFooter }) => fixedFooter && theme.orbit.spaceLarge, ({ fixedFooter }) => fixedFooter && "0", ({ fixedFooter, theme }) => fixedFooter && css(["", "{padding:", ";box-shadow:", ";position:fixed;transition:box-shadow ", " ease-in-out;}"], StyledModalFooter, theme.orbit.spaceMedium, ({ fullyScrolled }) => fullyScrolled ? `0 0 1px 0 ${theme.orbit.paletteCloudNormal}` : "0 -2px 4px 0 rgba(23, 27, 30, 0.1)", theme.orbit.durationFast), MobileHeader, ({ scrolled, theme }) => scrolled && theme.orbit.spaceXLarge, ({ scrolled }) => scrolled && "1", CloseContainer, ({ scrolled, fixedClose }) => (fixedClose || scrolled) && "32px", ({ scrolled, fixedClose }) => (fixedClose || scrolled) && "fixed", ({ scrolled }) => scrolled && `0 2px 4px 0 rgba(23, 27, 30, 0.1)`, ({ theme, scrolled }) => scrolled && theme.orbit.paletteWhite, media.desktop` position: relative; border-radius: ${({ theme }) => theme.orbit.borderRadiusNormal}; padding-bottom: 0; height: auto; overflow: visible; ${StyledModalSection}:last-of-type { padding-bottom: ${({ theme }) => theme.orbit.spaceXXLarge}; margin-bottom: 0; &::after { content: none; } } ${StyledModalFooter} { position: relative; box-shadow: none; padding: ${({ theme }) => `0 ${theme.orbit.spaceXXLarge} ${theme.orbit.spaceXXLarge}`}; } ${CloseContainer} { position: absolute; top: 0; } `); ModalWrapperContent.defaultProps = { theme: defaultTokens }; const CloseElement = ({ onClose, closable }) => React.createElement(CloseContainer, null, closable && React.createElement(ButtonLink, { onClick: onClose, size: "normal", icon: React.createElement(Close, null), transparent: true })); class Modal extends React.PureComponent { constructor() { super(); // $FlowExpected _defineProperty(this, "state", { scrolled: false, loaded: false, fixedClose: false, fullyScrolled: false }); _defineProperty(this, "handleKeyDown", ev => { const { onClose, closable } = this.props; if (closable && ev.key === "Escape" && onClose) { onClose(ev); } }); _defineProperty(this, "handleClickOutside", ev => { var _this$node; const { onClose, closable } = this.props; if (closable && onClose && ((_this$node = this.node) === null || _this$node === void 0 ? void 0 : _this$node.current) && ev.target instanceof Node && !this.node.current.contains(ev.target)) { // If is clicked outside of modal onClose(ev); } }); _defineProperty(this, "node", React.createRef()); _defineProperty(this, "offset", 40); this.handleScroll = this.handleScroll.bind(this); } componentDidMount() { // eslint-disable-next-line setTimeout(() => this.setState({ loaded: true }), 150); } componentDidUpdate() { if (!this.state.scrolled) { this.setScrollPoint(); } } setScrollPoint() { setTimeout(() => { if (!this.state.scrolled) { const { node } = this; if (node instanceof HTMLElement) { const el = node.querySelector(`.${React.createElement(StyledHeading, null).type.styledComponentId}`); if (el) { this.offset = el.clientHeight + el.offsetTop; } } } }, 550); } handleScroll(ev) { if (ev.target instanceof HTMLDivElement && ev.target === this.node) { this.setState({ scrolled: ev.target.scrollTop >= this.offset, fixedClose: ev.target.scrollTop >= 1, fullyScrolled: this.props.fixedFooter && // set this state sooner than the exact end of the scroll (with 10 value) ev.target.scrollTop >= ev.target.scrollHeight - ev.target.clientHeight - 10 }); } } render() { const { onClose, children, size = SIZES.NORMAL, closable = true, fixedFooter = false, dataTest } = this.props; const { scrolled, loaded, fixedClose, fullyScrolled } = this.state; return React.createElement(ModalBody, { tabIndex: "0", onKeyDown: this.handleKeyDown, onClick: this.handleClickOutside, "data-test": dataTest }, React.createElement(ModalWrapper, { size: size, loaded: loaded, onScroll: this.handleScroll, fixedFooter: fixedFooter }, React.createElement(ModalWrapperContent, { size: size, fixedFooter: fixedFooter, scrolled: scrolled, ref: this.node, fixedClose: fixedClose, fullyScrolled: fullyScrolled }, React.createElement(CloseElement, { onClose: onClose, closable: closable }), children))); } } export default Modal;