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