Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 110 additions & 0 deletions src/components/Moments/IdBadgeMoment/IdBadgeMoment.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { momentStoryBookDecorator } from '../../../stories/decorators';
import IdBadgeMoment from './IdBadgeMoment';

const momentData = {
index: 0,
type: 'idBadge',
title: 'ID Badge Moment Example',
subtitle: 'Employment History',
icon: { name: 'badge', type: 'mui' },
color: { background: '#4a90e2', text: '#ffffff' },
label: 'Badges',
data: {
badges: [
{
backgroundImage: { url: 'https://upload.wikimedia.org/wikipedia/commons/5/56/Aerial_view_of_Apple_Park_dllu.jpg', alt: 'Apple HQ' },
logo: { url: 'https://upload.wikimedia.org/wikipedia/commons/f/fa/Apple_logo_black.svg', alt: 'Apple Logo' },
content: {
blocks: [
{ type: 'TEXT', text: 'Software Engineer', id: 'c1' },
{ type: 'TEXT', text: 'Start: Jan 2021 – End: Present', id: 'c2' },
],
caption: {
blocks: [
{ type: 'TEXT', text: 'Apple Inc.', id: 'company-name' },
{ type: 'TEXT', text: 'Innovative tech company', id: 'company-desc' },
{ type: 'BUTTON', button: { label: 'Learn More', url: 'https://apple.com' } },
],
},
},
},
{
backgroundImage: { url: 'https://upload.wikimedia.org/wikipedia/commons/5/5f/Aerial_Microsoft_West_Campus_August_2009.jpg', alt: 'Microsoft HQ' },
logo: {
url: 'https://upload.wikimedia.org/wikipedia/commons/4/44/Microsoft_logo.svg',
alt: 'Microsoft Logo',
},
content: {
blocks: [
{ type: 'TEXT', text: 'Product Manager', id: 'c3' },
{ type: 'TEXT', text: 'Start: Mar 2018 – End: Dec 2020', id: 'c4' },
],
caption: {
blocks: [
{ type: 'TEXT', text: 'Microsoft', id: 'company-name' },
{ type: 'TEXT', text: 'Empowering every person and organization on the planet to achieve more', id: 'company-desc' },
{ type: 'BUTTON', button: { label: 'Learn More', url: 'https://microsoft.com' } },
],
},
},
},
{
logo: {
url: 'https://upload.wikimedia.org/wikipedia/commons/2/2f/Google_2015_logo.svg',
alt: 'Google Logo',
},
content: {
blocks: [
{ type: 'TEXT', text: 'UX Designer', id: 'c5' },
{ type: 'TEXT', text: 'Start: Feb 2016 – End: Feb 2018', id: 'c6' },
],
caption: {
blocks: [
{ type: 'TEXT', text: 'Google', id: 'company-name' },
{ type: 'TEXT', text: 'Organizing the world’s information and making it universally accessible.', id: 'company-desc' },
{ type: 'BUTTON', button: { label: 'Learn More', url: 'https://google.com' } },
],
},
},
},
{
logo: {
url: 'https://upload.wikimedia.org/wikipedia/commons/0/05/Facebook_Logo_%282019%29.png',
alt: 'Meta Logo',
},
content: {
blocks: [
{ type: 'TEXT', text: 'Data Scientist', id: 'c7' },
{ type: 'TEXT', text: 'Start: May 2014 – End: Jan 2016', id: 'c8' },
],
caption: {
blocks: [
{ type: 'TEXT', text: 'Meta (Facebook)', id: 'company-name' },
{ type: 'TEXT', text: 'Building technologies that help people connect, find communities, and grow businesses.', id: 'company-desc' },
{ type: 'BUTTON', button: { label: 'Learn More', url: 'https://meta.com' } },
],
},
},
},
],
layout: 'zigzag',
},
};

export default {
title: 'Moments/IdBadge Moment',
component: IdBadgeMoment,
decorators: [momentStoryBookDecorator],
parameters: {
deepControls: { enabled: true },
layout: 'centered',
},
tags: ['autodocs'],
};

