Skip to content

Commit

Permalink
feat: Add Image component (#131)
Browse files Browse the repository at this point in the history
  • Loading branch information
RabbitDoge authored Jan 15, 2021
1 parent 03ec590 commit dc95cfc
Show file tree
Hide file tree
Showing 10 changed files with 210 additions and 65 deletions.
30 changes: 30 additions & 0 deletions src/components/Image/BackgroundImage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React, { useEffect, useRef } from "react";
import observerOptions from "./options";
import Wrapper from "./Wrapper";
import { ImageProps } from "./types";

const BackgroundImage: React.FC<ImageProps> = ({ src, ...otherProps }) => {
const imgRef = useRef(null);

useEffect(() => {
const img = (imgRef.current as unknown) as HTMLElement;
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
const { isIntersecting } = entry;
if (isIntersecting) {
img.style.backgroundImage = `url("${src}")`;
observer.disconnect();
}
});
}, observerOptions);
observer.observe(img);

return () => {
observer.disconnect();
};
}, [src]);

return <Wrapper ref={imgRef} {...otherProps} />;
};

export default BackgroundImage;
53 changes: 53 additions & 0 deletions src/components/Image/Image.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import React, { useEffect, useRef, useState } from "react";
import styled from "styled-components";
import observerOptions from "./options";
import Wrapper from "./Wrapper";
import { ImageProps } from "./types";

const StyledImage = styled.img`
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
max-width: 100%;
`;

const Placeholder = styled.div`
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
`;

const Image: React.FC<ImageProps> = ({ src, alt, ...otherProps }) => {
const imgRef = useRef(null);
const [isLoaded, setIsLoaded] = useState(false);

useEffect(() => {
const img = (imgRef.current as unknown) as HTMLImageElement;
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
const { isIntersecting } = entry;
if (isIntersecting) {
setIsLoaded(true);
observer.disconnect();
}
});
}, observerOptions);
observer.observe(img);

return () => {
observer.disconnect();
};
}, [src]);

return (
<Wrapper ref={imgRef} {...otherProps}>
{isLoaded ? <StyledImage src={src} alt={alt} /> : <Placeholder />}
</Wrapper>
);
};

export default Image;
18 changes: 18 additions & 0 deletions src/components/Image/Wrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import styled from "styled-components";
import { space } from "styled-system";
import { ContainerProps } from "./types";

const Wrapper = styled.div<ContainerProps>`
position: relative;
background-position: center center;
background-repeat: no-repeat;
background-size: cover;
height: ${({ height, responsive }) => (responsive ? 0 : height)}px;
max-width: ${({ width }) => width}px;
max-height: ${({ height }) => height}px;
width: 100%;
padding-top: ${({ width, height, responsive }) => (responsive ? (height / width) * 100 : 0)}%;
${space}
`;

export default Wrapper;
80 changes: 80 additions & 0 deletions src/components/Image/index.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import React from "react";
import times from "lodash/times";
import Flex from "../Flex/Flex";
import BackgroundImage from "./BackgroundImage";
import Img from "./Image";

export default {
title: "Components/Image",
argTypes: {},
};

export const Image: React.FC = () => {
return (
<div>
<Img src="https://via.placeholder.com/800x400" width={800} height={400} alt="test" />
<div>Image</div>
</div>
);
};

export const ImageResponsive: React.FC = () => {
return (
<div>
<Img src="https://via.placeholder.com/800x400" width={800} height={400} responsive />
<div>Image</div>
</div>
);
};

export const Background: React.FC = () => {
return (
<div>
<BackgroundImage src="https://via.placeholder.com/800x400" width={800} height={400} mr="16px" />
<div>Background Image</div>
</div>
);
};

export const BackgroundResponsive: React.FC = () => {
return (
<div>
<BackgroundImage src="https://via.placeholder.com/800x400" width={800} height={400} responsive mr="16px" />
<div>Background Image</div>
</div>
);
};

export const LazyImages: React.FC = () => {
return (
<Flex flexWrap="wrap">
{times(40, (index) => (
<Img
key={index}
src={`https://via.placeholder.com/${150 + index}`}
width={150}
height={150}
mb="16px"
mr="16px"
/>
))}
</Flex>
);
};

