feat(Activity): show the correct number of cards per row

This commit is contained in:
Aminejv 2021-07-14 11:16:52 +01:00
parent d1a8ea2172
commit 2570a2b126
8 changed files with 142 additions and 40 deletions

View File

@ -195,3 +195,24 @@ export const filetypes = {
}; };
export const linkPreviewSizeLimit = 5000000; //NOTE(martina): 5mb limit for twitter preview images export const linkPreviewSizeLimit = 5000000; //NOTE(martina): 5mb limit for twitter preview images
// NOTE(amine): used to calculate how many cards will fit into a row in sceneActivity
export const grids = {
activity: {
profileInfo: {
width: 260,
},
},
object: {
desktop: { width: 248, rowGap: 16 },
mobile: { width: 166, rowGap: 8 },
},
collection: {
desktop: { width: 432, rowGap: 16 },
mobile: { width: 300, rowGap: 8 },
},
profile: {
desktop: { width: 432, rowGap: 16 },
mobile: { width: 344, rowGap: 8 },
},
};

View File

@ -235,14 +235,14 @@ export const IMAGE_FIT = css`
`; `;
/* COMMON GRIDS */ /* COMMON GRIDS */
export const OBJECTS_PREVIEW_GRID = css` export const OBJECTS_PREVIEW_GRID = (theme) => css`
display: grid; display: grid;
grid-template-columns: repeat(auto-fill, minmax(248px, 1fr)); grid-template-columns: repeat(auto-fill, minmax(${theme.grids.object.desktop.width}px, 1fr));
grid-gap: 24px 16px; grid-gap: 24px ${theme.grids.object.desktop.rowGap}px;
@media (max-width: ${Constants.sizes.mobile}px) { @media (max-width: ${Constants.sizes.mobile}px) {
grid-gap: 20px 8px; grid-gap: 20px ${theme.grids.object.mobile.rowGap}px;
grid-template-columns: repeat(auto-fill, minmax(166px, 1fr)); grid-template-columns: repeat(auto-fill, minmax(${theme.grids.object.mobile.width}px, 1fr));
} }
`; `;
@ -254,26 +254,24 @@ export const BUTTON_RESET = css`
${HOVERABLE} ${HOVERABLE}
`; `;
export const COLLECTIONS_PREVIEW_GRID = css` export const COLLECTIONS_PREVIEW_GRID = (theme) => css`
display: grid; display: grid;
display: grid; grid-template-columns: repeat(auto-fill, minmax(${theme.grids.collection.desktop.width}px, 1fr));
grid-template-columns: repeat(auto-fill, minmax(432px, 1fr)); grid-gap: 24px ${theme.grids.collection.desktop.rowGap}px;
grid-gap: 24px 16px;
@media (max-width: ${Constants.sizes.desktop}px) { @media (max-width: ${Constants.sizes.desktop}px) {
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); grid-gap: 20px ${theme.grids.collection.mobile.rowGap}px;
grid-gap: 20px 8px; grid-template-columns: repeat(auto-fill, minmax(${theme.grids.collection.mobile.width}px, 1fr));
} }
`; `;
export const PROFILE_PREVIEW_GRID = css` export const PROFILE_PREVIEW_GRID = (theme) => css`
display: grid; display: grid;
display: grid; grid-template-columns: repeat(auto-fill, minmax(${theme.grids.profile.desktop.width}px, 1fr));
grid-template-columns: repeat(auto-fill, minmax(432px, 1fr)); grid-gap: 24px ${theme.grids.profile.desktop.rowGap}px;
grid-gap: 24px 16px;
@media (max-width: ${Constants.sizes.mobile}px) { @media (max-width: ${Constants.sizes.mobile}px) {
grid-gap: 20px 8px; grid-gap: 20px ${theme.grids.profile.mobile.rowGap}px;
grid-template-columns: repeat(auto-fill, minmax(344px, 1fr)); grid-template-columns: repeat(auto-fill, minmax(${theme.grids.profile.mobile.width}px, 1fr));
} }
`; `;

View File

