Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions apps/website/screens/components/avatar/code/AvatarCodePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import basicUsage from "./examples/basicUsage";
import clickable from "./examples/clickable";
import tooltip from "./examples/tooltip";
import status from "./examples/status";
import StatusBadge from "@/common/StatusBadge";

const statusTypeString = `{
mode: 'default' | 'info' |
Expand Down Expand Up @@ -100,6 +101,19 @@ const sections = [
<td>Text to be displayed as label next to the avatar.</td>
<td>-</td>
</tr>
<tr>
<td>
<DxcFlex direction="column" gap="var(--spacing-gap-xs)" alignItems="baseline">
<StatusBadge status="new" />
ref
</DxcFlex>
</td>
<td>
<TableCode>{"React.Ref<HTMLDivElement>"}</TableCode>
</td>
<td>Ref to the avatar element.</td>
<td>-</td>
</tr>
<tr>
<td>secondaryText</td>
<td>
Expand Down
14 changes: 14 additions & 0 deletions apps/website/screens/components/badge/code/BadgeCodePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import Example from "@/common/example/Example";
import contextual from "./examples/contextual";
import notification from "./examples/notification";
import icons from "./examples/icons";
import StatusBadge from "@/common/StatusBadge";

const sections = [
{
Expand Down Expand Up @@ -79,6 +80,19 @@ const sections = [
<TableCode>99</TableCode>
</td>
</tr>
<tr>
<td>
<DxcFlex direction="column" gap="var(--spacing-gap-xs)" alignItems="baseline">
<StatusBadge status="new" />
ref
</DxcFlex>
</td>
<td>
<TableCode>{"React.Ref<HTMLDivElement>"}</TableCode>
</td>
<td>Ref to the badge element.</td>
<td>-</td>
</tr>
<tr>
<td>size</td>
<td>
Expand Down
14 changes: 14 additions & 0 deletions apps/website/screens/components/button/code/ButtonCodePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import semantic from "./examples/semantic";
import icons from "./examples/icons";
import sizes from "./examples/sizes";
import Code, { TableCode } from "@/common/Code";
import StatusBadge from "@/common/StatusBadge";

const sections = [
{
Expand Down Expand Up @@ -94,6 +95,19 @@ const sections = [
<td>This function will be called when the user clicks the button.</td>
<td>-</td>
</tr>
<tr>
<td>
<DxcFlex direction="column" gap="var(--spacing-gap-xs)" alignItems="baseline">
<StatusBadge status="new" />
ref
</DxcFlex>
</td>
<td>
<TableCode>{"React.Ref<HTMLButtonElement>"}</TableCode>
</td>
<td>Ref to the button element.</td>
<td>-</td>
</tr>
<tr>
<td>semantic</td>
<td>
Expand Down
16 changes: 16 additions & 0 deletions apps/website/screens/components/card/code/CardCodePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,22 @@ const sections = [
</td>
<td>-</td>
</tr>
<tr>
<td>
<DxcFlex direction="column" gap="var(--spacing-gap-xs)" alignItems="baseline">
<StatusBadge status="new" />
ref
</DxcFlex>
</td>
<td>
<TableCode>{"React.Ref<HTMLDivElement | HTMLAnchorElement>"}</TableCode>
</td>
<td>
Ref to the card element. The type is determined by whether the card is rendered as a div or an anchor
element based on the props such as <Code>href</Code>.
</td>
<td>-</td>
</tr>
<tr>
<td>
<DxcFlex direction="column" gap="var(--spacing-gap-xs)" alignItems="baseline">
Expand Down
13 changes: 13 additions & 0 deletions apps/website/screens/components/chip/code/ChipCodePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,19 @@ const sections = [
</td>
<td>-</td>
</tr>
<tr>
<td>
<DxcFlex direction="column" gap="var(--spacing-gap-xs)" alignItems="baseline">
<StatusBadge status="new" />
ref
</DxcFlex>
</td>
<td>
<TableCode>{"React.Ref<HTMLDivElement>"}</TableCode>
</td>
<td>Ref to the chip element.</td>
<td>-</td>
</tr>
<tr>
<td>
<DxcFlex direction="column" gap="var(--spacing-gap-xs)" alignItems="baseline">
Expand Down
228 changes: 133 additions & 95 deletions packages/lib/src/avatar/Avatar.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { memo, useCallback, useMemo, useState } from "react";
import AvatarPropsType from "./types";
import { forwardRef, useCallback, useMemo, useState } from "react";
import AvatarPropsType, { ContentType, LabelWrapperType } from "./types";
import { getBorderWidth, getFontSize, getInitials, getModeColor } from "./utils";
import DxcTypography from "../typography/Typography";
import DxcImage from "../image/Image";
Expand Down Expand Up @@ -63,107 +63,145 @@ const StatusContainer = styled.div<{
background-color: ${({ status }) => getModeColor(status!.mode)};
`;

