From 56d82188c62acf1a4c8f789bc4f9246f0a0f3449 Mon Sep 17 00:00:00 2001 From: Johan Sosa Date: Thu, 11 Jan 2024 21:37:02 +0100 Subject: [PATCH] fix(TotalLayout): Container width feat(HeaderDropdown): Refactor in order to use Mantine Popover and Portals --- .../TotalLayoutContainer.js | 2 +- .../TotalLayoutContainer.styles.js | 1 + packages/components/src/misc/Logo/Logo.js | 39 +-- .../components/src/misc/Logo/Logo.styles.js | 18 +- .../HeaderDropdown.constants.js | 2 + .../common/HeaderDropdown/HeaderDropdown.js | 172 ++++++----- .../HeaderDropdown/HeaderDropdown.styles.js | 278 +++++++++--------- 7 files changed, 249 insertions(+), 263 deletions(-) diff --git a/packages/components/src/layout/TotalLayout/TotalLayoutContainer/TotalLayoutContainer.js b/packages/components/src/layout/TotalLayout/TotalLayoutContainer/TotalLayoutContainer.js index ebfaa0b5f..251de8f36 100644 --- a/packages/components/src/layout/TotalLayout/TotalLayoutContainer/TotalLayoutContainer.js +++ b/packages/components/src/layout/TotalLayout/TotalLayoutContainer/TotalLayoutContainer.js @@ -33,7 +33,7 @@ const TotalLayoutContainer = ({ Header, Footer, scrollRef, children }) => { }, [scrollRef?.current, handleScroll]); return ( - + {isFunction(Header) ?
: Header} diff --git a/packages/components/src/layout/TotalLayout/TotalLayoutContainer/TotalLayoutContainer.styles.js b/packages/components/src/layout/TotalLayout/TotalLayoutContainer/TotalLayoutContainer.styles.js index 91a7ce8e8..e0b4d773c 100644 --- a/packages/components/src/layout/TotalLayout/TotalLayoutContainer/TotalLayoutContainer.styles.js +++ b/packages/components/src/layout/TotalLayout/TotalLayoutContainer/TotalLayoutContainer.styles.js @@ -4,6 +4,7 @@ const TotalLayoutContainerStyles = createStyles((theme, { topScroll }) => ({ root: { height: '100vh', backgroundColor: theme.other.core.color.neutral['50'], + width: '100%', }, header: { boxShadow: topScroll && '0 8px 24px rgba(149, 157, 165, 0.2)', diff --git a/packages/components/src/misc/Logo/Logo.js b/packages/components/src/misc/Logo/Logo.js index 24b8b20c6..8c467eec7 100644 --- a/packages/components/src/misc/Logo/Logo.js +++ b/packages/components/src/misc/Logo/Logo.js @@ -5,51 +5,28 @@ import { LogoStyles } from './Logo.styles'; export const LOGO_VARIANTS = ['positive', 'negative']; export const Logo = ({ variant = LOGO_VARIANTS[0], isotype, className }) => { - const width = isotype ? '25' : '200'; + const width = isotype ? '110' : '387'; const { classes, cx } = LogoStyles({}); return ( - - - - - - diff --git a/packages/components/src/misc/Logo/Logo.styles.js b/packages/components/src/misc/Logo/Logo.styles.js index 45e87da5e..600b02c10 100644 --- a/packages/components/src/misc/Logo/Logo.styles.js +++ b/packages/components/src/misc/Logo/Logo.styles.js @@ -1,12 +1,10 @@ import { createStyles } from '@mantine/styles'; -export const LogoStyles = createStyles((theme, {}, getRef) => { - return { - positive: { - color: theme.colors.text01, - }, - negative: { - color: theme.colors.text07, - }, - }; -}); +export const LogoStyles = createStyles((theme, {}, getRef) => ({ + positive: { + color: '#0D2023', + }, + negative: { + color: 'white', + }, +})); diff --git a/packages/leemons/src/common/HeaderDropdown/HeaderDropdown.constants.js b/packages/leemons/src/common/HeaderDropdown/HeaderDropdown.constants.js index 55c7da761..bcfbc3228 100644 --- a/packages/leemons/src/common/HeaderDropdown/HeaderDropdown.constants.js +++ b/packages/leemons/src/common/HeaderDropdown/HeaderDropdown.constants.js @@ -4,6 +4,7 @@ export const HEADER_DROPDOWN_DEFAULT_PROPS = { data: [], placeholder: '', readOnly: false, + withSearchInput: false, }; export const HEADER_DROPDOWN_PROP_TYPES = { data: PropTypes.arrayOf(PropTypes.object), @@ -13,4 +14,5 @@ export const HEADER_DROPDOWN_PROP_TYPES = { value: PropTypes.shape({ id: PropTypes.string }), readOnly: PropTypes.bool, onChange: PropTypes.func, + withSearchInput: PropTypes.bool, }; diff --git a/packages/leemons/src/common/HeaderDropdown/HeaderDropdown.js b/packages/leemons/src/common/HeaderDropdown/HeaderDropdown.js index b95621107..1d6c0e43a 100644 --- a/packages/leemons/src/common/HeaderDropdown/HeaderDropdown.js +++ b/packages/leemons/src/common/HeaderDropdown/HeaderDropdown.js @@ -1,4 +1,4 @@ -import React, { useEffect, useRef, useState } from 'react'; +import React, { useEffect, useState } from 'react'; import { ActionButton, Box, @@ -7,21 +7,21 @@ import { Text, TextClamp, } from '@bubbles-ui/components'; +import { Popover } from '@mantine/core'; import { isEmpty, isFunction, isNil } from 'lodash'; import { useClickOutside } from '@mantine/hooks'; +import { CheckIcon, ChevDownIcon, ChevUpIcon } from '@bubbles-ui/icons/outline'; import { HeaderDropdownStyles } from './HeaderDropdown.styles'; import { HEADER_DROPDOWN_DEFAULT_PROPS, HEADER_DROPDOWN_PROP_TYPES, } from './HeaderDropdown.constants'; -import { CheckIcon, ChevDownIcon, ChevUpIcon } from '@bubbles-ui/icons/outline'; -const normalizeString = (string) => { - return string +const normalizeString = (string) => + string ?.toLowerCase() .normalize('NFD') .replace(/[\u0300-\u036f]/g, ''); -}; const HeaderDropdown = ({ data, @@ -31,7 +31,7 @@ const HeaderDropdown = ({ value, readOnly, onChange, - withSearchInput = true, + withSearchInput, ...props }) => { const [isOpened, setIsOpened] = useState(false); @@ -40,11 +40,18 @@ const HeaderDropdown = ({ const [selectedItem, setSelectedItem] = useState( data.find((item) => item?.id === value?.id) || data[0] || {}, ); - const headerRef = useRef(null); + + const { classes, cx } = HeaderDropdownStyles( + { + withSearchInput, + hasDescription: !isEmpty(selectedItem?.description), + }, + { name: 'HeaderDropdown' }, + ); const onChangeHandler = (item) => { setSelectedItem(item); - isFunction(onChange) && onChange(item); + if (isFunction(onChange)) onChange(item); setIsOpened(false); }; @@ -53,7 +60,7 @@ const HeaderDropdown = ({ const itemLabel = normalizeString(item?.label); const itemDescription = normalizeString(item?.description); const filterValue = normalizeString(filter); - return itemLabel.includes(filterValue) || itemDescription.includes(filterValue); + return itemLabel?.includes(filterValue) || itemDescription?.includes(filterValue); }); return itemListToReturn.map((item, index) => @@ -112,78 +119,91 @@ const HeaderDropdown = ({ setSelectedItem(data.find((item) => item?.id === value?.id) || data[0] || {}); }, [JSON.stringify(value)]); - const { classes, cx } = HeaderDropdownStyles( - { - isOpened, - headerRef, - withSearchInput, - }, - { name: 'HeaderDropdown' }, - ); - return ( - - {valueComponent ? ( - React.cloneElement(valueComponent, [...selectedItem]) - ) : ( - - {!isNil(selectedItem?.image) && - !isEmpty(selectedItem?.image) && - !selectedItem?.showIcon ? ( - - ) : null} - {!isNil(selectedItem?.icon) && - !isEmpty(selectedItem?.icon) && - !isNil(selectedItem?.color) && - !isEmpty(selectedItem?.color) && - selectedItem?.showIcon ? ( - - + setIsOpened(false)} + onChange={setIsOpened} + position="bottom-start" + shadow="lg" + withinPortal + classNames={{ dropdown: classes.dropDown }} + > + + + {valueComponent ? ( + React.cloneElement(valueComponent, [...selectedItem]) + ) : ( + + {!isNil(selectedItem?.image) && + !isEmpty(selectedItem?.image) && + !selectedItem?.showIcon ? ( + + ) : null} + {!isNil(selectedItem?.icon) && + !isEmpty(selectedItem?.icon) && + !isNil(selectedItem?.color) && + !isEmpty(selectedItem?.color) && + selectedItem?.showIcon ? ( + + + + ) : null} + + + {selectedItem?.label} + + {selectedItem?.description ? ( + {selectedItem?.description} + ) : null} + - ) : null} - - - {selectedItem?.label} - - {classes.description ? ( - {selectedItem?.description} - ) : null} - + )} + {!readOnly && ( + + + ) : ( + + ) + } + onClick={() => setIsOpened(!isOpened)} + color="primary" + active={isOpened} + /> + + )} - )} - {!readOnly && ( - - ) : ( - - ) - } - onClick={() => setIsOpened(!isOpened)} - color="primary" - active={isOpened} - /> - )} - - {!readOnly && ( - - {withSearchInput ? ( - - - - ) : null} + - {loadItemList()} - - )} + + + {withSearchInput ? ( + setIsOpened(true)} + onBlurCapture={() => setIsOpened(false)} + > + + + ) : null} + + {loadItemList()} + + + ); }; diff --git a/packages/leemons/src/common/HeaderDropdown/HeaderDropdown.styles.js b/packages/leemons/src/common/HeaderDropdown/HeaderDropdown.styles.js index e6fb24d39..37dc096f2 100644 --- a/packages/leemons/src/common/HeaderDropdown/HeaderDropdown.styles.js +++ b/packages/leemons/src/common/HeaderDropdown/HeaderDropdown.styles.js @@ -1,147 +1,135 @@ import { createStyles, getFontExpressive } from '@bubbles-ui/components'; -export const HeaderDropdownStyles = createStyles( - (theme, { isOpened, headerRef, withSearchInput }) => { - const { height, top } = headerRef?.current?.getBoundingClientRect() || { height: 0, top: 0 }; - const headerHeight = height + top || 0; - const globalTheme = theme.other.global; - return { - root: { - ...getFontExpressive(theme.fontSizes['2']), - width: '100%', - position: 'relative', - }, - header: { - display: 'flex', - alignItems: 'center', - }, - dropDown: { - position: 'absolute', - top: 50, - right: 0, - left: 0, - backgroundColor: theme.colors.mainWhite, - opacity: isOpened ? 1 : 0, - transform: isOpened ? 'translateY(0)' : 'translateY(-10px)', - transition: 'opacity 0.2s ease-in-out, transform 0.2s ease-in-out', - zIndex: 200, - pointerEvents: isOpened ? 'all' : 'none', - paddingBottom: 20, - width: 'max(400px, 100%)', - maxHeight: 300, - maxWidth: 360, - border: '1px solid #C2CAD6', - boxShadow: - '0px 10px 36px 0px rgba(26, 32, 43, 0.12), 0px 2px 0px 0px rgba(217, 225, 237, 0.16)', - borderRadius: 4, - overflow: 'hidden', - }, - itemList: { - padding: 16, - paddingBottom: 4, - display: 'flex', - flexDirection: 'column', - overflowY: 'auto', - overflowX: 'hidden', - maxHeight: withSearchInput ? 230 : 290, - }, - searchInput: { - padding: 16, - paddingBottom: 0, - }, - dropDownIcon: { - // color: theme.colors.text01, - marginLeft: 16, - alignSelf: 'baseline', - }, - valueComponent: { - display: 'flex', - gap: 16, - alignItems: 'center', - }, - itemComponent: { - justifyContent: 'space-between', - display: 'flex', - gap: 8, - alignItems: 'center', - cursor: 'pointer', - padding: 8, - borderRadius: 4, - }, - itemComponentSelected: { - justifyContent: 'space-between', - display: 'flex', - gap: 8, - alignItems: 'center', - cursor: 'pointer', - padding: 8, - borderRadius: 4, - backgroundColor: '#E7F7B0', - }, - itemComponentContent: { - display: 'flex', - gap: 8, - alignItems: 'center', - }, - itemComponentIcon: { - color: '#5CBC6A', - fontSize: 14, - }, - itemComponentLabel: { - fontFamily: 'Albert Sans', - fontSize: '16px', - color: '#2F463F', - fontWeight: 400, - }, - itemComponentDescription: { - fontFamily: 'Albert Sans', - fontSize: '13px', - fontWeight: 400, - color: '#4D5358', - }, - itemIcon: { - borderRadius: '50%', - display: 'flex', - justifyContent: 'center', - alignItems: 'center', - img: { - filter: 'brightness(0) invert(1)', - }, - height: 48, - width: 48, - }, - itemIconSmall: { - borderRadius: '50%', - display: 'flex', - justifyContent: 'center', - alignItems: 'center', - img: { - filter: 'brightness(0) invert(1)', - }, - height: 32, - width: 32, - }, - content: { - display: 'flex', - flexDirection: 'column', - gap: 4, - }, - valueItemContent: { - display: 'flex', - flexDirection: 'column', - }, - title: { - fontFamily: 'Albert Sans', - fontSize: '18px', - fontWeight: 600, - color: '#2F463F', - }, - description: { - fontFamily: 'Albert Sans', - fontSize: '14px', - fontWeight: 400, - color: '#4D5358', - }, - }; - }, -); +const HeaderDropdownStyles = createStyles((theme, { withSearchInput, hasDescription }) => ({ + root: { + ...getFontExpressive(theme.fontSizes['2']), + width: '100%', + position: 'relative', + }, + header: { + display: 'flex', + alignItems: hasDescription ? 'flex-start' : 'center', + }, + dropDown: { + // marginTop: 10, + padding: 20, + paddingRight: 0, + zIndex: 999, + }, + dropDownContent: { + // padding: 10, + // paddingRight: 0, + maxHeight: 300, + maxWidth: 360, + }, + itemList: { + // padding: 16, + // paddingBottom: 4, + paddingRight: 20, + display: 'flex', + flexDirection: 'column', + overflowY: 'auto', + overflowX: 'hidden', + maxHeight: withSearchInput ? 230 : 290, + }, + searchInput: { + // padding: 16, + paddingRight: 20, + paddingBottom: 20, + }, + dropDownIcon: { + // color: theme.colors.text01, + marginLeft: 16, + alignSelf: 'baseline', + }, + valueComponent: { + display: 'flex', + gap: 16, + alignItems: 'center', + }, + itemComponent: { + justifyContent: 'space-between', + display: 'flex', + gap: 8, + alignItems: 'center', + cursor: 'pointer', + padding: 8, + borderRadius: 4, + }, + itemComponentSelected: { + justifyContent: 'space-between', + display: 'flex', + gap: 8, + alignItems: 'center', + cursor: 'pointer', + padding: 8, + borderRadius: 4, + backgroundColor: '#E7F7B0', + }, + itemComponentContent: { + display: 'flex', + gap: 8, + alignItems: 'center', + }, + itemComponentIcon: { + color: '#5CBC6A', + fontSize: 14, + }, + itemComponentLabel: { + fontFamily: 'Albert Sans', + fontSize: '16px', + color: '#2F463F', + fontWeight: 400, + }, + itemComponentDescription: { + fontFamily: 'Albert Sans', + fontSize: '13px', + fontWeight: 400, + color: '#4D5358', + }, + itemIcon: { + borderRadius: '50%', + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + img: { + filter: 'brightness(0) invert(1)', + }, + height: 48, + width: 48, + }, + itemIconSmall: { + borderRadius: '50%', + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + img: { + filter: 'brightness(0) invert(1)', + }, + height: 32, + width: 32, + }, + content: { + display: 'flex', + flexDirection: 'column', + gap: 4, + }, + valueItemContent: { + display: 'flex', + flexDirection: 'column', + }, + title: { + fontFamily: 'Albert Sans', + fontSize: '18px', + fontWeight: 600, + color: '#2F463F', + }, + description: { + fontFamily: 'Albert Sans', + fontSize: '14px', + fontWeight: 400, + color: '#4D5358', + }, +})); + +export { HeaderDropdownStyles };