diff --git a/package.json b/package.json index 3566d553..a17f68a7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@qoretechnologies/reqore", - "version": "0.55.5", + "version": "0.55.6", "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/Breadcrumbs/index.tsx b/src/components/Breadcrumbs/index.tsx index f1f93b20..9ab4fb02 100644 --- a/src/components/Breadcrumbs/index.tsx +++ b/src/components/Breadcrumbs/index.tsx @@ -50,17 +50,29 @@ export interface IReqoreBreadcrumbsProps interface IStyledBreadcrumbs extends Omit { theme: IReqoreTheme; + $size?: string; + $flat?: boolean; + $padded?: boolean; + $margin?: string; + $responsive?: boolean; } const StyledReqoreBreadcrumbs = styled.div` - ${({ theme, size, flat, padded = true, margin = 'both', responsive }: IStyledBreadcrumbs) => css` - width: ${responsive ? '100%' : undefined}; - margin-top: ${margin === 'both' || margin === 'top' ? MARGIN_FROM_SIZE[size!] : 0}px; - margin-bottom: $ ${margin === 'both' || margin === 'bottom' ? MARGIN_FROM_SIZE[size!] : 0}px; + ${({ + theme, + $size, + $flat, + $padded = true, + $margin = 'both', + $responsive, + }: IStyledBreadcrumbs) => css` + width: ${$responsive ? '100%' : undefined}; + margin-top: ${$margin === 'both' || $margin === 'top' ? MARGIN_FROM_SIZE[$size!] : 0}px; + margin-bottom: ${$margin === 'both' || $margin === 'bottom' ? MARGIN_FROM_SIZE[$size!] : 0}px; display: flex; - padding: 0 ${padded ? PADDING_FROM_SIZE[size!] : 0}px; + padding: 0 ${$padded ? PADDING_FROM_SIZE[$size!] : 0}px; justify-content: space-between; - border-bottom: ${flat ? undefined : `1px solid ${changeLightness(theme.main, 0.05)}`}; + border-bottom: ${$flat ? undefined : `1px solid ${changeLightness(theme.main, 0.05)}`}; background-color: ${({ theme }: { theme: IReqoreTheme }) => theme.breadcrumbs?.main || 'transparent'}; @@ -240,16 +252,20 @@ const ReqoreBreadcrumbs: React.FC = ({ ); }; + const { padded, margin, ...domRest } = rest as any; + return (
{transformedItems.map( diff --git a/src/components/Checkbox/index.tsx b/src/components/Checkbox/index.tsx index 46c8ab18..6aff92ed 100644 --- a/src/components/Checkbox/index.tsx +++ b/src/components/Checkbox/index.tsx @@ -65,6 +65,13 @@ export interface IReqoreCheckboxProps export interface IReqoreCheckboxStyle extends IReqoreCheckboxProps { theme: IReqoreTheme; + $size?: TSizes; + $fluid?: boolean; + $fixed?: boolean; + $disabled?: boolean; + $readOnly?: boolean; + $checked?: boolean; + $parentHasGradient?: boolean; } const StyledSwitchToggle = styled.div` @@ -101,10 +108,10 @@ const StyledSwitch = styled(StyledEffect)` justify-content: center; flex-shrink: 0; - height: ${({ size }) => SWITCH_SIZE_TO_PX[size]}px; - min-width: ${({ size }) => SWITCH_SIZE_TO_PX[size] * 1.8}px; + height: ${({ $size }) => SWITCH_SIZE_TO_PX[$size]}px; + min-width: ${({ $size }) => SWITCH_SIZE_TO_PX[$size] * 1.8}px; - border: 1px solid ${({ theme, checked }) => changeLightness(theme.main, checked ? 0.35 : 0.2)}; + border: 1px solid ${({ theme, $checked }) => changeLightness(theme.main, $checked ? 0.35 : 0.2)}; border-radius: 50px; background-color: ${({ theme }) => rgba(changeLightness(theme.main, 0.3), 0.1)}; @@ -121,24 +128,24 @@ const StyledSwitch = styled(StyledEffect)` `; const StyledSwitchTextWrapper = styled(StyledTextEffect)` - margin: 0 ${({ size }) => PADDING_FROM_SIZE[size]}px; + margin: 0 ${({ $size }) => PADDING_FROM_SIZE[$size]}px; display: flex; align-items: center; justify-content: center; z-index: 1; - min-width: ${({ size }) => SWITCH_SIZE_TO_PX[size]}px; + min-width: ${({ $size }) => SWITCH_SIZE_TO_PX[$size]}px; `; const StyledOnSwitchText = styled(StyledSwitchTextWrapper)` - color: ${({ theme, checked, parentHasGradient }) => - !parentHasGradient && - getReadableColorFrom(checked ? changeLightness(theme.main, 0.25) : theme.originalMain)}; + color: ${({ theme, $checked, $parentHasGradient }) => + !$parentHasGradient && + getReadableColorFrom($checked ? changeLightness(theme.main, 0.25) : theme.originalMain)}; `; const StyledOffSwitchText = styled(StyledSwitchTextWrapper)` - color: ${({ theme, checked, parentHasGradient }) => - !parentHasGradient && - getReadableColorFrom(checked ? theme.originalMain : changeLightness(theme.main, 0.2))}; + color: ${({ theme, $checked, $parentHasGradient }) => + !$parentHasGradient && + getReadableColorFrom($checked ? theme.originalMain : changeLightness(theme.main, 0.2))}; `; const StyledCheckbox = styled.div` @@ -148,33 +155,33 @@ const StyledCheckbox = styled.div` padding: 0px; transition: all 0.2s ease-out; - height: ${({ size }) => SIZE_TO_PX[size]}px; - font-size: ${({ size }) => CONTROL_TEXT_FROM_SIZE[size]}px; + height: ${({ $size }) => SIZE_TO_PX[$size]}px; + font-size: ${({ $size }) => CONTROL_TEXT_FROM_SIZE[$size]}px; - max-width: ${({ fluid, fixed }) => (fluid && !fixed ? '100%' : undefined)}; - flex: ${({ fluid, fixed }) => (fixed ? '0 auto' : fluid ? '1 auto' : '0 0 auto')}; + max-width: ${({ $fluid, $fixed }) => ($fluid && !$fixed ? '100%' : undefined)}; + flex: ${({ $fluid, $fixed }) => ($fixed ? '0 auto' : $fluid ? '1 auto' : '0 0 auto')}; - ${({ disabled }) => - disabled && + ${({ $disabled }) => + $disabled && css` ${DisabledElement}; `} - ${({ readOnly }) => - readOnly && + ${({ $readOnly }) => + $readOnly && css` ${ReadOnlyElement}; `} - color: ${({ theme, checked }) => - getReadableColor(theme, undefined, undefined, !checked, theme.originalMain)}; + color: ${({ theme, $checked }) => + getReadableColor(theme, undefined, undefined, !$checked, theme.originalMain)}; &:hover { color: ${({ theme }) => getReadableColor(theme, undefined, undefined, false, theme.originalMain)}; > ${StyledSwitch} { - border-color: ${({ theme, checked }) => changeLightness(theme.main, checked ? 0.4 : 0.35)}; + border-color: ${({ theme, $checked }) => changeLightness(theme.main, $checked ? 0.4 : 0.35)}; } } `; @@ -232,12 +239,14 @@ const Checkbox = forwardRef( { ...rest, theme, - size, tooltip, - disabled, - checked, - readOnly, className: `${className || ''} reqore-checkbox reqore-control`, + $size: size, + $disabled: disabled, + $checked: checked, + $readOnly: readOnly, + $fluid: rest.fluid, + $fixed: rest.fixed, }, StyledCheckbox, ref @@ -267,10 +276,9 @@ const Checkbox = forwardRef( ) : null} {asSwitch ? ( ( {offText || offText === 0 ? ( ( {onText || onText === 0 ? ( ; } -export const StyledCollectionWrapper = styled(StyledColumns)` - height: ${({ height }) => (height ? `${height}` : 'auto')}; - flex: ${({ fill }) => (fill ? 1 : undefined)}; +interface IStyledCollectionWrapperInternal { + $height?: string; + $fill?: boolean; + $rounded?: boolean; + $stacked?: boolean; +} + +export const StyledCollectionWrapper = styled(StyledColumns).withConfig({ + shouldForwardProp: (prop) => !prop.startsWith('$'), +})` + height: ${({ $height }) => ($height ? `${$height}` : 'auto')}; + flex: ${({ $fill }) => ($fill ? 1 : undefined)}; - ${({ rounded, stacked }: IReqoreCollectionProps) => - (!rounded || stacked) && + ${({ $rounded, $stacked }: IStyledCollectionWrapperInternal) => + (!$rounded || $stacked) && css` - border-radius: ${stacked && rounded ? '10px' : undefined}; + border-radius: ${$stacked && $rounded ? '10px' : undefined}; `} overflow: auto; @@ -374,16 +383,16 @@ export const ReqoreCollection = memo( ) : ( {(() => { @@ -481,15 +490,15 @@ export const ReqoreCollection = memo( return ( diff --git a/src/components/Collection/item.tsx b/src/components/Collection/item.tsx index c8e2a2ed..e9f6a830 100644 --- a/src/components/Collection/item.tsx +++ b/src/components/Collection/item.tsx @@ -47,14 +47,14 @@ export interface IReqoreCollectionItemProps } export const StyledCollectionItemContent = styled.div` - max-height: ${({ providedHeight }) => (providedHeight ? `${providedHeight}px` : 'auto')}; - overflow: ${({ isSelected }) => (isSelected ? 'auto' : 'hidden')}; + max-height: ${({ $providedHeight }) => ($providedHeight ? `${$providedHeight}px` : 'auto')}; + overflow: ${({ $isSelected }) => ($isSelected ? 'auto' : 'hidden')}; position: relative; transition: 0.3s ease-in-out; flex: 1; - ${({ contentOverflows, theme, opacity = 1 }) => - contentOverflows && + ${({ $contentOverflows, theme, $opacity = 1 }) => + $contentOverflows && css` &:after { content: ''; @@ -66,7 +66,7 @@ export const StyledCollectionItemContent = styled.div` transition: 0.3s ease-in-out; background: linear-gradient( to top, - ${rgba(changeDarkness(getMainBackgroundColor(theme), 0.03), opacity)} 0%, + ${rgba(changeDarkness(getMainBackgroundColor(theme), 0.03), $opacity)} 0%, transparent 100% ); } @@ -230,10 +230,10 @@ export const ReqoreCollectionItem = ({ > ` +export const StyledColumns = styled.div.withConfig({ + shouldForwardProp: (prop) => !prop.toString().startsWith('$'), +})` display: grid; ${({ - minColumnWidth = '300px', - maxColumnWidth = '1fr', - columns = 'auto-fit', - columnsGap, - alignItems = 'normal', + $minColumnWidth = '300px', + $maxColumnWidth = '1fr', + $columns = 'auto-fit', + $columnsGap, + $alignItems = 'normal', }: IStyledColumns) => css` - grid-template-columns: repeat(${columns}, minmax(${minColumnWidth}, ${maxColumnWidth})); + grid-template-columns: repeat(${$columns}, minmax(${$minColumnWidth}, ${$maxColumnWidth})); grid-auto-rows: max-content; - grid-gap: ${columnsGap}; - align-items: ${alignItems}; + grid-gap: ${$columnsGap}; + align-items: ${$alignItems}; `} `; export const ReqoreColumns = memo(({ children, className, ...rest }: IReqoreColumnsProps) => { return ( - + {children} ); diff --git a/src/components/Comment/index.tsx b/src/components/Comment/index.tsx index 45474fd4..2d6f5206 100644 --- a/src/components/Comment/index.tsx +++ b/src/components/Comment/index.tsx @@ -43,12 +43,12 @@ export const StyledInfoWrapper = styled.div` overflow: hidden; `; -export const StyledCommentText = styled(ReqoreP)<{ marginTop?: string }>` +export const StyledCommentText = styled(ReqoreP)<{ $marginTop?: string }>` min-height: 10px; overflow: hidden; padding: 0; margin: 0; - margin-top: ${({ marginTop }: { marginTop?: string }) => marginTop}; + margin-top: ${({ $marginTop }: { $marginTop?: string }) => $marginTop}; `; export const StyledCommentTitle = styled(ReqoreH3)` @@ -131,7 +131,7 @@ export const ReqoreComment = ({ ) : null} - + {children} diff --git a/src/components/CommentFeed/index.tsx b/src/components/CommentFeed/index.tsx index 95bee0b5..74ba2b24 100644 --- a/src/components/CommentFeed/index.tsx +++ b/src/components/CommentFeed/index.tsx @@ -6,10 +6,10 @@ export interface IReqoreCommentFeedProps extends React.HTMLAttributes` +export const StyledCommentFeed = styled.div<{ $gapSize: string }>` ${StyledPanel} { &:not(:first-child) { - margin-top: ${({ gapSize }) => gapSize}; + margin-top: ${({ $gapSize }) => $gapSize}; } } `; @@ -23,7 +23,7 @@ export const ReqoreCommentFeed = ({ return ( ` +export const StyledReqoreControlGroup = styled(StyledEffect).withConfig({ + shouldForwardProp: (prop) => !prop.toString().startsWith('$'), +})` display: flex; - flex: ${({ fluid, fixed }) => (fixed ? '0 0 auto' : fluid ? undefined : '0 0 auto')}; - width: ${({ fluid, fixed }) => (fluid && !fixed ? '100%' : undefined)}; - justify-content: ${({ fluid, vertical, spaceBetween }) => - vertical && fluid - ? spaceBetween + flex: ${({ $fluid, $fixed }) => ($fixed ? '0 0 auto' : $fluid ? undefined : '0 0 auto')}; + width: ${({ $fluid, $fixed }) => ($fluid && !$fixed ? '100%' : undefined)}; + justify-content: ${({ $fluid, $vertical, $spaceBetween }) => + $vertical && $fluid + ? $spaceBetween ? 'space-between' : 'stretch' - : !vertical && spaceBetween + : !$vertical && $spaceBetween ? 'space-between' : undefined}; - align-items: ${({ vertical, fluid, horizontalAlign, spaceBetween }) => - vertical - ? fluid - ? spaceBetween + align-items: ${({ $vertical, $fluid, $horizontalAlign, $spaceBetween }) => + $vertical + ? $fluid + ? $spaceBetween ? 'space-between' : 'stretch' - : horizontalAlign - : vertical && spaceBetween + : $horizontalAlign + : $vertical && $spaceBetween ? 'space-between' : undefined}; - flex-flow: ${({ vertical }) => (vertical ? 'column' : 'row')}; - gap: ${({ gapSize, stack }) => (!stack ? `${GAP_FROM_SIZE[gapSize]}px` : undefined)}; - flex-wrap: ${({ wrap }) => (wrap ? 'wrap' : 'nowrap')}; + flex-flow: ${({ $vertical }) => ($vertical ? 'column' : 'row')}; + gap: ${({ $gapSize, $stack }) => (!$stack ? `${GAP_FROM_SIZE[$gapSize]}px` : undefined)}; + flex-wrap: ${({ $wrap }) => ($wrap ? 'wrap' : 'nowrap')}; // If the group has the fill prop, // we need to make sure that the children are vertically stretched // kind of like 'fluid' but vertically // ! Only works when the group is horizontal & wrap is false - ${({ fill, vertical }) => { - if (fill && !vertical) { + ${({ $fill, $vertical }) => { + if ($fill && !$vertical) { return css` > * { max-height: 100% !important; @@ -111,31 +123,35 @@ export const StyledReqoreControlGroup = styled(StyledEffect) *, > ${StyledParagraph}, > ${StyledHeader} { - margin-top: ${({ fill, verticalAlign }) => - !fill && (verticalAlign === 'flex-end' || verticalAlign === 'center') ? 'auto' : undefined}; - margin-bottom: ${({ fill, verticalAlign }) => - !fill && (verticalAlign === 'flex-start' || verticalAlign === 'center') ? 'auto' : undefined}; - margin-right: ${({ horizontalAlign }) => (horizontalAlign === 'center' ? 'auto' : undefined)}; - - ${({ vertical }) => - vertical && + margin-top: ${({ $fill, $verticalAlign }) => + !$fill && ($verticalAlign === 'flex-end' || $verticalAlign === 'center') + ? 'auto' + : undefined}; + margin-bottom: ${({ $fill, $verticalAlign }) => + !$fill && ($verticalAlign === 'flex-start' || $verticalAlign === 'center') + ? 'auto' + : undefined}; + margin-right: ${({ $horizontalAlign }) => ($horizontalAlign === 'center' ? 'auto' : undefined)}; + + ${({ $vertical }) => + $vertical && css` - margin-left: ${({ horizontalAlign }) => - horizontalAlign === 'flex-end' || horizontalAlign === 'center' ? 'auto' : undefined}; + margin-left: ${({ $horizontalAlign }) => + $horizontalAlign === 'flex-end' || $horizontalAlign === 'center' ? 'auto' : undefined}; `} } - ${({ vertical }) => - !vertical && + ${({ $vertical }) => + !$vertical && css` > *:first-child { - margin-left: ${({ horizontalAlign }) => - horizontalAlign === 'flex-end' || horizontalAlign === 'center' ? 'auto' : undefined}; + margin-left: ${({ $horizontalAlign }) => + $horizontalAlign === 'flex-end' || $horizontalAlign === 'center' ? 'auto' : undefined}; } `} > * { - border-radius: ${({ stack }) => (!stack ? undefined : '0')}; + border-radius: ${({ $stack }) => (!$stack ? undefined : '0')}; } `; @@ -495,22 +511,22 @@ const ReqoreControlGroup = memo( {clone(_children)} diff --git a/src/components/Effect/index.tsx b/src/components/Effect/index.tsx index b90bfb8a..8fb39723 100644 --- a/src/components/Effect/index.tsx +++ b/src/components/Effect/index.tsx @@ -154,7 +154,9 @@ const StyledGradientKeyframes = keyframes` } `; -export const StyledEffect = styled.span` +export const StyledEffect = styled.span.withConfig({ + shouldForwardProp: (prop) => !prop.toString().startsWith('$'), +})` // If gradient was supplied ${({ effect, theme, minimal, active, transparent, isText }: IReqoreTextEffectProps) => { if (!effect || !effect.gradient) { diff --git a/src/components/Input/index.tsx b/src/components/Input/index.tsx index c44b3438..70cd3cff 100644 --- a/src/components/Input/index.tsx +++ b/src/components/Input/index.tsx @@ -74,25 +74,34 @@ export interface IReqoreInputProps export interface IReqoreInputStyle extends IReqoreInputProps { theme: IReqoreTheme; - _size?: TSizes; - clearable?: boolean; - hasIcon?: boolean; + $_size?: TSizes; + $clearable?: boolean; + $hasIcon?: boolean; + $fluid?: boolean; + $fixed?: boolean; + $flat?: boolean; + $minimal?: boolean; + $readOnly?: boolean; + $disabled?: boolean; + $pill?: boolean; + $rounded?: boolean; + $hasRightIcon?: boolean; } export const StyledInputWrapper = styled.div` - height: ${({ _size }) => SIZE_TO_PX[_size]}px; + height: ${({ $_size }) => SIZE_TO_PX[$_size]}px; width: ${({ width }) => (width ? `${width}px` : 'auto')}; - max-width: ${({ fluid, fixed }) => (fluid && !fixed ? '100%' : undefined)}; + max-width: ${({ $fluid, $fixed }) => ($fluid && !$fixed ? '100%' : undefined)}; min-width: 60px; - flex: ${({ fluid, fixed }) => (fixed ? '0 auto' : fluid ? '1 auto' : '0 1 auto')}; - align-self: ${({ fixed, fluid }) => (fixed ? 'flex-start' : fluid ? 'stretch' : undefined)}; - font-size: ${({ _size }) => CONTROL_TEXT_FROM_SIZE[_size]}px; + flex: ${({ $fluid, $fixed }) => ($fixed ? '0 auto' : $fluid ? '1 auto' : '0 1 auto')}; + align-self: ${({ $fixed, $fluid }) => ($fixed ? 'flex-start' : $fluid ? 'stretch' : undefined)}; + font-size: ${({ $_size }) => CONTROL_TEXT_FROM_SIZE[$_size]}px; position: relative; overflow: hidden; - border-radius: ${({ minimal, rounded, _size, pill }) => - minimal || rounded === false + border-radius: ${({ $minimal, $rounded, $_size, $pill }) => + $minimal || $rounded === false ? 0 - : RADIUS_FROM_SIZE[_size] * (pill ? PILL_RADIUS_MODIFIER : 1)}px; + : RADIUS_FROM_SIZE[$_size] * ($pill ? PILL_RADIUS_MODIFIER : 1)}px; ${InactiveIconScale} @@ -110,8 +119,8 @@ export const StyledInputWrapper = styled.div` const StyledIconWrapper = styled.div` position: absolute; - height: ${({ _size }) => SIZE_TO_PX[_size]}px; - width: ${({ _size }) => SIZE_TO_PX[_size]}px; + height: ${({ $_size }) => SIZE_TO_PX[$_size]}px; + width: ${({ $_size }) => SIZE_TO_PX[$_size]}px; right: ${({ position }) => (position === 'right' ? 0 : undefined)}; top: 0; display: flex; @@ -119,33 +128,35 @@ const StyledIconWrapper = styled.div` align-items: center; `; -export const StyledInput = styled(StyledEffect)` +export const StyledInput = styled(StyledEffect).withConfig({ + shouldForwardProp: (prop) => !prop.startsWith('$'), +})` height: 100%; width: 100%; flex: 1; margin: 0; - padding: ${({ _size }) => PADDING_FROM_SIZE[_size] / 2}px 7px; - padding-right: ${({ clearable, hasRightIcon, _size }) => { + padding: ${({ $_size }) => PADDING_FROM_SIZE[$_size] / 2}px 7px; + padding-right: ${({ $clearable, $hasRightIcon, $_size }) => { let padding = 7; - if (clearable || hasRightIcon) { + if ($clearable || $hasRightIcon) { padding = 0; - padding += clearable ? SIZE_TO_PX[_size] : 0; - padding += hasRightIcon ? SIZE_TO_PX[_size] : 0; + padding += $clearable ? SIZE_TO_PX[$_size] : 0; + padding += $hasRightIcon ? SIZE_TO_PX[$_size] : 0; } return padding; }}px; - padding-left: ${({ hasIcon, _size }) => (hasIcon ? SIZE_TO_PX[_size] : 7)}px; - font-size: ${({ _size }) => CONTROL_TEXT_FROM_SIZE[_size]}px; + 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: inherit; - border: ${({ minimal, theme, flat }) => - !minimal && !flat ? `1px solid ${changeLightness(theme.main, 0.13)}` : 0}; - border-bottom: ${({ minimal, theme, flat }) => - minimal && !flat ? `0.5px solid ${changeLightness(theme.main, 0.13)}` : undefined}; + border: ${({ $minimal, theme, $flat }) => + !$minimal && !$flat ? `1px solid ${changeLightness(theme.main, 0.13)}` : 0}; + border-bottom: ${({ $minimal, theme, $flat }) => + $minimal && !$flat ? `0.5px solid ${changeLightness(theme.main, 0.13)}` : undefined}; ${({ disabled, readOnly }) => !disabled && !readOnly @@ -158,8 +169,8 @@ export const StyledInput = styled(StyledEffect)` ` : undefined} - background-color: ${({ theme, minimal, transparent }: IReqoreInputStyle) => - minimal || transparent ? 'transparent' : rgba(theme.main, 0.1)}; + background-color: ${({ theme, $minimal, transparent }: IReqoreInputStyle) => + $minimal || transparent ? 'transparent' : rgba(theme.main, 0.1)}; color: ${({ theme }: IReqoreInputStyle) => getReadableColor(theme, undefined, undefined, true, theme.originalMain)}; @@ -168,8 +179,8 @@ export const StyledInput = styled(StyledEffect)` &:active, &:focus { outline: none; - background-color: ${({ theme, minimal, transparent }: IReqoreInputStyle) => - minimal || transparent ? 'transparent' : rgba(theme.main, 0.15)}; + background-color: ${({ theme, $minimal, transparent }: IReqoreInputStyle) => + $minimal || transparent ? 'transparent' : rgba(theme.main, 0.15)}; } &::placeholder { @@ -185,11 +196,13 @@ export const StyledInput = styled(StyledEffect)` } } - ${({ readOnly }) => readOnly && ReadOnlyElement}; + ${({ $readOnly }) => $readOnly && ReadOnlyElement}; - &:disabled { - ${DisabledElement}; - } + ${({ $disabled }) => + $disabled && + css` + ${DisabledElement}; + `} `; const ReqoreInput = forwardRef( @@ -238,23 +251,23 @@ const ReqoreInput = forwardRef( {hasLeftIcon && ( - + ( onChange={!readOnly && !rest?.disabled ? rest?.onChange : undefined} ref={(ref) => setInputRef(ref)} theme={theme} - _size={size} - minimal={minimal} - flat={flat} - rounded={rounded} - hasIcon={!!icon} - hasRightIcon={!!rightIcon} - clearable={ + $_size={size} + $minimal={minimal} + $flat={flat} + $rounded={rounded} + $hasIcon={!!icon} + $hasRightIcon={!!rightIcon} + $clearable={ !rest?.disabled && !readOnly && !!(onClearClick && (rest.as || rest.children || rest?.onChange)) } className={`${className || ''} reqore-control reqore-input`} - readOnly={readOnly || loading} - pill={pill} + $readOnly={readOnly || loading} + $pill={pill} + $disabled={rest.disabled} > {rest?.children} @@ -304,7 +318,7 @@ const ReqoreInput = forwardRef( show={rest?.value && rest.value !== '' ? true : false} /> {hasRightIcon && ( - + +const getPopoverArrowColor = ({ + theme, + $dim: dim, + $intent: intent, + $flat: flat, + $effect: effect, + $isOpaque: isOpaque, +}) => rgba( effect ? changeLightness( @@ -49,7 +56,9 @@ const getPopoverArrowColor = ({ theme, dim, intent, flat, effect, isOpaque }) => dim ? 0.3 : 1 ); -const StyledPopoverArrow = styled.div<{ theme: IReqoreTheme }>` +const StyledPopoverArrow = styled.div.withConfig({ + shouldForwardProp: (prop) => !prop.startsWith('$'), +})<{ theme: IReqoreTheme }>` width: 10px; height: 10px; position: absolute; @@ -65,23 +74,40 @@ const StyledPopoverArrow = styled.div<{ theme: IReqoreTheme }>` } `; -export const StyledPopoverWrapper = styled.div<{ theme: IReqoreTheme }>` - ${({ animate }) => - animate && +interface IStyledPopoverWrapperInternal { + theme: IReqoreTheme; + $animate?: boolean; + $maxWidth?: string | number; + $minWidth?: string | number; + $maxHeight?: string | number; + $transparent?: boolean; + $intent?: any; + $flat?: boolean; + $noWrapper?: boolean; + $dim?: boolean; + $effect?: any; + $isOpaque?: boolean; +} + +export const StyledPopoverWrapper = styled.div.withConfig({ + shouldForwardProp: (prop) => !prop.startsWith('$'), +})` + ${({ $animate }) => + $animate && css` animation: 0.2s ${fadeIn} ease-out; `} - max-width: ${({ maxWidth }) => maxWidth}; - min-width: ${({ minWidth }) => minWidth}; - max-height: ${({ maxHeight }) => maxHeight}; + max-width: ${({ $maxWidth }) => $maxWidth}; + min-width: ${({ $minWidth }) => $minWidth}; + max-height: ${({ $maxHeight }) => $maxHeight}; z-index: 999999; border-radius: ${RADIUS_FROM_SIZE.normal}px; - border: ${({ flat, noWrapper, ...rest }: any) => - !flat && noWrapper ? `1px solid ${getPopoverArrowColor({ ...rest, flat })}` : undefined}; + border: ${({ $flat, $noWrapper, ...rest }: any) => + !$flat && $noWrapper ? `1px solid ${getPopoverArrowColor({ ...rest, $flat })}` : undefined}; - ${({ transparent }) => - !transparent && + ${({ $transparent }) => + !$transparent && css` box-shadow: rgba(31, 26, 34, 0.7) 0px 0px 10px; `} @@ -282,20 +308,20 @@ const InternalPopover: React.FC = memo( return createPortal( diff --git a/src/components/Layout/index.tsx b/src/components/Layout/index.tsx index 680bbbe3..67539e9b 100644 --- a/src/components/Layout/index.tsx +++ b/src/components/Layout/index.tsx @@ -11,7 +11,7 @@ export interface IReqoreLayoutWrapperProps } const StyledReqoreLayoutWrapper = styled.div<{ - withSidebar: boolean; + $withSidebar: boolean; theme: IReqoreTheme; }>` display: flex; @@ -28,8 +28,8 @@ const StyledReqoreLayoutWrapper = styled.div<{ text-decoration: none; } - ${({ withSidebar, theme }) => css` - flex-flow: ${withSidebar ? 'row' : 'column'}; + ${({ $withSidebar, theme }) => css` + flex-flow: ${$withSidebar ? 'row' : 'column'}; background-color: ${changeLightness(theme.main, 0.02)}; color: ${getReadableColor(theme, undefined, undefined, true)}; `} @@ -45,7 +45,7 @@ const ReqoreLayoutWrapper = ({ {children} diff --git a/src/components/Menu/index.tsx b/src/components/Menu/index.tsx index d4b68977..361836ad 100644 --- a/src/components/Menu/index.tsx +++ b/src/components/Menu/index.tsx @@ -45,38 +45,46 @@ export interface IReqoreMenuProps export interface IReqoreMenuStyle extends IReqoreMenuProps { theme: IReqoreTheme; + $padded?: boolean; + $size?: string; + $transparent?: boolean; + $rounded?: boolean; + $position?: 'left' | 'right'; + $isResizableLeft?: boolean; + $isResizableRight?: boolean; + $showResizableBorder?: boolean; } const StyledReqoreMenu = styled.div` width: ${({ width }) => width || undefined}; min-width: ${({ width }) => (width ? undefined : '160px')}; - padding: ${({ padded = true, _size }) => - padded ? `${HALF_PADDING_FROM_SIZE[_size]}px` : undefined}; + padding: ${({ $padded = true, $size }) => + $padded ? `${HALF_PADDING_FROM_SIZE[$size]}px` : undefined}; max-height: ${({ maxHeight }) => maxHeight || undefined}; overflow-y: auto; overflow-x: hidden; display: flex; flex-flow: column nowrap; - background-color: ${({ theme, transparent }) => - transparent ? 'transparent' : changeDarkness(getMainBackgroundColor(theme), 0.03)}; - border-radius: ${({ rounded, _size }) => (rounded ? `${RADIUS_FROM_SIZE[_size]}px` : `0`)}; + background-color: ${({ theme, $transparent }) => + $transparent ? 'transparent' : changeDarkness(getMainBackgroundColor(theme), 0.03)}; + border-radius: ${({ $rounded, $size }) => ($rounded ? `${RADIUS_FROM_SIZE[$size]}px` : `0`)}; - ${({ theme, position, _size, padded, isResizableLeft, showResizableBorder }) => - position === 'right' || (isResizableLeft && showResizableBorder) + ${({ theme, $position, $size, $padded, $isResizableLeft, $showResizableBorder }) => + $position === 'right' || ($isResizableLeft && $showResizableBorder) ? css` - border-left: 1px ${isResizableLeft && showResizableBorder ? 'dashed' : 'solid'} + border-left: 1px ${$isResizableLeft && $showResizableBorder ? 'dashed' : 'solid'} ${changeLightness(theme.main, 0.05)}; - padding-left: ${!padded ? `${HALF_PADDING_FROM_SIZE[_size]}px` : undefined}; + padding-left: ${!$padded ? `${HALF_PADDING_FROM_SIZE[$size]}px` : undefined}; ` : undefined} - ${({ theme, position, _size, padded, isResizableRight, showResizableBorder }) => - position === 'left' || (isResizableRight && showResizableBorder) + ${({ theme, $position, $size, $padded, $isResizableRight, $showResizableBorder }) => + $position === 'left' || ($isResizableRight && $showResizableBorder) ? css` - border-right: 1px ${isResizableRight && showResizableBorder ? 'dashed' : 'solid'} + border-right: 1px ${$isResizableRight && $showResizableBorder ? 'dashed' : 'solid'} ${changeLightness(theme.main, 0.05)}; - padding-right: ${!padded ? `${HALF_PADDING_FROM_SIZE[_size]}px` : undefined}; + padding-right: ${!$padded ? `${HALF_PADDING_FROM_SIZE[$size]}px` : undefined}; ` : undefined} `; @@ -122,11 +130,16 @@ const ReqoreMenu = memo( {...rest} {...resizable} as={!!resizable ? Resizable : 'div'} - isResizableRight={resizable?.enable?.right} - isResizableLeft={resizable?.enable?.left} - flat={flat} - position={position} - _size={size} + width={rest.width} + maxHeight={rest.maxHeight} + $isResizableRight={resizable?.enable?.right} + $isResizableLeft={resizable?.enable?.left} + $position={position} + $size={size} + $padded={rest.padded} + $transparent={rest.transparent} + $rounded={rest.rounded} + $showResizableBorder={rest.showResizableBorder} className={`${rest.className || ''} reqore-menu`} theme={theme} ref={(curRef) => { diff --git a/src/components/Message/index.tsx b/src/components/Message/index.tsx index 15c5bdf6..e3d59da5 100644 --- a/src/components/Message/index.tsx +++ b/src/components/Message/index.tsx @@ -82,6 +82,7 @@ const ReqoreMessage = memo( fixed, fluid, iconProps, + margin, ...rest }, ref @@ -136,18 +137,22 @@ const ReqoreMessage = memo( Component={StyledReqoreNotification} as={animated.div} key={`${duration}${intent}${title}${children}`} - intent={intent} - timeout={duration} - clickable={!!onClick} + // Transient props to StyledReqoreNotification + $intent={intent} + $timeout={duration} + $clickable={!!onClick} onClick={onClick} - flat={flat} - minimal={minimal} - asMessage - fluid={fluid} - fixed={fixed} + $flat={flat} + $minimal={minimal} + $asMessage + $fluid={fluid} + $fixed={fixed} + // Map public margin to transient styled prop + margin={margin} + $margin={margin} className={`${rest?.className || ''} reqore-message`} ref={ref} - size={size} + $size={size} theme={theme} effect={effect} > diff --git a/src/components/Navbar/divider.tsx b/src/components/Navbar/divider.tsx index 0fa0afb3..b8c68fb3 100644 --- a/src/components/Navbar/divider.tsx +++ b/src/components/Navbar/divider.tsx @@ -10,7 +10,7 @@ export interface IReqoreNavbarDividerProps extends React.HTMLAttributes` position: relative; width: 20px; @@ -24,13 +24,13 @@ const StyledNavbarDivider = styled.div<{ display: block; width: 1px; height: 50%; - background-color: ${({ theme, type }) => + background-color: ${({ theme, $type }) => getReadableColor( theme, undefined, undefined, true, - theme[type]?.background || theme[type]?.main || theme.main + theme[$type]?.background || theme[$type]?.main || theme.main )}; opacity: 0.2; } @@ -40,7 +40,7 @@ const ReqoreNavbarDivider = forwardRef ( diff --git a/src/components/Navbar/group.tsx b/src/components/Navbar/group.tsx index e3ab0c52..8cb19f80 100644 --- a/src/components/Navbar/group.tsx +++ b/src/components/Navbar/group.tsx @@ -11,12 +11,13 @@ export interface IReqoreNavbarGroupProps extends React.HTMLAttributes` - ${({ position }: IReqoreNavbarGroupStyle) => css` + ${({ $position }: IReqoreNavbarGroupStyle) => css` height: 100%; - float: ${position}; + float: ${$position}; color: inherit; background-color: inherit; display: flex; @@ -30,7 +31,7 @@ const ReqoreNavbarGroup = forwardRef( {React.Children.map(children, (child) => diff --git a/src/components/Navbar/index.tsx b/src/components/Navbar/index.tsx index 56dd4ccc..0f68a478 100644 --- a/src/components/Navbar/index.tsx +++ b/src/components/Navbar/index.tsx @@ -15,32 +15,34 @@ export interface IReqoreNavbarProps extends React.HTMLAttributes export interface IReqoreNavbarStyle extends IReqoreNavbarProps { theme: IReqoreTheme; + $type?: 'header' | 'footer'; + $flat?: boolean; } export const StyledNavbar = styled.div` - ${({ theme, type }: IReqoreNavbarStyle) => css` + ${({ theme, $type }: IReqoreNavbarStyle) => css` height: 50px; flex-shrink: 0; width: 100%; padding: 0 10px; - color: ${theme[type]?.color || + color: ${theme[$type]?.color || getReadableColor( theme, undefined, undefined, true, - theme[type]?.background || theme[type]?.main || theme.main + theme[$type]?.background || theme[$type]?.main || theme.main )}; - background-color: ${theme[type]?.background || theme[type]?.main || theme.main}; + background-color: ${theme[$type]?.background || theme[$type]?.main || theme.main}; `} - ${({ theme, type, flat }: IReqoreNavbarStyle) => - !flat + ${({ theme, $type, $flat }: IReqoreNavbarStyle) => + !$flat ? css` - box-shadow: rgba(31, 26, 34, 0.05) 0px ${type === 'header' ? '2px' : '-2px'} 6px; + box-shadow: rgba(31, 26, 34, 0.05) 0px ${$type === 'header' ? '2px' : '-2px'} 6px; - border-${type === 'header' ? 'bottom' : 'top'}: 1px solid ${ - theme[type]?.border || darken(0.05, getMainColor(theme, type)) + border-${$type === 'header' ? 'bottom' : 'top'}: 1px solid ${ + theme[$type]?.border || darken(0.05, getMainColor(theme, $type)) }; ` : undefined} @@ -55,7 +57,8 @@ const ReqoreNavbar = forwardRef( {...rest} className={`${rest.className || ''} reqore-navbar-${type}`} position={position} - type={type} + $type={type} + $flat={rest.flat} ref={ref} theme={theme} > diff --git a/src/components/Navbar/item.tsx b/src/components/Navbar/item.tsx index 523f4bff..683002cd 100644 --- a/src/components/Navbar/item.tsx +++ b/src/components/Navbar/item.tsx @@ -20,6 +20,10 @@ export interface IReqoreNavbarItemProps extends React.HTMLAttributes` @@ -30,13 +34,13 @@ const StyledNavbarItem = styled.div` align-items: center; padding: 10px; color: inherit; - background-color: ${({ theme, active, type }) => - active && (theme[type]?.hoverColor || changeLightness(getMainColor(theme, type), 0.05))}; + background-color: ${({ theme, $active, $type }) => + $active && (theme[$type]?.hoverColor || changeLightness(getMainColor(theme, $type), 0.05))}; ${InactiveIconScale} - ${({ active }: IReqoreNavbarItemStyle) => { - if (active) { + ${({ $active }: IReqoreNavbarItemStyle) => { + if ($active) { return css` ${ActiveIconScale} `; @@ -45,14 +49,14 @@ const StyledNavbarItem = styled.div` return undefined; }} - ${({ interactive, theme, type }: IReqoreNavbarItemStyle) => { - if (interactive) { + ${({ $interactive, theme, $type }: IReqoreNavbarItemStyle) => { + if ($interactive) { return css` transition: background-color 0.2s ease-out; cursor: pointer; &:hover { - background-color: ${theme[type]?.hoverColor || - changeLightness(getMainColor(theme, type), 0.05)}; + background-color: ${theme[$type]?.hoverColor || + changeLightness(getMainColor(theme, $type), 0.05)}; } ${ScaleIconOnHover} `; @@ -61,10 +65,10 @@ const StyledNavbarItem = styled.div` return undefined; }} - opacity: ${({ disabled }) => disabled && 0.3}; + opacity: ${({ $disabled }) => $disabled && 0.3}; - ${({ disabled }) => - disabled && + ${({ $disabled }) => + $disabled && css` ${DisabledElement} `} @@ -78,10 +82,10 @@ const ReqoreNavbarItem = forwardRef( {children} diff --git a/src/components/Notifications/index.tsx b/src/components/Notifications/index.tsx index a63b85b9..ecc608e9 100644 --- a/src/components/Notifications/index.tsx +++ b/src/components/Notifications/index.tsx @@ -15,25 +15,25 @@ export type IReqoreNotificationsPosition = | 'BOTTOM RIGHT'; export interface IReqoreNotificationsStyle { - positions: string[]; + $positions: string[]; } const StyledNotificationsWrapper = styled.div` position: fixed; z-index: 10000; padding: 0 20px 20px 20px; - ${({ positions }) => { - const hasTwoPositions = positions.length > 1; + ${({ $positions }) => { + const hasTwoPositions = $positions.length > 1; if (hasTwoPositions) { return css` - ${positions[0]}: 30px; - ${positions[1]}: 30px; + ${$positions[0]}: 30px; + ${$positions[1]}: 30px; `; } return css` - ${positions[0]}: 30px; + ${$positions[0]}: 30px; left: 50%; transform: translateX(-50%); `; @@ -45,7 +45,7 @@ const ReqoreNotificationsWrapper: React.FC = f IReqoreNotificationsWrapperProps >(({ children, position = 'TOP' }, ref: any) => ( p.toLowerCase())} ref={ref} diff --git a/src/components/Notifications/notification.tsx b/src/components/Notifications/notification.tsx index 82446f52..ea4d326a 100644 --- a/src/components/Notifications/notification.tsx +++ b/src/components/Notifications/notification.tsx @@ -50,17 +50,21 @@ export interface IReqoreNotificationProps export interface IReqoreNotificationStyle extends IWithReqoreOpaque { theme: IReqoreTheme; - type?: IReqoreNotificationType; - clickable?: boolean; - timeout?: number; - intent?: TReqoreIntent; - hasShadow?: boolean; - fluid?: boolean; - flat?: boolean; - minimal?: boolean; - size?: TSizes; - asMessage?: boolean; - margin?: 'top' | 'bottom' | 'both' | 'none'; + // Transient ($) internal styling props consumed by styled component + $type?: IReqoreNotificationType; + $clickable?: boolean; + $timeout?: number; + $intent?: TReqoreIntent; + $hasShadow?: boolean; + $fluid?: boolean; + $flat?: boolean; + $minimal?: boolean; + $size?: TSizes; + $asMessage?: boolean; + $margin?: 'top' | 'bottom' | 'both' | 'none'; + $fixed?: boolean; + $opaque?: boolean; // mapped from public opaque + $maxWidth?: string; // mapped from public maxWidth } const timeoutAnimation = keyframes` @@ -72,29 +76,32 @@ const timeoutAnimation = keyframes` } `; -export const StyledReqoreNotification = styled(StyledEffect)` - min-width: ${({ fluid }) => (!fluid ? '30px' : undefined)}; - max-width: ${({ maxWidth, fluid, fixed }) => maxWidth || (fluid && !fixed ? '100%' : undefined)}; +export const StyledReqoreNotification = styled(StyledEffect).withConfig({ + shouldForwardProp: (prop) => !prop.startsWith('$'), +})` + min-width: ${({ $fluid }) => (!$fluid ? '30px' : undefined)}; + max-width: ${({ $maxWidth, $fluid, $fixed }) => + $maxWidth || ($fluid && !$fixed ? '100%' : undefined)}; border-radius: 5px; display: flex; - flex: ${({ fluid, fixed }) => (fixed ? '0 0 auto' : fluid ? '1 auto' : '0 0 auto')}; - align-self: ${({ fixed, fluid }) => (fixed ? 'flex-start' : fluid ? 'stretch' : undefined)}; + flex: ${({ $fluid, $fixed }) => ($fixed ? '0 0 auto' : $fluid ? '1 auto' : '0 0 auto')}; + align-self: ${({ $fixed, $fluid }) => ($fixed ? 'flex-start' : $fluid ? 'stretch' : undefined)}; overflow: hidden; position: relative; transition: all 0.2s ease-out; - ${({ margin, size = 'normal' }) => css` - margin-top: ${margin === 'top' || margin === 'both' - ? `${PADDING_FROM_SIZE[size]}px` + ${({ $margin, $size = 'normal' }) => css` + margin-top: ${$margin === 'top' || $margin === 'both' + ? `${PADDING_FROM_SIZE[$size]}px` : undefined}; - margin-bottom: ${margin === 'bottom' || margin === 'both' - ? `${PADDING_FROM_SIZE[size]}px` + margin-bottom: ${$margin === 'bottom' || $margin === 'both' + ? `${PADDING_FROM_SIZE[$size]}px` : undefined}; `}; // Do not fade in the component if it's a message - ${({ asMessage }) => { - if (asMessage) { + ${({ $asMessage }) => { + if ($asMessage) { return undefined; } return css` @@ -103,35 +110,35 @@ export const StyledReqoreNotification = styled(StyledEffect) - asMessage ? undefined : `${PADDING_FROM_SIZE[size]}px`}; + margin-top: ${({ $asMessage, $size }) => + $asMessage ? undefined : `${PADDING_FROM_SIZE[$size]}px`}; } ${({ theme, - type, - intent, - clickable, - timeout, - hasShadow, - flat, - minimal, - opaque = true, + $type, + $intent, + $clickable, + $timeout, + $hasShadow, + $flat, + $minimal, + $opaque = true, }: IReqoreNotificationStyle) => css` - background-color: ${minimal + background-color: ${$minimal ? 'transparent' - : opaque - ? changeLightness(theme.main, 0.1) - : rgba(getNotificationIntent(theme, intent || type), 0.3)}; - border: ${flat ? 0 : '1px solid'}; - border-color: ${changeLightness(getNotificationIntent(theme, intent || type), 0.2)}; + : $opaque + ? changeLightness(getNotificationIntent(theme, $intent || $type), 0.1) + : rgba(getNotificationIntent(theme, $intent || $type), 0.3)}; + border: ${$flat ? 0 : '1px solid'}; + border-color: ${changeLightness(getNotificationIntent(theme, $intent || $type), 0.2)}; - ${hasShadow && + ${$hasShadow && css` box-shadow: 0px 0px 30px 10px ${rgba('#000000', 0.3)}; `} - ${timeout && + ${$timeout && css` &::before { content: ''; @@ -139,32 +146,34 @@ export const StyledReqoreNotification = styled(StyledEffect)` +export const StyledIconWrapper = styled.div< + Pick +>` flex: 0 1 auto; flex-shrink: 0; display: flex; @@ -172,8 +181,8 @@ export const StyledIconWrapper = styled.div` align-items: center; transition: all 0.2s ease-out; - ${({ clickable, theme, intent, type }) => - clickable && + ${({ $clickable, theme, $intent, $type }) => + $clickable && css` margin-top: unset; height: unset; @@ -184,7 +193,7 @@ export const StyledIconWrapper = styled.div` &:hover { cursor: pointer; - background-color: ${changeLightness(getNotificationIntent(theme, intent || type), 0.02)}; + background-color: ${changeLightness(getNotificationIntent(theme, $intent || $type), 0.02)}; .reqore-icon { transform: scale(1); } @@ -192,7 +201,7 @@ export const StyledIconWrapper = styled.div` `} `; -export const StyledNotificationInnerContent = styled.div` +export const StyledNotificationInnerContent = styled.div` flex: 1; display: flex; flex-flow: column; @@ -203,11 +212,11 @@ export const StyledNotificationInnerContent = styled.div` +export const StyledNotificationContentWrapper = styled.div<{ $size?: TSizes }>` flex: 1; display: flex; justify-content: center; - padding: ${({ size = 'normal' }: IReqoreNotificationStyle) => `${PADDING_FROM_SIZE[size]}px`}; + padding: ${({ $size = 'normal' }: IReqoreNotificationStyle) => `${PADDING_FROM_SIZE[$size]}px`}; `; export const StyledNotificationTitle = styled.h4` @@ -217,7 +226,11 @@ export const StyledNotificationTitle = styled.h4` align-items: center; `; -export const StyledNotificationContent = styled.div` +export const StyledNotificationContent = styled.div<{ + hasTitle?: boolean; + size?: TSizes; + theme?: IReqoreTheme; +}>` margin: 0; padding: 0; flex: 1; @@ -248,23 +261,26 @@ const ReqoreNotification = forwardRef( { type, intent, - icon, title, content, + icon, onClose, onClick, duration, onFinish, + fluid, flat, + size = 'normal', minimal, opaque = true, - size = 'normal', customTheme, + // transient-like external props + ...rest }, - ref: any + ref ) => { - const [internalTimeout, setInternalTimeout] = useState(null); - const theme = useReqoreTheme('main', customTheme, type || intent, 'notifications'); + const [internalTimeout, setInternalTimeout] = useState(null); + const theme = useReqoreTheme('main', customTheme, intent || type); const transitions = useTransition(true, { from: { opacity: 0, transform: 'scale(0.9)' }, @@ -307,22 +323,26 @@ const ReqoreNotification = forwardRef( onClick?.()} - flat={flat} - minimal={minimal} + $flat={flat} + $minimal={minimal} className='reqore-notification' ref={ref} style={styles} - size={size} - opaque={opaque} + $size={size} + $opaque={opaque} theme={theme} - maxWidth='450px' + $maxWidth='450px' + $fluid={fluid} + $fixed={(rest as any)?.fixed} + // Forward effect if provided for custom colors / gradients + effect={(rest as any)?.effect} > - + {type || intent || icon ? ( <> {intent === 'pending' || type === 'pending' ? ( @@ -345,9 +365,9 @@ const ReqoreNotification = forwardRef( {onClose ? ( { event.stopPropagation(); diff --git a/src/components/Panel/index.tsx b/src/components/Panel/index.tsx index 7d870444..462599b0 100644 --- a/src/components/Panel/index.tsx +++ b/src/components/Panel/index.tsx @@ -150,11 +150,37 @@ export interface IReqorePanelProps responsiveActionsWrapperProps?: Partial; } +// Internal styled props (transient) to prevent passing non-DOM props to HTML elements export interface IStyledPanel extends IReqorePanelProps { theme: IReqoreTheme; noHorizontalPadding?: boolean; } +// Transient version used inside styled-components (prefixed with $ when mapped) +interface IStyledPanelInternal { + $theme: IReqoreTheme; + $noHorizontalPadding?: boolean; + $flat?: boolean; + $intent?: TReqoreIntent; + $rounded?: boolean; + $interactive?: boolean; + $opacity?: number; + $fluid?: boolean; + $blur?: number; + $isCollapsed?: boolean; + $collapsible?: boolean; + $minimal?: boolean; + $size?: TSizes; + $padded?: boolean; + $wrapperPadding?: 'top' | 'bottom' | 'both' | 'none'; + $transparent?: boolean; + $hasLabel?: boolean; + $hasBottomActions?: boolean; + $disabled?: boolean; + $responsive?: boolean; + $isMobile?: boolean; +} + export const StyledPanelTitleHeader = styled.div` display: flex; justify-content: flex-start; @@ -204,26 +230,29 @@ export type TPanelStyle = React.FC< } >; -export const StyledPanel: TPanelStyle = styled(StyledEffect)` - background-color: ${({ theme, opacity = 1 }: IStyledPanel) => - rgba(changeDarkness(getMainBackgroundColor(theme), 0.03), opacity)}; - border-radius: ${({ rounded }) => (rounded ? RADIUS_FROM_SIZE.normal : 0)}px; - border: ${({ theme, flat, intent }) => - flat && !intent +export const StyledPanel: TPanelStyle = styled(StyledEffect).withConfig({ + shouldForwardProp: (prop) => !prop.startsWith('$'), +})` + background-color: ${({ $theme, $opacity = 1 }: IStyledPanelInternal) => + rgba(changeDarkness(getMainBackgroundColor($theme), 0.03), $opacity)}; + border-radius: ${({ $rounded }) => ($rounded ? RADIUS_FROM_SIZE.normal : 0)}px; + border: ${({ $theme, $flat, $intent }) => + $flat && !$intent ? undefined : `1px solid ${changeLightness( - intent ? theme.intents[intent] : getMainBackgroundColor(theme), + $intent ? $theme.intents[$intent] : getMainBackgroundColor($theme), 0.08 )}`}; - color: ${({ theme }) => getReadableColor(theme, undefined, undefined, true)}; + color: ${({ $theme }) => getReadableColor($theme, undefined, undefined, true)}; overflow: hidden; display: flex; flex-flow: column; position: relative; - backdrop-filter: ${({ blur, opacity }) => (blur && opacity < 1 ? `blur(${blur}px)` : undefined)}; - width: ${({ fluid }) => (fluid ? '100%' : undefined)}; + backdrop-filter: ${({ $blur, $opacity }) => + $blur && $opacity < 1 ? `blur(${$blur}px)` : undefined}; + width: ${({ $fluid }) => ($fluid ? '100%' : undefined)}; max-width: 100%; - flex: ${({ fluid }) => (fluid ? '1 auto' : '0 0 auto')}; + flex: ${({ $fluid }) => ($fluid ? '1 auto' : '0 0 auto')}; &:not(:hover) { .reqore-panel-action-hidden { @@ -231,8 +260,8 @@ export const StyledPanel: TPanelStyle = styled(StyledEffect)` } } - ${({ interactive, theme, opacity = 1, flat, intent }) => - interactive + ${({ $interactive, $theme, $opacity = 1, $flat, $intent }) => + $interactive ? css` cursor: pointer; @@ -241,21 +270,24 @@ export const StyledPanel: TPanelStyle = styled(StyledEffect)` transform: scale(${ACTIVE_ICON_SCALE}); } - background-color: ${opacity === 0 && flat + background-color: ${$opacity === 0 && $flat ? undefined : rgba( - darken(0.025, rgba(changeDarkness(getMainBackgroundColor(theme), 0.03), opacity)), - opacity + darken( + 0.025, + rgba(changeDarkness(getMainBackgroundColor($theme), 0.03), $opacity) + ), + $opacity )}; - border-color: ${flat && !intent + border-color: ${$flat && !$intent ? undefined : `${changeLightness( - intent ? theme.intents[intent] : getMainBackgroundColor(theme), + $intent ? $theme.intents[$intent] : getMainBackgroundColor($theme), 0.25 )}`}; - ${opacity !== 0 && + ${$opacity !== 0 && css` ${StyledCollectionItemContent}:after { background: linear-gradient( @@ -263,9 +295,9 @@ export const StyledPanel: TPanelStyle = styled(StyledEffect)` ${rgba( darken( 0.025, - rgba(changeDarkness(getMainBackgroundColor(theme), 0.03), opacity) + rgba(changeDarkness(getMainBackgroundColor($theme), 0.03), $opacity) ), - opacity + $opacity )} 0%, transparent 100% @@ -276,33 +308,42 @@ export const StyledPanel: TPanelStyle = styled(StyledEffect)` ` : undefined} - ${({ fill, isCollapsed }) => - !isCollapsed && fill + ${({ fill, $isCollapsed }) => + !$isCollapsed && fill ? css` height: 100%; flex: 1; ` : undefined} - ${({ disabled }) => disabled && DisabledElement} + ${({ $disabled }) => $disabled && DisabledElement} `; -export const StyledPanelTitle = styled.div` +export const StyledPanelTitle = styled.div.withConfig({ + shouldForwardProp: (prop) => !prop.startsWith('$'), +})` display: flex; - flex-flow: ${({ responsive, isMobile }) => (responsive ? (isMobile ? 'column' : 'row') : 'row')}; - background-color: ${({ theme, opacity = 1 }: IStyledPanel) => - rgba(changeLightness(getMainBackgroundColor(theme), 0.03), opacity)}; + flex-flow: ${({ $responsive, $isMobile }) => + $responsive ? ($isMobile ? 'column' : 'row') : 'row'}; + background-color: ${({ $theme, $opacity = 1 }: IStyledPanelInternal) => + rgba(changeLightness(getMainBackgroundColor($theme), 0.03), $opacity)}; justify-content: space-between; - padding: ${({ noHorizontalPadding, size, transparent, flat, intent }: IStyledPanel) => - `${transparent && flat && !intent ? 0 : PADDING_FROM_SIZE[size]}px ${ - noHorizontalPadding ? 0 : `${PADDING_FROM_SIZE[size]}px` + padding: ${({ + $noHorizontalPadding, + $size, + $transparent, + $flat, + $intent, + }: IStyledPanelInternal) => + `${$transparent && $flat && !$intent ? 0 : PADDING_FROM_SIZE[$size]}px ${ + $noHorizontalPadding ? 0 : `${PADDING_FROM_SIZE[$size]}px` }`}; align-items: center; - border-bottom: ${({ theme, isCollapsed, flat, opacity = 1 }) => - !isCollapsed && !flat && opacity - ? `1px solid ${rgba(changeLightness(getMainBackgroundColor(theme), 0.08), opacity)}` + border-bottom: ${({ $theme, $isCollapsed, $flat, $opacity = 1 }) => + !$isCollapsed && !$flat && $opacity + ? `1px solid ${rgba(changeLightness(getMainBackgroundColor($theme), 0.08), $opacity)}` : null}; transition: background-color 0.2s ease-out; overflow: hidden; @@ -313,8 +354,8 @@ export const StyledPanelTitle = styled.div` transform: scale(${INACTIVE_ICON_SCALE}); } - ${({ collapsible }) => - collapsible && + ${({ $collapsible }) => + $collapsible && css` cursor: pointer; &:hover { @@ -322,67 +363,71 @@ export const StyledPanelTitle = styled.div` transform: scale(${ACTIVE_ICON_SCALE}); } - background-color: ${({ theme, opacity = 1 }: IStyledPanel) => - rgba(changeLightness(getMainBackgroundColor(theme), 0.05), opacity)}; + background-color: ${({ $theme, $opacity = 1 }: IStyledPanelInternal) => + rgba(changeLightness(getMainBackgroundColor($theme), 0.05), $opacity)}; } `} `; export const StyledPanelTopBar = styled(StyledPanelTitle)` - min-height: ${({ size, wrapperPadding }) => - SIZE_TO_PX[size] + - (wrapperPadding === 'both' || wrapperPadding === 'top' ? PADDING_FROM_SIZE[size] * 2 : 0)}px; - padding-bottom: ${({ padded, size, isCollapsed }: IStyledPanel) => - !padded || isCollapsed ? `${PADDING_FROM_SIZE[size]}px` : undefined}; - padding-top: ${({ minimal, size, wrapperPadding }: IStyledPanel) => - wrapperPadding === 'bottom' || wrapperPadding === 'none' + min-height: ${({ $size, $wrapperPadding }: IStyledPanelInternal) => + SIZE_TO_PX[$size] + + ($wrapperPadding === 'both' || $wrapperPadding === 'top' ? PADDING_FROM_SIZE[$size] * 2 : 0)}px; + padding-bottom: ${({ $padded, $size, $isCollapsed }: IStyledPanelInternal) => + !$padded || $isCollapsed ? `${PADDING_FROM_SIZE[$size]}px` : undefined}; + padding-top: ${({ $minimal, $size, $wrapperPadding }: IStyledPanelInternal) => + $wrapperPadding === 'bottom' || $wrapperPadding === 'none' ? undefined - : minimal - ? `${PADDING_FROM_SIZE[size]}px` + : $minimal + ? `${PADDING_FROM_SIZE[$size]}px` : undefined}; `; export const StyledPanelBottomActions = styled(StyledPanelTitle)` - min-height: ${({ size, wrapperPadding }) => - SIZE_TO_PX[size] + - (wrapperPadding === 'both' || wrapperPadding === 'bottom' ? PADDING_FROM_SIZE[size] * 2 : 0)}px; - padding-top: ${({ padded, size }: IStyledPanel) => - !padded ? `${PADDING_FROM_SIZE[size]}px` : undefined}; - padding-bottom: ${({ minimal, size, wrapperPadding }: IStyledPanel) => - wrapperPadding === 'top' || wrapperPadding === 'none' + min-height: ${({ $size, $wrapperPadding }: IStyledPanelInternal) => + SIZE_TO_PX[$size] + + ($wrapperPadding === 'both' || $wrapperPadding === 'bottom' + ? PADDING_FROM_SIZE[$size] * 2 + : 0)}px; + padding-top: ${({ $padded, $size }: IStyledPanelInternal) => + !$padded ? `${PADDING_FROM_SIZE[$size]}px` : undefined}; + padding-bottom: ${({ $minimal, $size, $wrapperPadding }: IStyledPanelInternal) => + $wrapperPadding === 'top' || $wrapperPadding === 'none' ? undefined - : minimal - ? `${PADDING_FROM_SIZE[size]}px` + : $minimal + ? `${PADDING_FROM_SIZE[$size]}px` : undefined}; border-bottom: 0; - border-top: ${({ theme, flat, opacity = 1 }) => - !flat - ? `1px solid ${rgba(changeLightness(getMainBackgroundColor(theme), 0.08), opacity)}` + border-top: ${({ $theme, $flat, $opacity = 1 }) => + !$flat + ? `1px solid ${rgba(changeLightness(getMainBackgroundColor($theme), 0.08), $opacity)}` : null}; `; -export const StyledPanelContent = styled.div` - display: ${({ isCollapsed }) => (isCollapsed ? 'none !important' : undefined)}; - padding: ${({ padded, size, noHorizontalPadding, minimal }) => - !padded +export const StyledPanelContent = styled.div.withConfig({ + shouldForwardProp: (prop) => !prop.startsWith('$'), +})` + display: ${({ $isCollapsed }) => ($isCollapsed ? 'none !important' : undefined)}; + padding: ${({ $padded, $size, $noHorizontalPadding, $minimal }: IStyledPanelInternal) => + !$padded ? undefined - : noHorizontalPadding - ? `${PADDING_FROM_SIZE[size]}px 0` - : `${PADDING_FROM_SIZE[size] / (minimal ? 2 : 1)}px ${PADDING_FROM_SIZE[size]}px`}; + : $noHorizontalPadding + ? `${PADDING_FROM_SIZE[$size]}px 0` + : `${PADDING_FROM_SIZE[$size] / ($minimal ? 2 : 1)}px ${PADDING_FROM_SIZE[$size]}px`}; // The padding is not needed when the panel is minimal and has title, since // the title already has padding and is transparent - padding-top: ${({ minimal, hasLabel, padded, size }) => - minimal && hasLabel && padded ? 0 : padded ? `${PADDING_FROM_SIZE[size]}px` : undefined}; - padding-bottom: ${({ minimal, padded, size, hasBottomActions }) => - minimal && hasBottomActions && padded + padding-top: ${({ $minimal, $hasLabel, $padded, $size }: IStyledPanelInternal) => + $minimal && $hasLabel && $padded ? 0 : $padded ? `${PADDING_FROM_SIZE[$size]}px` : undefined}; + padding-bottom: ${({ $minimal, $padded, $size, $hasBottomActions }: IStyledPanelInternal) => + $minimal && $hasBottomActions && $padded ? 0 - : padded - ? `${PADDING_FROM_SIZE[size]}px` + : $padded + ? `${PADDING_FROM_SIZE[$size]}px` : undefined}; flex: 1; overflow: auto; overflow-wrap: anywhere; - font-size: ${({ size }) => TEXT_FROM_SIZE[size]}px; + font-size: ${({ $size }: IStyledPanelInternal) => TEXT_FROM_SIZE[$size]}px; `; export const ReqorePanelSkeleton = memo( @@ -786,39 +831,50 @@ export const ReqorePanel = forwardRef( {...omit(rest, ['onResize'])} {..._resizable} as={rest.as || (!!resizable && !disabled && !_isCollapsed) ? Resizable : 'div'} + // Public props (may be used by tooltip / effect logic) isCollapsed={_isCollapsed} rounded={rounded} flat={flat} intent={intent} - className={`${className || ''} reqore-panel`} interactive={interactive} theme={theme} - effect={transformedContentEffect} opacity={opacity} fluid={fluid} disabled={disabled} + // Transient props consumed by StyledPanel + $isCollapsed={_isCollapsed} + $rounded={rounded} + $flat={flat} + $intent={intent} + $interactive={interactive} + $theme={theme} + $opacity={opacity} + $fluid={fluid} + $disabled={disabled} + className={`${className || ''} reqore-panel`} + effect={transformedContentEffect} Component={StyledPanel} ref={handleRef} > {hasTitleBar && ( {hasTitleHeader && ( @@ -957,31 +1013,31 @@ export const ReqorePanel = forwardRef( {children} ) : null} {hasBottomActions && !_isCollapsed ? ( {hasNonResponsiveActions(leftBottomActions) ? ( diff --git a/src/components/Spacer/index.tsx b/src/components/Spacer/index.tsx index a2a82caa..3ae7b398 100644 --- a/src/components/Spacer/index.tsx +++ b/src/components/Spacer/index.tsx @@ -7,17 +7,17 @@ import { IReqoreIntent, IWithReqoreCustomTheme, IWithReqoreEffect } from '../../ import { StyledEffect } from '../Effect'; export const StyledSpacer = styled.div` - display: ${({ horizontal }) => (horizontal ? 'inline-flex' : 'flex')}; + display: ${({ $horizontal }) => ($horizontal ? 'inline-flex' : 'flex')}; vertical-align: middle; - ${({ horizontal, vertical }) => { - if (horizontal) { + ${({ $horizontal, $vertical }) => { + if ($horizontal) { return css` flex-flow: row; `; } - if (vertical) { + if ($vertical) { return css` flex-flow: column; `; @@ -31,18 +31,18 @@ export const StyledSpace = styled.div` display: inline-block; flex: 0 0 auto; - ${({ horizontal, vertical, width, height, lineSize }) => { - if (horizontal) { + ${({ $horizontal, $vertical, $width, $height, $lineSize }) => { + if ($horizontal) { return css` - width: ${width / 2 - lineSize / 2}px; - height: ${height ? `${height}px` : `${TEXT_FROM_SIZE.normal}px`}; + width: ${$width / 2 - $lineSize / 2}px; + height: ${$height ? `${$height}px` : `${TEXT_FROM_SIZE.normal}px`}; `; } - if (vertical) { + if ($vertical) { return css` - width: ${width ? `${width}px` : '100%'}; - height: ${height / 2 - lineSize / 2}px; + width: ${$width ? `${$width}px` : '100%'}; + height: ${$height / 2 - $lineSize / 2}px; `; } }} @@ -50,21 +50,21 @@ export const StyledSpace = styled.div` export const StyledLine = styled(StyledEffect)` flex-shrink: 0; - background-color: ${({ theme, lineSize }) => - lineSize === 'none' ? 'transparent' : changeLightness(theme.main, 0.2)}; + background-color: ${({ theme, $lineSize }) => + $lineSize === 'none' ? 'transparent' : changeLightness(theme.main, 0.2)}; - ${({ horizontal, vertical, width, height, lineSize }) => { - if (horizontal) { + ${({ $horizontal, $vertical, $width, $height, $lineSize }) => { + if ($horizontal) { return css` - width: ${lineSize}px; - height: ${height ? `${height}px` : `${TEXT_FROM_SIZE.normal}px`}; + width: ${$lineSize}px; + height: ${$height ? `${$height}px` : `${TEXT_FROM_SIZE.normal}px`}; `; } - if (vertical) { + if ($vertical) { return css` - width: ${width ? `${width}px` : '100%'}; - height: ${lineSize}px; + width: ${$width ? `${$width}px` : '100%'}; + height: ${$lineSize}px; `; } }} @@ -111,19 +111,21 @@ export const ReqoreSpacer = memo( - + - + ); } diff --git a/src/components/Table/cell.tsx b/src/components/Table/cell.tsx index d759c1a1..18e8dbe4 100644 --- a/src/components/Table/cell.tsx +++ b/src/components/Table/cell.tsx @@ -20,7 +20,9 @@ export interface IReqoreTableBodyCellProps padded?: IReqoreTableColumn['cell']['padded']; } -export const StyledTableCell = styled.div` +export const StyledTableCell = styled.div.withConfig({ + shouldForwardProp: (prop) => !prop.startsWith('$'), +})` ${({ width, minWidth, maxWidth, grow }) => css` width: ${width}px; diff --git a/src/components/Table/header.tsx b/src/components/Table/header.tsx index b06ed3c6..9108f6f1 100644 --- a/src/components/Table/header.tsx +++ b/src/components/Table/header.tsx @@ -52,7 +52,10 @@ export interface IReqoreTableSectionStyle { size?: TSizes; } -const StyledTableHeaderWrapper = styled.div` +const StyledTableHeaderWrapper = styled.div.withConfig({ + shouldForwardProp: (prop) => + !['leftScroll', 'hasVerticalScroll', 'heightAsGroup', 'size'].includes(prop as string), +})` ${({ heightAsGroup, size }) => css` display: flex; diff --git a/src/components/Table/index.tsx b/src/components/Table/index.tsx index 8b2d1f70..c9ec802f 100644 --- a/src/components/Table/index.tsx +++ b/src/components/Table/index.tsx @@ -183,8 +183,8 @@ const StyledTableWrapper = styled.div` display: flex; flex-flow: column; - ${({ isPinned }) => - isPinned + ${({ $isPinned }: { $isPinned?: boolean }) => + $isPinned ? css` flex-shrink: 0; z-index: 1; @@ -201,8 +201,8 @@ const StyledTablesWrapper = styled.div` flex-flow: row; overflow: hidden; - ${({ rounded, size = 'normal' }) => css` - border-radius: ${rounded === false ? 0 : RADIUS_FROM_SIZE[size]}px; + ${({ $rounded, $size = 'normal' }: { $rounded?: boolean; $size?: string }) => css` + border-radius: ${$rounded === false ? 0 : RADIUS_FROM_SIZE[$size]}px; `} `; @@ -773,7 +773,7 @@ const ReqoreTable = ({ } return ( - + {renderTable('left', applyPaging(transformedData))} {renderTable('main', applyPaging(transformedData))} diff --git a/src/components/Tabs/content.tsx b/src/components/Tabs/content.tsx index d5ab3781..deffe86f 100644 --- a/src/components/Tabs/content.tsx +++ b/src/components/Tabs/content.tsx @@ -26,26 +26,26 @@ const StyledTabsContent = styled.div` flex-flow: column; overflow: hidden; flex: 1; - padding: ${({ padded, size }: IReqoreTabsContent) => - padded === 'none' || padded === false + padding: ${({ $padded, $size }: { $padded: TReqoreTabsContentPadding; $size: TSizes }) => + $padded === 'none' || $padded === false ? undefined : `${ - padded === 'both' || padded === true || padded === 'vertical' - ? `${PADDING_FROM_SIZE[size]}px` + $padded === 'both' || $padded === true || $padded === 'vertical' + ? `${PADDING_FROM_SIZE[$size]}px` : 0 } ${ - padded === 'both' || padded === true || padded === 'horizontal' - ? `${PADDING_FROM_SIZE[size]}px` + $padded === 'both' || $padded === true || $padded === 'horizontal' + ? `${PADDING_FROM_SIZE[$size]}px` : 0 }`}; - padding-top: ${({ padded, size }: IReqoreTabsContent) => - padded === 'top' ? `${PADDING_FROM_SIZE[size]}px` : undefined}; - padding-bottom: ${({ padded, size }: IReqoreTabsContent) => - padded === 'bottom' ? `${PADDING_FROM_SIZE[size]}px` : undefined}; - padding-left: ${({ padded, size }: IReqoreTabsContent) => - padded === 'left' ? `${PADDING_FROM_SIZE[size]}px` : undefined}; - padding-right: ${({ padded, size }: IReqoreTabsContent) => - padded === 'right' ? `${PADDING_FROM_SIZE[size]}px` : undefined}; + padding-top: ${({ $padded, $size }: { $padded: TReqoreTabsContentPadding; $size: TSizes }) => + $padded === 'top' ? `${PADDING_FROM_SIZE[$size]}px` : undefined}; + padding-bottom: ${({ $padded, $size }: { $padded: TReqoreTabsContentPadding; $size: TSizes }) => + $padded === 'bottom' ? `${PADDING_FROM_SIZE[$size]}px` : undefined}; + padding-left: ${({ $padded, $size }: { $padded: TReqoreTabsContentPadding; $size: TSizes }) => + $padded === 'left' ? `${PADDING_FROM_SIZE[$size]}px` : undefined}; + padding-right: ${({ $padded, $size }: { $padded: TReqoreTabsContentPadding; $size: TSizes }) => + $padded === 'right' ? `${PADDING_FROM_SIZE[$size]}px` : undefined}; `; const ReqoreTabsContent = ({ @@ -57,8 +57,8 @@ const ReqoreTabsContent = ({ }: IReqoreTabsContent) => ( {children} diff --git a/src/components/Tabs/index.tsx b/src/components/Tabs/index.tsx index acecf9e0..551d28be 100644 --- a/src/components/Tabs/index.tsx +++ b/src/components/Tabs/index.tsx @@ -49,10 +49,10 @@ export interface IReqoreTabsProps extends IReqoreComponent, React.HTMLAttributes const StyledTabs = styled.div>` display: flex; - ${({ vertical, fillParent, width }) => css` + ${({ $vertical, $fillParent, width }) => css` width: ${width ? `${width}px` : '100%'}; - height: ${fillParent ? '100%' : undefined}; - flex-flow: ${vertical ? 'row' : 'column'}; + height: ${$fillParent ? '100%' : undefined}; + flex-flow: ${$vertical ? 'row' : 'column'}; `} `; @@ -63,6 +63,7 @@ const ReqoreTabs = ({ className, onTabChange, fill, + fillParent, _testWidth, vertical, activeTabIntent, @@ -108,8 +109,9 @@ const ReqoreTabs = ({ ` - ${({ disabled, vertical, fill, fixed, padded }: IReqoreTabListItemStyle) => { + ${({ $disabled, $vertical, $fill, $fixed, $padded }: IReqoreTabListItemStyle) => { return css` display: flex; flex-shrink: 0; position: relative; align-items: center; - width: ${vertical ? `100%` : undefined}; + width: ${$vertical ? `100%` : undefined}; - ${vertical + ${$vertical ? css` ${StyledButton}:last-child { border-right: 0; @@ -49,7 +54,7 @@ export const StyledTabListItem = styled.div` border-bottom-right-radius: 0 !important; } - ${padded === false && + ${$padded === false && css` &:first-child { padding-top: 0; @@ -70,7 +75,7 @@ export const StyledTabListItem = styled.div` border-bottom-right-radius: 0 !important; } - ${padded === false && + ${$padded === false && css` &:first-child { padding-left: 0; @@ -81,13 +86,13 @@ export const StyledTabListItem = styled.div` `} `} - ${fill && !fixed + ${$fill && !$fixed ? css` flex: 1 0 auto; ` : undefined} - ${disabled && + ${$disabled && css` cursor: not-allowed; > * { @@ -197,16 +202,13 @@ const ReqoreTabsListItem = memo( ref={targetRef} {...props} className={className} - intent={intent} as={as} - size={size} - active={active} - disabled={disabled} - vertical={vertical} theme={theme} - fill={fill} - fixed={rest.fixed} - padded={padded} + $disabled={disabled} + $vertical={vertical} + $fill={fill} + $fixed={rest.fixed} + $padded={padded} > {!onCloseClick || disabled ? ( renderButton() diff --git a/src/components/Tabs/list.tsx b/src/components/Tabs/list.tsx index 99e48df3..bec17e24 100644 --- a/src/components/Tabs/list.tsx +++ b/src/components/Tabs/list.tsx @@ -39,30 +39,36 @@ export interface IReqoreTabsListProps export interface IReqoreTabsListStyle extends Omit { theme: IReqoreTheme; + $fill?: boolean; + $vertical?: boolean; + $size?: TSizes; + $padded?: boolean; + $flat?: boolean; + $currentTabColor?: string; } export const StyledReqoreTabsList = styled.div` - ${({ fill, vertical, size, padded, flat, currentTabColor, width }) => css` - height: ${vertical ? '100%' : undefined}; - width: ${vertical ? width || '200px' : '100%'}; - flex-flow: ${vertical ? 'column' : 'row'}; + ${({ $fill, $vertical, $size, $padded, $flat, $currentTabColor, width }) => css` + height: ${$vertical ? '100%' : undefined}; + width: ${$vertical ? width || '200px' : '100%'}; + flex-flow: ${$vertical ? 'column' : 'row'}; display: flex; align-items: center; - gap: ${GAP_FROM_SIZE[size]}px; + gap: ${GAP_FROM_SIZE[$size]}px; - ${padded && + ${$padded && css` - padding: 0 ${PADDING_FROM_SIZE[size]}px; + padding: 0 ${PADDING_FROM_SIZE[$size]}px; `} - ${!flat && + ${!$flat && css` - border-${vertical ? 'right' : 'bottom'}: 1px solid ${changeLightness(currentTabColor, 0.175)}; + border-${$vertical ? 'right' : 'bottom'}: 1px solid ${changeLightness($currentTabColor, 0.175)}; `} - ${fill && + ${$fill && css` justify-content: space-around; `} @@ -258,14 +264,15 @@ const ReqoreTabsList = ({ {transformedItems.map((item: IReqoreTabsListItem | IReqoreTabsListItem[], index: number) => isArray(item) ? ( diff --git a/src/components/Tag/group.tsx b/src/components/Tag/group.tsx index 917f89ce..1e60ec8b 100644 --- a/src/components/Tag/group.tsx +++ b/src/components/Tag/group.tsx @@ -16,22 +16,22 @@ export interface IReqoreTagGroup } const StyledTagGroup = styled.div` - flex-shrink: ${({ wrap }: IReqoreTagGroup) => (wrap ? 1 : 0)}; - flex-grow: ${({ fluid }: IReqoreTagGroup) => (fluid ? 1 : undefined)}; + flex-shrink: ${({ $wrap }: { $wrap?: boolean }) => ($wrap ? 1 : 0)}; + flex-grow: ${({ $fluid }: { $fluid?: boolean }) => ($fluid ? 1 : undefined)}; display: flex; - flex-wrap: ${({ wrap }: IReqoreTagGroup) => (wrap ? 'wrap' : 'nowrap')}; - gap: ${({ gapSize }: IReqoreTagGroup) => GAP_FROM_SIZE[gapSize]}px; + flex-wrap: ${({ $wrap }: { $wrap?: boolean }) => ($wrap ? 'wrap' : 'nowrap')}; + gap: ${({ $gapSize }: { $gapSize?: TSizes }) => GAP_FROM_SIZE[$gapSize]}px; align-items: center; - ${({ align }) => { - if (align === 'right') { + ${({ $align }: { $align?: 'left' | 'center' | 'right' }) => { + if ($align === 'right') { return css` margin-left: auto; justify-content: flex-end; `; } - if (align === 'center') { + if ($align === 'center') { return css` margin: 0 auto; justify-content: center; @@ -51,9 +51,10 @@ const ReqoreTagGroup = ({ ...rest }: IReqoreTagGroup) => ( {React.Children.map(children, (child) => diff --git a/src/components/Tag/index.tsx b/src/components/Tag/index.tsx index 3ae9650d..c1e1115b 100644 --- a/src/components/Tag/index.tsx +++ b/src/components/Tag/index.tsx @@ -97,11 +97,28 @@ export interface IReqoreTagProps extends Omit, 'children' | 'color'>, IReqoreCustomTagProps {} -export interface IReqoreTagStyle extends IReqoreTagProps { +// Internal styling props (transient with $ prefix on the styled component) +interface IReqoreTagStyleInternal { + $size?: TSizes; + $asBadge?: boolean; + $fixed?: IReqoreTagProps['fixed']; + $fluid?: boolean; + $intent?: TReqoreIntent; + $color?: TReqoreColor; + $minimal?: boolean; + $rounded?: boolean; + $wrap?: boolean; + $hasWidth?: boolean; + $labelKey?: IReqoreTagProps['labelKey']; + $interactive?: boolean; + $removable?: boolean; + $effect?: IReqoreEffect; + $disabled?: boolean; +} + +export interface IReqoreTagStyle extends IReqoreTagProps, IReqoreTagStyleInternal { theme: IReqoreTheme; - removable?: boolean; - interactive?: boolean; - color?: TReqoreColor; + color?: TReqoreColor; // kept for backward compatibility inside logic } export const StyledTag = styled(StyledEffect)` @@ -112,22 +129,23 @@ export const StyledTag = styled(StyledEffect)` font-family: system-ui; overflow: hidden; vertical-align: middle; - font-size: ${({ size }) => TAG_TEXT_FROM_SIZE[size]}px; + font-size: ${({ $size = 'normal' }) => TAG_TEXT_FROM_SIZE[$size]}px; line-height: 1.1; - min-width: ${({ size, asBadge }) => (asBadge ? BADGE_SIZE_TO_PX[size] : TAG_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')}; - justify-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, rounded }) => + min-width: ${({ $size = 'normal', $asBadge }) => + $asBadge ? BADGE_SIZE_TO_PX[$size] : TAG_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')}; + justify-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 = 'normal', rounded }) => rounded === false ? undefined - : asBadge - ? `${BADGE_RADIUS_FROM_SIZE[size]}px` - : `${TAG_RADIUS_FROM_SIZE[size]}px`}; + : $asBadge + ? `${BADGE_RADIUS_FROM_SIZE[$size]}px` + : `${TAG_RADIUS_FROM_SIZE[$size]}px`}; width: ${({ width }) => width || undefined}; transition: all 0.2s ease-out; @@ -153,18 +171,18 @@ export const StyledTag = styled(StyledEffect)` ${InactiveIconScale}; - ${({ wrap, hasWidth }) => - wrap || hasWidth + ${({ $wrap, $hasWidth }) => + $wrap || $hasWidth ? css` - min-height: ${({ size, asBadge }) => - asBadge ? BADGE_SIZE_TO_PX[size] : TAG_SIZE_TO_PX[size]}px; + min-height: ${({ $size = 'normal', $asBadge }) => + $asBadge ? BADGE_SIZE_TO_PX[$size] : TAG_SIZE_TO_PX[$size]}px; ` : css` - height: ${({ size, asBadge }) => - asBadge ? BADGE_SIZE_TO_PX[size] : TAG_SIZE_TO_PX[size]}px; + height: ${({ $size = 'normal', $asBadge }) => + $asBadge ? BADGE_SIZE_TO_PX[$size] : TAG_SIZE_TO_PX[$size]}px; `} - ${({ theme, color, labelKey, minimal }: IReqoreTagStyle) => { + ${({ theme, $color: color, $labelKey: labelKey, minimal }: IReqoreTagStyle) => { return css` background-color: ${minimal ? color @@ -183,7 +201,7 @@ export const StyledTag = styled(StyledEffect)` `; }} - ${({ theme, color, interactive, minimal, effect }) => + ${({ theme, $color: color, $interactive: interactive, minimal, $effect: effect }) => interactive ? css` cursor: pointer; @@ -211,7 +229,7 @@ export const StyledTag = styled(StyledEffect)` : undefined} - ${({ disabled }) => + ${({ $disabled: disabled }) => disabled && css` opacity: 0.5; @@ -395,18 +413,23 @@ const ReqoreTag = forwardRef( theme={theme} effect={effect} width={width} - labelKey={labelKey} - color={getCustomColor(intent)} + // Transient styling props ($ prefix) to prevent DOM leakage + $labelKey={labelKey} + $color={getCustomColor(intent)} className={`${className || ''} reqore-tag`} - size={size} + $size={size} ref={ref} - asBadge={asBadge} + $asBadge={asBadge} minimal={minimal} - removable={!!onRemoveClick} - interactive={!!onClick && !rest.disabled} + $removable={!!onRemoveClick} + $interactive={!!onClick && !rest.disabled} tabIndex={onClick && !rest.disabled ? 0 : undefined} - wrap={wrap} - hasWidth={!!width} + $wrap={wrap} + $hasWidth={!!width} + $fixed={rest.fixed} + $fluid={rest.fluid} + $disabled={rest.disabled} + $effect={effect} > {labelKey || hasLeftIcon ? ( ` height: ${({ height }) => (height ? `${height}px` : undefined)}; - min-height: ${({ _size }) => SIZE_TO_PX[_size]}px; + min-height: ${({ $_size }) => SIZE_TO_PX[$_size]}px; max-height: 100%; - width: ${({ width, fluid }) => (fluid ? '100%' : width ? `${width}px` : 'auto')}; - flex: ${({ fluid, fixed }) => (fixed ? '0 0 auto' : fluid ? '1 auto' : '0 0 auto')}; - align-self: ${({ fixed, fluid }) => (fixed ? 'flex-start' : fluid ? 'stretch' : undefined)}; + width: ${({ width, $fluid }) => ($fluid ? '100%' : width ? `${width}px` : 'auto')}; + flex: ${({ $fluid, $fixed }) => ($fixed ? '0 0 auto' : $fluid ? '1 auto' : '0 0 auto')}; + 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; + border-radius: ${({ $minimal, $rounded = true, $_size = 'normal' }) => + $minimal || !$rounded ? 0 : RADIUS_FROM_SIZE[$_size]}px; &:focus-within { .reqore-clear-input-button { @@ -95,35 +101,35 @@ export const StyledTextarea = styled(StyledEffect)` width: 100%; max-width: 100%; max-height: 100%; - font-size: ${({ _size = 'normal' }) => CONTROL_TEXT_FROM_SIZE[_size]}px; + font-size: ${({ $_size = 'normal' }) => CONTROL_TEXT_FROM_SIZE[$_size]}px; margin: 0; - padding: ${({ _size = 'normal' }) => TEXTAREA_PADDING_FROM_SIZE[_size]}px; - padding-right: ${({ hasClearButton, _size = 'normal' }) => - hasClearButton ? `${SIZE_TO_PX[_size]}px` : undefined}; - min-height: ${({ _size = 'normal' }) => SIZE_TO_PX[_size]}px; - line-height: ${({ _size = 'normal' }) => SIZE_TO_PX[_size] - CONTROL_TEXT_FROM_SIZE[_size]}px; + padding: ${({ $_size = 'normal' }) => TEXTAREA_PADDING_FROM_SIZE[$_size]}px; + padding-right: ${({ $hasClearButton, $_size = 'normal' }) => + $hasClearButton ? `${SIZE_TO_PX[$_size]}px` : undefined}; + min-height: ${({ $_size = 'normal' }) => SIZE_TO_PX[$_size]}px; + line-height: ${({ $_size = 'normal' }) => SIZE_TO_PX[$_size] - CONTROL_TEXT_FROM_SIZE[$_size]}px; vertical-align: middle; - background-color: ${({ theme, minimal, transparent }: IReqoreTextareaStyle) => - minimal || transparent ? 'transparent' : rgba(theme.main, 0.1)}; + background-color: ${({ theme, $minimal, transparent }: IReqoreTextareaStyle) => + $minimal || transparent ? 'transparent' : rgba(theme.main, 0.1)}; color: ${({ theme }: IReqoreInputStyle) => getReadableColor(theme, undefined, undefined, true, theme.originalMain)}; &:active, &:focus { outline: none; - background-color: ${({ theme, minimal, transparent }: IReqoreTextareaStyle) => - minimal || transparent ? 'transparent' : rgba(theme.main, 0.15)}; + background-color: ${({ theme, $minimal, transparent }: IReqoreTextareaStyle) => + $minimal || transparent ? 'transparent' : rgba(theme.main, 0.15)}; } border-radius: inherit; - border: ${({ minimal, theme, flat }) => - !minimal && !flat ? `1px solid ${changeLightness(theme.main, 0.13)}` : 0}; - border-bottom: ${({ minimal, theme, flat }) => - minimal && !flat ? `0.5px solid ${changeLightness(theme.main, 0.13)}` : undefined}; + border: ${({ $minimal, theme, flat }) => + !$minimal && !flat ? `1px solid ${changeLightness(theme.main, 0.13)}` : 0}; + border-bottom: ${({ $minimal, theme, flat }) => + $minimal && !flat ? `0.5px solid ${changeLightness(theme.main, 0.13)}` : undefined}; - ${({ disabled, readOnly }) => - !disabled && !readOnly + ${({ $disabled, $readOnly }) => + !$disabled && !$readOnly ? css` &:active, &:focus, @@ -146,8 +152,8 @@ export const StyledTextarea = styled(StyledEffect)` } } - ${({ readOnly }) => readOnly && ReadOnlyElement}; - ${({ disabled }) => disabled && DisabledElement}; + ${({ $readOnly }) => $readOnly && ReadOnlyElement}; + ${({ $disabled }) => $disabled && DisabledElement}; &:disabled { ${DisabledElement}; @@ -236,15 +242,18 @@ function Textarea( }} as={rest.as || 'textarea'} className={`${className || ''} reqore-control reqore-textarea`} - _size={size} + $_size={size} ref={(ref) => setInputRef(ref)} theme={theme} - rounded={rounded} + $rounded={rounded} rows={1} value={_value} readonly={rest?.readOnly} tabIndex={rest?.disabled ? -1 : 0} - hasClearButton={!rest?.readOnly && !rest?.disabled && !!(onClearClick && onChange)} + $hasClearButton={!rest?.readOnly && !rest?.disabled && !!(onClearClick && onChange)} + $minimal={rest?.minimal} + $disabled={rest?.disabled} + $readOnly={rest?.readOnly} /> ( return ( > component={StyledTextareaWrapper} - disabled={rest.disabled} - readOnly={rest.readOnly} className={`${className || ''} reqore-control-wrapper`} width={width} - filterable height={height} - fluid={fluid} - fixed={fixed} - _size={size} theme={theme} style={wrapperStyle} ref={targetRef} + filterable + $fluid={fluid} + $fixed={fixed} + $_size={size} + $disabled={rest.disabled} + $readOnly={rest.readOnly} onItemSelect={handleItemSelect} {...templates} popoverId={`id-${uuid.current}`} @@ -290,12 +299,12 @@ function Textarea( className={`${className || ''} reqore-control-wrapper`} width={width} height={height} - fluid={fluid} - fixed={fixed} - _size={size} theme={theme} style={wrapperStyle} ref={targetRef} + $fluid={fluid} + $fixed={fixed} + $_size={size} > {renderChildren()} diff --git a/src/components/Tree/index.tsx b/src/components/Tree/index.tsx index 59220579..d96b5f84 100644 --- a/src/components/Tree/index.tsx +++ b/src/components/Tree/index.tsx @@ -65,8 +65,8 @@ export interface ITreeStyle { interactive?: boolean; theme: IReqoreTheme; level?: number; - size?: TSizes; - expandable?: boolean; + $size?: TSizes; + $expandable?: boolean; } export const StyledTreeLabel = styled(ReqoreP)` @@ -78,9 +78,9 @@ export const StyledTreeLabel = styled(ReqoreP)` export const StyledTreeWrapper = styled.div` display: flex; flex-flow: column; - gap: ${({ size }) => GAP_FROM_SIZE[size]}px; - margin-left: ${({ size }) => ICON_FROM_SIZE[size]}px; - cursor: ${({ expandable }) => (expandable ? 'pointer' : 'default')}; + gap: ${({ $size }) => GAP_FROM_SIZE[$size]}px; + margin-left: ${({ $size }) => ICON_FROM_SIZE[$size]}px; + cursor: ${({ $expandable }) => ($expandable ? 'pointer' : 'default')}; `; export const ReqoreTree = ({ @@ -240,7 +240,7 @@ export const ReqoreTree = ({ return (