diff --git a/packages/lib/src/card/Card.tsx b/packages/lib/src/card/Card.tsx index 2c7fb92bf..50bf8a6f6 100644 --- a/packages/lib/src/card/Card.tsx +++ b/packages/lib/src/card/Card.tsx @@ -2,7 +2,7 @@ import { forwardRef, Ref, useContext, useEffect, useState } from "react"; import styled from "@emotion/styled"; import CardPropsType from "./types"; import DxcImage from "../image/Image"; -import { getCardStyles, emptyIconSizes, handleEvent } from "./utils"; +import { getCardStyles, emptyIconSizes, handleEvent, getSelectableWrapperStyles } from "./utils"; import DxcFlex from "../flex/Flex"; import DxcIcon from "../icon/Icon"; import DxcTypography from "../typography/Typography"; @@ -12,19 +12,29 @@ const Card = styled.div<{ mode: CardPropsType["mode"]; direction?: CardPropsType["direction"]; imagePosition?: CardPropsType["imagePosition"]; + selectable?: boolean; interactive?: boolean; - size?: CardPropsType["size"]; selected?: boolean; + size?: CardPropsType["size"]; }>` display: flex; flex-direction: ${({ direction, imagePosition }) => imagePosition === "before" ? direction : `${direction}-reverse`}; gap: var(--spacing-gap-s); + padding: ${({ selectable }) => (selectable ? "var(--spacing-padding-none)" : "var(--spacing-padding-xs)")}; box-sizing: border-box; - padding: var(--spacing-padding-xs); border-radius: var(--border-radius-l); + border-style: var(--border-style-default); + border-color: transparent; ${({ mode, interactive, selected, size }) => getCardStyles(mode, interactive ?? false, selected, size)} + outline-offset: ${({ selected }) => (selected ? "var(--border-width-m)" : "var(--border-width-s)")}; background: var(--color-bg-neutral-lightest); +`; + +const SelectableWrapper = styled.div<{ selected: boolean }>` + ${({ selected }) => getSelectableWrapperStyles(selected)} + border-radius: var(--border-radius-l); + padding: var(--spacing-padding-xs); overflow: hidden; `; @@ -190,8 +200,9 @@ const DxcCard = forwardRef( size={size} imagePosition={imagePosition} mode={mode} - interactive={isInteractive} + selectable={selectable} selected={selectable && internalSelected} + interactive={isInteractive} tabIndex={isInteractive ? tabIndex : undefined} role={selectable ? "checkbox" : isInteractive ? "button" : undefined} onClick={(event) => { @@ -203,12 +214,25 @@ const DxcCard = forwardRef( aria-checked={selectable ? internalSelected : undefined} ref={ref as Ref} > - {image && ( - - - + {selectable ? ( + + {image && ( + + + + )} + {children} + + ) : ( + <> + {image && ( + + + + )} + {children} + )} - {children} ); } diff --git a/packages/lib/src/card/utils.ts b/packages/lib/src/card/utils.ts index 5ac47f2e5..b1eefa070 100644 --- a/packages/lib/src/card/utils.ts +++ b/packages/lib/src/card/utils.ts @@ -1,8 +1,8 @@ import CardPropsType from "./types"; -const calculateSize = (size?: CardPropsType["size"], selected?: boolean, borderWidth?: string, outlined?: boolean) => { - return `width: ${size?.width === "fillParent" ? "100%" : selected || outlined ? `calc-size(fit-content, size - (${borderWidth} * 2))` : "fit-content"}; - height: ${size?.height === "fillParent" ? "100%" : selected || outlined ? `calc-size(fit-content, size - (${borderWidth} * 2))` : "fit-content"};`; +const calculateSize = (size?: CardPropsType["size"]) => { + return `width: ${size?.width === "fillParent" ? "100%" : "fit-content"}; + height: ${size?.height === "fillParent" ? "100%" : "fit-content"};`; }; export const getCardStyles = ( mode: CardPropsType["mode"], @@ -13,6 +13,7 @@ export const getCardStyles = ( let hover = ""; let focus = ""; let active = ""; + const borderWidth = selected ? "var(--border-width-m)" : mode === "outlined" @@ -24,30 +25,30 @@ export const getCardStyles = ( ${ mode === "outlined" && !selected ? `border: ${borderWidth} var(--border-style-default) var(--border-color-neutral-light);` - : selected - ? `border: ${borderWidth} var(--border-style-default) var(--border-color-primary-strong);` - : `` + : "border-width: var(--border-width-none);" } ${interactive ? "cursor: pointer;" : ""} - ${calculateSize(size, selected, borderWidth, mode === "outlined")} + ${calculateSize(size)} `; - if (interactive && !selected) { + + if (interactive) { if (mode === "elevated") { hover = `box-shadow: var(--shadow-300);`; focus = `box-shadow: var(--shadow-100); - outline: var(--border-width-m) var(--border-style-default) var(--border-color-secondary-medium); outline-offset: calc(var(--border-width-m) * -1);`; + outline: var(--border-width-m) var(--border-style-default) var(--border-color-secondary-medium);`; active = `box-shadow: var(--shadow-100);`; } else if (mode === "outlined") { - hover = `border-width: var(--border-width-m); border-color: var(--border-color-neutral-medium); ${calculateSize(size, selected, "var(--border-width-m)", true)}`; - focus = `border-width: var(--border-width-m); - outline: var(--border-width-m) var(--border-style-default) var(--border-color-secondary-medium); - ${calculateSize(size, selected, "var(--border-width-m)", true)}`; - active = `border-width: var(--border-width-m); border-color: var(--border-color-neutral-strong); ${calculateSize(size, selected, "var(--border-width-m)", true)}`; + if (selected) { + hover = `border-width: var(--border-width-none);`; + focus = `border-width: var(--border-width-none); outline: var(--border-width-m) var(--border-style-default) var(--border-color-secondary-medium);`; + active = `border-width: var(--border-width-none);`; + } else { + hover = `border-width: var(--border-width-m); border-color: var(--border-color-neutral-medium);outline-offset: var(--border-width-none);`; + focus = `border-width: var(--boselectedrder-width-m); + outline: var(--border-width-m) var(--border-style-default) var(--border-color-secondary-medium);outline-offset: var(--border-width-none);`; + active = `border-width: var(--border-width-m); border-color: var(--border-color-neutral-strong);outline-offset: var(--border-width-none);`; + } } - } else if (selected) { - hover = `border-color: var(--border-color-primary-stronger);`; - focus = `outline: var(--border-width-m) var(--border-style-default) var(--border-color-secondary-medium);`; - active = `border-color: var(--border-color-primary-strong);`; } return ` @@ -58,6 +59,25 @@ export const getCardStyles = ( `; }; +export const getSelectableWrapperStyles = (selected: boolean) => { + let hover = ""; + let focus = ""; + let active = ""; + + if (selected) { + hover = `outline-color: var(--border-color-primary-stronger);`; + focus = `outline: var(--border-width-m) var(--border-style-default) var(--border-color-secondary-medium);`; + active = `outline-color: var(--border-color-primary-strong);`; + } + + return ` + &:hover { ${hover} } + &:focus { ${focus} } + &:active { ${active} } + outline: ${selected ? "var(--border-width-m) var(--border-style-default) var(--border-color-primary-strong)" : ""}; + `; +}; + export const handleEvent = ( event: React.KeyboardEvent | React.MouseEvent, onClick?: CardPropsType["onClick"],