@lote-design-system/marketing-blocks
Version:
471 lines (436 loc) • 15.2 kB
JavaScript
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;