Skip to content

Commit

Permalink
feat: introduce PhotoAlbum 'sizes' parameter
Browse files Browse the repository at this point in the history
  • Loading branch information
igordanchenko committed Jan 16, 2022
1 parent 238b688 commit 866dfe3
Show file tree
Hide file tree
Showing 5 changed files with 1,287 additions and 28 deletions.
4 changes: 4 additions & 0 deletions src/PhotoAlbum.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const resolveLayoutOptions = <T extends Photo>({
columns,
spacing,
padding,
sizes,
}: Omit<PhotoAlbumProps<T>, "photos"> & {
viewportWidth?: number;
containerWidth: number;
Expand All @@ -35,6 +36,7 @@ const resolveLayoutOptions = <T extends Photo>({
(w) => w / 3,
(w) => w / 2,
]),
sizes,
});

const PhotoAlbum = <T extends Photo>(props: PhotoAlbumProps<T>): JSX.Element => {
Expand All @@ -44,6 +46,7 @@ const PhotoAlbum = <T extends Photo>(props: PhotoAlbumProps<T>): JSX.Element =>
columns,
spacing,
padding,
sizes,
onClick,
targetRowHeight,
defaultContainerWidth = 800,
Expand Down Expand Up @@ -113,6 +116,7 @@ const PhotoAlbum = <T extends Photo>(props: PhotoAlbumProps<T>): JSX.Element =>
padding,
columns,
targetRowHeight,
sizes,
});

const commonLayoutProps = { photos, renderPhoto, instrumentation };
Expand Down
57 changes: 29 additions & 28 deletions src/components/renderers/PhotoRenderer.tsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,29 @@
import * as React from "react";

import round from "../../utils/round";
import { LayoutOptions, Photo, PhotoLayout, PhotoProps, RenderPhoto } from "../../types";