export const DefaultIdBadgeMoment = {
name: 'Default ID Badge',
args: {
moment: momentData,
},
};
30 changes: 30 additions & 0 deletions src/components/Moments/IdBadgeMoment/IdBadgeMoment.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { observer } from 'mobx-react-lite';

import IdBadge from '../../UI/IdBadge/IdBadge';
import CardsBaseMoment from '../CardsBaseMoment/CardsBaseMoment';
import type { IdBadgeMomentProps } from './IdBadgeMoment.types';

const IdBadgeMoment = observer(({ moment }: IdBadgeMomentProps) => {
const badges = moment.items ?? [];

if (badges.length === 0) return null;

return (
<CardsBaseMoment
moment={moment}
>
{badges.map((badge, index) => (
<IdBadge
key={badge.content?.id || badge.logo?.url || `badge-${index}`}
backgroundImage={badge.backgroundImage}
content={badge.content}
information={badge.information}
logo={badge.logo}
/>
))}

</CardsBaseMoment>
);
});

export default IdBadgeMoment;
4 changes: 4 additions & 0 deletions src/components/Moments/IdBadgeMoment/IdBadgeMoment.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import type IdBadgeMomentStore from '../../../state/moments/idBadgeMomentStore';
import type { BaseMomentPropsWithoutChildren } from '../BaseMoment/BaseMoment.types';

export type IdBadgeMomentProps = BaseMomentPropsWithoutChildren<IdBadgeMomentStore>;
5 changes: 5 additions & 0 deletions src/components/UI/Cards/Cards.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import CardsGridLayout from './CardsGridLayout';
import CardsOrbitLayout from './CardsOrbitLayout';
import CardsRowLayout from './CardsRowLayout';
import CardsStackLayout from './CardsStackLayout';
import CardsZigZagLayout from './CardsZigZagLayout';

const Cards = observer((props: CardsProps) => {
const { layout = 'grid' } = props;
Expand Down Expand Up @@ -37,6 +38,10 @@ const Cards = observer((props: CardsProps) => {
<Case condition={layout === 'stack'}>
<CardsStackLayout {...layoutProps} />
</Case>

<Case condition={layout === 'zigzag'}>
<CardsZigZagLayout {...layoutProps} />
</Case>
</Switch>
);
});
Expand Down
111 changes: 111 additions & 0 deletions src/components/UI/Cards/CardsZigZagLayout.styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
const styles = {
zigZagLayoutRoot: {
display: 'flex',
flexDirection: 'column',
width: '100%',
},

zigZagSection: {
position: 'relative',
minHeight: 200,
'&:hover': { boxShadow: 'none' },
},

zigZagSectionLeft: {
backgroundColor: 'background.default',
color: 'text.primary',
},

zigZagSectionRight: {
backgroundColor: 'primary.main',
color: 'primary.contrastText',
},

angle: {
backgroundColor: 'inherit',
height: '25%',
left: 0,
position: 'absolute',
right: 0,
top: 0,
transformOrigin: 'top left',
zIndex: 0,
},

angleLeft: {
transform: 'skewY(-6deg)',
},

angleRight: {
transform: 'skewY(6deg)',
transformOrigin: 'top right',
},

angledSectionBackground: {
marginTop: '-50px',
paddingTop: 100,
paddingBottom: 250,
position: 'relative',
width: '100%',
zIndex: 1,
},

zigZagGrid: {
marginTop: '-25px',
position: 'relative',
zIndex: 1,
},

zigZagCard: {
alignItems: 'center',
display: 'flex',
justifyContent: 'center',
maxWidth: 400,
transformStyle: 'preserve-3d',
transition: 'transform 0.4s ease, box-shadow 0.4s ease',
},

zigZagCaption: {
container: {
textAlign: 'center',
width: '100%',
marginBottom: '12px',
'& *': {
color: 'inherit',
},
},
textBlock: {
'&:nth-of-type(1)': {
fontSize: '1.25rem',
fontWeight: 600,
lineHeight: 1.2,
marginBottom: '6px',
},
'&:nth-of-type(n+2)': {
fontSize: '0.95rem',
fontWeight: 400,
lineHeight: 1.4,
marginBottom: '8px',
},
},
buttonBlock: {
backgroundColor: 'primary.main',
border: '1px solid',
borderColor: 'primary.main',
borderRadius: 4,
cursor: 'pointer',
display: 'inline-block',
fontSize: '0.85rem',
fontWeight: 500,
padding: '6px 16px',
textAlign: 'center',
textDecoration: 'none',
transition: 'background-color 0.3s ease, color 0.3s ease',
'&:hover': {
backgroundColor: 'primary.dark',
},
},
},
};

export default styles;
120 changes: 120 additions & 0 deletions src/components/UI/Cards/CardsZigZagLayout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import Box from '@mui/material/Box';
import Grid from '@mui/material/Grid2';
import React, { isValidElement } from 'react';

import { deepMerge } from '../../../utils/object';
import type { CardsLayoutProps } from './Cards.types';
import CardsItem from './CardsItem';
import styles from './CardsZigZagLayout.styles';

export default function CardsZigZagLayout({
children,
keyFn,
sx,
}: CardsLayoutProps) {
const cards = React.Children.toArray(children);

type CaptionBlock =
| { id: string; type: 'TEXT'; text: string }
| { id: string; type: 'BUTTON'; button: { collection_id?: number; label?: string } };

return (
<Box sx={deepMerge(styles.zigZagLayoutRoot, sx)}>
{cards.map((card, index) => {
const isEven = index % 2 === 0;

const caption = isValidElement(card)
&& card.props?.content?.caption
&& Array.isArray(card.props.content.caption.blocks)
? card.props.content.caption
: undefined;

const renderCaption = caption ? (
<Box sx={styles.zigZagCaption.container}>
{caption.blocks.map((block: CaptionBlock) => {
if (block.type === 'TEXT' && 'text' in block) {
return (
<Box
key={block.id}
sx={styles.zigZagCaption.textBlock}
>
{block.text}
</Box>
);
}
if (block.type === 'BUTTON' && 'button' in block) {
return (
<Box
key={block.id}
component="a"
href={block.button.collection_id ? `/collection/${block.button.collection_id}` : '#'}
sx={{
...styles.zigZagCaption.buttonBlock,
color: isEven ? 'primary.main' : 'white',
backgroundColor: isEven ? 'transparent' : 'primary.dark',
borderColor: isEven ? 'primary.main' : 'primary.dark',
'&:hover': {
backgroundColor: isEven ? 'primary.light' : 'primary.main',
color: isEven ? 'white' : 'white',
},
}}
>
{block.button.label}
</Box>
);
}
return null;
})}
</Box>
) : null;

return (
<Box
key={keyFn(index)}
sx={{
...styles.zigZagSection,
...(isEven ? styles.zigZagSectionLeft : styles.zigZagSectionRight),
}}
>
<Box
sx={{
...styles.angle,
...(isEven ? styles.angleLeft : styles.angleRight),
}}
/>

<Grid
alignItems="center"
container
justifyContent={isEven ? 'flex-start' : 'flex-end'}
spacing={4}
sx={styles.zigZagGrid}
>
{isEven ? (
<>
<Grid size={{ xs: 12, md: 5 }}>{renderCaption}</Grid>

<Grid size={{ xs: 12, md: 7 }}>
<CardsItem disableAnimation sx={styles.zigZagCard}>
{card}
</CardsItem>
</Grid>
</>
) : (
<>
<Grid size={{ xs: 12, md: 7 }}>
<CardsItem disableAnimation sx={styles.zigZagCard}>
{card}
</CardsItem>
</Grid>

<Grid size={{ xs: 12, md: 5 }}>{renderCaption}</Grid>
</>
)}
</Grid>
</Box>
);
})}
</Box>
);
}
Loading