Skip to content

Commit

Permalink
[FRNT-555] feat: implement avatar component (#143)
Browse files Browse the repository at this point in the history
* feat: implement avatar component

* feat: pass AvatarBase to styled instead of declare AvatarContainer

Co-authored-by: Roman Chubarkin <r.chubarkin@opends.tech>
  • Loading branch information
rchubarkin and Roman Chubarkin committed Jun 23, 2021
1 parent 82fc58c commit 3eeac28
Show file tree
Hide file tree
Showing 6 changed files with 158 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/static/icons/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ export { default as IconPlus } from './plus.svg';
export { default as IconSearch } from './search.svg';
export { default as IconFilledUnchecked } from './check-filled-unchecked.svg';
export { default as IconSpinner } from './spinner.svg';
export { default as IconProfile } from './profile.svg';
5 changes: 5 additions & 0 deletions src/static/icons/profile.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
49 changes: 49 additions & 0 deletions src/woly/atoms/avatar/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import React from 'react';
import styled, { StyledComponent } from 'styled-components';
import { IconProfile } from 'static/icons';

import { useImageLoad } from './use-image-load';

interface AvatarProps {
alt?: string;
src?: string;
srcSet?: string;
children?: React.ReactNode;
}

const AvatarBase: React.FC<AvatarProps> = ({
alt,
children: childrenProp,
src,
srcSet,
...props
}) => {
const loadFailed = useImageLoad({ src, srcSet });
const hasImg = src || srcSet;
let children = null;

if (hasImg && !loadFailed) {
children = <img alt={alt} src={src} srcSet={srcSet} />;
} else if (childrenProp) {
children = childrenProp;
} else {
// render fallback if image loading failed or no src attributes / children provided
children = <IconProfile />;
}

return <div {...props}>{children}</div>;
};

export const Avatar = styled(AvatarBase)`
--local-size: calc((var(--woly-component-level) + 2) * 2 * var(--woly-const-m));
width: var(--local-size);
height: var(--local-size);
& > * {
width: 100%;
height: 100%;
border-radius: 50%;
}
` as StyledComponent<'div', Record<string, unknown>, AvatarProps>;
67 changes: 67 additions & 0 deletions src/woly/atoms/avatar/usage.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import {Avatar} from 'ui'
import {Playground, block} from 'lib/playground'

`Avatar` shows user avatar

### Example

<Playground>
<Avatar src="https://image.flaticon.com/icons/png/512/168/168726.png" />
</Playground>

### Sizes

<Playground>
<block.N>
<Avatar src="https://image.flaticon.com/icons/png/512/168/168726.png" />
</block.N>
<block.S>
<Avatar src="https://image.flaticon.com/icons/png/512/168/168726.png" />
</block.S>
<block.M>
<Avatar src="https://image.flaticon.com/icons/png/512/168/168726.png" />
</block.M>
<block.L>
<Avatar src="https://image.flaticon.com/icons/png/512/168/168726.png" />
</block.L>
<block.XL>
<Avatar src="https://image.flaticon.com/icons/png/512/168/168726.png" />
</block.XL>
<block.H>
<Avatar src="https://image.flaticon.com/icons/png/512/168/168726.png" />
</block.H>
</Playground>

### Fallback

<Playground>
<Avatar src="http://broken.url/image" />
</Playground>

### Custom child component

<Playground>
<Avatar>
<div
style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'black',
color: 'white',
fontSize: '20px',
}}
>
RC
</div>
</Avatar>
</Playground>

### Props

| Name | Type | Default | Description |
| ---------- | ----------------- | ----------- | ---------------------------------------- |
| `alt` | `string` | `''` | text description of the image |
| `children` | `React.ReactNode` | `undefined` | use if no src attributes provided |
| `src` | `string` | `''` | avatar src |
| `srcSet` | `string` | `''` | avatar src set for multiple screen sizes |
35 changes: 35 additions & 0 deletions src/woly/atoms/avatar/use-image-load.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { useEffect, useState } from 'react';

export function useImageLoad({ src, srcSet }: { src?: string; srcSet?: string }) {
const [failed, setFailed] = useState(false);

useEffect(() => {
if (!src && !srcSet) {
return;
}

setFailed(false);

const image = new Image();
image.src = src ?? '';
image.srcset = srcSet ?? '';

const onLoad = () => {
setFailed(false);
};

const onError = () => {
setFailed(true);
};

image.addEventListener('load', onLoad);
image.addEventListener('error', onError);

return () => {
image.removeEventListener('load', onLoad);
image.removeEventListener('error', onError);
};
}, [src, srcSet]);

return failed;
}
1 change: 1 addition & 0 deletions src/woly/atoms/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ export { Text } from './text';
export { TextArea } from './text-area';
export { Tooltip } from './tooltip';
export { UploadArea } from './upload-area';
export { Avatar } from './avatar';

0 comments on commit 3eeac28

Please sign in to comment.