const cssWidth = (photoLayout: PhotoLayout, layoutOptions: LayoutOptions) => {
const { width } = photoLayout;
const { spacing, padding, layout, containerWidth } = layoutOptions;
const calcWidth = (
base: string,
{ width, photosCount }: PhotoLayout,
{ spacing, padding, containerWidth }: LayoutOptions
) => {
const gaps = spacing * (photosCount - 1) + 2 * padding * photosCount;
return `calc((${base} - ${gaps}px) / ${round((containerWidth - gaps) / width, 5)})`;
};

if (layout !== "rows") {
return `calc(100% - ${2 * padding}px)`;
const cssWidth = (layout: PhotoLayout, layoutOptions: LayoutOptions) => {
if (layoutOptions.layout !== "rows") {
return `calc(100% - ${2 * layoutOptions.padding}px)`;
}

const rowSize = photoLayout.photosCount;
return `calc((100% - ${spacing * (rowSize - 1) + 2 * padding * rowSize}px) / ${round(
(containerWidth - spacing * (rowSize - 1) - 2 * padding * rowSize) / width,
5
)})`;
return calcWidth("100%", layout, layoutOptions);
};

const srcSetAndSizes = <T extends Photo = Photo>({
photo,
layout,
layoutOptions,
}: {
photo: T;
layout: PhotoLayout;
layoutOptions: LayoutOptions;
}) => {
let srcSet;
let sizes;
const calculateSizesValue = (size: string, layout: PhotoLayout, layoutOptions: LayoutOptions) =>
calcWidth(size.match(/calc\((.*)\)/)?.[1] ?? size, layout, layoutOptions);

const srcSetAndSizes = <T extends Photo = Photo>(photo: T, layout: PhotoLayout, layoutOptions: LayoutOptions) => {
let srcSet, sizes;

if (photo.images && photo.images.length > 0) {
srcSet = photo.images
Expand All @@ -41,15 +37,23 @@ const srcSetAndSizes = <T extends Photo = Photo>({
.sort((first, second) => first.width - second.width)
.map((image) => `${image.src} ${image.width}w`)
.join(", ");
}

if (!layoutOptions.viewportWidth && layoutOptions.sizes) {
sizes = (layoutOptions.sizes.sizes || [])
.map(({ viewport, size }) => `${viewport} ${calculateSizesValue(size, layout, layoutOptions)}`)
.concat(calculateSizesValue(layoutOptions.sizes.size, layout, layoutOptions))
.join(", ");
} else {
sizes = `${Math.ceil((layout.width / (layoutOptions.viewportWidth || layoutOptions.containerWidth)) * 100)}vw`;
}

return { srcSet, sizes };
};

const DefaultPhotoRenderer = <T extends Photo = Photo>({ imageProps }: PhotoProps<T>) => {
const { src, alt, ...rest } = imageProps;
return <img src={src} alt={alt} {...rest} />;
const { src, alt, srcSet, sizes, ...rest } = imageProps;
return <img src={src} alt={alt} {...(srcSet ? { srcSet, sizes } : null)} {...rest} />;
};

type PhotoRendererProps<T extends Photo = Photo> = {
Expand Down Expand Up @@ -83,17 +87,14 @@ const PhotoRenderer = <T extends Photo = Photo>(props: PhotoRendererProps<T>) =>
}
: undefined;

const { srcSet, sizes } = srcSetAndSizes({ photo, layout, layoutOptions });

const imageProps = {
src: photo.src,
alt: photo.alt ?? "",
title: photo.title,
onClick: handleClick,
style,
sizes,
srcSet,
className: "react-photo-album--photo",
...srcSetAndSizes(photo, layout, layoutOptions),
};

const Component = renderPhoto || DefaultPhotoRenderer;
Expand Down
16 changes: 16 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,18 @@ export type ResponsiveParameterProvider = (containerWidth: number) => number;

export type ResponsiveParameter = number | ResponsiveParameterProvider;

export type ResponsiveSizes = {
/** default size e.g. 100vw or calc(100vw - 200px) */
size: string;
/** array of sizes at various breakpoint */
sizes?: {
/** viewport size media query e.g. (max-width: 600px) */
viewport: string;
/** photo album width at given viewport size e.g. calc(100vw - 50px) */
size: string;
}[];
};

export interface Image {
/** image source */
src: string;
Expand Down Expand Up @@ -72,6 +84,8 @@ export type PhotoAlbumProps<T extends Photo = Photo> = {
padding?: ResponsiveParameter;
/** target row height in 'rows' layout */
targetRowHeight?: ResponsiveParameter;
/** photo album size at various viewport sizes */
sizes?: ResponsiveSizes;
/** photo click handler */
onClick?: ClickHandler;
/** default container width to be used in SSR render */
Expand Down Expand Up @@ -103,6 +117,8 @@ export type GenericLayoutOptions = {
viewportWidth?: number;
/** photo click handler */
onClick?: ClickHandler;
/** photo album size at various viewport sizes */
sizes?: ResponsiveSizes;
};

export type RowsLayoutOptions = GenericLayoutOptions & {
Expand Down
20 changes: 20 additions & 0 deletions test/PhotoAlbum.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,26 @@ describe("PhotoAlbum", () => {
expect(onClick.mock.calls[0][1]).toBe(testPhotos[0]);
});

it("supports sizes attribute", () => {
whenAskedToRender(<PhotoAlbum layout={"rows"} photos={photos} sizes={{ size: "100vw" }} />);

whenAskedToRender(
<PhotoAlbum
layout={"rows"}
photos={photos}
sizes={{ size: "50vw", sizes: [{ viewport: "(max-width: 600px)", size: "100vw" }] }}
/>
);

whenAskedToRender(
<PhotoAlbum
layout={"rows"}
photos={photos}
sizes={{ size: "calc(50vw - 50px)", sizes: [{ viewport: "(max-width: 600px)", size: "100vw" }] }}
/>
);
});

it("supports global ResizeObserver", () => {
const resizeObserverRef = global.ResizeObserver;
try {
Expand Down
Loading

0 comments on commit 866dfe3

Please sign in to comment.