diff --git a/packages/epo-react-lib/.storybook/utilities/helpers.ts b/packages/epo-react-lib/.storybook/utilities/helpers.ts index 73f42174..86a9c16d 100644 --- a/packages/epo-react-lib/.storybook/utilities/helpers.ts +++ b/packages/epo-react-lib/.storybook/utilities/helpers.ts @@ -19,3 +19,16 @@ export const getGradientImage = (width: number = 200, height: number = 200) => { return canvas.toDataURL(); }; + +export const generateCantoSrcSet = (cantoUrl: string, width?: number) => { + const sizes = [100, 240, 320, 500, 640, 800, 2050].filter((size) => + width ? size < width : true + ); + + return sizes.map((size) => { + return { + src: `${cantoUrl}${size}`, + size, + }; + }); +}; diff --git a/packages/epo-react-lib/jest.config.ts b/packages/epo-react-lib/jest.config.ts index 266ca3c5..836ff28d 100644 --- a/packages/epo-react-lib/jest.config.ts +++ b/packages/epo-react-lib/jest.config.ts @@ -15,6 +15,7 @@ const config: JestConfigWithTsJest = { "^@/hooks(.*)$": "/src/hooks$1", "^@/layout(.*)$": "/src/layout$1", "^@/lib(.*)$": "/src/lib$1", + "^@/molecules(.*)$": "/src/molecules$1", "^@/storybook(.*)$": "/.storybook$1", "^@/styles(.*)$": "/src/styles$1", "^@/svg(.*)$": "/src/svg$1", diff --git a/packages/epo-react-lib/src/atomic/Frame/index.tsx b/packages/epo-react-lib/src/atomic/Frame/index.tsx new file mode 100644 index 00000000..4668aaba --- /dev/null +++ b/packages/epo-react-lib/src/atomic/Frame/index.tsx @@ -0,0 +1,47 @@ +import { FunctionComponent, PropsWithChildren } from "react"; +import * as Styled from "./styles"; + +export interface FrameProps { + aspectRatio: string; + zoom?: number; + position?: string; + className?: string; +} + +const defaultRatio = "16:9"; +const defaultPosition = "50% 50%"; +const defaultZoom = 1; + +const Frame: FunctionComponent> = ({ + aspectRatio = defaultRatio, + position = defaultPosition, + zoom = defaultZoom, + className, + children, +}) => { + let ratio = aspectRatio.split(":"); + + if (ratio.length < 2) { + ratio = defaultRatio.split(":"); + } + + const [n, d] = ratio; + + return ( + + {children} + + ); +}; + +Frame.displayName = "Atomic.Frame"; + +export default Frame; diff --git a/packages/epo-react-lib/src/atomic/Frame/styles.ts b/packages/epo-react-lib/src/atomic/Frame/styles.ts new file mode 100644 index 00000000..3aaa873f --- /dev/null +++ b/packages/epo-react-lib/src/atomic/Frame/styles.ts @@ -0,0 +1,18 @@ +import styled from "styled-components"; + +export const Frame = styled.div` + aspect-ratio: var(--n) / var(--d); + overflow: hidden; + display: flex; + justify-content: center; + align-items: center; + + & > img, + & > video { + inline-size: 100%; + block-size: 100%; + object-fit: cover; + object-position: var(--position-img); + transform: scale(var(--zoom-img)); + } +`; diff --git a/packages/epo-react-lib/src/atomic/Picture/Picture.stories.tsx b/packages/epo-react-lib/src/atomic/Picture/Picture.stories.tsx index ae58396d..6549fd1c 100644 --- a/packages/epo-react-lib/src/atomic/Picture/Picture.stories.tsx +++ b/packages/epo-react-lib/src/atomic/Picture/Picture.stories.tsx @@ -1,5 +1,6 @@ import { Meta, StoryFn } from "@storybook/react"; import { className } from "@/storybook/utilities/argTypes"; +import { generateCantoSrcSet } from "@/storybook/utilities/helpers"; import styled from "styled-components"; import Picture from "."; @@ -12,19 +13,6 @@ const meta: Meta = { }; export default meta; -const generateCantoSrcSet = (cantoUrl: string, width?: number) => { - const sizes = [100, 240, 320, 500, 640, 800, 2050].filter((size) => - width ? size < width : true - ); - - return sizes.map((size) => { - return { - src: `${cantoUrl}${size}`, - size, - }; - }); -}; - const LandscapeTitle = styled.h2` @media screen and (orientation: portrait) { display: none; diff --git a/packages/epo-react-lib/src/atomic/ResponsiveImage/ResponsiveImage.tsx b/packages/epo-react-lib/src/atomic/ResponsiveImage/ResponsiveImage.tsx deleted file mode 100644 index 2fdaa8cc..00000000 --- a/packages/epo-react-lib/src/atomic/ResponsiveImage/ResponsiveImage.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { FunctionComponent } from "react"; -import { ImageProps } from "../Image"; -import * as Styled from "./styles"; - -interface ResponsiveImageProps extends ImageProps { - ratio: string; -} - -const ResponsiveImage: FunctionComponent = ({ - image, - ratio = "8:5", - className, - title, - ...props -}) => { - const aspectRatio = ratio.includes(":") ? ratio.replace(":", "/") : ratio; - - return ( - - - - ); -}; - -ResponsiveImage.displayName = "Atomic.ResponsiveImage"; - -export default ResponsiveImage; diff --git a/packages/epo-react-lib/src/atomic/ResponsiveImage/index.ts b/packages/epo-react-lib/src/atomic/ResponsiveImage/index.ts deleted file mode 100644 index 5cae5e8d..00000000 --- a/packages/epo-react-lib/src/atomic/ResponsiveImage/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default } from "./ResponsiveImage"; diff --git a/packages/epo-react-lib/src/atomic/ResponsiveImage/styles.ts b/packages/epo-react-lib/src/atomic/ResponsiveImage/styles.ts deleted file mode 100644 index 3203a227..00000000 --- a/packages/epo-react-lib/src/atomic/ResponsiveImage/styles.ts +++ /dev/null @@ -1,21 +0,0 @@ -import styled from "styled-components"; -import BaseImage from "../Image"; - -export const ResponsiveImageContainer = styled.div` - position: relative; - overflow: hidden; - width: 100%; - aspect-ratio: var(--aspect-ratio); -`; - -export const Image = styled(BaseImage)` - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - width: auto; - height: auto; - min-width: 100%; - min-height: 100%; - object-fit: cover; -`; diff --git a/packages/epo-react-lib/src/index.ts b/packages/epo-react-lib/src/index.ts index 5cee6a0f..9eb4badb 100644 --- a/packages/epo-react-lib/src/index.ts +++ b/packages/epo-react-lib/src/index.ts @@ -22,7 +22,7 @@ export { default as ProgressBar } from "@/atomic/Progress/Bar"; export { default as Marker } from "@/atomic/Progress/Marker"; export { type PictureProps, default as Picture } from "@/atomic/Picture"; export { default as ProgressRadial } from "@/atomic/Progress/Radial"; -export { default as ResponsiveImage } from "@/atomic/ResponsiveImage"; +export { default as ResponsiveImage } from "@/molecules/ResponsiveImage"; export * from "@/atomic/Share"; export { default as Toast } from "@/atomic/Toast"; export { default as Video } from "@/atomic/Video"; diff --git a/packages/epo-react-lib/src/layout/MasonryGrid/MasonryGridTile.tsx b/packages/epo-react-lib/src/layout/MasonryGrid/MasonryGridTile.tsx index b601ccea..87614f8d 100644 --- a/packages/epo-react-lib/src/layout/MasonryGrid/MasonryGridTile.tsx +++ b/packages/epo-react-lib/src/layout/MasonryGrid/MasonryGridTile.tsx @@ -1,6 +1,6 @@ import * as Styled from "./styles"; import Link from "next/link"; -import ResponsiveImage from "@/atomic/ResponsiveImage"; +import ResponsiveImage from "@/molecules/ResponsiveImage"; import IconComposer from "@/svg/IconComposer"; import { FunctionComponent } from "react"; import { ImageShape } from "@/types/image"; @@ -23,7 +23,7 @@ const Tile: FunctionComponent = ({ return ( - + {isVideo && ( diff --git a/packages/epo-react-lib/src/atomic/ResponsiveImage/ResponsiveImage.stories.tsx b/packages/epo-react-lib/src/molecules/ResponsiveImage/ResponsiveImage.stories.tsx similarity index 50% rename from packages/epo-react-lib/src/atomic/ResponsiveImage/ResponsiveImage.stories.tsx rename to packages/epo-react-lib/src/molecules/ResponsiveImage/ResponsiveImage.stories.tsx index 3c8cb31e..dfd7537b 100644 --- a/packages/epo-react-lib/src/atomic/ResponsiveImage/ResponsiveImage.stories.tsx +++ b/packages/epo-react-lib/src/molecules/ResponsiveImage/ResponsiveImage.stories.tsx @@ -2,6 +2,7 @@ import { Meta, StoryObj } from "@storybook/react"; import { className } from "@/storybook/utilities/argTypes"; import ResponsiveImage from "."; +import { generateCantoSrcSet } from "@/storybook/utilities/helpers"; const meta: Meta = { component: ResponsiveImage, @@ -17,6 +18,15 @@ const meta: Meta = { title: { control: "text", }, + position: { + control: "text", + }, + zoom: { + control: { + type: "number", + step: 0.1, + }, + }, }, }; export default meta; @@ -25,11 +35,12 @@ export const Primary: StoryObj = { args: { image: { altText: "A placeholder image", - url: "https://via.placeholder.com/150", - url2x: "https://via.placeholder.com/300", - url3x: "https://via.placeholder.com/450", - width: 150, - height: 150, + url: "https://rubin.canto.com/direct/image/92ks9squih3nt4q34e18h3fp3m/sWu7y1OuXVmQ73ZyCpXfzEe687Y/original?content-type=image%2Fjpeg&name=Rubin+Marzo+2024+N%C2%BA49.jpg", + srcSet: generateCantoSrcSet( + "https://rubin.canto.com/direct/image/92ks9squih3nt4q34e18h3fp3m/0LtY2_W-ennwJhtJ4FVU_tCLmds/m800/" + ), + width: 5464, + height: 3070, }, }, }; diff --git a/packages/epo-react-lib/src/molecules/ResponsiveImage/index.tsx b/packages/epo-react-lib/src/molecules/ResponsiveImage/index.tsx new file mode 100644 index 00000000..33578b57 --- /dev/null +++ b/packages/epo-react-lib/src/molecules/ResponsiveImage/index.tsx @@ -0,0 +1,36 @@ +import { FunctionComponent } from "react"; +import { ImageProps } from "@/atomic/Image"; +import Frame from "@/atomic/Frame"; +import Image from "@/atomic/Image"; + +interface ResponsiveImageProps extends ImageProps { + /** @deprecated use `aspectRatio` instead */ + ratio?: string; + aspectRatio: string; + position?: string; + zoom?: number; +} + +const defaultRatio = "8:5"; + +const ResponsiveImage: FunctionComponent = ({ + ratio = defaultRatio, + aspectRatio = defaultRatio, + zoom, + position, + className, + ...props +}) => { + return ( + + + + ); +}; + +ResponsiveImage.displayName = "Atomic.ResponsiveImage"; + +export default ResponsiveImage; diff --git a/packages/epo-react-lib/tsconfig.json b/packages/epo-react-lib/tsconfig.json index 46ff6ad1..60d69766 100644 --- a/packages/epo-react-lib/tsconfig.json +++ b/packages/epo-react-lib/tsconfig.json @@ -28,6 +28,7 @@ "@/hooks/*": ["src/hooks/*"], "@/layout/*": ["src/layout/*"], "@/lib/*": ["src/lib/*"], + "@/molecules/*": ["src/molecules/*"], "@/storybook/*": [".storybook/*"], "@/styles/*": ["src/styles/*"], "@/svg/*": ["src/svg/*"], diff --git a/packages/epo-react-lib/vite.config.mts b/packages/epo-react-lib/vite.config.mts index 8ff2d105..756b8c6f 100644 --- a/packages/epo-react-lib/vite.config.mts +++ b/packages/epo-react-lib/vite.config.mts @@ -40,10 +40,6 @@ const entry = { __dirname, "src/atomic/Progress/Radial/ProgressRadial.tsx" ), - ResponsiveImage: resolve( - __dirname, - "src/atomic/ResponsiveImage/ResponsiveImage.tsx" - ), Share: resolve(__dirname, "src/atomic/Share"), Toast: resolve(__dirname, "src/atomic/Toast/Toast.tsx"), SimpleTable: resolve(