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