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
// 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 */
export const OBJECTS_PREVIEW_GRID = css`
export const OBJECTS_PREVIEW_GRID = (theme) => css`
display: grid;
grid-template-columns: repeat(auto-fill, minmax(248px, 1fr));
grid-gap: 24px 16px;
grid-template-columns: repeat(auto-fill, minmax(${theme.grids.object.desktop.width}px, 1fr));
grid-gap: 24px ${theme.grids.object.desktop.rowGap}px;
@media (max-width: ${Constants.sizes.mobile}px) {
grid-gap: 20px 8px;
grid-template-columns: repeat(auto-fill, minmax(166px, 1fr));
grid-gap: 20px ${theme.grids.object.mobile.rowGap}px;
grid-template-columns: repeat(auto-fill, minmax(${theme.grids.object.mobile.width}px, 1fr));
}
`;
@ -254,26 +254,24 @@ export const BUTTON_RESET = css`
${HOVERABLE}
`;
export const COLLECTIONS_PREVIEW_GRID = css`
export const COLLECTIONS_PREVIEW_GRID = (theme) => css`
display: grid;
display: grid;
grid-template-columns: repeat(auto-fill, minmax(432px, 1fr));
grid-gap: 24px 16px;
grid-template-columns: repeat(auto-fill, minmax(${theme.grids.collection.desktop.width}px, 1fr));
grid-gap: 24px ${theme.grids.collection.desktop.rowGap}px;
@media (max-width: ${Constants.sizes.desktop}px) {
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
grid-gap: 20px 8px;
grid-gap: 20px ${theme.grids.collection.mobile.rowGap}px;
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;
grid-template-columns: repeat(auto-fill, minmax(432px, 1fr));
grid-gap: 24px 16px;
grid-template-columns: repeat(auto-fill, minmax(${theme.grids.profile.desktop.width}px, 1fr));
grid-gap: 24px ${theme.grids.profile.desktop.rowGap}px;
@media (max-width: ${Constants.sizes.mobile}px) {
grid-gap: 20px 8px;
grid-template-columns: repeat(auto-fill, minmax(344px, 1fr));
grid-gap: 20px ${theme.grids.profile.mobile.rowGap}px;
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 { elements, restElements } = React.useMemo(() => {
if (!Array.isArray(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]);
const [showMore, setShowMore] = React.useState(false);

View File

@ -1,6 +1,7 @@
import * as React from "react";
import * as Strings from "~/common/strings";
import * as Utilities from "~/common/utilities";
import * as Styles from "~/common/styles";
import { css } from "@emotion/react";
import { motion } from "framer-motion";
@ -8,20 +9,9 @@ import { ViewMoreContent, ProfileInfo } from "~/components/core/ActivityGroup/co
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`
display: grid;
grid-template-columns: 260px 1fr;
grid-template-columns: ${theme.grids.activity.profileInfo.width}px 1fr;
grid-row-gap: 32px;
border-bottom: 1px solid ${theme.semantic.bgLight};
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 { elements, restElements } = React.useMemo(() => {
if (!Array.isArray(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]);
const [showMore, setShowMore] = React.useState(false);
@ -78,7 +71,7 @@ export default function ActivityFileGroup({ viewer, group, onAction }) {
onAction={onAction}
/>
<div>
<div css={STYLES_OBJECT_GRID}>
<div css={Styles.OBJECTS_PREVIEW_GRID}>
{elements.map((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 { elements, restElements } = React.useMemo(() => {
if (!Array.isArray(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]);
const [showMore, setShowMore] = React.useState(false);

View File

@ -16,7 +16,7 @@ const STYLES_GROUP_GRID = (theme) => css`
padding-bottom: 24px;
`;
export default function ActivityGroup({ onAction, viewer, external, group }) {
export default function ActivityGroup({ onAction, viewer, external, group, nbrOfCardsPerRow }) {
const { type } = group;
if (
type === "CREATE_FILE" ||
@ -24,16 +24,36 @@ export default function ActivityGroup({ onAction, viewer, external, group }) {
type === "LIKE_FILE" ||
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") {
return <ActivityCollectionGroup onAction={onAction} viewer={viewer} group={group} />;
return (
<ActivityCollectionGroup
onAction={onAction}
viewer={viewer}
group={group}
nbrOfCollectionsPerRow={nbrOfCardsPerRow.collection}
/>
);
}
if (type === "SUBSCRIBE_USER") {
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,
typescale: Constants.typescale,
semantic: Constants.semantic,
grids: Constants.grids,
...theme,
}),
[theme]

View File

@ -41,6 +41,9 @@ export default function SceneActivity({ page, viewer, external, onAction }) {
});
const divRef = React.useRef();
const nbrOfCardsInRow = useNbrOfCardsPerRow(divRef);
useIntersection({
ref: divRef,
onIntersect: () => {
@ -69,6 +72,7 @@ export default function SceneActivity({ page, viewer, external, onAction }) {
<div css={STYLES_GROUPS_CONTAINER}>
{feed?.map((group) => (
<ActivityGroup
nbrOfCardsPerRow={nbrOfCardsInRow}
key={group.id}
viewer={viewer}
external={external}
@ -84,3 +88,50 @@ export default function SceneActivity({ page, viewer, external, onAction }) {
</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;
}