Skip to content

Commit

Permalink
Merge pull request #102 from galaxyproject/fran/66-carousel
Browse files Browse the repository at this point in the history
feat: add carousel to home page (#66)
  • Loading branch information
NoopDog authored Sep 26, 2024
2 parents 465901f + b8eb483 commit c4cfe8f
Show file tree
Hide file tree
Showing 26 changed files with 699 additions and 164 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { CardProps } from "@databiosphere/findable-ui/lib/components/common/Card/card";
import * as MDX from "../content";

export const CAROUSEL_CARDS: Pick<CardProps, "text">[] = [
{
text: MDX.ShareUsageAndJoinAdvisoryPanel({}),
},
{
text: MDX.LearnToAnalyzeData({}),
},
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import {
mediaDesktopSmallUp,
mediaTabletUp,
} from "@databiosphere/findable-ui/lib/styles/common/mixins/breakpoints";
import styled from "@emotion/styled";
import { Bullets } from "../../../../../../../common/Bullets/bullets";
import {
CAROUSEL_HEIGHT,
CAROUSEL_HEIGHT_SM,
MAX_CARD_WIDTH,
} from "./common/constants";

export const CarouselView = styled.div`
grid-column: 1 / -1;
max-width: ${MAX_CARD_WIDTH}px;
width: 100%;
${mediaDesktopSmallUp} {
grid-column: 7 / -1;
grid-row: 1 / 3;
justify-self: flex-end;
}
`;

export const Carousel = styled.div`
cursor: grab;
height: ${CAROUSEL_HEIGHT_SM}px;
position: relative; /* Positions CardPositioner. */
user-select: none;
&:active {
cursor: grabbing;
}
${mediaTabletUp} {
height: ${CAROUSEL_HEIGHT}px;
}
.MuiIconButton-root {
opacity: 0;
transition: opacity 150ms ease-in-out;
}
&:hover {
> .MuiIconButton-root {
opacity: 1;
}
}
`;

export const StyledBullets = styled(Bullets)`
bottom: 14px;
left: 50%;
position: absolute;
transform: translateX(-50%);
z-index: 100;
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { SWIPE_ACTION } from "../../../../../../../../hooks/useSwipeInteraction/common/entities";
import {
Carousel as CarouselCards,
CarouselView,
StyledBullets,
} from "./carousel.styles";
import { Arrow } from "./components/Arrow/arrow";
import { Cards } from "./components/Cards/cards";
import { useInteractiveCarousel } from "./hooks/useInteractiveCarousel";

export const Carousel = (): JSX.Element => {
const {
activeIndex,
interactiveAction,
interactiveCards,
interactiveIndexes,
onSetActiveIndex,
onSetSwipeAction,
} = useInteractiveCarousel();
return (
<CarouselView>
<CarouselCards {...interactiveAction}>
<Arrow
onClick={(): void => onSetSwipeAction(SWIPE_ACTION.SWIPE_BACKWARD)}
swipeAction={SWIPE_ACTION.SWIPE_BACKWARD}
/>
<Cards activeIndex={activeIndex} cards={interactiveCards} />
<Arrow
onClick={(): void => onSetSwipeAction(SWIPE_ACTION.SWIPE_FORWARD)}
swipeAction={SWIPE_ACTION.SWIPE_FORWARD}
/>
<StyledBullets
activeBullet={activeIndex}
bullets={interactiveIndexes}
onBullet={onSetActiveIndex}
/>
</CarouselCards>
</CarouselView>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export const CARD_OFFSET_Y = 8;
export const CARD_SCALE_X = 40;
export const MAX_CARD_HEIGHT = 216;
export const MAX_CARD_HEIGHT_SM = 280;
export const MAX_DECK_SIZE = 1; // Currently, deck size is only 1 additional card.
export const MAX_CARD_WIDTH = 504;
export const CAROUSEL_HEIGHT = MAX_CARD_HEIGHT + MAX_DECK_SIZE * CARD_OFFSET_Y;
export const CAROUSEL_HEIGHT_SM =
MAX_CARD_HEIGHT_SM + MAX_DECK_SIZE * CARD_OFFSET_Y;
export const TRANSITION_DELAY = 100;
export const TRANSITION_DURATION = 100;
export const ARROW_OFFSET_Y = (CARD_OFFSET_Y * MAX_DECK_SIZE) / 2;
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { SWIPE_ACTION } from "../../../../../../../../../hooks/useSwipeInteraction/common/entities";
import {
ARROW_OFFSET_Y,
CARD_OFFSET_Y,
CARD_SCALE_X,
MAX_CARD_WIDTH,
MAX_DECK_SIZE,
TRANSITION_DELAY,
TRANSITION_DURATION,
} from "./constants";

/**
* Returns the arrow's transform scaleX and translateY.
* @param swipeAction - Swipe action.
* @returns arrow's transform.
*/
export function getArrowTransform(swipeAction: SWIPE_ACTION): string {
return swipeAction === SWIPE_ACTION.SWIPE_FORWARD
? `translate(24px, calc(${ARROW_OFFSET_Y}px - 50%)) scaleX(-1)`
: `translate(-24px, calc(${ARROW_OFFSET_Y}px - 50%))`;
}

/**
* Returns the carousel card's position in the deck.
* @param index - Card index.
* @param activeIndex - Active index.
* @param lastIndex - Last index.
* @returns card position (position zero to deck size).
*/
export function getCardPosition(
index: number,
activeIndex: number,
lastIndex: number
): number {
const order = index - activeIndex;
if (order >= 0) return order;
// If the order is negative, stack the card to the end of the deck.
// Grab the last (positive) position in the deck and add the card position (index + 1).
return lastIndex - activeIndex + index + 1;
}

/**
* Returns the carousel card's x-axis scale.
* @param cardPosition - Card position.
* @returns card x-axis scale.
*/
export function getCardScaleX(cardPosition: number): string {
if (cardPosition === 0) return "scaleX(1)"; // The active card is scaled to 1.
return `scaleX(${
(MAX_CARD_WIDTH - cardPosition * CARD_SCALE_X) / MAX_CARD_WIDTH
})`;
}

/**
* Returns the carousel card's transform scaleX and translateY.
* @param cardPosition - Card position.
* @returns card transform.
*/
export function getCardTransform(cardPosition: number): string {
return `${getCardScaleX(cardPosition)} ${getCardTranslateY(cardPosition)}`;
}

/**
* Returns the carousel card's transition.
* @param cardPosition - Card position.
* @returns card transition.
*/
export function getCardTransition(cardPosition: number): string {
return `all ${TRANSITION_DURATION}ms ease-in-out ${
cardPosition * TRANSITION_DELAY
}ms, z-index 0ms ${TRANSITION_DELAY}ms`;
}

/**
* Returns the carousel card's y-axis offset.
* @param cardPosition - Card position.
* @returns y-axis offset.
*/
export function getCardTranslateY(cardPosition: number): string {
return `translateY(${(MAX_DECK_SIZE - cardPosition) * CARD_OFFSET_Y}px)`;
}

/**
* Returns the carousel card's z-index.
* @param cardPosition - Card position.
* @returns card z-index.
*/
export function getCardZIndex(cardPosition: number): number {
return MAX_DECK_SIZE - cardPosition;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { mediaTabletDown } from "@databiosphere/findable-ui/lib/styles/common/mixins/breakpoints";
import {
inkMain,
smokeDark,
smokeLightest,
white,
} from "@databiosphere/findable-ui/lib/styles/common/mixins/colors";
import { black08 } from "@databiosphere/findable-ui/lib/theme/common/palette";
import { css } from "@emotion/react";
import styled from "@emotion/styled";
import { IconButton as MIconButton } from "@mui/material";
import {
SwipeAction,
SWIPE_ACTION,
} from "../../../../../../../../../../hooks/useSwipeInteraction/common/entities";
import { MAX_DECK_SIZE } from "../../common/constants";
import { getArrowTransform } from "../../common/utils";

interface Props {
swipeAction: SwipeAction;
}

export const IconButton = styled(MIconButton, {
shouldForwardProp: (props) => props !== "swipeAction",
})<Props>`
& {
background-color: ${white};
border-radius: 50%;
box-shadow: inset 0 0 0 1px ${smokeDark}, 0 1px 0 0 ${black08};
color: ${inkMain};
position: absolute;
top: 50%;
transform: ${({ swipeAction }) => getArrowTransform(swipeAction)};
z-index: ${MAX_DECK_SIZE + 1};
&:hover {
background-color: ${smokeLightest};
}
&:active {
box-shadow: inset 0 0 0 1px ${smokeDark};
}
${mediaTabletDown} {
display: none;
}
}
${({ swipeAction }) =>
swipeAction === SWIPE_ACTION.SWIPE_BACKWARD &&
css`
left: 0;
`}
${({ swipeAction }) =>
swipeAction === SWIPE_ACTION.SWIPE_FORWARD &&
css`
right: 0;
`}
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { SouthIcon } from "@databiosphere/findable-ui/lib/components/common/CustomIcon/components/SouthIcon/southIcon";
import { SwipeAction } from "../../../../../../../../../../hooks/useSwipeInteraction/common/entities";
import { IconButton } from "./arrow.styles";

interface ArrowProps {
onClick: () => void;
swipeAction: SwipeAction;
}

export const Arrow = ({ onClick, swipeAction }: ArrowProps): JSX.Element => {
return (
<IconButton
color="secondary"
onClick={onClick}
size="large"
swipeAction={swipeAction}
>
<SouthIcon fontSize="small" />
</IconButton>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import {
mediaTabletDown,
mediaTabletUp,
} from "@databiosphere/findable-ui/lib/styles/common/mixins/breakpoints";
import {
inkLight,
smokeMain,
} from "@databiosphere/findable-ui/lib/styles/common/mixins/colors";
import {
textBody500,
textBodyLarge500,
textBodySmall4002Lines,
} from "@databiosphere/findable-ui/lib/styles/common/mixins/fonts";
import { elevation01 } from "@databiosphere/findable-ui/lib/theme/common/shadows";
import styled from "@emotion/styled";
import { Card as MCard } from "@mui/material";
import {
MAX_CARD_HEIGHT,
MAX_CARD_HEIGHT_SM,
MAX_CARD_WIDTH,
} from "../../common/constants";
import {
getCardTransform,
getCardTransition,
getCardZIndex,
} from "../../common/utils";

interface Props {
cardPosition: number;
}

export const CardPositioner = styled("div")<Props>`
display: grid;
height: 100%;
max-height: ${MAX_CARD_HEIGHT_SM}px;
max-width: ${MAX_CARD_WIDTH}px;
position: absolute;
transform: ${({ cardPosition }) => getCardTransform(cardPosition)};
transition: ${({ cardPosition }) => getCardTransition(cardPosition)};
width: 100%;
z-index: ${({ cardPosition }) => getCardZIndex(cardPosition)};
${mediaTabletUp} {
max-height: ${MAX_CARD_HEIGHT}px;
}
`;

export const Card = styled(MCard)`
border: none;
box-shadow: ${elevation01}, inset 0 0 0 1px ${smokeMain};
display: flex;
height: 100%;
width: 100%;
` as typeof MCard;

export const CardSection = styled.div`
display: flex;
flex: 1;
flex-direction: column;
padding: 24px;
`;

export const CardContent = styled.div`
h1,
h2,
h3 {
${textBodyLarge500};
margin: 0;
}
p {
${textBodySmall4002Lines};
color: ${inkLight};
margin: 8px 0;
&:last-of-type {
margin-bottom: 0;
}
}
${mediaTabletDown} {
-webkit-box-orient: vertical;
display: -webkit-box;
-webkit-line-clamp: 10;
overflow: hidden;
}
`;

export const CardActions = styled.div`
${textBody500};
align-items: center;
display: flex;
gap: 16px;
margin-top: 16px;
`;
Loading

0 comments on commit c4cfe8f

Please sign in to comment.