Skip to content

Instantly share code, notes, and snippets.

@tzynwang
Created September 19, 2022 23:49
Show Gist options
  • Select an option

  • Save tzynwang/1f561a290868e79cad29b6c2ea46ac62 to your computer and use it in GitHub Desktop.

Select an option

Save tzynwang/1f561a290868e79cad29b6c2ea46ac62 to your computer and use it in GitHub Desktop.
import React, { memo, useMemo } from 'react';
import cn from 'classnames';
import { css } from '@emotion/css';
import SpaceWrapper from '@Components/Layout/SpaceWrapper';
import Stack from '@Components/Layout/Stack';
import Divider from '@Components/Common/Divider';
import Image from '@Components/Common/Image';
import foxSrc from '@Assets/erik-mclean-OVWn1sbGIYQ-unsplash.jpg';
const linkStyle = css({
padding: '0 4px',
fontSize: 'inherit',
fontFamily: 'inherit',
color: '#8c7851',
textDecoration: 'none',
transition: 'all .2s ease',
'&:visited': {
color: '#51658c',
},
'&:hover': {
color: '#b29c59',
textDecoration: 'underline',
},
});
function ImageDemo(): React.ReactElement {
/* States */
const foxCaption = useMemo(
() => (
<React.Fragment>
Photo by
<a
className={cn(linkStyle)}
href="https://unsplash.com/@introspectivedsgn?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText"
target="_blank"
rel="noreferrer"
>
Erik Mclean
</a>
on
<a
className={cn(linkStyle)}
href="https://unsplash.com/s/photos/fox?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText"
target="_blank"
rel="noreferrer"
>
Unsplash
</a>
</React.Fragment>
),
[]
);
/* Main */
return (
<SpaceWrapper padding={[16, 0]}>
<Stack divider={<Divider />}>
<Image
height="300px"
caption={foxCaption}
src={foxSrc}
alt="fox on the road"
/>
<Image src="..." />
</Stack>
</SpaceWrapper>
);
}
export default memo(ImageDemo);
import React, { memo } from 'react';
import { css } from '@emotion/css';
import cn from 'classnames';
import ImageBase from '@Components/Base/ImageBase';
import type { ImageProps } from './types';
const onErrorStyle = css({
display: 'block',
width: '200px',
height: '200px',
'&::after': {
content: '"image unavailable"',
width: '100%',
height: '100%',
position: 'absolute',
inset: 0,
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#e0e0e0',
color: 'rgba(0, 0, 0, 0.8)',
fontFamily: 'inherit',
fontSize: '12px',
},
});
const captionStyle = css({
textAlign: 'center',
fontSize: '12px',
color: '#78909c',
});
function Image(props: ImageProps): React.ReactElement {
/* States */
const { classes, ...rest } = props;
/* Main */
return (
<ImageBase
classes={{
figure: cn(classes?.figure),
img: cn(classes?.img),
caption: cn(captionStyle, classes?.caption),
onError: cn(onErrorStyle, classes?.onError),
}}
{...rest}
/>
);
}
export default memo(Image);
import type { ImageBaseProps } from '@Components/Base/ImageBase/types';
export type ImageProps = ImageBaseProps;
import React, { memo, useState, useMemo, useCallback, useEffect } from 'react';
import { css } from '@emotion/css';
import cn from 'classnames';
import type { ImageBaseProps } from './types';
function ImageBase(props: ImageBaseProps): React.ReactElement {
/* States */
const {
height,
width,
objectFit = 'cover',
caption = '',
loading = 'lazy',
alt = 'image',
classes = { figure: '', img: '', caption: '', onError: '' },
imageOnly = false,
...rest
} = props;
delete rest.className;
const [hasError, setHasError] = useState<boolean>(false);
const imgStyle = useMemo(
() =>
css({
width: width ? width : undefined,
height: height ? height : undefined,
objectFit,
position: 'relative',
}),
[height, width, objectFit]
);
const loadCaption = useMemo(() => !hasError && caption, [hasError, caption]);
/* Functions */
const handleImgLoadError = useCallback((): void => {
setHasError(true);
}, []);
/* Hooks */
useEffect(() => () => setHasError(false), []);
/* Main */
return imageOnly ? (
<img
alt={alt}
className={cn(imgStyle, hasError && classes.onError, classes.img)}
loading={loading}
onError={handleImgLoadError}
{...rest}
/>
) : (
<figure className={cn(classes.figure)}>
<img
alt={alt}
className={cn(imgStyle, hasError && classes.onError, classes.img)}
loading={loading}
onError={handleImgLoadError}
{...rest}
/>
{loadCaption && (
<figcaption className={cn(classes.caption)}>{caption}</figcaption>
)}
</figure>
);
}
export default memo(ImageBase);
import type React from 'react';
import type { Property } from 'csstype';
export interface ImageBaseProps
extends React.ImgHTMLAttributes<HTMLImageElement> {
width?: Property.Width;
height?: Property.Height;
objectFit?: Property.ObjectFit;
caption?: React.ReactNode;
classes?: Partial<ImageClasses>;
imageOnly?: boolean;
}
interface ImageClasses {
figure: string;
img: string;
caption: string;
onError: string;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment