Skip to content

Commit

Permalink
Fixed typing issues
Browse files Browse the repository at this point in the history
  • Loading branch information
vicky-comeau committed Jun 19, 2024
1 parent 5262602 commit 84c4d39
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 49 deletions.
79 changes: 73 additions & 6 deletions packages/components/src/Avatar/src/Avatar.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,26 @@
--hop-Avatar-border-radius: var(--hop-shape-circle);
--hop-Avatar-mix-blend-mode: normal;

/* Colors */
--hop-Avatar-decorative-1-background-color: var(--hop-decorative-option1-surface-strong);
--hop-Avatar-decorative-2-background-color: var(--hop-decorative-option2-surface-strong);
--hop-Avatar-decorative-3-background-color: var(--hop-decorative-option3-surface-strong);
--hop-Avatar-decorative-4-background-color: var(--hop-decorative-option4-surface-strong);
--hop-Avatar-decorative-5-background-color: var(--hop-decorative-option5-surface-strong);
--hop-Avatar-decorative-6-background-color: var(--hop-decorative-option6-surface-strong);
--hop-Avatar-decorative-7-background-color: var(--hop-decorative-option7-surface-strong);
--hop-Avatar-decorative-8-background-color: var(--hop-decorative-option8-surface-strong);
--hop-Avatar-decorative-9-background-color: var(--hop-decorative-option9-surface-strong);
--hop-Avatar-decorative-1-text-color: var(--hop-decorative-option1-text);
--hop-Avatar-decorative-2-text-color: var(--hop-decorative-option2-text);
--hop-Avatar-decorative-3-text-color: var(--hop-decorative-option3-text);
--hop-Avatar-decorative-4-text-color: var(--hop-decorative-option4-text);
--hop-Avatar-decorative-5-text-color: var(--hop-decorative-option5-text);
--hop-Avatar-decorative-6-text-color: var(--hop-decorative-option6-text);
--hop-Avatar-decorative-7-text-color: var(--hop-decorative-option7-text);
--hop-Avatar-decorative-8-text-color: var(--hop-decorative-option8-text);
--hop-Avatar-decorative-9-text-color: var(--hop-decorative-option9-text);

/* xs */
--hop-Avatar-xs-size: 1rem;

Expand All @@ -22,7 +42,8 @@
--hop-Avatar-2xl-size: 6rem;

/* disabled */
--hop-Avatar-image-background-color-disabled: var(--hop-neutral-surface-disabled);
--hop-Avatar-background-color-disabled: var(--hop-neutral-surface-disabled);
--hop-Avatar-text-color-disabled: var(--hop-neutral-text-disabled);
--hop-Avatar-mix-blend-mode-disabled: luminosity;

/* Internal variables */
Expand All @@ -32,6 +53,7 @@

position: relative;

overflow: hidden;
display: flex;
flex: 0 0 auto;
align-items: center;
Expand All @@ -41,6 +63,9 @@
inline-size: var(--size, var(--hop-Avatar-md-size));
block-size: var(--size, var(--hop-Avatar-md-size));

color: var(--text-color, var(--hop-neutral-text));

background-color: var(--background-color, transparent);
border-radius: var(--hop-Avatar-border-radius);
}

Expand Down Expand Up @@ -68,16 +93,58 @@
--size: var(--hop-Avatar-2xl-size);
}

