diff --git a/package.json b/package.json index 7d7c9931..961abaf8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@qoretechnologies/reqore", - "version": "0.48.18", + "version": "0.48.19", "description": "ReQore is a highly theme-able and modular UI library for React", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/src/components/Button/index.tsx b/src/components/Button/index.tsx index 002f3abc..e27b2bda 100644 --- a/src/components/Button/index.tsx +++ b/src/components/Button/index.tsx @@ -133,6 +133,13 @@ const getButtonMainColor = ( return theme.main; }; +const getButtonTextColor = ({ minimal, transparent, color, theme }) => + minimal || transparent + ? isAchromatic(color) + ? getReadableColorFrom(theme.originalMain, true) + : saturate(1, tint(0.8, color)) + : getReadableColorFrom(color, true); + export const StyledAnimatedTextWrapper = styled.span` min-width: 5px; overflow: hidden; @@ -208,11 +215,7 @@ export const StyledButton = styled(StyledEffect)` color: ${({ color, minimal, transparent, theme, effect }) => { const finalColor = getButtonMainColor(theme, color, effect); - return minimal || transparent - ? isAchromatic(finalColor) - ? getReadableColorFrom(theme.originalMain, true) - : saturate(1, tint(0.8, finalColor)) - : getReadableColorFrom(finalColor, true); + return getButtonTextColor({ minimal, transparent, color: finalColor, theme }); }}; ${InactiveIconScale}; @@ -324,6 +327,8 @@ export const StyledButton = styled(StyledEffect)` .reqore-button-description { padding-top: ${({ size }) => PADDING_FROM_SIZE[size] / 2}px; + color: ${({ theme, color, minimal, transparent }) => + rgba(getButtonTextColor({ minimal, transparent, color, theme }), 0.7)}; } `; @@ -377,23 +382,39 @@ export const ButtonBadge = memo( const content = Array.isArray(props.content) ? props.content : [props.content]; - const leftBadges = content.filter( - (badge) => typeof badge === 'string' || typeof badge === 'number' || !badge?.align + const leftBadges = useMemo( + () => + content.filter( + (badge) => typeof badge === 'string' || typeof badge === 'number' || !badge?.align + ), + [content] ); - const rightBadges = content.filter( - (badge) => typeof badge !== 'string' && typeof badge !== 'number' && badge?.align === 'right' + + const rightBadges = useMemo( + () => + content.filter( + (badge) => + typeof badge !== 'string' && typeof badge !== 'number' && badge?.align === 'right' + ), + [content] ); - const middleBadges = content.filter( - (badge) => typeof badge !== 'string' && typeof badge !== 'number' && badge?.align === 'center' + + const middleBadges = useMemo( + () => + content.filter( + (badge) => + typeof badge !== 'string' && typeof badge !== 'number' && badge?.align === 'center' + ), + [content] ); - const buildContent = (badge: TReqoreBadge) => { + const buildContent = useCallback((badge: TReqoreBadge) => { if (typeof badge === 'string' || typeof badge === 'number') { return { label: badge, align: undefined }; } return { ...badge, align: undefined }; - }; + }, []); return ( <> @@ -480,15 +501,11 @@ const ReqoreButton = memo( useTooltip(buttonRef, tooltip); // If color or intent was specified, set the color - const customColor = intent ? theme.main : changeLightness(theme.main, 0.07); + const customColor = useMemo( + () => (intent ? theme.main : changeLightness(theme.main, 0.07)), + [intent, theme.main] + ); const _flat = minimal ? flat : flat === true; - - const color: TReqoreHexColor = customColor - ? minimal - ? getReadableColor(theme, undefined, undefined, true, theme.originalMain) - : getReadableColorFrom(customColor, true) - : getReadableColor(theme, undefined, undefined, true); - const _children = useMemo(() => label || children, [label, children]); const _compact = compact ?? theme.buttons?.compact ?? !children; const hasLeftIcon = icon || leftIconProps?.image; @@ -684,7 +701,6 @@ const ReqoreButton = memo( effect={{ textSize: getOneLessSize(size), weight: 'light', - color: `${color}90`, textAlign, ...descriptionEffect, }} diff --git a/src/components/Collection/index.tsx b/src/components/Collection/index.tsx index 08f77836..b6668b25 100644 --- a/src/components/Collection/index.tsx +++ b/src/components/Collection/index.tsx @@ -1,5 +1,5 @@ import { map, size } from 'lodash'; -import { useCallback, useMemo, useState } from 'react'; +import { memo, useCallback, useMemo, useState } from 'react'; import { useUpdateEffect } from 'react-use'; import styled, { css } from 'styled-components'; import { useReqoreProperty } from '../..'; @@ -90,333 +90,339 @@ export const zoomToWidth = { 2: '600px', }; -export const ReqoreCollection = ({ - items, - stacked, - rounded = true, - fill, - maxItemHeight, - filterable, - inputInTitle = true, - - sortable, - zoomable, - - defaultZoom, - showSelectedFirst, - inputProps, - sortButtonProps, - displayButtonProps, - headerSize = 2, - showAs = 'grid', - selectedIcon, - flat = true, - minimal, - transparent = true, - - defaultQuery, - defaultSort = 'asc', - defaultSortBy = 'label', - - sortKeys = {}, - - onQueryChange, - contentRenderer = (children, _items, searchInput) => ( - <> - {searchInput} - {!!searchInput && } - {children} - - ), - searchDelay = 300, - emptyMessage = 'No data in this collection, try changing your search query or filters', - sortButtonTooltip = (sort) => (sort === 'desc' ? 'Sort ascending' : 'Sort descending'), - displayButtonTooltip = (display) => (display === 'grid' ? 'Show as list' : 'Show as grid'), - inputPlaceholder = (items) => `Search in ${size(items)} items`, - paging, - height, - - // Columns props - minColumnWidth, - maxColumnWidth, - columns, - alignItems, - columnsGap = '10px', - - ...rest -}: IReqoreCollectionProps) => { - const [_showAs, setShowAs] = useState<'list' | 'grid'>(showAs); - const [sort, setSort] = useState<'asc' | 'desc'>(defaultSort); - const [sortBy, setSortBy] = useState(defaultSortBy); - const [contentRef, setContentRef] = useState(undefined); - const isMobile = useReqoreProperty('isMobile'); - const [zoom, setZoom] = useState(defaultZoom || sizeToZoom.normal); - - const { query, setQuery, preQuery, setPreQuery } = useQueryWithDelay( +export const ReqoreCollection = memo( + ({ + items, + stacked, + rounded = true, + fill, + maxItemHeight, + filterable, + inputInTitle = true, + + sortable, + zoomable, + + defaultZoom, + showSelectedFirst, + inputProps, + sortButtonProps, + displayButtonProps, + headerSize = 2, + showAs = 'grid', + selectedIcon, + flat = true, + minimal, + transparent = true, + defaultQuery, - searchDelay, - onQueryChange - ); - - useUpdateEffect(() => { - setShowAs(showAs); - }, [showAs]); - - const sortedItems: IReqoreCollectionItemProps[] = useMemo(() => { - if (!sortable) { - return items; - } - - const _sortBy = !!sortKeys[sortBy] ? (v) => v?.metadata?.[sortBy] : sortBy; - - if (showSelectedFirst) { - // Filter out the selected items - const selectedItems = items.filter((item) => item.selected); - // Filter out the unselected items - const unselectedItems = items.filter((item) => !item.selected); - // Sort the selected items - const sortedSelectedItems = sortTableData(selectedItems, { - by: _sortBy, - direction: sort, - }); - // Sort the unselected items - const sortedUnselectedItems = sortTableData(unselectedItems, { + defaultSort = 'asc', + defaultSortBy = 'label', + + sortKeys = {}, + + onQueryChange, + contentRenderer = (children, _items, searchInput) => ( + <> + {searchInput} + {!!searchInput && } + {children} + + ), + searchDelay = 300, + emptyMessage = 'No data in this collection, try changing your search query or filters', + sortButtonTooltip = (sort) => (sort === 'desc' ? 'Sort ascending' : 'Sort descending'), + displayButtonTooltip = (display) => (display === 'grid' ? 'Show as list' : 'Show as grid'), + inputPlaceholder = (items) => `Search in ${size(items)} items`, + paging, + height, + + // Columns props + minColumnWidth, + maxColumnWidth, + columns, + alignItems, + columnsGap = '10px', + + ...rest + }: IReqoreCollectionProps) => { + const [_showAs, setShowAs] = useState<'list' | 'grid'>(showAs); + const [sort, setSort] = useState<'asc' | 'desc'>(defaultSort); + const [sortBy, setSortBy] = useState(defaultSortBy); + const [contentRef, setContentRef] = useState(undefined); + const isMobile = useReqoreProperty('isMobile'); + const [zoom, setZoom] = useState(defaultZoom || sizeToZoom.normal); + + const { query, setQuery, preQuery, setPreQuery } = useQueryWithDelay( + defaultQuery, + searchDelay, + onQueryChange + ); + + useUpdateEffect(() => { + setShowAs(showAs); + }, [showAs]); + + const sortedItems: IReqoreCollectionItemProps[] = useMemo(() => { + if (!sortable) { + return items; + } + + const _sortBy = !!sortKeys[sortBy] ? (v) => v?.metadata?.[sortBy] : sortBy; + + if (showSelectedFirst) { + // Filter out the selected items + const selectedItems = items.filter((item) => item.selected); + // Filter out the unselected items + const unselectedItems = items.filter((item) => !item.selected); + // Sort the selected items + const sortedSelectedItems = sortTableData(selectedItems, { + by: _sortBy, + direction: sort, + }); + // Sort the unselected items + const sortedUnselectedItems = sortTableData(unselectedItems, { + by: _sortBy, + direction: sort, + }); + + return [...sortedSelectedItems, ...sortedUnselectedItems]; + } + + return sortTableData(items, { by: _sortBy, direction: sort, }); + }, [items, sort, sortBy, showSelectedFirst, sortable]); - return [...sortedSelectedItems, ...sortedUnselectedItems]; - } + const filteredItems: IReqoreCollectionItemProps[] = useMemo(() => { + if (!filterable || query === '') { + return sortedItems; + } + + return sortedItems.filter((item) => { + const text = `${item.label}${item.content?.toString()}${item.expandedContent?.toString()}${ + item.searchString || '' + }`; - return sortTableData(items, { - by: _sortBy, - direction: sort, - }); - }, [items, sort, sortBy, showSelectedFirst, sortable]); + if (!text) { + return false; + } - const filteredItems: IReqoreCollectionItemProps[] = useMemo(() => { - if (!filterable || query === '') { - return sortedItems; - } + return text.toString().toLowerCase().indexOf(query.toLowerCase()) !== -1; + }); + }, [items, query, sortedItems]); - return sortedItems.filter((item) => { - const text = `${item.label}${item.content?.toString()}${item.expandedContent?.toString()}${ - item.searchString || '' - }`; + const handlePreQueryChange = useCallback((event: React.ChangeEvent) => { + setPreQuery(event.target.value); + }, []); - if (!text) { - return false; - } + const handlePreQueryClearClick = useCallback(() => { + setQuery(''); + setPreQuery(''); + }, []); - return text.toString().toLowerCase().indexOf(query.toLowerCase()) !== -1; - }); - }, [items, query, sortedItems]); - - const handlePreQueryChange = (event: React.ChangeEvent) => { - setPreQuery(event.target.value); - }; - - const finalActions: TReqorePanelActions = useMemo(() => { - let actions: TReqorePanelActions = rest.actions ? [...rest.actions] : []; - - const toolbarGroup: IReqorePanelAction = { - responsive: false, - icon: 'MoreLine', - className: 'reqore-collection-more', - actions: [ - { - icon: _showAs === 'grid' ? 'ListUnordered' : 'GridLine', - onClick: () => setShowAs(_showAs === 'grid' ? 'list' : 'grid'), - tooltip: displayButtonTooltip(_showAs), - label: displayButtonTooltip(_showAs), - disabled: !size(filteredItems), - ...displayButtonProps, - }, - ], - }; - - if (zoomable) { - toolbarGroup.actions = [ - ...toolbarGroup.actions, - { divider: true, line: true }, - ...getZoomActions('reqore-collection', zoom, setZoom, true), - ]; - } - - if (filterable && inputInTitle) { - actions = [ - ...actions, - { - as: ReqoreInput, - props: { - key: 'search', - fixed: false, - placeholder: inputPlaceholder(items), - onClearClick: () => { - setQuery(''); - setPreQuery(''); - }, - onChange: handlePreQueryChange, - value: preQuery, - icon: 'Search2Line', - minimal: false, - ...inputProps, - }, - }, - ]; - } - - if (sortable) { - const _sortKeys = { - label: 'Label', - intent: 'Intent', - ...sortKeys, - }; + const finalActions: TReqorePanelActions = useMemo(() => { + let actions: TReqorePanelActions = rest.actions ? [...rest.actions] : []; - actions.push({ - icon: sort === 'desc' ? 'SortDesc' : 'SortAsc', - tooltip: sortButtonTooltip(sort), - className: 'reqore-collection-sort', - fixed: true, - fluid: false, - disabled: !size(filteredItems), + const toolbarGroup: IReqorePanelAction = { + responsive: false, + icon: 'MoreLine', + className: 'reqore-collection-more', actions: [ { - divider: true, - label: 'Sort by', - dividerAlign: 'left', - minimal: true, - dividerPadded: 'bottom', + icon: _showAs === 'grid' ? 'ListUnordered' : 'GridLine', + onClick: () => setShowAs(_showAs === 'grid' ? 'list' : 'grid'), + tooltip: displayButtonTooltip(_showAs), + label: displayButtonTooltip(_showAs), + disabled: !size(filteredItems), + ...displayButtonProps, }, - ...map( - _sortKeys, - (label, key): IReqorePanelSubAction => ({ - label, - selected: sortBy === key, - onClick: () => { - setSortBy(key); - setSort(sort === 'desc' ? 'asc' : 'desc'); - }, - }) - ), ], - ...sortButtonProps, - }); - } + }; - return [...actions, toolbarGroup]; - }, [ - filterable, - preQuery, - query, - rest.actions, - _showAs, - sort, - sortable, - filteredItems, - isMobile, - zoom, - zoomable, - ]); - - const renderContent = useCallback(() => { - return contentRenderer( - - {(_items, _children, { applyPaging }) => - !size(applyPaging(filteredItems)) ? ( - - {emptyMessage} - - ) : ( - - {applyPaging(filteredItems)?.map((item, index) => ( - - ))} - - ) - } - , + if (zoomable) { + toolbarGroup.actions = [ + ...toolbarGroup.actions, + { divider: true, line: true }, + ...getZoomActions('reqore-collection', zoom, setZoom, true), + ]; + } + + if (filterable && inputInTitle) { + actions = [ + ...actions, + { + as: ReqoreInput, + props: { + key: 'search', + fixed: false, + placeholder: inputPlaceholder(items), + onClearClick: () => { + setQuery(''); + setPreQuery(''); + }, + onChange: handlePreQueryChange, + value: preQuery, + icon: 'Search2Line', + minimal: false, + ...inputProps, + }, + }, + ]; + } + + if (sortable) { + const _sortKeys = { + label: 'Label', + intent: 'Intent', + ...sortKeys, + }; + + actions.push({ + icon: sort === 'desc' ? 'SortDesc' : 'SortAsc', + tooltip: sortButtonTooltip(sort), + className: 'reqore-collection-sort', + fixed: true, + fluid: false, + disabled: !size(filteredItems), + actions: [ + { + divider: true, + label: 'Sort by', + dividerAlign: 'left', + minimal: true, + dividerPadded: 'bottom', + }, + ...map( + _sortKeys, + (label, key): IReqorePanelSubAction => ({ + label, + selected: sortBy === key, + onClick: () => { + setSortBy(key); + setSort(sort === 'desc' ? 'asc' : 'desc'); + }, + }) + ), + ], + ...sortButtonProps, + }); + } + + return [...actions, toolbarGroup]; + }, [ + filterable, + preQuery, + query, + rest.actions, + _showAs, + sort, + sortable, filteredItems, - !inputInTitle && filterable ? ( - - { - setQuery(''); - setPreQuery(''); - }} - onChange={handlePreQueryChange} - value={preQuery} - icon='Search2Line' - minimal={false} - size={rest.size} - {...inputProps} - /> - - ) : undefined - ); - }, [ - contentRenderer, - filteredItems, - _showAs, - columnsGap, - emptyMessage, - maxItemHeight, - rest, - rounded, - selectedIcon, - stacked, - zoom, - ]); - - return ( - { + return contentRenderer( + + {(_items, _children, { applyPaging }) => + !size(applyPaging(filteredItems)) ? ( + + {emptyMessage} + + ) : ( + + {applyPaging(filteredItems)?.map((item, index) => ( + + ))} + + ) + } + , + filteredItems, + !inputInTitle && filterable ? ( + + + + ) : undefined + ); + }, [ + contentRenderer, + filteredItems, + _showAs, + columnsGap, + emptyMessage, + maxItemHeight, + rest, + rounded, + selectedIcon, + stacked, + zoom, + ]); + + const contentStyle = useMemo( + () => ({ overflow: fill ? 'hidden' : 'auto', display: 'flex', flexFlow: 'column', - }} - transparent={transparent} - minimal={minimal} - flat={flat} - actions={finalActions} - className={`reqore-collection ${rest.className || ''}`} - > - {renderContent()} - - ); -}; + }), + [fill] + ); + + return ( + + {renderContent()} + + ); + } +); diff --git a/src/components/Columns/index.tsx b/src/components/Columns/index.tsx index 7e25acee..e843450f 100644 --- a/src/components/Columns/index.tsx +++ b/src/components/Columns/index.tsx @@ -1,4 +1,5 @@ import classNames from 'classnames'; +import { memo } from 'react'; import styled, { css } from 'styled-components'; import { IReqoreTheme } from '../../constants/theme'; @@ -32,10 +33,10 @@ export const StyledColumns = styled.div` `} `; -export const ReqoreColumns = ({ children, className, ...rest }: IReqoreColumnsProps) => { +export const ReqoreColumns = memo(({ children, className, ...rest }: IReqoreColumnsProps) => { return ( {children} ); -}; +}); diff --git a/src/components/Drawer/index.tsx b/src/components/Drawer/index.tsx index c68ded7a..e2007069 100644 --- a/src/components/Drawer/index.tsx +++ b/src/components/Drawer/index.tsx @@ -1,6 +1,6 @@ import { animated, useTransition } from '@react-spring/web'; import { Resizable } from 're-resizable'; -import { useEffect, useMemo, useState } from 'react'; +import { memo, useEffect, useMemo, useState } from 'react'; import { createPortal } from 'react-dom'; import styled, { css } from 'styled-components'; import { useReqoreProperty } from '../..'; @@ -148,239 +148,248 @@ const getSpringConfig = (isModal?: boolean, position?: TPosition, floating?: boo leave: { opacity: 0, [position]: '-80px' }, }; -export const ReqoreDrawer: React.FC = ({ - children, - isOpen, - isHidden, - customTheme, - position = 'right', - maxSize, - minSize = '150px', - onClose, - hasBackdrop = true, - size, - resizable = true, - hidable, - onHideToggle, - className, - flat, - floating, - blur, - opacity, - intent, - _isModal, - width, - height, - actions = [], - customZIndex, - panelSize, - ...rest -}: IReqoreDrawerProps) => { - const animations = useReqoreProperty('animations'); - const getAndIncreaseZIndex = useReqoreProperty('getAndIncreaseZIndex'); - const theme = useReqoreTheme('main', customTheme, intent); - const layout = useMemo( - () => - _isModal ? 'center' : position === 'top' || position === 'bottom' ? 'horizontal' : 'vertical', - [position, _isModal] - ); - const [_isHidden, setIsHidden] = useState(isHidden || false); - const [_size, setSize] = useState({ - width: width || (layout === 'horizontal' ? 'auto' : size || '300px'), - height: height || (layout === 'vertical' ? 'auto' : size || '300px'), - }); - - useEffect(() => { - setSize({ +export const ReqoreDrawer: React.FC = memo( + ({ + children, + isOpen, + isHidden, + customTheme, + position = 'right', + maxSize, + minSize = '150px', + onClose, + hasBackdrop = true, + size, + resizable = true, + hidable, + onHideToggle, + className, + flat, + floating, + blur, + opacity, + intent, + _isModal, + width, + height, + actions = [], + customZIndex, + panelSize, + ...rest + }: IReqoreDrawerProps) => { + const animations = useReqoreProperty('animations'); + const getAndIncreaseZIndex = useReqoreProperty('getAndIncreaseZIndex'); + const theme = useReqoreTheme('main', customTheme, intent); + const layout = useMemo( + () => + _isModal + ? 'center' + : position === 'top' || position === 'bottom' + ? 'horizontal' + : 'vertical', + [position, _isModal] + ); + const [_isHidden, setIsHidden] = useState(isHidden || false); + const [_size, setSize] = useState({ width: width || (layout === 'horizontal' ? 'auto' : size || '300px'), height: height || (layout === 'vertical' ? 'auto' : size || '300px'), }); - }, [position, size, width, height]); - const transitions = useTransition(isOpen, { - ...getSpringConfig(_isModal, position, floating), - config: animations.dialogs ? SPRING_CONFIG : SPRING_CONFIG_NO_ANIMATIONS, - }); + useEffect(() => { + setSize({ + width: width || (layout === 'horizontal' ? 'auto' : size || '300px'), + height: height || (layout === 'vertical' ? 'auto' : size || '300px'), + }); + }, [position, size, width, height]); - const zIndex = useMemo(() => customZIndex || getAndIncreaseZIndex(), [customZIndex]); - const wrapperZIndex = useMemo(() => customZIndex + 1 || getAndIncreaseZIndex(), [customZIndex]); - const _actions: IReqorePanelAction[] = useMemo(() => { - const builtActions: IReqorePanelAction[] = [...actions]; + const transitions = useTransition(isOpen, { + ...getSpringConfig(_isModal, position, floating), + config: animations.dialogs ? SPRING_CONFIG : SPRING_CONFIG_NO_ANIMATIONS, + }); - /* Adding a hide/show button to the drawer. */ - if (hidable) { - builtActions.push({ - responsive: false, - icon: getHideShowIcon(position, _isHidden), - onClick: () => { - setIsHidden(!_isHidden); + const zIndex = useMemo(() => customZIndex || getAndIncreaseZIndex(), [customZIndex]); + const wrapperZIndex = useMemo(() => customZIndex + 1 || getAndIncreaseZIndex(), [customZIndex]); + const _actions: IReqorePanelAction[] = useMemo(() => { + const builtActions: IReqorePanelAction[] = [...actions]; - if (onHideToggle) { - onHideToggle(!_isHidden); - } - }, - className: 'reqore-drawer-hide-button', - }); - } + /* Adding a hide/show button to the drawer. */ + if (hidable) { + builtActions.push({ + responsive: false, + icon: getHideShowIcon(position, _isHidden), + onClick: () => { + setIsHidden(!_isHidden); + + if (onHideToggle) { + onHideToggle(!_isHidden); + } + }, + className: 'reqore-drawer-hide-button', + }); + } - return builtActions; - }, [hidable, position, onClose, actions, _isHidden]); + return builtActions; + }, [hidable, position, onClose, actions, _isHidden]); - const positions = useMemo(() => { - /* Centering the modal. */ - if (_isModal) { - return {}; - } + const positions = useMemo(() => { + /* Centering the modal. */ + if (_isModal) { + return {}; + } - return { - top: position === 'top' || layout === 'vertical' ? (floating ? '10px' : 0) : undefined, - bottom: position === 'bottom' || layout === 'vertical' ? (floating ? '10px' : 0) : undefined, - right: position === 'right' || layout === 'horizontal' ? (floating ? '10px' : 0) : undefined, - left: position === 'left' || layout === 'horizontal' ? (floating ? '10px' : 0) : undefined, - }; - }, [_isModal, position, layout, floating]); + return { + top: position === 'top' || layout === 'vertical' ? (floating ? '10px' : 0) : undefined, + bottom: + position === 'bottom' || layout === 'vertical' ? (floating ? '10px' : 0) : undefined, + right: + position === 'right' || layout === 'horizontal' ? (floating ? '10px' : 0) : undefined, + left: position === 'left' || layout === 'horizontal' ? (floating ? '10px' : 0) : undefined, + }; + }, [_isModal, position, layout, floating]); - return createPortal( - transitions((styles: any, item) => - item ? ( - - {hasBackdrop && !_isHidden ? ( - - ) : null} - - { - setSize({ - width: component.style.width, - height: component.style.height, - }); - } - : undefined - } - enable={{ - top: (resizable && position === 'bottom') || _isModal ? true : false, - right: (resizable && position === 'left') || _isModal ? true : false, - left: (resizable && position === 'right') || _isModal ? true : false, - bottom: (resizable && position === 'top') || _isModal ? true : false, - bottomLeft: _isModal, - bottomRight: _isModal, - topLeft: _isModal, - topRight: _isModal, - }} - > - {_isHidden && hidable ? ( - - { - setIsHidden(!_isHidden); - onHideToggle?.(!_isHidden); + return createPortal( + transitions((styles: any, item) => + item ? ( + + {hasBackdrop && !_isHidden ? ( + + ) : null} + + { + setSize({ + width: component.style.width, + height: component.style.height, + }); + } + : undefined + } + enable={{ + top: (resizable && position === 'bottom') || _isModal ? true : false, + right: (resizable && position === 'left') || _isModal ? true : false, + left: (resizable && position === 'right') || _isModal ? true : false, + bottom: (resizable && position === 'top') || _isModal ? true : false, + bottomLeft: _isModal, + bottomRight: _isModal, + topLeft: _isModal, + topRight: _isModal, + }} + > + {_isHidden && hidable ? ( + + { + setIsHidden(!_isHidden); + onHideToggle?.(!_isHidden); + }} + /> + + ) : null} + {!_isHidden && ( + - - ) : null} - {!_isHidden && ( - - {children} - - )} - - - - ) : null - ), - document.querySelector('#reqore-portal')! - ); -}; + className={`reqore-drawer`} + style={{ + width: '100%', + maxHeight: '100%', + ...rest.style, + }} + > + {children} + + )} + + + + ) : null + ), + document.querySelector('#reqore-portal')! + ); + } +); diff --git a/src/components/Modal/index.tsx b/src/components/Modal/index.tsx index 2c309c9d..3e0d7d45 100644 --- a/src/components/Modal/index.tsx +++ b/src/components/Modal/index.tsx @@ -1,4 +1,4 @@ -import { useEffect, useMemo } from 'react'; +import { memo, useCallback, useEffect, useMemo } from 'react'; import shortid from 'shortid'; import { useReqoreProperty } from '../..'; import { IReqoreTheme } from '../../constants/theme'; @@ -15,59 +15,60 @@ export interface IReqoreModalStyle extends IReqoreModalProps { zIndex?: number; } -export const ReqoreModal = ({ - width = '80vw', - height = 'fit-content', - ...rest -}: IReqoreModalProps) => { - const id = useMemo(() => shortid.generate(), []); - const escClosableModals = useReqoreProperty('escClosableModals'); - const closeModalsOnEscPress = useReqoreProperty('closeModalsOnEscPress'); - const add = useReqoreProperty('addEscClosableModal'); - const remove = useReqoreProperty('removeEscClosableModal'); +export const ReqoreModal = memo( + ({ width = '80vw', height = 'fit-content', ...rest }: IReqoreModalProps) => { + const id = useMemo(() => shortid.generate(), []); + const escClosableModals = useReqoreProperty('escClosableModals'); + const closeModalsOnEscPress = useReqoreProperty('closeModalsOnEscPress'); + const add = useReqoreProperty('addEscClosableModal'); + const remove = useReqoreProperty('removeEscClosableModal'); - const isEscClosable = - rest.isOpen && - rest.onClose && - !rest.disabled && - (rest.closeOnEscPress ?? closeModalsOnEscPress); + const isEscClosable = + rest.isOpen && + rest.onClose && + !rest.disabled && + (rest.closeOnEscPress ?? closeModalsOnEscPress); - // Close last popover when ESC is pressed - const handleKeyDown = (event: KeyboardEvent) => { - if (event.key === 'Escape') { - remove(id, rest.onClose); - } - }; + // Close last popover when ESC is pressed + const handleKeyDown = useCallback( + (event: KeyboardEvent) => { + if (event.key === 'Escape') { + remove(id, rest.onClose); + } + }, + [id, rest.onClose, remove] + ); - useEffect(() => { - if (isEscClosable) { - document.addEventListener('keydown', handleKeyDown); - } + useEffect(() => { + if (isEscClosable) { + document.addEventListener('keydown', handleKeyDown); + } - return () => { - document.removeEventListener('keydown', handleKeyDown); - }; - }, [escClosableModals, isEscClosable]); + return () => { + document.removeEventListener('keydown', handleKeyDown); + }; + }, [escClosableModals, isEscClosable]); - useEffect(() => { - if (rest.isOpen) { - add(id); - } + useEffect(() => { + if (rest.isOpen) { + add(id); + } - return () => { - remove(id); - }; - }, [id, rest.isOpen]); + return () => { + remove(id); + }; + }, [id, rest.isOpen]); - return ( - - ); -}; + return ( + + ); + } +); diff --git a/src/components/Panel/index.tsx b/src/components/Panel/index.tsx index 10f9b38a..acb84035 100644 --- a/src/components/Panel/index.tsx +++ b/src/components/Panel/index.tsx @@ -524,7 +524,7 @@ export const ReqorePanel = forwardRef( ( action: IReqorePanelAction, index: number, - includeResponsive, + includeResponsive: boolean, align: 'flex-start' | 'center' | 'flex-end' = 'flex-end' ) => { if ( @@ -596,13 +596,16 @@ export const ReqorePanel = forwardRef( fixed className={className} {...props} - // key={props.key || index} - // reactKey={props.key || index} + key={props.key || index} customTheme={props.customTheme || theme} - onClick={(e: React.MouseEvent) => { - e.stopPropagation(); - props?.onClick?.(e); - }} + onClick={ + props.onClick + ? (e: React.MouseEvent) => { + e.stopPropagation(); + props?.onClick?.(e); + } + : undefined + } /> ); } diff --git a/src/stories/Button/Button.stories.tsx b/src/stories/Button/Button.stories.tsx index 53721571..c2dfabe3 100644 --- a/src/stories/Button/Button.stories.tsx +++ b/src/stories/Button/Button.stories.tsx @@ -216,6 +216,13 @@ const Template: StoryFn = (buttonProps) => { > With Default Description + + Minimal With Description +