@ -29,13 +29,22 @@ const STYLES_VIEWMORE_CONTAINER = (theme) => css`
} }
`; `;
export default function ActivityCollectionGroup({ onAction, viewer, group, ...props }) { export default function ActivityCollectionGroup({
onAction,
viewer,
group,
nbrOfCollectionsPerRow = 2,
...props
}) {
const { owner, slate, type, createdAt } = group; const { owner, slate, type, createdAt } = group;
const { elements, restElements } = React.useMemo(() => { const { elements, restElements } = React.useMemo(() => {
if (!Array.isArray(slate)) { if (!Array.isArray(slate)) {
return { elements: [slate] }; return { elements: [slate] };
} }
return { elements: slate.slice(0, 2), restElements: slate.slice(2) }; return {
elements: slate.slice(0, nbrOfCollectionsPerRow),
restElements: slate.slice(nbrOfCollectionsPerRow),
};
}, [slate]); }, [slate]);
const [showMore, setShowMore] = React.useState(false); const [showMore, setShowMore] = React.useState(false);

View File

@ -1,6 +1,7 @@
import * as React from "react"; import * as React from "react";
import * as Strings from "~/common/strings"; import * as Strings from "~/common/strings";
import * as Utilities from "~/common/utilities"; import * as Utilities from "~/common/utilities";
import * as Styles from "~/common/styles";
import { css } from "@emotion/react"; import { css } from "@emotion/react";
import { motion } from "framer-motion"; import { motion } from "framer-motion";
@ -8,20 +9,9 @@ import { ViewMoreContent, ProfileInfo } from "~/components/core/ActivityGroup/co
import ObjectPreview from "~/components/core/ObjectPreview"; import ObjectPreview from "~/components/core/ObjectPreview";
const STYLES_OBJECT_GRID = (theme) => css`
display: grid;
grid-template-columns: repeat(auto-fill, minmax(248px, 1fr));
grid-gap: 20px 12px;
@media (max-width: ${theme.sizes.mobile}px) {
grid-row-gap: 24px;
grid-template-columns: repeat(auto-fill, minmax(169px, 1fr));
}
`;
const STYLES_GROUP_GRID = (theme) => css` const STYLES_GROUP_GRID = (theme) => css`
display: grid; display: grid;
grid-template-columns: 260px 1fr; grid-template-columns: ${theme.grids.activity.profileInfo.width}px 1fr;
grid-row-gap: 32px; grid-row-gap: 32px;
border-bottom: 1px solid ${theme.semantic.bgLight}; border-bottom: 1px solid ${theme.semantic.bgLight};
padding-bottom: 24px; padding-bottom: 24px;
@ -38,14 +28,17 @@ const STYLES_VIEWMORE_CONTAINER = (theme) => css`
} }
`; `;
export default function ActivityFileGroup({ viewer, group, onAction }) { export default function ActivityFileGroup({ viewer, group, onAction, nbrOfObjectsPerRow = 4 }) {
const { file, owner, slate, type, createdAt } = group; const { file, owner, slate, type, createdAt } = group;
const { elements, restElements } = React.useMemo(() => { const { elements, restElements } = React.useMemo(() => {
if (!Array.isArray(file)) { if (!Array.isArray(file)) {
return { elements: [file] }; return { elements: [file] };
} }
return { elements: file.slice(0, 4), restElements: file.slice(4) }; return {
elements: file.slice(0, nbrOfObjectsPerRow),
restElements: file.slice(nbrOfObjectsPerRow),
};
}, [file]); }, [file]);
const [showMore, setShowMore] = React.useState(false); const [showMore, setShowMore] = React.useState(false);
@ -78,7 +71,7 @@ export default function ActivityFileGroup({ viewer, group, onAction }) {
onAction={onAction} onAction={onAction}
/> />
<div> <div>
<div css={STYLES_OBJECT_GRID}> <div css={Styles.OBJECTS_PREVIEW_GRID}>
{elements.map((file) => ( {elements.map((file) => (
<ObjectPreview viewer={viewer} owner={file.owner} key={file.id} file={file} /> <ObjectPreview viewer={viewer} owner={file.owner} key={file.id} file={file} />
))} ))}

View File

@ -30,14 +30,23 @@ const STYLES_VIEWMORE_CONTAINER = (theme) => css`
} }
`; `;
export default function ActivityProfileGroup({ viewer, external, group, onAction }) { export default function ActivityProfileGroup({
viewer,
external,
group,
nbrOfProfilesPerRow = 2,
onAction,
}) {
const { owner, user, createdAt } = group; const { owner, user, createdAt } = group;
const { elements, restElements } = React.useMemo(() => { const { elements, restElements } = React.useMemo(() => {
if (!Array.isArray(user)) { if (!Array.isArray(user)) {
return { elements: [user] }; return { elements: [user] };
} }
return { elements: user.slice(0, 3), restElements: user.slice(3) }; return {
elements: user.slice(0, nbrOfProfilesPerRow),
restElements: user.slice(nbrOfProfilesPerRow),
};
}, [user]); }, [user]);
const [showMore, setShowMore] = React.useState(false); const [showMore, setShowMore] = React.useState(false);

View File

@ -16,7 +16,7 @@ const STYLES_GROUP_GRID = (theme) => css`
padding-bottom: 24px; padding-bottom: 24px;
`; `;
export default function ActivityGroup({ onAction, viewer, external, group }) { export default function ActivityGroup({ onAction, viewer, external, group, nbrOfCardsPerRow }) {
const { type } = group; const { type } = group;
if ( if (
type === "CREATE_FILE" || type === "CREATE_FILE" ||
@ -24,16 +24,36 @@ export default function ActivityGroup({ onAction, viewer, external, group }) {
type === "LIKE_FILE" || type === "LIKE_FILE" ||
type === "SAVE_COPY" type === "SAVE_COPY"
) { ) {
return <ActivityFileGroup viewer={viewer} onAction={onAction} group={group} />; return (
<ActivityFileGroup
nbrOfObjectsPerRow={nbrOfCardsPerRow.object}
viewer={viewer}
onAction={onAction}
group={group}
/>
);
} }
if (type === "CREATE_SLATE" || type === "SUBSCRIBE_SLATE") { if (type === "CREATE_SLATE" || type === "SUBSCRIBE_SLATE") {
return <ActivityCollectionGroup onAction={onAction} viewer={viewer} group={group} />; return (
<ActivityCollectionGroup
onAction={onAction}
viewer={viewer}
group={group}
nbrOfCollectionsPerRow={nbrOfCardsPerRow.collection}
/>
);
} }
if (type === "SUBSCRIBE_USER") { if (type === "SUBSCRIBE_USER") {
return ( return (
<ActivityProfileGroup onAction={onAction} viewer={viewer} external={external} group={group} /> <ActivityProfileGroup
nbrOfProfilesPerRow={nbrOfCardsPerRow.profile}
onAction={onAction}
viewer={viewer}
external={external}
group={group}
/>
); );
} }

View File

@ -28,6 +28,7 @@ export default function ThemeProvider({ children }) {
font: Constants.font, font: Constants.font,
typescale: Constants.typescale, typescale: Constants.typescale,
semantic: Constants.semantic, semantic: Constants.semantic,
grids: Constants.grids,
...theme, ...theme,
}), }),
[theme] [theme]

View File

@ -41,6 +41,9 @@ export default function SceneActivity({ page, viewer, external, onAction }) {
}); });
const divRef = React.useRef(); const divRef = React.useRef();
const nbrOfCardsInRow = useNbrOfCardsPerRow(divRef);
useIntersection({ useIntersection({
ref: divRef, ref: divRef,
onIntersect: () => { onIntersect: () => {
@ -69,6 +72,7 @@ export default function SceneActivity({ page, viewer, external, onAction }) {
<div css={STYLES_GROUPS_CONTAINER}> <div css={STYLES_GROUPS_CONTAINER}>
{feed?.map((group) => ( {feed?.map((group) => (
<ActivityGroup <ActivityGroup
nbrOfCardsPerRow={nbrOfCardsInRow}
key={group.id} key={group.id}
viewer={viewer} viewer={viewer}
external={external} external={external}
@ -84,3 +88,50 @@ export default function SceneActivity({ page, viewer, external, onAction }) {
</WebsitePrototypeWrapper> </WebsitePrototypeWrapper>
); );
} }
let NbrOfCardsInRow = {};
function useNbrOfCardsPerRow(ref) {
const calculateNbrOfCards = (card) => {
const isMobile = window.matchMedia(`(max-width: ${Constants.sizes.mobile}px)`).matches;
const profileInfoWidth = isMobile ? 0 : Constants.grids.activity.profileInfo.width;
const containerWidth = ref.current.offsetWidth - profileInfoWidth;
const nbrOfCardsWithoutGap = Math.floor(containerWidth / card.width);
const gapsWidth = (nbrOfCardsWithoutGap - 1) * card.gap;
return Math.floor((containerWidth - gapsWidth) / card.width) || 1;
};
React.useEffect(() => {
if (JSON.stringify(NbrOfCardsInRow) !== "{}") return;
const isMobile = window.matchMedia(`(max-width: ${Constants.sizes.mobile}px)`).matches;
const responsiveKey = isMobile ? "mobile" : "desktop";
const { width: objectPreviewWidth, rowGap: objectPreviewGridRowGap } =
Constants.grids.object[responsiveKey];
NbrOfCardsInRow.object = calculateNbrOfCards({
width: objectPreviewWidth,
gap: objectPreviewGridRowGap,
});
const { width: collectionPreviewWidth, rowGap: collectionPreviewGridRowGap } =
Constants.grids.collection[responsiveKey];
NbrOfCardsInRow.collection = calculateNbrOfCards({
width: collectionPreviewWidth,
gap: collectionPreviewGridRowGap,
});
const { width: profilePreviewWidth, rowGap: profilePreviewGridRowGap } =
Constants.grids.profile[responsiveKey];
NbrOfCardsInRow.profile = calculateNbrOfCards({
width: profilePreviewWidth,
gap: profilePreviewGridRowGap,
});
}, []);
return NbrOfCardsInRow;
}