UNPKG

@kiwicom/orbit-components

Version:

<div align="center"> <a href="https://orbit.kiwi" target="_blank"> <img alt="orbit-components" src="https://orbit.kiwi/wp-content/uploads/2018/08/orbit-components.png" srcset="https://orbit.kiwi/wp-content/uploads/2018/08/orbit-components@2x.png 2x"

209 lines (186 loc) 7.99 kB
import * as React from "react"; import styled 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 ClickOutside from "../ClickOutside"; import { StyledModalSection } from "./ModalSection"; import { StyledHeading } from "../Heading"; const getToken = (theme, type, name) => { const tokens = { // TODO: create tokens widthModalSmall,... modalWidth: { [SIZES.SMALL]: "540px", [SIZES.NORMAL]: "740px", [SIZES.LARGE]: "1280px" } }; return tokens[name][type]; }; const ModalBody = styled.div.withConfig({ displayName: "Modal__ModalBody" })(["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; `); ModalBody.defaultProps = { theme: defaultTokens }; const ModalWrapper = styled.div.withConfig({ displayName: "Modal__ModalWrapper" })(["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` padding: ${({ theme }) => theme.orbit.spaceXXLarge}; position: relative; top: 0; max-width: ${({ theme, size }) => getToken(theme, size, "modalWidth")}; align-items: center; `); ModalWrapper.defaultProps = { theme: defaultTokens }; const CloseContainer = styled.div.withConfig({ displayName: "Modal__CloseContainer" })(["display:flex;position:fixed;top:32px;right:0;z-index:1;justify-content:flex-end;align-items:center;box-sizing:border-box;height:52px;width:100%;transition:all ", " ease-in-out;border-top-left-radius:", ";border-top-right-radius:", ";opacity:1;& + ", ":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.borderRadiusNormal, ({ theme }) => theme.orbit.borderRadiusNormal, 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" })(["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:", ";}", "{padding:", ";box-shadow:", ";position:", ";}", "{top:", ";opacity:", ";}", "{top:", ";position:", ";box-shadow:", ";background-color:", ";opacity:", ";}", ";"], ({ 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", StyledModalFooter, ({ theme, fixedFooter }) => fixedFooter && theme.orbit.spaceMedium, ({ fixedFooter }) => fixedFooter && `0 -2px 4px 0 rgba(23, 27, 30, 0.1)`, ({ fixedFooter }) => fixedFooter && "fixed", MobileHeader, ({ scrolled, theme }) => scrolled && theme.orbit.spaceXLarge, ({ scrolled }) => scrolled && "1", CloseContainer, ({ scrolled }) => scrolled && "32px", ({ scrolled }) => scrolled && "fixed", ({ scrolled }) => scrolled && `0 2px 4px 0 rgba(23, 27, 30, 0.1)`, ({ theme, scrolled }) => scrolled && theme.orbit.paletteWhite, ({ scrolled }) => scrolled && "1", media.desktop` position: relative; border-radius: ${({ theme }) => theme.orbit.borderRadiusNormal}; padding-bottom: 0; ${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 }) => React.createElement( CloseContainer, null, React.createElement(ButtonLink, { onClick: onClose, size: "normal", icon: React.createElement(Close, null), transparent: true }) ); const Content = ({ closable, onClose, size, children, scrolled, fixedFooter }) => closable ? React.createElement( ClickOutside, { onClickOutside: onClose }, React.createElement( ModalWrapperContent, { size: size, fixedFooter: fixedFooter, scrolled: scrolled }, React.createElement(CloseElement, { onClose: onClose }), children ) ) : React.createElement( ModalWrapperContent, { size: size, fixedFooter: fixedFooter, scrolled: scrolled }, children ); class Modal extends React.PureComponent { constructor() { super(); // $FlowExpected this.state = { scrolled: false, loaded: false }; this.handleKeyDown = ev => { const { onClose } = this.props; if (ev.key === "Escape" && onClose) { onClose(ev); } }; 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) { this.setState({ scrolled: ev.target.scrollTop >= this.offset }); } } render() { const { onClose, children, size = SIZES.NORMAL, closable = true, fixedFooter = false, dataTest } = this.props; const { scrolled, loaded } = this.state; return React.createElement( ModalBody, { tabIndex: "0", onKeyDown: closable ? this.handleKeyDown : undefined, "data-test": dataTest }, React.createElement( ModalWrapper, { size: size, loaded: loaded, onScroll: this.handleScroll, innerRef: node => { this.node = node; }, fixedFooter: fixedFooter }, React.createElement( Content, { closable: closable, onClose: onClose, size: size, scrolled: scrolled, fixedFooter: fixedFooter }, children ) ) ); } } export default Modal;