/* The avatar that contains an image */
.hop-Avatar--image {
overflow: hidden;
/* Colors */
.hop-Avatar--decorative-option1 {
--background-color: var(--hop-Avatar-decorative-1-background-color);
--text-color: var(--hop-Avatar-decorative-1-text-color);
}

.hop-Avatar--decorative-option2 {
--background-color: var(--hop-Avatar-decorative-2-background-color);
--text-color: var(--hop-Avatar-decorative-2-text-color);
}

.hop-Avatar--decorative-option3 {
--background-color: var(--hop-Avatar-decorative-3-background-color);
--text-color: var(--hop-Avatar-decorative-3-text-color);
}

.hop-Avatar--decorative-option4 {
--background-color: var(--hop-Avatar-decorative-4-background-color);
--text-color: var(--hop-Avatar-decorative-4-text-color);
}

.hop-Avatar--decorative-option5 {
--background-color: var(--hop-Avatar-decorative-5-background-color);
--text-color: var(--hop-Avatar-decorative-5-text-color);
}

.hop-Avatar--decorative-option6 {
--background-color: var(--hop-Avatar-decorative-6-background-color);
--text-color: var(--hop-Avatar-decorative-6-text-color);
}

.hop-Avatar--decorative-option7 {
--background-color: var(--hop-Avatar-decorative-7-background-color);
--text-color: var(--hop-Avatar-decorative-7-text-color);
}

.hop-Avatar--decorative-option8 {
--background-color: var(--hop-Avatar-decorative-8-background-color);
--text-color: var(--hop-Avatar-decorative-8-text-color);
}

.hop-Avatar--decorative-option9 {
--background-color: var(--hop-Avatar-decorative-9-background-color);
--text-color: var(--hop-Avatar-decorative-9-text-color);
}

.hop-Avatar--image[data-disabled] {
.hop-Avatar[data-disabled] {
--background-color: var(--hop-Avatar-background-color-disabled);
--text-color: var(--hop-Avatar-text-color-disabled);
--mlx-blend-mode: var(--hop-Avatar-mix-blend-mode-disabled);

isolation: isolate;
background-color: var(--hop-Avatar-image-background-color-disabled);
}

.hop-Avatar__image {
Expand Down
80 changes: 41 additions & 39 deletions packages/components/src/Avatar/src/Avatar.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { BrokenImageRichIcon } from "@hopper-ui/icons";
import { type ResponsiveProp, useStyledSystem, type StyledSystemProps, useResponsiveValue, type BackgroundColorValue, type ColorValue } from "@hopper-ui/styled-system";
import { type ResponsiveProp, useStyledSystem, type StyledSystemProps, useResponsiveValue } from "@hopper-ui/styled-system";
import clsx from "clsx";
import { type CSSProperties, forwardRef, type ForwardedRef, useMemo } from "react";
import { useContextProps } from "react-aria-components";
Expand All @@ -16,16 +16,18 @@ import styles from "./Avatar.module.css";
export const GlobalAvatarCssSelector = "hop-Avatar";
export type AvatarSize = "xs" | "sm" | "md" | "lg" | "xl" | "2xl";

type ImageOmittedProps = "name" | "size" | "slot" | "content" | "color" | "height" | "width";
type AvatarOmittedProps = "content" | "color" | "height" | "width";
type AvatarImageBaseProps = Omit<React.HTMLProps<HTMLImageElement>, ImageOmittedProps>;
type AvatarImageBaseProps = React.HTMLProps<HTMLImageElement>;

interface AvatarBaseProps extends StyledSystemProps, BaseComponentProps {
export interface AvatarProps extends StyledSystemProps, BaseComponentProps {
/**
* The src of the image to display if the image fails to load. If set to null, the initials will be displayed instead.
* * @default "BrokenImageRichIcon"
*/
fallbackSrc?: string | null;
/**
* Props to add to the img element when src is provided.
*/
imageProps?: AvatarImageBaseProps;
/**
* Whether or not the avatar is disabled.
*/
Expand All @@ -44,16 +46,11 @@ interface AvatarBaseProps extends StyledSystemProps, BaseComponentProps {
*/
src?: string;
}
export type AvatarProps = AvatarBaseProps & AvatarImageBaseProps;

interface AvatarInitialsProps extends Omit<AvatarProps, "src"> {
size: AvatarSize;
}
interface AvatarInitialsProps extends Omit<AvatarProps, "src"> {}

interface AvatarImageProps extends Omit<AvatarBaseProps, AvatarOmittedProps>, AvatarImageBaseProps {
size: AvatarSize;
interface AvatarImageProps extends AvatarProps {
src: string;
children: React.ReactNode;
}

export const AvatarToTextSizeAdapter: SizeAdapter<AvatarProps["size"], TextProps["size"]> = {
Expand All @@ -79,20 +76,18 @@ function getColorIndexForInitial(name: string, maxNumberOfColor: number) {
}

function AvatarInitials(props: AvatarInitialsProps) {
const { ...ownProps } = useStyledSystem(props);
const {
"aria-label": ariaLabel,
className,
isDisabled,
name,
size,
size: sizeValue,
slot,
...otherProps
} = props;

const classNames = clsx(
className,
styles["hop-Avatar--initials"]
);
} = ownProps;

const size = useResponsiveValue(sizeValue) ?? "md";

const initials = useMemo(() => {
const cleanName = name.replace(/\s+/g, " ").trim();
Expand All @@ -106,8 +101,13 @@ function AvatarInitials(props: AvatarInitialsProps) {

const variantToUse = useMemo(() => `option${getColorIndexForInitial(name, 8) + 1}`, [name]);

const tokenBackgroundColor = isDisabled ? "neutral-disabled" : `decorative-${variantToUse}-strong` as BackgroundColorValue;
const tokenTextColor = isDisabled ? "neutral-disabled" : `decorative-${variantToUse}` as ColorValue;
const colorName = `decorative-${variantToUse}`;

const classNames = clsx(
className,
styles["hop-Avatar--initials"],
styles[`hop-Avatar--${colorName}`]
);

return (
<Text
Expand All @@ -116,8 +116,6 @@ function AvatarInitials(props: AvatarInitialsProps) {
role="img"
size={AvatarToTextSizeAdapter[size]}
slot={slot || undefined}
backgroundColor={tokenBackgroundColor}
color={tokenTextColor}
className={classNames}
data-disabled={isDisabled || undefined}
>
Expand All @@ -127,31 +125,34 @@ function AvatarInitials(props: AvatarInitialsProps) {
}

function AvatarImage(props: AvatarImageProps) {
const { ...ownProps } = useStyledSystem(props);
const {
"aria-label": ariaLabel,
children,
className,
imageProps,
isDisabled,
src,
fallbackSrc,
name,
slot,
size,
onError,
...otherProps
} = props;
} = ownProps;

const classNames = clsx(
className,
styles["hop-Avatar--image"]
);

const { onError, ...otherImageProps } = imageProps ?? {};

const [imageUrl, handleImageError, imageFailed] = useImageFallback(src, fallbackSrc);

if (imageFailed) {
if (fallbackSrc === undefined) {
return (
<RichIconAvatarImage aria-label={ariaLabel ?? name} size={size} isDisabled={isDisabled}>
<RichIconAvatarImage aria-label={ariaLabel ?? name} size={size} isDisabled={isDisabled} className={className}>
<BrokenImageRichIcon />
</RichIconAvatarImage>
);
Expand All @@ -162,14 +163,15 @@ function AvatarImage(props: AvatarImageProps) {

return (
<div
{...otherProps}
data-disabled={isDisabled || undefined}
slot={slot || undefined}
role="img"
aria-label={ariaLabel ?? name}
className={classNames}
>
<img
{...otherProps}
{...otherImageProps}
src={imageUrl}
alt={name}
onError={onError || handleImageError}
Expand All @@ -179,7 +181,7 @@ function AvatarImage(props: AvatarImageProps) {
);
}

function Avatar(props: AvatarProps, ref: ForwardedRef<HTMLElement>) {
function Avatar(props: AvatarProps, ref: ForwardedRef<HTMLDivElement>) {
[props, ref] = useContextProps(props, ref, AvatarContext);
const { stylingProps, ...ownProps } = useStyledSystem(props);
const {
Expand Down Expand Up @@ -209,29 +211,29 @@ function Avatar(props: AvatarProps, ref: ForwardedRef<HTMLElement>) {
...style
};

const commonProps = {
className: classNames,
size,
style: mergedStyles
};

const content = src ? (
<AvatarImage
{...otherProps}
{...commonProps}
src={src}
fallbackSrc={fallbackSrc}
className={classNames}
size={size}
style={mergedStyles}
>
{/* Anything added in children is a fallback if AvatarImage fails to load. */}
<AvatarInitials
{...otherProps}
className={classNames}
size={size}
style={mergedStyles}
{...commonProps}
/>
</AvatarImage>
) : (
<AvatarInitials
{...otherProps}
className={classNames}
size={size}
style={mergedStyles}
{...otherProps}
{...commonProps}
/>
);

Expand All @@ -245,7 +247,7 @@ function Avatar(props: AvatarProps, ref: ForwardedRef<HTMLElement>) {
*
* [View Documentation](TODO)
*/
const _Avatar = forwardRef<HTMLElement, AvatarProps>(Avatar);
const _Avatar = forwardRef<HTMLDivElement, AvatarProps>(Avatar);
_Avatar.displayName = "Avatar";

export { _Avatar as Avatar };
2 changes: 1 addition & 1 deletion packages/components/src/Avatar/src/AvatarContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ import type { ContextValue } from "react-aria-components";

import type { AvatarProps } from "./Avatar.tsx";

export const AvatarContext = createContext<ContextValue<AvatarProps, HTMLElement>>({});
export const AvatarContext = createContext<ContextValue<AvatarProps, HTMLDivElement>>({});

AvatarContext.displayName = "AvatarContext";
8 changes: 5 additions & 3 deletions packages/components/src/Avatar/src/RichIconAvatarImage.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { RichIconContext, type RichIconProps } from "@hopper-ui/icons";
import { useStyledSystem, type StyledSystemProps } from "@hopper-ui/styled-system";
import { type ResponsiveProp, useResponsiveValue, useStyledSystem, type StyledSystemProps } from "@hopper-ui/styled-system";
import clsx from "clsx";
import { type CSSProperties, forwardRef, type ForwardedRef, type HTMLAttributes } from "react";
import { useContextProps } from "react-aria-components";
Expand All @@ -22,7 +22,7 @@ export interface RichIconAvatarImageProps extends StyledSystemProps, BaseCompone
* The size of the image.
* * @default "md"
*/
size?: AvatarSize;
size?: ResponsiveProp<AvatarSize>;
}

export const AvatarToIconSizeAdapter: SizeAdapter<AvatarProps["size"], RichIconProps["size"]> = {
Expand All @@ -43,9 +43,11 @@ function RichIconAvatarImage(props: RichIconAvatarImageProps, ref: ForwardedRef<
isDisabled,
style,
slot,
size = "md",
size: sizeValue,
...otherProps
} = ownProps;

const size = useResponsiveValue(sizeValue) ?? "md";

const classNames = clsx(
className,
Expand Down

0 comments on commit 84c4d39

Please sign in to comment.