From c9e54625029b043168ff768c01a794a77912c0eb Mon Sep 17 00:00:00 2001 From: Helder Oliveira Date: Wed, 15 Apr 2026 17:03:51 +0100 Subject: [PATCH 01/25] =?UTF-8?q?refactor(CardHorizontal):=20=F0=9F=92=A1?= =?UTF-8?q?=20Migrate=20from=20styled-components=20to=20CSS=20Modules?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CardHorizontal/CardHorizontal.module.css | 245 ++++++++++ .../CardHorizontal/CardHorizontal.stories.tsx | 11 +- .../CardHorizontal/CardHorizontal.tsx | 458 +++++++----------- 3 files changed, 416 insertions(+), 298 deletions(-) create mode 100644 src/components/CardHorizontal/CardHorizontal.module.css diff --git a/src/components/CardHorizontal/CardHorizontal.module.css b/src/components/CardHorizontal/CardHorizontal.module.css new file mode 100644 index 000000000..54d176e6a --- /dev/null +++ b/src/components/CardHorizontal/CardHorizontal.module.css @@ -0,0 +1,245 @@ +.card-horizontal { + align-items: center; + border: 1px solid transparent; + border-radius: var(--click-card-horizontal-radii-all); + display: inline-flex; + font: var(--click-card-horizontal-typography-title-default); + justify-content: flex-start; + max-width: 100%; + width: 100%; +} + +.card-horizontal__content { + display: flex; + flex-direction: row; + gap: var(--click-card-horizontal-space-md-gap); + width: 100%; +} + +.card-horizontal__icon-text-content { + align-items: center; + display: flex; + flex-direction: row; + gap: var(--click-card-horizontal-space-md-gap); + width: 100%; +} + +.card-horizontal__icon { + height: var(--click-card-horizontal-icon-size-all); + width: var(--click-card-horizontal-icon-size-all); +} + +.card-horizontal__header { + display: flex; + flex-flow: row; + gap: inherit; + max-width: 100%; + width: 100%; + align-items: center; + justify-content: space-between; +} + +.card-horizontal__description { + align-self: start; + display: flex; + flex: 1; + flex-direction: column; + font: var(--click-card-horizontal-typography-description-default); + gap: var(--click-card-horizontal-space-md-gap); + width: 100%; +} + +.card-horizontal_md { + padding: var(--click-card-horizontal-space-md-y) var(--click-card-horizontal-space-md-x); +} + +.card-horizontal_sm { + padding: var(--click-card-horizontal-space-sm-y) var(--click-card-horizontal-space-sm-x); +} + +.card-horizontal_md .card-horizontal__content { + gap: var(--click-card-horizontal-space-md-gap); +} + +.card-horizontal_sm .card-horizontal__content { + gap: var(--click-card-horizontal-space-sm-gap); +} + +.card-horizontal_md .card-horizontal__icon-text-content { + gap: var(--click-card-horizontal-space-md-gap); +} + +.card-horizontal_sm .card-horizontal__icon-text-content { + gap: var(--click-card-horizontal-space-sm-gap); +} + +.card-horizontal_default { + background: var(--click-card-horizontal-default-color-background-default); + border-color: var(--click-card-horizontal-default-color-stroke-default); + color: var(--click-card-horizontal-default-color-title-default); +} + +.card-horizontal_default .card-horizontal__description { + color: var(--click-card-horizontal-default-color-description-default); +} + +.card-horizontal_muted { + background: var(--click-card-horizontal-muted-color-background-default); + border-color: var(--click-card-horizontal-muted-color-stroke-default); + color: var(--click-card-horizontal-muted-color-title-default); +} + +.card-horizontal_muted .card-horizontal__description { + color: var(--click-card-horizontal-muted-color-description-default); +} + +.card-horizontal_selectable { + cursor: pointer; +} + +.card-horizontal_default.card-horizontal_selectable:not(.card-horizontal_selected) { + border-color: var(--click-card-horizontal-default-color-stroke-hover); +} + +.card-horizontal_muted.card-horizontal_selectable:not(.card-horizontal_selected) { + border-color: var(--click-card-horizontal-muted-color-stroke-hover); +} + +.card-horizontal_default.card-horizontal_selectable:hover:not(.card-horizontal_disabled) { + background-color: var(--click-card-horizontal-default-color-background-hover); + border-color: var(--click-card-horizontal-default-color-stroke-default); + color: var(--click-card-horizontal-default-color-title-hover); + font: var(--click-card-horizontal-typography-title-hover); +} + +.card-horizontal_default.card-horizontal_selectable:hover:not(.card-horizontal_disabled) .card-horizontal__description { + color: var(--click-card-horizontal-default-color-description-hover); + font: var(--click-card-horizontal-typography-description-hover); +} + +.card-horizontal_muted.card-horizontal_selectable:hover:not(.card-horizontal_disabled) { + background-color: var(--click-card-horizontal-muted-color-background-hover); + border-color: var(--click-card-horizontal-muted-color-stroke-default); + color: var(--click-card-horizontal-muted-color-title-hover); + font: var(--click-card-horizontal-typography-title-hover); +} + +.card-horizontal_muted.card-horizontal_selectable:hover:not(.card-horizontal_disabled) .card-horizontal__description { + color: var(--click-card-horizontal-muted-color-description-hover); + font: var(--click-card-horizontal-typography-description-hover); +} + +.card-horizontal_default.card-horizontal_selected, +.card-horizontal_default.card-horizontal_selectable.card-horizontal_selected { + background-color: var(--click-card-horizontal-default-color-background-active); + border-color: var(--click-card-horizontal-default-color-stroke-active); + color: var(--click-card-horizontal-default-color-title-active); +} + +.card-horizontal_default.card-horizontal_selected .card-horizontal__description, +.card-horizontal_default.card-horizontal_selectable.card-horizontal_selected .card-horizontal__description { + color: var(--click-card-horizontal-default-color-description-active); + font: var(--click-card-horizontal-typography-description-active); +} + +.card-horizontal_muted.card-horizontal_selected, +.card-horizontal_muted.card-horizontal_selectable.card-horizontal_selected { + background-color: var(--click-card-horizontal-muted-color-background-active); + border-color: var(--click-card-horizontal-muted-color-stroke-active); + color: var(--click-card-horizontal-muted-color-title-active); +} + +.card-horizontal_muted.card-horizontal_selected .card-horizontal__description, +.card-horizontal_muted.card-horizontal_selectable.card-horizontal_selected .card-horizontal__description { + color: var(--click-card-horizontal-muted-color-description-active); + font: var(--click-card-horizontal-typography-description-active); +} + +.card-horizontal_default:active:not(.card-horizontal_disabled), +.card-horizontal_default:focus:not(.card-horizontal_disabled), +.card-horizontal_default:focus-within:not(.card-horizontal_disabled) { + background-color: var(--click-card-horizontal-default-color-background-active); + border-color: var(--click-card-horizontal-default-color-stroke-active); + color: var(--click-card-horizontal-default-color-title-active); +} + +.card-horizontal_default:active:not(.card-horizontal_disabled) .card-horizontal__description, +.card-horizontal_default:focus:not(.card-horizontal_disabled) .card-horizontal__description, +.card-horizontal_default:focus-within:not(.card-horizontal_disabled) .card-horizontal__description { + color: var(--click-card-horizontal-default-color-description-active); + font: var(--click-card-horizontal-typography-description-active); +} + +.card-horizontal_muted:active:not(.card-horizontal_disabled), +.card-horizontal_muted:focus:not(.card-horizontal_disabled), +.card-horizontal_muted:focus-within:not(.card-horizontal_disabled) { + background-color: var(--click-card-horizontal-muted-color-background-active); + border-color: var(--click-card-horizontal-muted-color-stroke-active); + color: var(--click-card-horizontal-muted-color-title-active); +} + +.card-horizontal_muted:active:not(.card-horizontal_disabled) .card-horizontal__description, +.card-horizontal_muted:focus:not(.card-horizontal_disabled) .card-horizontal__description, +.card-horizontal_muted:focus-within:not(.card-horizontal_disabled) .card-horizontal__description { + color: var(--click-card-horizontal-muted-color-description-active); + font: var(--click-card-horizontal-typography-description-active); +} + +.card-horizontal_default.card-horizontal_selected:hover:not(.card-horizontal_disabled), +.card-horizontal_default.card-horizontal_selectable.card-horizontal_selected:hover:not(.card-horizontal_disabled) { + border-color: var(--click-card-horizontal-default-color-stroke-active); +} + +.card-horizontal_muted.card-horizontal_selected:hover:not(.card-horizontal_disabled), +.card-horizontal_muted.card-horizontal_selectable.card-horizontal_selected:hover:not(.card-horizontal_disabled) { + border-color: var(--click-card-horizontal-muted-color-stroke-active); +} + +.card-horizontal_disabled { + cursor: not-allowed; + pointer-events: none; +} + +.card-horizontal_default.card-horizontal_disabled, +.card-horizontal_default.card-horizontal_disabled:hover, +.card-horizontal_default.card-horizontal_disabled:active, +.card-horizontal_default.card-horizontal_disabled:focus, +.card-horizontal_default.card-horizontal_disabled:focus-within { + background-color: var(--click-card-horizontal-default-color-background-disabled); + border-color: var(--click-card-horizontal-default-color-stroke-disabled); + color: var(--click-card-horizontal-default-color-title-disabled); +} + +.card-horizontal_default.card-horizontal_disabled .card-horizontal__description, +.card-horizontal_default.card-horizontal_disabled:hover .card-horizontal__description, +.card-horizontal_default.card-horizontal_disabled:active .card-horizontal__description, +.card-horizontal_default.card-horizontal_disabled:focus .card-horizontal__description, +.card-horizontal_default.card-horizontal_disabled:focus-within .card-horizontal__description { + color: var(--click-card-horizontal-default-color-description-disabled); + font: var(--click-card-horizontal-typography-description-disabled); +} + +.card-horizontal_muted.card-horizontal_disabled, +.card-horizontal_muted.card-horizontal_disabled:hover, +.card-horizontal_muted.card-horizontal_disabled:active, +.card-horizontal_muted.card-horizontal_disabled:focus, +.card-horizontal_muted.card-horizontal_disabled:focus-within { + background-color: var(--click-card-horizontal-muted-color-background-disabled); + border-color: var(--click-card-horizontal-muted-color-stroke-disabled); + color: var(--click-card-horizontal-muted-color-title-disabled); +} + +.card-horizontal_muted.card-horizontal_disabled .card-horizontal__description, +.card-horizontal_muted.card-horizontal_disabled:hover .card-horizontal__description, +.card-horizontal_muted.card-horizontal_disabled:active .card-horizontal__description, +.card-horizontal_muted.card-horizontal_disabled:focus .card-horizontal__description, +.card-horizontal_muted.card-horizontal_disabled:focus-within .card-horizontal__description { + color: var(--click-card-horizontal-muted-color-description-disabled); + font: var(--click-card-horizontal-typography-description-disabled); +} + +@media (max-width: var(--breakpoint-sizes-md)) { + .card-horizontal__content { + flex-direction: column; + } +} diff --git a/src/components/CardHorizontal/CardHorizontal.stories.tsx b/src/components/CardHorizontal/CardHorizontal.stories.tsx index 2ee9695eb..8b731707a 100644 --- a/src/components/CardHorizontal/CardHorizontal.stories.tsx +++ b/src/components/CardHorizontal/CardHorizontal.stories.tsx @@ -1,16 +1,9 @@ import { Meta, StoryObj } from '@storybook/react-vite'; -import { styled } from 'styled-components'; import { ICON_NAMES } from '../Icon/IconCommon'; import { CardHorizontal } from '@/components/CardHorizontal'; -const GridCenter = styled.div` - display: grid; - width: 60%; - max-width: 480px; -`; - const meta: Meta = { component: CardHorizontal, title: 'Cards/Horizontal Card', @@ -29,9 +22,9 @@ const meta: Meta = { size: { type: { name: 'enum', value: ['sm', 'md'] } }, }, decorators: Story => ( - +
- +
), }; diff --git a/src/components/CardHorizontal/CardHorizontal.tsx b/src/components/CardHorizontal/CardHorizontal.tsx index f330a310a..9f4b1fdb5 100644 --- a/src/components/CardHorizontal/CardHorizontal.tsx +++ b/src/components/CardHorizontal/CardHorizontal.tsx @@ -1,305 +1,185 @@ -import { type KeyboardEvent, type MouseEvent } from 'react'; -import { styled } from 'styled-components'; +import { forwardRef, type KeyboardEvent, type MouseEvent } from 'react'; import { Badge } from '@/components/Badge'; import { Button } from '@/components/Button'; import { Container } from '@/components/Container'; import { Icon } from '@/components/Icon'; -import { CardHorizontalProps, CardSize, CardColor } from './CardHorizontal.types'; +import { cn, cva } from '@/lib/cva'; +import { CardHorizontalProps } from './CardHorizontal.types'; +import styles from './CardHorizontal.module.css'; -const Header = styled.div` - max-width: 100%; - gap: inherit; -`; +const cardHorizontalVariants = cva(styles['card-horizontal'], { + variants: { + color: { + default: styles['card-horizontal_default'], + muted: styles['card-horizontal_muted'], + }, + size: { + sm: styles['card-horizontal_sm'], + md: styles['card-horizontal_md'], + }, + disabled: { + true: styles['card-horizontal_disabled'], + }, + selected: { + true: styles['card-horizontal_selected'], + }, + selectable: { + true: styles['card-horizontal_selectable'], + }, + }, + defaultVariants: { + color: 'default', + size: 'md', + }, +}); -const Description = styled.div` - display: flex; - flex-direction: column; - align-self: start; - gap: ${({ theme }) => theme.click.card.horizontal.space.md.gap}; - flex: 1; - width: 100%; -`; - -const Wrapper = styled.div<{ - $hasShadow?: boolean; - $disabled?: boolean; - $isSelected?: boolean; - $isSelectable?: boolean; - $color: CardColor; - $size?: CardSize; -}>` - display: inline-flex; - width: 100%; - max-width: 100%; - align-items: center; - justify-content: flex-start; - - ${({ theme, $color, $size, $isSelected, $isSelectable, $disabled }) => ` - background: ${theme.click.card.horizontal[$color].color.background.default}; - color: ${theme.click.card.horizontal[$color].color.title.default}; - border-radius: ${theme.click.card.horizontal.radii.all}; - border: 1px solid ${ - theme.click.card.horizontal[$color].color.stroke[ - $isSelectable ? ($isSelected ? 'active' : 'hover') : 'default' - ] - }; - padding: ${ - $size === 'md' - ? `${theme.click.card.horizontal.space.md.y} ${theme.click.card.horizontal.space.md.x}` - : `${theme.click.card.horizontal.space.sm.y} ${theme.click.card.horizontal.space.sm.x}` - }; - font: ${theme.click.card.horizontal.typography.title.default}; - ${Description} { - color: ${theme.click.card.horizontal[$color].color.description.default}; - font: ${theme.click.card.horizontal.typography.description.default}; - } - &:hover{ - background-color: ${ - theme.click.card.horizontal[$color].color.background[ - $isSelectable ? 'hover' : 'default' - ] - }; - color: ${ - theme.click.card.horizontal[$color].color.title[ - $isSelectable ? 'hover' : 'default' - ] - }; - border: 1px solid ${ - theme.click.card.horizontal[$color].color.stroke[ - $isSelectable ? ($isSelected ? 'active' : 'default') : 'default' - ] - }; - cursor: ${$isSelectable ? 'pointer' : 'default'}; - font: ${theme.click.card.horizontal.typography.title.hover}; - ${Description} { - color: ${ - theme.click.card.horizontal[$color].color.description[ - $isSelectable ? 'hover' : 'default' - ] - }; - font: ${ - theme.click.card.horizontal.typography.description[ - $isSelectable ? 'hover' : 'default' - ] - }; +export const CardHorizontal = forwardRef( + ( + { + title, + icon, + description, + disabled = false, + infoText, + infoUrl, + isSelected, + isSelectable = infoText ? false : true, + children, + color = 'default', + size = 'md', + badgeText, + badgeState, + badgeIcon, + badgeIconDir, + onButtonClick, + className, + ...props + }, + ref + ) => { + const handleClick = (e: MouseEvent) => { + if (disabled) { + e.preventDefault(); + return; } - } - &:active, &:focus, &:focus-within { - background-color: ${ - theme.click.card.horizontal[$color].color.background[ - $isSelectable ? 'active' : 'default' - ] - }; - color: ${ - theme.click.card.horizontal[$color].color.title[ - $isSelectable ? 'active' : 'default' - ] - }; - border: 1px solid ${ - theme.click.card.horizontal[$color].color.stroke[ - $isSelectable ? 'active' : 'default' - ] - }; - ${Description} { - color: ${ - theme.click.card.horizontal[$color].color.description[ - $isSelectable ? 'active' : 'default' - ] - }; - font: ${ - theme.click.card.horizontal.typography.description[ - $isSelectable ? 'active' : 'default' - ] - }; + if (typeof onButtonClick === 'function') { + onButtonClick(e); } - } - ${ - $disabled - ? ` - pointer-events: none; - &, - &:hover, - &:active, &:focus, &:focus-within { - background-color: ${ - theme.click.card.horizontal[$color].color.background.disabled - }; - color: ${theme.click.card.horizontal[$color].color.title.disabled}; - border: 1px solid ${ - theme.click.card.horizontal[$color].color.stroke[ - $isSelected ? 'active' : 'disabled' - ] - }; - cursor: not-allowed; - ${Description} { - color: ${theme.click.card.horizontal[$color].color.description.disabled}; - font: ${theme.click.card.horizontal.typography.description.disabled}; - } - }, - &:active, &:focus, &:focus-within { - border: 1px solid ${theme.click.card.horizontal[$color].color.stroke.active}; - } - ` - : '' - } - `} -`; - -const CardIcon = styled(Icon)` - ${({ theme }) => ` - height: ${theme.click.card.horizontal.icon.size.all}; - width: ${theme.click.card.horizontal.icon.size.all}; - `} -`; - -const ContentWrapper = styled.div<{ $size: CardSize }>` - display: flex; - flex-direction: row; - width: 100%; - gap: ${({ theme, $size }) => - $size === 'md' - ? theme.click.card.horizontal.space.md.gap - : theme.click.card.horizontal.space.sm.gap}; - - @media (max-width: ${({ theme }) => theme.breakpoint.sizes.md}) { - flex-direction: column; - } -`; - -const IconTextContentWrapper = styled.div<{ $size: CardSize }>` - display: flex; - flex-direction: row; - align-items: center; - width: 100%; - gap: ${({ theme, $size }) => - $size === 'md' - ? theme.click.card.horizontal.space.md.gap - : theme.click.card.horizontal.space.sm.gap}; -`; - -export const CardHorizontal = ({ - title, - icon, - description, - disabled = false, - infoText, - infoUrl, - isSelected, - isSelectable = infoText ? false : true, - children, - color = 'default', - size = 'md', - badgeText, - badgeState, - badgeIcon, - badgeIconDir, - onButtonClick, - ...props -}: CardHorizontalProps) => { - const handleClick = (e: MouseEvent) => { - if (disabled) { - e.preventDefault(); - return; - } - - if (typeof onButtonClick === 'function') { - onButtonClick(e); - } - if (infoUrl && infoUrl.length > 0) { - window.open(infoUrl, '_blank'); - } - }; + if (infoUrl && infoUrl.length > 0) { + window.open(infoUrl, '_blank'); + } + }; - const handleKeyDown = (e: KeyboardEvent) => { - if (isSelectable && !disabled && e.key === ' ') { - e.preventDefault(); - handleClick(e as unknown as MouseEvent); - } - }; + const handleKeyDown = (e: KeyboardEvent) => { + if (isSelectable && !disabled && e.key === ' ') { + e.preventDefault(); + handleClick(e as unknown as MouseEvent); + } + }; - return ( - - - - {icon && ( - - )} - - {title && ( -
- - {title} - - {badgeText && ( + return ( +
+
+
+ {icon && ( + + )} + + {title && ( +
- + + {title} + + {badgeText && ( + + + + )} - )} -
- )} + + )} - {description && {description}} - {children && {children}} -
-
- {infoText && ( - -