UNPKG

@lote-design-system/marketing-blocks

Version:
471 lines (436 loc) 15.2 kB
import _extends from "@babel/runtime/helpers/esm/extends"; import _objectWithoutProperties from "@babel/runtime/helpers/esm/objectWithoutProperties"; import React, { useState, useEffect } from 'react'; import PropTypes from 'prop-types'; import styled, { css, keyframes } from 'styled-components'; import { rem } from 'polished'; import { LogoDefault } from '../svg'; import { Icon } from '@lote-design-system/icons'; import { Container, Button, useWindowSize } from '@lote-design-system/core'; // => Dependent styled-components // Our main component depends upon the following styled-components const NavBar = styled.nav.withConfig({ displayName: "NavBar1__NavBar", componentId: "pcc9fm-0" })(["padding:", " 0;", ";", ";", ";"], rem(16), ({ fixed }) => { if (fixed === 'top') { return css(["position:fixed;top:0;right:0;left:0;z-index:1030;"]); } else if (fixed === 'bottom') { return css(["position:fixed;right:0;bottom:0;left:0;z-index:1030;"]); } else { return css([""]); } }, ({ sticky }) => { if (sticky) { return css(["@supports ((position:-webkit-sticky) or (position:sticky)){position:sticky;top:0;z-index:1020;}"]); } else { return css([""]); } }, ({ styled }) => { if (styled) { return css(["&{", ";}"], styled); } }); // This container is very important component for the Nav aligning const ContainerModify = styled(Container).withConfig({ displayName: "NavBar1__ContainerModify", componentId: "pcc9fm-1" })(["position:relative;display:flex;flex-wrap:wrap;align-items:center;", " justify-content:space-between;max-width:", ";"], '' /* Remember this is a very important property if you remove it your NavBarStart, NavBarEnd and BurgerButton will not display left of even right side of the bar. */ , ({ theme }) => theme.maxContainerWidth); const NavBarBrand = styled.a.withConfig({ displayName: "NavBar1__NavBarBrand", componentId: "pcc9fm-2" })(["display:inline-flex;align-items:center;padding:", " 0;white-space:nowrap;> svg,> img{width:56px;height:56px;}}}"], rem(4)); const NavBarButtonToggler = styled(Button).withConfig({ displayName: "NavBar1__NavBarButtonToggler", componentId: "pcc9fm-3" })(["width:48px;height:48px;padding:", " ", ";display:inline-flex;justify-content:center;align-items:center;", "{display:none;}"], rem(4), rem(12), ({ theme }) => theme.mediaQueries.lg); const navbarOverlayOpacity = keyframes(["0%{opacity:0;}100%{opacity:1;}"]); const NavBarPlate = styled.div.withConfig({ displayName: "NavBar1__NavBarPlate", componentId: "pcc9fm-4" })(["", " display:none;", " ", "{display:flex;align-items:center;}@media (max-width:991px){display:block;position:fixed;z-index:", ";width:0;height:100%;transition:transform 0.4s linear,height 0s ease 0.4s,width 0s ease 0.4s;top:0;", " ", "}"], '' /* Default NavBarPlate will be disabled on Mobile devices. */ , '' /* NavBarPlate will be enabled on Desktop devices. */ , ({ theme }) => theme.mediaQueries.lg, ({ zIndex }) => zIndex, props => { if (props.placement === 'left') { return { left: 0 }; } else if (props.placement === 'right') { return { right: 0 }; } }, props => props.visible && css(["width:100%;transition:transform 0.4s linear;", "{height:100%;opacity:1;transition:none;animation:", " 0.4s linear;}", "{visibility:visible;", "}"], NavBarOverlay, navbarOverlayOpacity, NavBarMenu, props => { if (props.placement === 'left') { return { transform: 'translateX(100%)' }; } else if (props.placement === 'right') { return { transform: 'translateX(-100%)' }; } })); const NavBarOverlay = styled.div.withConfig({ displayName: "NavBar1__NavBarOverlay", componentId: "pcc9fm-5" })(["@media (max-width:991px){position:fixed;top:0;left:0;width:100%;height:0;opacity:0;transition:opacity 0.4s linear,height 0s ease 0.4s;background-color:rgba(0,0,0,0.7);}"]); // NavBarMenu is important component because all placement of links depend upon this. const NavBarMenu = styled.div.withConfig({ displayName: "NavBar1__NavBarMenu", componentId: "pcc9fm-6" })(["", " ", "{display:flex;align-items:center;}@media (max-width:991px){", ";position:fixed;", ";height:100%;background-color:#ffffff;overflow-y:auto;visibility:hidden;transition:transform 0.2s linear,visibility 0.2s linear;}"], '' /* Styling for the NavBarMenu will be disabled on Desktop devices. */ , ({ theme }) => theme.mediaQueries.lg, ({ theme, placement }) => { if (placement === 'left') { return { boxShadow: theme.shadowSide.right, right: '100%' }; } else if (placement === 'right') { return { boxShadow: theme.shadowSide.left, left: '100%' }; } }, ({ w }) => { if (typeof w === 'number') { return css(["width:", "px;"], w); } else if (typeof w === 'string') { return css(["width:", ";"], w); } else { return css(["width:", "px;"], w); } }); const NavBarHeader = styled.div.withConfig({ displayName: "NavBar1__NavBarHeader", componentId: "pcc9fm-7" })(["display:none;@media (max-width:991px){display:flex;flex-wrap:wrap;align-items:center;border-bottom:1px solid #f8f8f8;", ""], ({ hasTitle, hasClosable }) => { if (hasTitle && hasClosable) { return css(["justify-content:space-between;padding:", " ", ";"], rem(8), rem(18)); } else if (hasTitle && !hasClosable) { return css(["padding:", ";"], rem(18)); } else if (!hasTitle && hasClosable) { return css(["justify-content:flex-end;padding:", " ", ";"], rem(8), rem(18)); } }); const NavBarText = styled.span.withConfig({ displayName: "NavBar1__NavBarText", componentId: "pcc9fm-8" })(["font-size:", ";font-weight:500;color:", ";"], rem(16), ({ theme: { colors } }) => colors.text.title); const NavBarCloseButton = styled(Button).withConfig({ displayName: "NavBar1__NavBarCloseButton", componentId: "pcc9fm-9" })(["width:46px;height:46px;padding:", ";font-size:", ";"], rem(4), rem(18)); // NavBarEnd Section i.e. unordered list display on the right side // ================================================================= // Anchor const NavBarEndLink = styled.a.withConfig({ displayName: "NavBar1__NavBarEndLink", componentId: "pcc9fm-10" })(["font-size:", ";color:", ";display:block;font-weight:500;padding:", " ", ";transition:color 0.4s linear;&:hover{color:", ";}"], rem(14), ({ theme: { colors } }) => colors.text.primary, rem(8), rem(16), ({ theme: { colors } }) => colors.primary); // Button const NavBarEndLinkButton = styled(Button).withConfig({ displayName: "NavBar1__NavBarEndLinkButton", componentId: "pcc9fm-11" })(["font-weight:500;margin:", ";@media (max-width:991px){display:block;}"], rem(8)); // ListItem const NavBarEndItem = styled.li.withConfig({ displayName: "NavBar1__NavBarEndItem", componentId: "pcc9fm-12" })(["", ";"], props => props.active && css(["> ", "{color:", ";}"], NavBarEndLink, ({ theme: { colors } }) => colors.primary)); // Unordered List const NavBarEnd = styled.ul.withConfig({ displayName: "NavBar1__NavBarEnd", componentId: "pcc9fm-13" })(["", " ", " padding:", ";margin-bottom:0;list-style:none;", "{padding:0;flex-direction:row;display:flex;align-items:center;}"], '' /* These styles will appear on both Desktop & Mobile devices. */ , '' /* But the bottom padding property will be only applied on Mobile Devices. Basically, It will add the padding on the list container. */ , rem(14), ({ theme }) => theme.mediaQueries.lg); // End ================================= // <= End Dependent styled-components const linkShape = { id: PropTypes.number.isRequired, type: PropTypes.oneOf(['link', 'button']).isRequired, href: PropTypes.string.isRequired, text: PropTypes.string.isRequired, active: PropTypes.bool, variation: PropTypes.string, color: PropTypes.string }; const linkSchema = [{ id: 1, type: 'link', href: '/', text: 'Home', active: true }, { id: 2, type: 'link', href: '/', text: 'Features' }, { id: 3, type: 'link', href: '/', text: 'Pricing' }, { id: 4, type: 'link', href: '/', text: 'Services' }, { id: 5, type: 'link', href: '/', text: 'About Us' }, { id: 6, type: 'button', href: '/', text: 'Get Started', variation: 'primarySubtle' }]; const propTypes = { /** Render new styles on the NavBar Container. */ styledCSS: PropTypes.string, /** Position an element at the top/bottom of the viewport, from edge to edge. Fixed NavBars use position: fixed, meaning they’re pulled from the normal flow of the DOM and may require custom CSS (e.g., padding-top on the <body>) to prevent overlap with other elements. */ fixed: PropTypes.oneOf(['top', 'bottom']), /** Position an element at the top of the viewport, from edge to edge, but only after you scroll past it. */ sticky: PropTypes.bool, /** Whether a close (X) button is visible on top right of the Drawer dialog or not. */ closable: PropTypes.bool, /** Whether to show mask or not. */ mask: PropTypes.bool, /** Clicking on the mask (area outside the Drawer) to close the Drawer or not. */ maskClosable: PropTypes.bool, /** The title of the Drawer. */ title: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), /** Width of the Drawer dialog. */ width: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), /** The z-index of the Drawer. */ zIndex: PropTypes.number, /** The placement of the Drawer. */ placement: PropTypes.oneOf(['right', 'left']), /** The logo appear on the NavBar, default it will be ui-kit logo. */ logo: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), /** Links appear on the NavBar, same links also render on the Drawer. */ links: PropTypes.arrayOf(PropTypes.shape(linkShape)).isRequired }; const defaultProps = { closable: true, mask: true, maskClosable: true, width: 256, zIndex: 1000, placement: 'right', logo: /*#__PURE__*/React.createElement(LogoDefault, { fill: "#01a1ff" }), links: linkSchema }; /** * Returns the JSX or null for the logo. * @param logo {*} - The logo will React.element or a path * @return {null|*} - Returns the JSX or null */ const renderLogo = logo => { if (React.isValidElement(logo)) { return logo; } else if (typeof logo === 'string' && logo.length > 0) { return /*#__PURE__*/React.createElement("img", { src: logo, alt: "Brand Logo" }); } else { return null; } }; /** * Render overlay mask on the back of the Drawer. * @param state {boolean} - Updated value of the state * @param mask {boolean} - Whether to show mask or not * @param maskClosable {boolean} - Clicking on the mask to close the Drawer or not * @param onClose {function} - A function that will be called when a user clicks mask * @return {null|*} - Returns the JSX or null */ const renderMask = (state, mask, maskClosable, onClose) => { if (mask) { if (state) { document.body.style.position = 'relative'; document.body.style.overflow = 'hidden'; document.body.style.width = `calc(${100}% - ${17}px)`; } else { document.body.style.position = ''; document.body.style.overflow = ''; document.body.style.width = ''; } if (maskClosable) { return /*#__PURE__*/React.createElement(NavBarOverlay, { onClick: onClose }); } else { return /*#__PURE__*/React.createElement(NavBarOverlay, null); } } else { return null; } }; /** * Render header on the Drawer * @param title {*|string} - The title of the Drawer * @param closable {boolean} - Whether a close (X) button is visible on top right of the Drawer dialog or not * @param onClose {function} - A function that will be called when a user clicks the close button * @return {null|*} - Returns the JSX or null */ const renderHeader = (title, closable, onClose) => { // If title is undefined & closeable is undefined then render nothing, else render deterministic elements if (!title && !closable) { return null; } else { return /*#__PURE__*/React.createElement(NavBarHeader, { hasTitle: !!title, hasClosable: !!closable }, title && /*#__PURE__*/React.createElement(NavBarText, null, title), closable && /*#__PURE__*/React.createElement(NavBarCloseButton, { type: "button", basic: true, variation: "lightTransparent", onClick: onClose }, /*#__PURE__*/React.createElement(Icon, { name: "x" }))); } }; /** * Render schema for the NavBar * @param data {Array} - Schema for the NavBar * @return {null|*} - Returns the JSX or null */ const renderBody = data => { if (Array.isArray(data) && data.length > 0) { return /*#__PURE__*/React.createElement(NavBarEnd, null, data.map(({ id, type, href, text, active, variation, color }) => /*#__PURE__*/React.createElement(NavBarEndItem, { key: id, active: active }, type === 'link' ? /*#__PURE__*/React.createElement(NavBarEndLink, { href: href }, text) : /*#__PURE__*/React.createElement(NavBarEndLinkButton, { href: href, variation: variation, color: color }, text)))); } else { return null; } }; export const NavBar1 = props => { const { innerWidth } = useWindowSize(); const { styledCSS, fixed, sticky, closable, mask, maskClosable, title, width, zIndex, logo: Logo, links, placement } = props, attributes = _objectWithoutProperties(props, ["styledCSS", "fixed", "sticky", "closable", "mask", "maskClosable", "title", "width", "zIndex", "logo", "links", "placement"]); const [visibleDrawer, setVisibleDrawer] = useState(false); const showDrawer = () => { setVisibleDrawer(!visibleDrawer); }; // This will be used inside the Drawer header, close button & MaskOverlay const onClose = () => { setVisibleDrawer(false); }; // Always make sure the Drawer don't show up on the big screen size, // so the state will always be false. useEffect(() => { if (innerWidth > 991) { setVisibleDrawer(false); } }, [innerWidth]); return /*#__PURE__*/React.createElement(NavBar, _extends({ styled: styledCSS, fixed: fixed, sticky: sticky }, attributes), /*#__PURE__*/React.createElement(ContainerModify, { fluid: true }, /*#__PURE__*/React.createElement(NavBarBrand, { href: "/" }, renderLogo(Logo)), /*#__PURE__*/React.createElement(NavBarButtonToggler, { basic: true, variation: "fill", color: "primary", active: visibleDrawer, onClick: showDrawer }, visibleDrawer ? /*#__PURE__*/React.createElement(Icon, { name: "x" }) : /*#__PURE__*/React.createElement(Icon, { name: "menu" })), /*#__PURE__*/React.createElement(NavBarPlate, { visible: visibleDrawer, placement: placement, zIndex: zIndex }, renderMask(visibleDrawer, mask, maskClosable, onClose), /*#__PURE__*/React.createElement(NavBarMenu, { w: width, placement: placement }, renderHeader(title, closable, onClose), renderBody(links))))); }; NavBar1.propTypes = propTypes; NavBar1.defaultProps = defaultProps;