Comment thread
PelayoFelgueroso marked this conversation as resolved.
const DxcAvatar = memo(
({
color = "neutral",
disabled = false,
icon = "person",
imageSrc,
label,
linkHref,
onClick,
primaryText,
secondaryText,
shape = "circle",
size = "medium",
status,
tabIndex = 0,
title,
}: AvatarPropsType) => {
const content = ({
hasAction,
onClick,
linkHref,
disabled,
imageSrc,
error,
handleError,
label,
title,
size,
initials,
icon,
color,
status,
}: ContentType) => (
<>
<ContentWrapper hasAction={hasAction}>
{(!!onClick || !!linkHref || disabled) && <Overlay disabled={disabled} aria-hidden="true" />}

{imageSrc && !error ? (
<DxcImage
src={imageSrc}
alt={label || title || "Avatar"}
onError={handleError}
width="100%"
height="100%"
objectFit="cover"
objectPosition="center"
/>
) : label ? (
<DxcTypography
as="span"
fontFamily="var(--typography-font-family)"
fontSize={getFontSize(size)}
fontWeight="var(--typography-label-semibold)"
fontStyle="normal"
lineHeight="normal"
color="inherit"
>
{initials}
</DxcTypography>
) : (
<IconContainer size={size} color={color}>
{icon && (typeof icon === "string" ? <DxcIcon icon={icon} /> : icon)}
</IconContainer>
)}
</ContentWrapper>
{status && <StatusContainer role="status" status={status} size={size} />}
</>
);

const LabelWrapper = ({ condition, children, primaryText, secondaryText }: LabelWrapperType) =>
condition ? (
<DxcFlex gap="var(--spacing-gap-s)" alignItems="center">
{children}
<DxcFlex direction="column" justifyContent="center" alignItems="flex-start" gap="var(--spacing-gap-none)">
{primaryText && (
<DxcTypography
as="h3"
color="var(--color-fg-neutral-dark)"
fontSize="var(--typography-label-l)"
fontFamily="var(--typography-font-family)"
fontStyle="normal"
fontWeight="var(--typography-label-regular)"
lineHeight="normal"
>
{primaryText}
</DxcTypography>
)}
{secondaryText && (
<DxcTypography
as="p"
color="var(--color-fg-neutral-stronger)"
fontSize="var(--typography-label-s)"
fontFamily="var(--typography-font-family)"
fontStyle="normal"
fontWeight="var(--typography-label-regular)"
lineHeight="normal"
>
{secondaryText}
</DxcTypography>
)}
</DxcFlex>
</DxcFlex>
) : (
<>{children}</>
);

const DxcAvatar = forwardRef<HTMLDivElement, AvatarPropsType>(
(
{
color = "neutral",
disabled = false,
icon = "person",
imageSrc,
label,
linkHref,
onClick,
primaryText,
secondaryText,
shape = "circle",
size = "medium",
status,
tabIndex = 0,
title,
}: AvatarPropsType,
ref
) => {
const [error, setError] = useState<boolean>(false);
const initials = useMemo(() => getInitials(label), [label]);
const handleError = useCallback(() => setError(true), []);
const hasAction = !disabled && (!!onClick || !!linkHref);

const content = (
<>
<ContentWrapper hasAction={hasAction}>
{(!!onClick || !!linkHref || disabled) && <Overlay disabled={disabled} aria-hidden="true" />}

{imageSrc && !error ? (
<DxcImage
src={imageSrc}
alt={label || title || "Avatar"}
onError={handleError}
width="100%"
height="100%"
objectFit="cover"
objectPosition="center"
/>
) : label ? (
<DxcTypography
as="span"
fontFamily="var(--typography-font-family)"
fontSize={getFontSize(size)}
fontWeight="var(--typography-label-semibold)"
fontStyle="normal"
lineHeight="normal"
color="inherit"
>
{initials}
</DxcTypography>
) : (
<IconContainer size={size} color={color}>
{icon && (typeof icon === "string" ? <DxcIcon icon={icon} /> : icon)}
</IconContainer>
)}
</ContentWrapper>
{status && <StatusContainer role="status" status={status} size={size} />}
</>
);

const LabelWrapper = ({ condition, children }: { condition: boolean; children: React.ReactNode }) =>
condition ? (
<DxcFlex gap="var(--spacing-gap-s)" alignItems="center">
{children}
<DxcFlex direction="column" justifyContent="center" alignItems="flex-start" gap="var(--spacing-gap-none)">
{primaryText && (
<DxcTypography
as="h3"
color="var(--color-fg-neutral-dark)"
fontSize="var(--typography-label-l)"
fontFamily="var(--typography-font-family)"
fontStyle="normal"
fontWeight="var(--typography-label-regular)"
lineHeight="normal"
>
{primaryText}
</DxcTypography>
)}
{secondaryText && (
<DxcTypography
as="p"
color="var(--color-fg-neutral-stronger)"
fontSize="var(--typography-label-s)"
fontFamily="var(--typography-font-family)"
fontStyle="normal"
fontWeight="var(--typography-label-regular)"
lineHeight="normal"
>
{secondaryText}
</DxcTypography>
)}
</DxcFlex>
</DxcFlex>
) : (
<>{children}</>
);

return (
<LabelWrapper condition={!!(primaryText || secondaryText)}>
<LabelWrapper
condition={!!(primaryText || secondaryText)}
primaryText={primaryText}
secondaryText={secondaryText}
>
<DxcActionIcon
ref={ref}
ariaLabel={label}
content={content}
content={content({
hasAction,
onClick,
linkHref,
disabled,
imageSrc,
error,
handleError,
label,
title,
size,
initials,
icon,
color,
status,
})}
color={
["primary", "secondary", "tertiary", "success", "info", "neutral", "warning", "error"].includes(color)
? color
Expand Down
Loading
Loading