UNPKG

@rarcifa/cronos-design-system

Version:

A typescript react component library following the Cronos branding standards

1,295 lines (1,230 loc) 59 kB
import { jsx, jsxs, Fragment } from 'react/jsx-runtime'; import styled, { css } from 'styled-components'; import { useRef, useEffect, useState } from 'react'; import { Link } from 'react-router-dom'; /****************************************************************************** Copyright (c) Microsoft Corporation. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ***************************************************************************** */ function __rest(s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; } typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) { var e = new Error(message); return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e; }; var Variant; (function (Variant) { Variant["Primary"] = "primary"; Variant["Secondary"] = "secondary"; Variant["Dark"] = "dark"; Variant["Active"] = "active"; Variant["Default"] = "default"; })(Variant || (Variant = {})); var Size; (function (Size) { Size["Lg"] = "lg"; Size["Md"] = "md"; Size["Sm"] = "sm"; })(Size || (Size = {})); var Color; (function (Color) { Color["Dark"] = "dark"; Color["Light"] = "light"; Color["Default"] = "default"; })(Color || (Color = {})); var FontWeight; (function (FontWeight) { FontWeight["Bold"] = "bold"; FontWeight["Medium"] = "medium"; FontWeight["Default"] = "default"; })(FontWeight || (FontWeight = {})); var TextAlign; (function (TextAlign) { TextAlign["Left"] = "left"; TextAlign["Right"] = "right"; TextAlign["Center"] = "center"; })(TextAlign || (TextAlign = {})); var Heading; (function (Heading) { Heading["H1"] = "h1"; Heading["H2"] = "h2"; Heading["H3"] = "h3"; Heading["H4"] = "h4"; Heading["H5"] = "h5"; })(Heading || (Heading = {})); const sizes = { xxxs: '6px', xxs: '8px', xs: '12px', sm: '15px', md: '18px', lg: '20px', xl: '25px', xxl: '32px', xxxl: '40px', }; const colors = { light: '#ffffff', dark: '#061121', grey: '#d1d3d7', primary: '#129dff', secondary: '#1cf1d8', tertiary: '#172d4a', default: '#082038', secondaryMenu: '#0b192e', green: '#1cf1d8', }; const mediaQueries = { xs: '@media (max-width: 360px)', sm: '@media (max-width: 549px)', md: '@media (max-width: 804px)', lg: '@media (max-width: 992px)', xl: '@media (max-width: 1162px)', xxl: '@media (max-width: 1024px)', xxxl: '@media (max-width: 1992px)', }; const borderRadius = { default: '8px', squared: '4px', rounded: '50px', }; const align = { left: 'left', center: 'center', right: 'right', }; const justify = { space: 'space-between', center: 'center', left: 'left', }; const display = { block: 'block', none: 'none', flex: 'flex', grid: 'grid', inline: 'inline-flex;', }; const empty = { zero: 0, string: '', }; const position = { relative: 'relative', absolute: 'absolute', static: 'static', sticky: 'sticky', }; const fontWeight = { bold: 700, medium: 500, default: 400, }; const helper$5 = { /** * Determines the appropriate background color for the button based on its props. * Considers the button's state and style props to apply the correct color. * * @param {ButtonProps} props - The props passed to the button component. * @returns {string} The calculated background color. */ getBackgroundColor: (props) => { switch (props.variant) { case Variant.Primary: return colors.primary; case Variant.Secondary: return colors.default; case Variant.Dark: return colors.dark; case Variant.Active: return colors.dark; default: return colors.default; } }, /** * Determines the appropriate border color for the button based on its props. * The function considers various states like primary, secondary, dark, and active * to apply the corresponding border color. * * @param {ButtonProps} props - The props passed to the button component. * @returns {string} The calculated border color. */ getBorderColor: (props) => { switch (props.variant) { case Variant.Primary: return colors.primary; case Variant.Secondary: return colors.default; case Variant.Dark: return colors.dark; case Variant.Active: return colors.primary; default: return colors.default; } }, /** * Calculates the font size for the button based on its size prop. * Supports 'sm', 'md', and 'lg' sizes, defaulting to 'sm' if no size is specified. * * @param {ButtonProps} props - The props passed to the button component. * @returns {string} The font size in pixels. */ getFontSize: (props) => { switch (props.size) { case Size.Lg: return '16px'; case Size.Md: return '14px'; case Size.Sm: default: return '12px'; } }, /** * Determines the padding for the button based on its size prop. * Provides custom padding for 'sm', 'md', and 'lg' sizes, * defaulting to the padding for 'sm' if no size is specified. * * @param {ButtonProps} props - The props passed to the button component. * @returns {string} The padding value in the format "vertical horizontal". */ getPadding: (props) => { switch (props.size) { case Size.Lg: return '12px 24px'; case Size.Md: return '11px 20px'; case Size.Sm: default: return '10px 16px'; } }, /** * Calculates the appropriate border-radius for the button based on the 'rounded' prop. * If 'rounded' is true, a larger border-radius is applied for a pill-shaped button. * Otherwise, a standard border-radius is used. * * @param {ButtonProps} props - The props passed to the button component. * @returns {string} The border-radius value in pixels. */ getBorderRadius: (props) => { return props.rounded ? borderRadius.rounded : borderRadius.squared; }, /** * Determines the text color of the button based on its variant. * Uses the Variant enum to set specific text colors for different button variants. * Defaults to a standard color if no matching variant is found. * * @param {ButtonProps} props - The props passed to the button component. * @returns {string} The calculated text color. */ getColor: (props) => { return props.variant === Variant.Primary ? colors.light : colors.grey; }, /** * Determines the font weight for the typography based on its fontWeight prop. * Maps fontWeight prop values to font-weight CSS values. * * @param {TypographyProps} props - The props passed to the typography component. * @returns {string} The CSS font-weight value. */ getFontWeight: (props) => { switch (props.fontWeight) { case FontWeight.Bold: return '700'; case FontWeight.Medium: return '500'; case FontWeight.Default: return '400'; default: return '400'; } }, /** * Determines the content to be displayed inside the Button component. * It prioritizes rendering `children` over `label`. If `children` is provided, * it will be used as the content of the button. Otherwise, the `label` will be used. * * @param {ButtonProps} props - The props passed to the button component. * @returns {React.ReactNode} The content to be rendered inside the button. */ getComponentValue: (props) => { return props.children || props.label; }, }; const StyledButton = styled.button ` background: ${helper$5.getBackgroundColor}; padding: ${helper$5.getPadding}; font-size: ${helper$5.getFontSize}; border: 1px solid ${helper$5.getBorderColor}; color: ${helper$5.getColor}; border-radius: ${helper$5.getBorderRadius}; font-weight: ${helper$5.getFontWeight}; letter-spacing: 0.05rem; cursor: pointer; display: inline-flex; align-items: center; `; function Button(_a) { var { variant = Variant.Primary, size = Size.Lg, rounded = false } = _a, props = __rest(_a, ["variant", "size", "rounded"]); return (jsx(StyledButton, Object.assign({ variant: variant, size: size, rounded: rounded }, props, { children: helper$5.getComponentValue(props) }))); } const StyledCard = styled.div ` display: inline-block; background: ${colors.default}; border-radius: ${borderRadius.default}; color: ${colors.light}; width: auto; `; const CardFooter = styled.div ` border-top: 1px solid #1199fa1a; `; function Card(_a) { var props = __rest(_a, []); return (jsxs(StyledCard, { children: [props.children, props.footer && jsx(CardFooter, { children: props.footer })] })); } var img$5 = "data:image/svg+xml,%3csvg width='20' height='20' viewBox='0 0 20 20' fill='none' xmlns='http://www.w3.org/2000/svg'%3e%3cg clip-path='url(%23clip0_307_2)'%3e%3cpath d='M16.25 8.125C16.25 9.91797 15.668 11.5742 14.6875 12.918L19.6328 17.8672C20.1211 18.3555 20.1211 19.1484 19.6328 19.6367C19.1445 20.125 18.3516 20.125 17.8633 19.6367L12.918 14.6875C11.5742 15.6719 9.91797 16.25 8.125 16.25C3.63672 16.25 0 12.6133 0 8.125C0 3.63672 3.63672 0 8.125 0C12.6133 0 16.25 3.63672 16.25 8.125ZM8.125 13.75C8.86369 13.75 9.59514 13.6045 10.2776 13.3218C10.9601 13.0391 11.5801 12.6248 12.1025 12.1025C12.6248 11.5801 13.0391 10.9601 13.3218 10.2776C13.6045 9.59514 13.75 8.86369 13.75 8.125C13.75 7.38631 13.6045 6.65486 13.3218 5.97241C13.0391 5.28995 12.6248 4.66985 12.1025 4.14752C11.5801 3.62519 10.9601 3.21086 10.2776 2.92818C9.59514 2.6455 8.86369 2.5 8.125 2.5C7.38631 2.5 6.65486 2.6455 5.97241 2.92818C5.28995 3.21086 4.66985 3.62519 4.14752 4.14752C3.62519 4.66985 3.21086 5.28995 2.92818 5.97241C2.64549 6.65486 2.5 7.38631 2.5 8.125C2.5 8.86369 2.64549 9.59514 2.92818 10.2776C3.21086 10.9601 3.62519 11.5801 4.14752 12.1025C4.66985 12.6248 5.28995 13.0391 5.97241 13.3218C6.65486 13.6045 7.38631 13.75 8.125 13.75Z' fill='white'/%3e%3c/g%3e%3cdefs%3e%3cclipPath id='clip0_307_2'%3e%3crect width='20' height='20' fill='white'/%3e%3c/clipPath%3e%3c/defs%3e%3c/svg%3e"; const useClickOutside = (isOpened, callback) => { const ref = useRef(null); useEffect(() => { const handleClickOutside = (event) => { if (ref.current && !ref.current.contains(event.target)) { callback(); } }; if (isOpened) { document.addEventListener('mousedown', handleClickOutside); } else { document.removeEventListener('mousedown', handleClickOutside); } return () => { document.removeEventListener('mousedown', handleClickOutside); }; }, [isOpened, callback, ref]); return ref; }; const helper$4 = { /** * Determines the appropriate background color for the searchbar based on its props. * Considers the state of the search and style props to apply the correct color. * * @param {ButtonProps} props - The props passed to the search bar component. * @returns {string} The calculated background color. */ getBackgroundColor: (props) => { switch (props.variant) { case Variant.Primary: return colors.default; case Variant.Secondary: return colors.dark; default: return colors.default; } }, /** * Determines the appropriate border color for the search bar based on its props. * The function considers the states primary and secondary * to apply the corresponding border color. * * @param {SearchProps} props - The props passed to the search bar component. * @returns {string} The calculated border color. */ getBorderColor: (props) => { switch (props.variant) { case Variant.Primary: return colors.default; case Variant.Secondary: return colors.tertiary; default: return colors.default; } }, /** * Calculates the appropriate border-radius for the search bar based on the 'rounded' prop. * If 'rounded' is true, a larger border-radius is applied for a pill-shaped search bar. * Otherwise, a standard border-radius is used. * * @param {SearchProps} props - The props passed to the search bar component. * @returns {string} The border-radius value in pixels. */ getBorderRadius: (props) => { return props.rounded ? borderRadius.rounded : borderRadius.squared; }, /** * Determines the width of the search bar based on its variant. * Uses the Variant enum to set specific width for different search bar variants. * Defaults to a standard width of 255px if no matching variant is found. * * @param {ButtonProps} props - The props passed to the search bar component. * @returns {string} The calculated width. */ getWidth: (props) => { return props.variant === Variant.Primary ? '250px' : '230px'; }, /** * Determines the padding of the search bar based on its variant. * Uses the Variant enum to set specific padding for different search bar variants. * * @param {ButtonProps} props - The props passed to the search bar component. * @returns {string} The calculated padding. */ getPadding: (props) => { switch (props.size) { case Size.Lg: return `10px 24px;`; case Size.Md: return '8px 18px'; default: return '8px 18px'; } }, }; const StyledSearchBar = styled.div ` position: ${position.relative}; display: ${display.flex}; align-items: ${align.center}; background: ${colors.default}; background: ${helper$4.getBackgroundColor}; border-radius: ${helper$4.getBorderRadius}; border: 1.5px solid ${helper$4.getBorderColor}; padding: ${helper$4.getPadding}; margin-left: auto; width: ${helper$4.getWidth}; ${mediaQueries.xl} { margin: ${sizes.xs}; } `; const StyledStyledIcon = styled.img ` width: 16px; height: 16px; margin-right: 8px; `; const StyledSearchInput = styled.input ` color: ${colors.grey}; background: transparent; border: none; flex: 1 1; font-family: 'Archivo'; font-size: 16px; letter-spacing: 0.8px; outline: none; &::placeholder { color: ${colors.grey}; opacity: 1; } &::-ms-input-placeholder { color: ${colors.grey}; } &::-moz-placeholder { color: ${colors.grey}; opacity: 1; } &::-webkit-input-placeholder { color: ${colors.grey}; opacity: 1; } `; const StyledDropdown = styled.div ` background-color: #132842; border: 1px solid #132842; border-radius: 0.5em; box-shadow: ${empty.zero} 2px 4px #0000001a; left: ${empty.zero}; max-height: 200px; overflow-y: auto; position: ${position.absolute}; top: 100%; width: 100%; z-index: 1; a { text-decoration: none; color: ${colors.light}; } `; const StyledDropdownItem = styled.div ` padding: ${sizes.xxs} ${sizes.md}; cursor: pointer; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; &:hover { background-color: #2a384f; } `; function Search(_a) { var { variant = Variant.Primary, size = Size.Md, rounded = false, placeholder = 'Search Project' } = _a, props = __rest(_a, ["variant", "size", "rounded", "placeholder"]); const [searchQuery, setSearchQuery] = useState(''); const [searchResults, setSearchResults] = useState([]); const [isDropdownOpen, setIsDropdownOpen] = useState(true); const handleSearch = async (e) => { const query = e.target.value; setSearchQuery(query); if (query.trim()) { try { const results = await props.getData({ query }); setSearchResults(results.data); setIsDropdownOpen(true); } catch (e) { console.error('Error:', e); } } else { setSearchResults([]); setIsDropdownOpen(false); } }; const handleToggleDropdown = () => { setIsDropdownOpen(!isDropdownOpen); }; const dropdownRef = useClickOutside(isDropdownOpen, handleToggleDropdown); return (jsxs(StyledSearchBar, Object.assign({ variant: variant, rounded: rounded, size: size }, props, { children: [jsx(StyledStyledIcon, { src: img$5, alt: "Search" }), jsx(StyledSearchInput, { type: "text", placeholder: placeholder, value: searchQuery, onChange: handleSearch }), isDropdownOpen && searchResults.length > 0 && (jsx(StyledDropdown, Object.assign({ ref: dropdownRef }, { children: searchResults.map((link, idx) => (jsx(Link, Object.assign({ to: link.to }, { children: jsx(StyledDropdownItem, { children: link.title }, idx) })))) })))] }))); } const helper$3 = { /** * Calculates the font size for the button based on its size prop. * Supports 'sm', 'md', and 'lg' sizes, defaulting to 'sm' if no size is specified. * * @param {ButtonProps} props - The props passed to the button component. * @returns {string} The font size in pixels. */ getFontSize: (props) => { if (props.customSize) { return `${props.customSize}px`; } switch (props.size) { case Size.Lg: return '16px'; case Size.Md: return '14px'; case Size.Sm: return '12px'; default: return '12px'; } }, /** * Determines the font weight for the typography based on its fontWeight prop. * Maps fontWeight prop values to font-weight CSS values. * * @param {TypographyProps} props - The props passed to the typography component. * @returns {string} The CSS font-weight value. */ getFontWeight: (props) => { switch (props.fontWeight) { case FontWeight.Bold: return '800'; case FontWeight.Medium: return '500'; case FontWeight.Default: return '400'; default: return '400'; } }, /** * Determines the color for the typography based on its color prop. * Maps color prop values to actual color codes. * * @param {TypographyProps} props - The props passed to the typography component. * @returns {string} The color value. */ getColor: (props) => { if (props.customGradientColor) { return `transparent; background: ${props.customGradientColor}; -webkit-background-clip: text; background-clip: text;`; } if (props.customColor) { return props.customColor; } switch (props.color) { case Color.Dark: return colors.dark; case Color.Light: return colors.light; case Color.Default: return colors.default; default: return colors.dark; } }, /** * Determines the gradient for the typography based on its gradient prop. * Maps gradient prop values to actual gradient codes. * * @param {TypographyProps} props - The props passed to the typography component. * @returns {string} The gradient value. */ getGradientStyle: (props) => { if (props.customGradientColor && props.customGradientColor.length > 1) { const gradientCSS = `linear-gradient(to right, ${props.customGradientColor.join(', ')})`; return ` color: transparent; background: ${gradientCSS}; -webkit-background-clip: text; background-clip: text; `; } return ''; }, /** * Determines the content to be displayed inside the Button component. * It prioritizes rendering `children` over `label`. If `children` is provided, * it will be used as the content of the button. Otherwise, the `label` will be used. * * @param {ButtonProps} props - The props passed to the button component. * @returns {React.ReactNode} The content to be rendered inside the button. */ getComponentValue: (props) => { return props.children || props.label; }, /** * Determines the line-height for the typography based on its customLineHeight prop. * Maps customLineHeight prop values to actual line-hight value. * * @param {ButtonProps} props - The props passed to the button component. * @returns {React.ReactNode} The content to be rendered inside the button. */ getLineHeight: (props) => { if (props.customLineHeight) { return props.customLineHeight; } return 1.5; }, /** * Determines the line-height for the typography based on its customLineHeight prop. * Maps customLineHeight prop values to actual line-hight value. * * @param {ButtonProps} props - The props passed to the button component. * @returns {React.ReactNode} The content to be rendered inside the button. */ getMarginBlock: (props) => { if (props.customMargin) { return `${props.customMargin}px`; } return `${empty.zero}`; }, /** * Applies truncation styles if isTruncated prop is true. Specifies max-width * if provided, defaults to 115px otherwise. * * @param {TypographyProps} props - The props passed to the typography component. * @returns {string} CSS styles for truncation or an empty string. */ getTruncationStyle: (props) => { if (props.truncated) { return `white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: ${props.maxWidth ? `${props.maxWidth}px` : '115px'};`; } return ''; }, /** * Determines the text alignment for the typography based on its textAlign prop. * Supports 'left', 'right', and 'center' alignments. * * @param {TypographyProps} props - The props passed to the typography component. * @returns {string} The text alignment. */ getTextAlign: (props) => { switch (props.textAlign) { case TextAlign.Left: return 'left'; case TextAlign.Right: return 'right'; case TextAlign.Center: return 'center'; default: return 'left'; } }, }; const StyledTypography = styled.div.attrs(({ as }) => ({ as: as || 'div', })) ` font-weight: ${helper$3.getFontWeight}; font-size: ${helper$3.getFontSize}; color: ${helper$3.getColor}; ${helper$3.getTruncationStyle}; text-align: ${helper$3.getTextAlign}; line-height: ${helper$3.getLineHeight}; margin-block-start: ${helper$3.getMarginBlock}; margin-block-end: ${helper$3.getMarginBlock}; ${helper$3.getGradientStyle}; `; function Typography(_a) { var { size = Size.Md } = _a, props = __rest(_a, ["size"]); const Component = props.headings ? props.headings : 'div'; return (jsx(StyledTypography, Object.assign({ as: Component, size: size }, props, { children: helper$3.getComponentValue(props) }))); } var img$4 = "data:image/svg+xml,%3csvg width='32' height='32' viewBox='0 0 32 32' fill='none' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M26.6673 9.33301H5.33398M26.6673 15.9997H5.33398M26.6673 22.6663H5.33398' stroke='white' stroke-width='1.5' stroke-linecap='round'/%3e%3c/svg%3e"; var img$3 = "data:image/svg+xml,%3csvg width='32' height='32' viewBox='0 0 32 32' fill='none' xmlns='http://www.w3.org/2000/svg'%3e%3cpath fill-rule='evenodd' clip-rule='evenodd' d='M15.9993 3.66699C9.18784 3.66699 3.66602 9.18881 3.66602 16.0003C3.66602 22.8118 9.18784 28.3337 15.9993 28.3337C22.8109 28.3337 28.3327 22.8118 28.3327 16.0003C28.3327 9.18881 22.8109 3.66699 15.9993 3.66699ZM1.66602 16.0003C1.66602 8.08424 8.08327 1.66699 15.9993 1.66699C23.9154 1.66699 30.3327 8.08424 30.3327 16.0003C30.3327 23.9164 23.9154 30.3337 15.9993 30.3337C8.08327 30.3337 1.66602 23.9164 1.66602 16.0003Z' fill='white'/%3e%3cpath fill-rule='evenodd' clip-rule='evenodd' d='M11.9589 11.9599C12.3494 11.5694 12.9826 11.5694 13.3731 11.9599L15.9993 14.5861L18.6256 11.9599C19.0161 11.5694 19.6493 11.5694 20.0398 11.9599C20.4303 12.3504 20.4303 12.9836 20.0398 13.3741L17.4136 16.0003L20.0398 18.6266C20.4303 19.0171 20.4303 19.6502 20.0398 20.0408C19.6493 20.4313 19.0161 20.4313 18.6256 20.0408L15.9993 17.4145L13.3731 20.0408C12.9826 20.4313 12.3494 20.4313 11.9589 20.0408C11.5684 19.6502 11.5684 19.0171 11.9589 18.6266L14.5851 16.0003L11.9589 13.3741C11.5684 12.9836 11.5684 12.3504 11.9589 11.9599Z' fill='white'/%3e%3c/svg%3e"; const helper$2 = { /** * Determines the CSS position property based on whether the component is a submenu. * * @param {NavigationProps} props - The props containing the isSubmenu flag. * @returns {string} The CSS position value. */ getPosition: (props) => { if (props.isSubmenu) { return position.absolute; } return position.static; }, /** * Updates the left margin for positioning based on the menu status. * * @param {NavigationProps} props - The props containing the isSubmenu flag. * @returns {string|undefined} The left margin CSS property value or undefined if not a menu. */ updateMainMenuPosition: (props) => { if (props.isMenu) { return '-15px'; } return undefined; }, /** * Calculates the left property for positioning based on the submenu status. * * @param {NavigationProps} props - The props containing the isSubmenu flag. * @returns {string|undefined} The left CSS property value or undefined if not a submenu. */ getLeft: (props) => { if (props.isSubmenu) { return '260px'; } return undefined; }, /** * Provides the appropriate background color depending on whether the component is part of a submenu. * * @param {NavigationProps} props - The props containing the isSubmenu flag. * @returns {string} The CSS color value. */ getBackgroundColor: (props) => { if (props.isSubmenu) { return colors.secondaryMenu; } if (props.isMenu) { return colors.default; } return 'transparent'; }, /** * Determines the background style for a menu on hover, based on menu or tab status. * * @param {NavigationProps} props - The props with status flags. * @returns {string|undefined} The CSS background property value. */ getOnHoverMenuBackground: (props) => { switch (true) { case props.isMenu && props.hasSubmenu: return 'linear-gradient(to right, transparent 0, #ffffff0f 0, #ffffff0f 250px, transparent 250px)'; case props.isTab: return '#ffffff0f'; case props.isMenu: return '#ffffff0f'; default: return undefined; } }, /** * Calculates the bottom property for CSS based on submenu status. * * @param {NavigationProps} props - The props containing the isSubmenu flag. * @returns {number|undefined} The CSS bottom property value or undefined if not applicable. */ getBottom: (props) => { if (props.isSubmenu) { return empty.zero; } return undefined; }, /** * Generates a border image gradient for tabs. * * @param {NavigationProps} props - The props containing the isTab flag and theme data. * @returns {string|undefined} The CSS border image property value or undefined if not a tab. */ getBorderImage: (props) => { var _a, _b; const turnvalue = props.isMobile ? 5 : 25; if (props.isTab) { return `linear-gradient(0.${turnvalue}turn, ${(_a = props.theme) === null || _a === void 0 ? void 0 : _a.primary}, ${(_b = props.theme) === null || _b === void 0 ? void 0 : _b.secondary})`; } return undefined; }, /** * Determines the slice of the border image to apply, used for tabs. * * @param {NavigationProps} props - The props containing the isTab flag. * @returns {number|undefined} The border-image-slice value or undefined if not a tab. */ getBorderImageSlice: (props) => { if (props.isTab) { return 1; } return undefined; }, /** * Provides the appropriate border radius depending on widget and submenu status. * * @param {NavigationProps} props - The props with various layout flags. * @returns {string} The CSS border-radius value. */ getBorderRadius: (props) => { switch (true) { case props.hasWidget: return `${empty.zero}`; case props.isMenu && props.isSubmenu: return `${sizes.xxs} ${empty.zero} ${empty.zero} ${sizes.xxs}`; case props.isSubmenu: return `${empty.zero} ${sizes.xxs} ${sizes.xxs} ${empty.zero}`; default: return borderRadius.default; } }, /** * Provides the border, used for the menu cards. * * @param {NavigationProps} props - The props containing the isMenu flag. * @returns {number|undefined} The border value or undefined if not a menu. */ getBorder: (props) => { if (props.isMenu) { return '1px solid #1e385b'; } if (props.isSubmenu) { return '1px solid #1e385b'; } return undefined; }, /** * Provides the border, used for the menu cards. * * @param {NavigationProps} props - The props containing the isMenu flag. * @returns {number|undefined} The border value or undefined if not a menu. */ getMobileBorder: (props) => { if (props.isMenu) { return '1px solid #1e385b'; } return undefined; }, /** * Provides the border margin, used for the menu cards. * * @param {NavigationProps} props - The props containing the isMenu flag. * @returns {number|undefined} The border margin value or undefined if not a menu. */ getMargin: (props) => { if (props.isMenu) { return sizes.md; } return undefined; }, /** * Provides the border padding, used for the menu cards. * * @param {NavigationProps} props - The props containing the isWidget flag. * @returns {number|undefined} The border padding value or undefined if not a menu. */ getPadding: (props) => { if (props.hasWidget) { return '10px'; } return undefined; }, /** * Determines the border radius on hover based on menu and submenu flags. * * @param {NavigationProps} props - The props containing the isMenu and hasSubmenu flags. * @returns {string} The CSS border-radius value on hover. */ getBorderRadiusOnHover: (props) => { switch (true) { case props.isMenu && props.hasSubmenu: return `${sizes.xxs} ${empty.zero} ${empty.zero} ${sizes.xxs}`; case props.isSubmenu && !props.hasWidget: return `${empty.zero} ${sizes.xxs} ${sizes.xxs} ${empty.zero}`; case props.hasWidget: return `${empty.zero}`; default: return borderRadius.default; } }, /** * Determines the width on the tab menu flag. * * @param {NavigationProps} props - The props containing the isMenu flag. * @returns {string|undefined} The CSS width value or undefined if not a tab. */ getTabMenuWidth: (props) => { if (props.isMenu && props.hasSubmenu) { return '260px'; } return undefined; }, /** * Provides the appropriate menu background depending on menu and submenu status. * * @param {NavigationProps} props - The props with various layout flags. * @returns {string} The CSS background value. */ getMobileMenuBackground: (props) => { if (props.isSubmenu) { return 'transparent'; } return colors.secondaryMenu; }, /** * Checks if the given URL is an external link. * * @param {string} url - The URL to be tested. * @returns {boolean} - Returns `true` if the URL is external, otherwise returns `false`. */ isExternalUrl: (url) => { return /^(https?:\/\/|mailto:|tel:)/.test(url); }, }; const StyledNavbarContainer = styled.nav ` background: ${colors.dark}; color: ${colors.light}; top: ${empty.zero}; border-bottom: 1px solid #1e385b; position: sticky; z-index: 2; *, *::before, *::after { box-sizing: border-box; } ${mediaQueries.xl} { justify-content: ${justify.space}; padding: ${sizes.sm}; } `; const StyledNavbar = styled.div ` display: ${display.flex}; align-items: ${align.center}; margin: ${empty.zero} auto; padding: ${empty.zero} 20px; gap: 30px; max-width: 1376px; justify-content: space-between; ${mediaQueries.xl} { padding: ${empty.zero}; max-width: none; } `; const StyledLogoContainer = styled.a ` display: ${display.inline}; align-items: ${align.center}; justify-content: ${justify.center}; margin: ${empty.zero}; padding: ${empty.zero}; height: 100%; `; const StyledLogo$1 = styled.img ` ${mediaQueries.xl} { width: 100px; } `; const StyledHamburger = styled.img ` display: ${display.none}; background: transparent; border: none; cursor: pointer; z-index: 1; ${mediaQueries.xl} { display: ${display.flex}; } `; const StyledChildren = styled.div ` &:first-of-type { margin-left: auto; } ${mediaQueries.xl} { display: ${display.none}; } `; const StyledSearchContainer = styled.div ` display: ${display.none}; ${mediaQueries.xl} { &.open { display: ${display.flex}; } } `; const StyledMenuList = styled.ul ` margin: ${empty.zero}; ${mediaQueries.xl} { left: ${empty.zero}; } li { &:last-child { margin-right: ${empty.zero}; } a { color: ${colors.light}; text-decoration: none; } } ${mediaQueries.xl} { display: ${display.none}; &.open { position: ${position.absolute}; top: 100%; display: ${display.flex}; flex-direction: column; box-sizing: border-box; width: 100vw; padding: ${empty.zero} ${empty.zero}; background: ${colors.dark}; li { &:last-child { margin-bottom: ${empty.zero}; } } } } `; const StyledMenuLabelContainer = styled.div ` display: ${display.flex}; align-items: ${align.center}; gap: 5px; `; const StyledIconContainer = styled.div ` justify-content: ${justify.center}; display: ${display.flex}; width: 24px; height: 24px; align-items: top; `; const StyledIconImage = styled.img ` width: 100%; height: 100%; `; const StyledNavigationContainer = styled.div ` display: ${display.flex}; gap: 10px; ${mediaQueries.xl} { display: ${display.none}; gap: ${empty.zero}; } `; const StyledMenuContainer = styled.div ` position: ${position.absolute}; display: ${display.none}; `; const StyledMenuGrid = styled.div ` display: ${display.grid}; grid-template-columns: 1fr 1fr; `; const StyledWidgetContainer = styled.div ` position: ${position.absolute}; background: ${colors.secondaryMenu}; border-radius: ${empty.zero} ${sizes.xxs} ${sizes.xxs} ${empty.zero}; border: 1px solid #1e385b; border-left: none; left: 531px; top: -380px; padding: 10px; height: 380px; `; const StyledWidgetLink = styled.div ` &:nth-child(2) { margin-top: 10px; } `; const StyledWidgetImage = styled.img ` width: 265px; `; const StyledMenu$1 = styled.div ` position: ${helper$2.getPosition}; left: ${helper$2.getLeft}; background: ${helper$2.getBackgroundColor}; bottom: ${helper$2.getBottom}; border-radius: ${helper$2.getBorderRadius}; margin-left: ${helper$2.updateMainMenuPosition}; border: ${helper$2.getBorder}; border-left: ${({ isSubmenu }) => isSubmenu && 'none'}; margin-top: ${sizes.lg}; display: ${display.block}; width: 272px; height: 380px; max-height: 380px; overflow-y: auto; overflow-x: hidden; padding: 10px; &:hover { border-radius: ${helper$2.getBorderRadiusOnHover}; } &::-webkit-scrollbar { width: 2px; } &::-webkit-scrollbar-track { margin: ${sizes.sm}; background: transparent; } &::-webkit-scrollbar-thumb { background-color: ${colors.primary}; border-radius: ${sizes.lg}; } `; const StyledInnerListParent = styled.li ` width: ${helper$2.getTabMenuWidth}; display: ${display.flex}; gap: ${sizes.sm}; border-bottom: 2px solid transparent; overflow: hidden; &:hover { background: ${helper$2.getOnHoverMenuBackground}; } &:hover > a { color: #129dff; } &.active { border-image: ${helper$2.getBorderImage}; border-image-slice: ${helper$2.getBorderImageSlice}; } &:hover > div { top: 100%; display: ${display.block}; } `; const StyledMenuLink = styled.span ` text-decoration: none; &.active { background: ${helper$2.getOnHoverMenuBackground}; border-left: 2px solid transparent; border-image: ${helper$2.getBorderImage}; border-image-slice: ${helper$2.getBorderImageSlice}; } `; const StyledLabelContainer = styled.div ` font-weight: ${fontWeight.medium}; display: ${display.flex}; font-size: 16px; gap: 10px; `; const StyledFloatingMenu = styled.ul ` padding: ${empty.zero}; list-style-type: none; `; const StyledLabel = styled.span ` font-weight: ${fontWeight.medium}; display: ${display.flex}; font-size: 16px; gap: 10px; `; const StyledSubmenuLabelContainer = styled.div ` align-content: ${align.center}; `; const StyledExpansionIcon = styled.img ` display: ${display.none}; background: transparent; border: none; cursor: pointer; z-index: 1; ${mediaQueries.xl} { display: ${display.flex}; margin-left: auto; } `; const StyledMobileNavigation = styled.div ` width: 100%; display: ${display.none}; ${mediaQueries.xl} { display: ${display.block}; } `; const StyledMobileContainer = styled.div ` border-radius: ${helper$2.getBorderRadius}; border: ${helper$2.getMobileBorder}; margin: ${helper$2.getMargin}; background: ${helper$2.getBackgroundColor}; padding: ${helper$2.getPadding}; display: ${display.block}; `; const StyledMobileMobileMenuItem = styled.div ` display: ${display.flex}; padding: ${sizes.md}; gap: 15px; `; const StyledMobileMenuItemContainer = styled.div ` display: ${display.block}; `; var img$2 = "data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-plus'%3e%3cline x1='12' y1='5' x2='12' y2='19'%3e%3c/line%3e%3cline x1='5' y1='12' x2='19' y2='12'%3e%3c/line%3e%3c/svg%3e"; var img$1 = "data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-minus'%3e%3cline x1='5' y1='12' x2='19' y2='12'%3e%3c/line%3e%3c/svg%3e"; function NavigationLink(_a) { var { to, children, RouterLink } = _a, props = __rest(_a, ["to", "children", "RouterLink"]); const external = helper$2.isExternalUrl(to); if (external) { return (jsx("a", Object.assign({ href: to, target: "_blank", rel: "noopener noreferrer" }, props, { children: children }))); } return (jsx(RouterLink, Object.assign({ to: to }, props, { children: children }))); } function MobileNavigationMenu(_a) { var _b; var { toggleItem } = _a, props = __rest(_a, ["toggleItem"]); return (jsx("div", { children: jsx(NavigationLink, Object.assign({ RouterLink: props.RouterLink, to: props.item.to, href: props.item.to }, { children: jsx(StyledMenuLink, Object.assign({ onClick: () => toggleItem(props.idx) }, { children: jsxs(StyledMobileMobileMenuItem, { children: [props.item.icon && (jsx(StyledIconContainer, { children: jsx(StyledIconImage, { src: props.item.icon, alt: "icon" }) })), jsxs("div", { children: [jsxs(StyledLabelContainer, { children: [jsx(Typography, Object.assign({ color: Color.Light }, { children: props.item.title })), (_b = props.item.tags) === null || _b === void 0 ? void 0 : _b.map((tag, idx) => (jsx("img", { src: tag, alt: `${idx}-tag` }, `${idx}-tag`)))] }), jsx(Typography, Object.assign({ customColor: "grey", size: Size.Sm }, { children: props.item.description }))] }), props.isSubmenu && (jsx(StyledExpansionIcon, { src: props.expandedMenu[props.idx] ? img$1 : img$2, alt: "expansion" }))] }) })) })) }, props.idx)); } function MobileNavigation(_a) { var props = __rest(_a, []); const [expandedMenu, setExpandedMenu] = useState({}); const [index, setIndex] = useState('0'); const toggleItem = (index) => { setIndex(String(index).split('-')[0]); setExpandedMenu((prev) => (Object.assign(Object.assign({}, prev), { [index]: !prev[index] }))); }; return (jsx(StyledMobileNavigation, { children: props.items.map((item, idx) => (jsxs(StyledMobileMenuItemContainer, { children: [jsx(NavigationLink, Object.assign({ RouterLink: props.RouterLink, to: item.to, href: item.to }, { children: jsxs(StyledMenuLink, Object.assign({ style: { display: display.flex, padding: sizes.md }, className: index === String(idx) ? 'active' : empty.string, onClick: () => toggleItem(String(idx)), isTab: true, isMobile: true, theme: props.theme }, { children: [jsx("div", { children: jsx(Typography, { fontWeight: FontWeight.Medium, color: Color.Light, size: Size.Lg, label: item.title }) }), item.menu && (jsx(StyledExpansionIcon, { src: expandedMenu[idx] ? img$1 : img$2, alt: "expansion" }))] })) })), expandedMenu[idx] && item.menu && (jsx(StyledMobileContainer, Object.assign({ isMenu: true }, { children: item.menu.map((item, subIdx) => (jsxs("div", { children: [jsx(MobileNavigationMenu, { item: item, idx: `${idx}-${subIdx}`, toggleItem: toggleItem, expandedMenu: expandedMenu, isSubmenu: Boolean(item.submenu), RouterLink: props.RouterLink }), expandedMenu[`${idx}-${subIdx}`] && item.submenu && (jsx(Fragment, { children: jsx(StyledMobileContainer, Object.assign({ isSubmenu: true }, { children: item.submenu.map((nestedItem, nestedIdx) => (jsx(MobileNavigationMenu, { item: nestedItem, idx: `${idx}-${subIdx}-${nestedIdx}-mobile-menu`, toggleItem: toggleItem, expandedMenu: expandedMenu, isSubmenu: Boolean(nestedItem.menu), RouterLink: props.RouterLink }))) })) }))] }, `${idx}-${subIdx}-mobile-submenu`))) })))] }, `${idx}-menu-item-container`))) })); } var img = "data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-chevron-down'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c/polyline%3e%3c/svg%3e"; function NavigationMenu(_a) { var props = __rest(_a, []); return (jsx(StyledFloatingMenu, { children: props.items.map((item, idx) => { var _a, _b; return (jsx(StyledMenuLink, { children: jsxs(StyledInnerListParent, Object.assign({}, props, { isMenu: true, hasSubmenu: Boolean(item.submenu) }, { children: [jsxs(NavigationLink, Object.assign({ RouterLink: props.RouterLink, to: item.to, href: item.to, style: { padding: `${sizes.lg} ${sizes.sm}`, display: display.flex, gap: 15, width: '100%', } }, { children: [item.icon && (jsx(StyledIconContainer, { children: jsx(StyledIconImage, { src: item.icon, alt: item.title }) })), jsxs(StyledSubmenuLabelContainer, { children: [jsxs(StyledLabel, { children: [item.title, (_a = item.tags) === null || _a === void 0 ? void 0 : _a.map((tag, idx) => (jsx("img", { src: tag, alt: `${idx}-tag` }, `${idx}-tag`)))] }), jsx(Typography, Object.assign({ customColor: "grey", size: Size.Sm }, { children: item.description }))] })] })), item.submenu && (jsx(StyledMenuContainer, { children: jsxs(StyledMenuGrid, { children: [jsx(StyledMenu$1, Object.assign({ isSubmenu: true, hasWidget: Boolean(item.widgets) }, { children: jsx(NavigationMenu, { isSubmenu: true, items: item.submenu, RouterLink: props.RouterLink }) })), item.widgets && (jsx(StyledWidgetContainer, { children: (_b = item.widgets) === null || _b === void 0 ? void 0 : _b.map((widget, idx) => (jsx(StyledWidgetLink, { children: jsx("a", Object.assign({ href: widget.to }, { children: jsx(StyledWidgetImage, { src: widget.image, alt: `${idx}-widget` }) })) }, `${idx}-widget-link`))) }))] }) }))] })) }, `${idx}-menu`)); }) })); } const Navigation = (_a) => { var props = __rest(_a, []); const [index, setIndex] = useState('0'); const handleIndex = (index) => { setIndex(String(index).split('-')[0]); }; return (jsx(StyledNavigationContainer, { children: props.items.map((item, idx) => (jsx(StyledMenuLink, Object.assign({ onClick: () => handleIndex(idx) }, { children: jsxs(StyledInnerListParent, Object.assign({ isTab: true, theme: props.theme, className: index === String(idx) ? 'active' : empty.string }, { children: [jsx(NavigationLink, Object.assign({ RouterLink: props.RouterLink, to: item.to, href: item.to, style: { padding: `${sizes.lg} ${sizes.sm}` } }, { children: jsxs(StyledMenuLabelContainer, Object.assign({ style: { display: display.flex } }, { children: [jsx(Typography, { fontWeight: FontWeight.Medium, color: Color.Light, size: Size.Lg, label: item.title }), item.menu && (jsx(StyledIconImage, { src: img, alt: "chevronDown", style: { width: 15 } }))] })) })), item.menu && (jsx(StyledMenuContainer, { children: jsx(StyledMenu$1, Object.assign({ isMenu: true, hasSubmenu: item.menu.every((item) => 'submenu' in item) }, { children: jsx(NavigationMenu, { items: item.menu, isMenu: true, RouterLink: props.RouterLink }) })) }))] })) }), `${idx}-menu-link`))) })); }; function Navbar(_a) { var { theme = { primary: colors.primary, secondary: colors.secondary }, RouterLink = Link } = _a, props = __rest(_a, ["theme", "RouterLink"]); const [isOpen, setIsOpen] = useState(false); const handleToggle = () => { setIsOpen(!isOpen); }; return (jsx(StyledNavbarContainer, { children: jsxs(StyledNavbar, { children: [jsx(StyledLogoContainer, Object.assign({ href: "/" }, { children: jsx(StyledLogo$1, { src: props.logo, alt: "logo" }) })), jsx(StyledHamburger, { onClick: handleToggle, src: isOpen ? img$3 : img$4, alt: "menu" }), jsxs(StyledMenuList, Object.assign({ className: `${isOpen ? 'open' : null}` }, { children: [jsx(Navigation, { items: props.links, theme: theme, RouterLink: RouterLink }), jsx(MobileNavigation, { items: props.links, theme: theme, RouterLink: RouterLink }), isOpen && (jsx(StyledSearchContainer, Object.assign({ className: `${isOpen ? 'open' : null}` }, { children: props.searchBar })))] })), jsx(StyledChildren, Object.assign({ className: `${isOpen ? 'open' :