Skip to content

Commit

Permalink
feat(app-board): add card animation in panel
Browse files Browse the repository at this point in the history
  • Loading branch information
rams23 committed Jan 18, 2021
1 parent 697d502 commit e450993
Show file tree
Hide file tree
Showing 6 changed files with 136 additions and 11 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import React, { Children, useCallback } from 'react';
import { calculateCoordinates, calculateItemPosition, calculateItemsPerRow } from './animatedGridUtils';

type Props = {
containerWidth: number;
itemWidth: number;
itemHeight: number;
margin: number;
marginVertical: number;
childClassName?: string;
className?: string;
transitionTime?: string;
transitionTimingFunction?: string;
children: React.ReactChild[];
};

const AnimatedGrid: React.FC<Props> = ({
margin,
children,
childClassName,
className,
containerWidth,
itemHeight,
itemWidth,
marginVertical,
transitionTime = '400ms',
transitionTimingFunction = 'ease-out',
}) => {
const getChildStyles = useCallback(
(top: number, left: number) => {
return {
position: 'absolute' as const,
top: `${top}px`,
left: `${left}px`,
transition: `${transitionTime} top ${transitionTimingFunction}, ${transitionTime} left ${transitionTimingFunction}`,
};
},
[transitionTime, transitionTimingFunction],
);

const renderChild = useCallback(
(child: React.ReactElement, top: number = 0, left: number = 0) => {
return (
<div className={childClassName} key={child.key} style={getChildStyles(top, left)}>
{child}
</div>
);
},
[childClassName, getChildStyles],
);

const parseChildren = useCallback(() => {
const elementsPerRow = calculateItemsPerRow(itemWidth, margin, containerWidth);
const newMargin = margin;
return Children.map(children, (child, index) => {
const { row, col } = calculateItemPosition(index, elementsPerRow);
const { top, left } = calculateCoordinates(row, col, itemWidth, itemHeight, newMargin, marginVertical);
return renderChild(child as React.ReactElement, top, left);
});
}, [children, containerWidth, itemHeight, itemWidth, margin, marginVertical, renderChild]);

return (
<div
className={className}
style={{
position: 'relative',
width: '100%',
height: '100%',
}}
>
{parseChildren()}
</div>
);
};

AnimatedGrid.displayName = 'AnimatedGrid';

export default React.memo(AnimatedGrid);
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/**
*
*
* | containerWidth |
* _______________________________________________________
* | itemWidth | margin | itemWidth | margin | itemWidth |
*
* Calculates the max number of columns
*
* @param itemWidth the width of the child
* @param margin the margin between the columns
* @param containerWidth the width of the container
*/
export function calculateItemsPerRow(itemWidth: number, margin: number, containerWidth: number) {
return Math.floor((containerWidth - margin) / itemWidth);
}

export function calculateItemPosition(elementIndex: number, itemsPerRow: number) {
const row = Math.floor(elementIndex / itemsPerRow);
return { row, col: elementIndex - row * itemsPerRow };
}

export function calculateCoordinates(
rowIndex: number,
colIndex: number,
itemWidth: number,
itemHeight: number,
margin: number,
verticalMargin?: number,
) {
const vMargin = verticalMargin === undefined ? margin : verticalMargin;
return {
left: (itemWidth + margin) * colIndex,
top: (itemHeight + vMargin) * rowIndex,
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import AnimatedGrid from './AnimatedGrid';

export default AnimatedGrid;
3 changes: 2 additions & 1 deletion packages/game-app/src/_shared/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ import IconButton from './IconButton';
import Link from './Link';
import Card from './Card';
import Box from './Box';
import AnimatedGrid from './AnimatedGrid';

export { TextInput, SelectInput, PasswordInput, TextArea, Button, Link, IconButton, Box, Card };
export { TextInput, SelectInput, PasswordInput, TextArea, Button, Link, IconButton, Box, Card, AnimatedGrid };
26 changes: 16 additions & 10 deletions packages/game-app/src/gameView/components/DeckPanel/DeckPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ import React, { useState } from 'react';
import DraggableCard from '../DraggableCard';
import styled from 'styled-components';
import { CardWrapper } from '../DraggableCard/DraggableCard';
import Box from '../../../_shared/components/Box';
import { IconButton } from '@pipeline/components';
import { IconButton, AnimatedGrid } from '@pipeline/components';
import { ReactComponent as StackedIcon } from '@assets/icons/stacked-cards.svg';
import DroppablePanelArea from '../DroppablePanelArea';

Expand Down Expand Up @@ -46,10 +45,7 @@ const DeckPanelContent = styled.div<{ mode: PanelMode }>`
`
: `
display: grid;
grid-template-columns: 280px 280px;
column-gap:16px;
row-gap:16px;
`}
`;

Expand All @@ -69,7 +65,7 @@ type Props = {
};

const DeckPanel: React.FC<Props> = ({ cardsIds }) => {
const [panelMode, setPanelMode] = useState<PanelMode>('stacked');
const [panelMode, setPanelMode] = useState<PanelMode>('tow-columns');

return (
<DroppablePanelArea mode={panelMode}>
Expand All @@ -82,9 +78,19 @@ const DeckPanel: React.FC<Props> = ({ cardsIds }) => {
</IconButton>
</PanelButtons>
<DeckPanelContent mode={panelMode}>
{cardsIds.map(id => (
<DraggableCard key={id} id={id} />
))}
<AnimatedGrid
itemWidth={280}
itemHeight={200}
margin={16}
marginVertical={panelMode === 'tow-columns' ? 16 : -90}
containerWidth={panelMode === 'tow-columns' ? 576 : 360}
transitionTime={'400ms'}
transitionTimingFunction={'ease-in-out'}
>
{cardsIds.map(id => (
<DraggableCard key={id} id={id} />
))}
</AnimatedGrid>
</DeckPanelContent>
</DroppablePanelArea>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ type Props = {

export const CardWrapper = styled.div`
cursor: pointer;
transition: all 0.3s ease-out;
`;

/**
Expand Down

0 comments on commit e450993

Please sign in to comment.