export const LazyBackgrounds: React.FC = () => {
return (
<Flex flexWrap="wrap">
{times(40, (index) => (
<BackgroundImage
key={index}
src={`https://via.placeholder.com/${150 + index}`}
width={150}
height={150}
mb="16px"
mr="16px"
/>
))}
</Flex>
);
};
2 changes: 2 additions & 0 deletions src/components/Image/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { default as BackgroundImage } from "./BackgroundImage";
export { default as Image } from "./Image";
5 changes: 5 additions & 0 deletions src/components/Image/options.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export default {
root: null,
rootMargin: "200px",
threshold: 0,
};
12 changes: 12 additions & 0 deletions src/components/Image/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { SpaceProps } from "styled-system";

export interface ContainerProps {
width: number;
height: number;
responsive?: boolean;
}

export interface ImageProps extends ContainerProps, SpaceProps {
src: string;
alt?: string;
}
57 changes: 0 additions & 57 deletions src/hooks/bunny-santa.svg

This file was deleted.

17 changes: 9 additions & 8 deletions src/hooks/useParticleBurst.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@ import React from "react";
import Button from "../components/Button/Button";
import Text from "../components/Text/Text";
import useParticleBurst from "./useParticleBurst";
import bunnySantaPath from "./bunny-santa.svg";

const imagePath = "https://via.placeholder.com/10";

export default {
title: "Hooks/useParticleBurst",
argTypes: {},
};

export const WithSelector: React.FC = () => {
useParticleBurst({ imgSrc: bunnySantaPath, selector: "button" });
useParticleBurst({ imgSrc: imagePath, selector: "button" });

return (
<div style={{ padding: "32px" }}>
Expand All @@ -28,7 +29,7 @@ export const WithSelector: React.FC = () => {
};

export const Document: React.FC = () => {
useParticleBurst({ imgSrc: bunnySantaPath });
useParticleBurst({ imgSrc: imagePath });

return (
<div style={{ padding: "32px" }}>
Expand All @@ -38,7 +39,7 @@ export const Document: React.FC = () => {
};

export const AdjustDistance: React.FC = () => {
useParticleBurst({ imgSrc: bunnySantaPath, particleOptions: { distance: 800 } });
useParticleBurst({ imgSrc: imagePath, particleOptions: { distance: 800 } });

return (
<div style={{ padding: "32px" }}>
Expand All @@ -48,7 +49,7 @@ export const AdjustDistance: React.FC = () => {
};

export const AdjustSize: React.FC = () => {
useParticleBurst({ imgSrc: bunnySantaPath, particleOptions: { size: 80 } });
useParticleBurst({ imgSrc: imagePath, particleOptions: { size: 80 } });

return (
<div style={{ padding: "32px" }}>
Expand All @@ -58,7 +59,7 @@ export const AdjustSize: React.FC = () => {
};

export const AdjustNumberOfParticles: React.FC = () => {
useParticleBurst({ imgSrc: bunnySantaPath, numberOfParticles: 100 });
useParticleBurst({ imgSrc: imagePath, numberOfParticles: 100 });

return (
<div style={{ padding: "32px" }}>
Expand All @@ -74,7 +75,7 @@ export const DisableUnderCondition: React.FC = () => {

return currentMinutes % 2 !== 0;
};
useParticleBurst({ selector: "button", imgSrc: bunnySantaPath, disableWhen });
useParticleBurst({ selector: "button", imgSrc: imagePath, disableWhen });

return (
<div style={{ padding: "32px" }}>
Expand All @@ -85,7 +86,7 @@ export const DisableUnderCondition: React.FC = () => {
};

export const StopAndStart: React.FC = () => {
const { initialize, teardown } = useParticleBurst({ imgSrc: bunnySantaPath });
const { initialize, teardown } = useParticleBurst({ imgSrc: imagePath });

const handleInitialize = () => initialize();
const handleTeardown = () => teardown();
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export * from "./components/Checkbox";
export * from "./components/Dropdown";
export * from "./components/Flex";
export * from "./components/Heading";
export * from "./components/Image";
export * from "./components/Input";
export * from "./components/Layouts";
export * from "./components/Svg";
Expand Down

0 comments on commit dc95cfc

Please sign in to comment.