diff --git a/package.json b/package.json index f1a079d7..0f6423a7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@qoretechnologies/reqore", - "version": "0.48.8", + "version": "0.48.9", "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 b6c11dd6..300aec4a 100644 --- a/src/components/Button/index.tsx +++ b/src/components/Button/index.tsx @@ -1,5 +1,5 @@ import { size } from 'lodash'; -import { rgba } from 'polished'; +import { rgba, tint } from 'polished'; import React, { forwardRef, memo, useCallback, useMemo, useState } from 'react'; import styled, { css } from 'styled-components'; import { @@ -64,6 +64,7 @@ export interface IReqoreButtonProps icon?: IReqoreIconName; size?: TSizes; minimal?: boolean; + transparent?: boolean; tooltip?: TReqoreTooltipProp; fluid?: boolean; fixed?: boolean; @@ -129,17 +130,18 @@ export const StyledButton = styled(StyledEffect)` flex-flow: column; justify-content: center; margin: 0; - font-weight: 600; + font-weight: 500; position: relative; overflow: hidden; vertical-align: middle; border: ${({ theme, color, flat }) => - !flat ? `1px solid ${changeLightness(getButtonMainColor(theme, color), 0.2)}` : 0}; + !flat ? `1px solid ${changeLightness(getButtonMainColor(theme, color), 0.05)}` : 0}; padding: ${({ size, compact, verticalPadding }) => `${verticalPadding ? PADDING_FROM_SIZE[verticalPadding] : 0}px ${ compact ? PADDING_FROM_SIZE[size] / 2 : PADDING_FROM_SIZE[size] }px`}; font-size: ${({ size }) => CONTROL_TEXT_FROM_SIZE[size]}px; + outline-offset: -2px; min-height: ${({ size }) => SIZE_TO_PX[size]}px; min-width: ${({ size }) => SIZE_TO_PX[size]}px; @@ -165,24 +167,30 @@ export const StyledButton = styled(StyledEffect)` ? 9999 : RADIUS_FROM_SIZE[size] * (pill ? PILL_RADIUS_MODIFIER : 1)}px; - background-color: ${({ minimal, color }) => { - if (minimal) { + background-color: ${({ minimal, color, theme, transparent }) => { + if (transparent) { return 'transparent'; } + if (minimal) { + return css` + ${rgba(getButtonMainColor(theme, color), 0.2)} + `; + } + return color; }}; - color: ${({ theme, color, minimal }) => + color: ${({ theme, color, minimal, transparent }) => color - ? minimal - ? getReadableColor(theme, undefined, undefined, true, theme.originalMain) + ? minimal || transparent + ? tint(0.75, color) : getReadableColorFrom(color, true) : getReadableColor(theme, undefined, undefined, true)}; ${InactiveIconScale} - ${({ readOnly, animate, active }) => + ${({ readOnly, animate, active, theme, color }) => !readOnly && !active ? css` &:not(:disabled) { @@ -192,24 +200,35 @@ export const StyledButton = styled(StyledEffect)` transition: all 0.2s ease-out; &:active { - transform: scale(0.97); + outline: 3px solid ${changeLightness(getButtonMainColor(theme, color), 0.25)}; + outline-offset: -3px; + transition: none; + } + + &:hover:not(:active) { + outline: 2px solid ${changeLightness(getButtonMainColor(theme, color), 0.25)}; + transition: none; } &:hover, - &:active, - &:focus { - background-color: ${({ theme, color, minimal }: IReqoreButtonStyle) => - minimal - ? rgba(changeLightness(getButtonMainColor(theme, color), 0.05), 0.2) - : changeLightness(getButtonMainColor(theme, color), 0.05)}; - color: ${({ theme, color, minimal }) => - getReadableColor( - { main: minimal ? theme.originalMain : getButtonMainColor(theme, color) }, - undefined, - undefined - )}; + &:active { border-color: ${({ flat, theme, color }) => - flat ? undefined : changeLightness(getButtonMainColor(theme, color), 0.35)}; + flat ? undefined : changeLightness(getButtonMainColor(theme, color), 0.25)}; + background-color: ${({ theme, color, minimal, transparent }: IReqoreButtonStyle) => + minimal || transparent + ? rgba( + changeLightness(getButtonMainColor(theme, color), 0.05), + transparent ? 0.2 : 0.4 + ) + : changeLightness(getButtonMainColor(theme, color), 0.05)}; + color: ${({ theme, color, minimal, transparent }) => + minimal || transparent + ? tint(0.85, color) + : getReadableColor( + { main: minimal ? theme.originalMain : getButtonMainColor(theme, color) }, + undefined, + undefined + )}; ${animate && css` @@ -267,12 +286,10 @@ export const StyledButton = styled(StyledEffect)` &:focus, &:active { - outline: none; - } - - &:focus { + outline: 2px solid + ${({ theme, color }) => changeLightness(getButtonMainColor(theme, color), 0.1)}; border-color: ${({ minimal, theme, color }) => - minimal ? undefined : changeLightness(getButtonMainColor(theme, color), 0.4)}; + minimal ? undefined : changeLightness(getButtonMainColor(theme, color), 0.1)}; } .reqore-button-description { @@ -385,6 +402,7 @@ const ReqoreButton = memo( icon, size = 'normal', minimal, + transparent, children, tooltip, className, @@ -431,7 +449,7 @@ const ReqoreButton = memo( // If color or intent was specified, set the color const customColor = intent ? theme.main : changeLightness(theme.main, 0.07); - const _flat = minimal ? flat : flat !== false; + const _flat = minimal ? flat : flat === true; const _compact = compact ?? theme.buttons?.compact; const color: TReqoreHexColor = customColor ? minimal @@ -478,7 +496,9 @@ const ReqoreButton = memo( fixed={fixed} maxWidth={maxWidth} minimal={minimal} + transparent={transparent} size={size} + intent={intent} color={customColor} animate={animate} flat={_flat} @@ -498,11 +518,13 @@ const ReqoreButton = memo( compact={_compact} {...leftIconProps} style={ - textAlign !== 'left' || iconsAlign === 'center' + textAlign !== 'left' || iconsAlign === 'center' || !_children ? { - marginRight: iconsAlign !== 'center' || !children ? 'auto' : undefined, + marginRight: iconsAlign !== 'center' || !_children ? 'auto' : undefined, marginLeft: - iconsAlign === 'center' || (textAlign === 'center' && !_children) + iconsAlign === 'center' || + !_children || + (textAlign === 'center' && !_children) ? 'auto' : undefined, } diff --git a/src/components/Checkbox/index.tsx b/src/components/Checkbox/index.tsx index 0537f6c9..fdf0d611 100644 --- a/src/components/Checkbox/index.tsx +++ b/src/components/Checkbox/index.tsx @@ -110,6 +110,12 @@ const StyledSwitch = styled(StyledEffect)` ${StyledIconWrapper} { z-index: 1; } + + &:focus, + &:active { + outline: 2px solid ${({ theme }) => changeLightness(theme.main, 0.25)}; + outline-offset: -2px; + } `; const StyledSwitchTextWrapper = styled(StyledTextEffect)` @@ -257,6 +263,7 @@ const Checkbox = forwardRef( {asSwitch ? ( ( ) : ( { const isStack = stack || isInsideStackGroup; @@ -467,9 +470,9 @@ const ReqoreControlGroup = memo( ), }} active={isOverflownDialogOpen} - flat fixed onClick={(e) => e.stopPropagation()} + {...overflowButtonProps} />, ] : children, diff --git a/src/components/Drawer/index.tsx b/src/components/Drawer/index.tsx index 6697a4be..f93079d5 100644 --- a/src/components/Drawer/index.tsx +++ b/src/components/Drawer/index.tsx @@ -49,6 +49,16 @@ export interface IReqoreDrawerStyle extends IReqoreDrawerProps { h?: number | string; } +export const StyledWrapper = styled.div` + z-index: ${({ zIndex }) => zIndex}; + position: fixed; + inset: 0; + display: flex; + justify-content: center; + align-items: center; + pointer-events: none; +`; + export const StyledCloseWrapper = styled.div` position: absolute; @@ -98,7 +108,9 @@ export const StyledCloseWrapper = styled.div` }} `; -export const StyledDrawerResizable = styled(animated.div)``; +export const StyledDrawerResizable = styled(animated.div)` + pointer-events: auto; +`; /** * It returns an icon name based on the position and whether the panel is hidden or not @@ -126,9 +138,9 @@ const getHideShowIcon = ( const getSpringConfig = (isModal?: boolean, position?: TPosition, floating?: boolean) => isModal ? { - from: { opacity: 0, transform: 'scale(0.5) translate(-50%, -50%)', filter: 'blur(10px)' }, - enter: { opacity: 1, transform: 'scale(1) translate(-50%, -50%)', filter: 'blur(0px)' }, - leave: { opacity: 0, transform: 'scale(0.5) translate(-50%, -50%)', filter: 'blur(10px)' }, + from: { opacity: 0, transform: 'scale(0.5)' }, + enter: { opacity: 1, transform: 'scale(1)' }, + leave: { opacity: 0, transform: 'scale(0.5)' }, } : { from: { opacity: 0, [position]: '-80px' }, @@ -217,11 +229,7 @@ export const ReqoreDrawer: React.FC = ({ const positions = useMemo(() => { /* Centering the modal. */ if (_isModal) { - return { - left: '50%', - top: '50%', - transform: 'translate(-50%)', - }; + return {}; } return { @@ -244,127 +252,132 @@ export const ReqoreDrawer: React.FC = ({ opacity={styles.opacity} /> ) : 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); + + { + 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} - - )} - + > + {children} + + )} + + ) : null ), diff --git a/src/components/Input/index.tsx b/src/components/Input/index.tsx index 84bf66ec..e10f33bc 100644 --- a/src/components/Input/index.tsx +++ b/src/components/Input/index.tsx @@ -100,6 +100,9 @@ export const StyledInputWrapper = styled.div` display: flex; } + outline: 2px solid ${({ theme }) => changeLightness(theme.main, 0.25)}; + outline-offset: -2px; + ${ActiveIconScale} } `; @@ -136,10 +139,8 @@ export const StyledInput = styled(StyledEffect)` padding-left: ${({ hasIcon, _size }) => (hasIcon ? SIZE_TO_PX[_size] : 7)}px; font-size: ${({ _size }) => CONTROL_TEXT_FROM_SIZE[_size]}px; transition: all 0.2s ease-out; - border-radius: ${({ minimal, rounded, _size, pill }) => - minimal || rounded === false - ? 0 - : RADIUS_FROM_SIZE[_size] * (pill ? PILL_RADIUS_MODIFIER : 1)}px; + border-radius: inherit; + border: ${({ minimal, theme, flat }) => !minimal && !flat ? `1px solid ${changeLightness(theme.main, 0.2)}` : 0}; border-bottom: ${({ minimal, theme, flat }) => @@ -151,7 +152,6 @@ export const StyledInput = styled(StyledEffect)` &:active, &:focus, &:hover { - outline: none; border-color: ${({ theme }) => changeLightness(theme.main, 0.35)}; } ` @@ -166,6 +166,7 @@ export const StyledInput = styled(StyledEffect)` &:active, &:focus { + outline: none; background-color: ${({ theme, minimal, transparent }: IReqoreInputStyle) => minimal || transparent ? 'transparent' : rgba(theme.main, 0.15)}; } diff --git a/src/components/Menu/item.tsx b/src/components/Menu/item.tsx index 6270461f..f943fc7b 100644 --- a/src/components/Menu/item.tsx +++ b/src/components/Menu/item.tsx @@ -93,7 +93,7 @@ const ReqoreMenuItem = memo( }, [itemRef, scrollIntoView]); return ( - + { +export const ReqoreModal = ({ + width = '80vw', + height = 'fit-content', + ...rest +}: IReqoreModalProps) => { const id = useMemo(() => shortid.generate(), []); const escClosableModals = useReqoreProperty('escClosableModals'); const closeModalsOnEscPress = useReqoreProperty('closeModalsOnEscPress'); diff --git a/src/components/Tabs/item.tsx b/src/components/Tabs/item.tsx index 5f4ffa50..2c5af0fe 100644 --- a/src/components/Tabs/item.tsx +++ b/src/components/Tabs/item.tsx @@ -35,7 +35,6 @@ export const StyledTabListItem = styled.div` return css` display: flex; flex-shrink: 0; - overflow: hidden; position: relative; align-items: center; width: ${vertical ? `100%` : undefined}; @@ -171,6 +170,27 @@ const ReqoreTabsListItem = memo( }); }; + const renderButton = () => ( + + {label} + + ); + return ( - {label || icon ? ( + {!onCloseClick || disabled ? ( + renderButton() + ) : ( - - {label} - + {renderButton()} {onCloseClick && !disabled ? ( ) : null} - ) : null} + )} ); } diff --git a/src/components/Tag/index.tsx b/src/components/Tag/index.tsx index 6bedbba8..fd95cab2 100644 --- a/src/components/Tag/index.tsx +++ b/src/components/Tag/index.tsx @@ -8,8 +8,9 @@ import { BADGE_SIZE_TO_PX, CONTROL_TEXT_FROM_SIZE, PADDING_FROM_SIZE, - RADIUS_FROM_SIZE, SIZE_TO_PX, + TAG_RADIUS_FROM_SIZE, + TAG_TEXT_FROM_SIZE, TSizes, } from '../../constants/sizes'; import { IReqoreTheme, TReqoreIntent } from '../../constants/theme'; @@ -109,16 +110,17 @@ export const StyledTag = styled(StyledEffect)` font-weight: 600; overflow: hidden; vertical-align: middle; - font-size: ${({ size }) => CONTROL_TEXT_FROM_SIZE[size]}px; + font-size: ${({ size }) => TAG_TEXT_FROM_SIZE[size]}px; + line-height: 1.1; - min-width: ${({ size }) => SIZE_TO_PX[size]}px; + min-width: ${({ size, asBadge }) => (asBadge ? BADGE_SIZE_TO_PX[size] : SIZE_TO_PX[size])}px; max-width: ${({ fixed }) => (fixed !== true ? '100%' : undefined)}; flex: ${({ fluid, fixed }) => (fixed === true ? '0 0 auto' : fluid ? '1 auto' : '0 0 auto')}; align-self: ${({ fixed, fluid }) => fixed === true ? 'flex-start' : fluid ? 'stretch' : undefined}; border: ${({ theme, color, flat = true }) => !flat ? `1px solid ${changeLightness(color || theme.main, 0.2)}` : 0}; - border-radius: ${({ asBadge, size }) => (asBadge ? 18 : RADIUS_FROM_SIZE[size])}px; + border-radius: ${({ asBadge, size }) => (asBadge ? 18 : TAG_RADIUS_FROM_SIZE[size])}px; width: ${({ width }) => width || undefined}; transition: all 0.2s ease-out; @@ -206,6 +208,12 @@ export const StyledTag = styled(StyledEffect)` display: none; } } + + &:focus, + &:active { + outline: 2px solid ${({ theme, color }) => changeLightness(color || theme.main, 0.25)}; + outline-offset: -2px; + } `; const StyledTagKeyWrapper = styled.span<{ size: TSizes }>` @@ -267,7 +275,7 @@ const StyledTagContentKey = styled(StyledTagContent)` const StyledButtonWrapper = styled.span` flex-shrink: 0; font-size: ${({ size }) => CONTROL_TEXT_FROM_SIZE[size]}px; - width: ${({ size }) => SIZE_TO_PX[size]}px; + width: ${({ size }) => BADGE_SIZE_TO_PX[size]}px; display: flex; justify-content: center; align-items: center; @@ -314,7 +322,7 @@ const ReqoreTag = forwardRef( rightIconColor, iconColor, labelKeyAlign = 'left', - labelAlign = 'left', + labelAlign, labelEffect, labelKeyEffect, leftIconProps, @@ -373,6 +381,7 @@ const ReqoreTag = forwardRef( minimal={minimal} removable={!!onRemoveClick} interactive={!!onClick && !rest.disabled} + tabIndex={onClick && !rest.disabled ? 0 : undefined} wrap={wrap} hasWidth={!!width} > @@ -431,7 +440,7 @@ const ReqoreTag = forwardRef( size={size} wrap={wrap} hasWidth={!!width} - labelAlign={labelAlign} + labelAlign={labelAlign || (labelKey ? 'left' : 'center')} compact={compact} effect={{ ...labelEffect, diff --git a/src/components/Textarea/index.tsx b/src/components/Textarea/index.tsx index 050a9eac..d0fe6c30 100644 --- a/src/components/Textarea/index.tsx +++ b/src/components/Textarea/index.tsx @@ -76,11 +76,16 @@ export const StyledTextareaWrapper = styled.div` align-self: ${({ fixed, fluid }) => (fixed ? 'flex-start' : fluid ? 'stretch' : undefined)}; position: relative; overflow: hidden; + border-radius: ${({ minimal, rounded = true, _size = 'normal' }) => + minimal || !rounded ? 0 : RADIUS_FROM_SIZE[_size]}px; &:focus-within { .reqore-clear-input-button { display: flex; } + + outline: 2px solid ${({ theme }) => changeLightness(theme.main, 0.25)}; + outline-offset: -2px; } `; @@ -104,12 +109,12 @@ export const StyledTextarea = styled(StyledEffect)` &:active, &:focus { + outline: none; background-color: ${({ theme, minimal, transparent }: IReqoreTextareaStyle) => minimal || transparent ? 'transparent' : rgba(theme.main, 0.15)}; } - border-radius: ${({ minimal, rounded = true, _size = 'normal' }) => - minimal || !rounded ? 0 : RADIUS_FROM_SIZE[_size]}px; + border-radius: inherit; border: ${({ minimal, theme, flat }) => !minimal && !flat ? `1px solid ${changeLightness(theme.main, 0.2)}` : 0}; border-bottom: ${({ minimal, theme, flat }) => diff --git a/src/components/Tree/index.tsx b/src/components/Tree/index.tsx index 88302f17..2fa1db05 100644 --- a/src/components/Tree/index.tsx +++ b/src/components/Tree/index.tsx @@ -183,7 +183,7 @@ export const ReqoreTree = ({ @@ -248,6 +247,7 @@ export const ReqoreTree = ({ { setManagementDialog({ @@ -257,8 +257,7 @@ export const ReqoreTree = ({ type: isArray(_data[key]) ? 'array' : 'object', }); }} - leftIconColor='info' - minimal + intent='info' compact /> @@ -287,7 +286,7 @@ export const ReqoreTree = ({ )} ) : ( - + = (buttonProps) => { Minimal + + Transparent + Compact diff --git a/src/styles.ts b/src/styles.ts index 8144c716..a12faefc 100644 --- a/src/styles.ts +++ b/src/styles.ts @@ -3,7 +3,7 @@ import { StyledTextEffect } from './components/Effect'; import { StyledIconWrapper } from './components/Icon'; export const INACTIVE_ICON_SCALE = 0.85; -export const ACTIVE_ICON_SCALE = 1; +export const ACTIVE_ICON_SCALE = 0.93; export const StyledContent = styled(StyledTextEffect)` position: relative; @@ -107,15 +107,14 @@ export const ChildActiveIconScale = css` `; export const ScaleIconOnHover = css` - &:hover, - &:focus { + &:hover { ${ActiveIconScale} } `; export const DisabledElement = css` pointer-events: none; - opacity: 0.5; + opacity: 0.4; cursor: not-allowed; `;