Skip to content

Commit

Permalink
Merge branch 'develop' into feat/#234-project-card-ui
Browse files Browse the repository at this point in the history
  • Loading branch information
solar3070 committed Oct 28, 2023
1 parent ed08189 commit b2e1936
Show file tree
Hide file tree
Showing 94 changed files with 1,557 additions and 1,550 deletions.
7 changes: 7 additions & 0 deletions next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ const nextConfig = {
'blogfiles.pstatic.net',
'images.velog.io',
'media.disquiet.io',
'blogfiles.pstatic.net',
'dthumb-phinf.pstatic.net',
'postfiles.pstatic.net',
'images.velog.io',
'media.disquiet.io',
'scontent-ssn1-1.xx.fbcdn.net',
'storep-phinf.pstatic.net',
],
formats: ['image/avif', 'image/webp'],
},
Expand Down
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/assets/icons/arrow_left_28x28.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion src/assets/icons/arrow_right_28x28.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions src/assets/icons/ic_arrow_stick_right.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions src/assets/icons/img_blog_default.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
19 changes: 9 additions & 10 deletions src/components/Layout/Layout.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import styled from '@emotion/styled';
import { CSSProperties, PropsWithChildren } from 'react';
import { SerializedStyles } from '@emotion/react';
import { PropsWithChildren } from 'react';

export function Layout({ children, moreStyle }: PropsWithChildren<{ moreStyle?: CSSProperties }>) {
return <Main style={moreStyle}>{children}</Main>;
export function Layout({
children,
moreStyle,
}: PropsWithChildren<{ moreStyle?: SerializedStyles }>) {
return <Main css={moreStyle}>{children}</Main>;
}

const Main = styled.div<{ moreStyle?: CSSProperties }>`
width: 100%;
@media (max-width: 1279px) {
display: flex;
flex-direction: column;
row-gap: 50px;
}
display: flex;
flex-direction: column;
`;
91 changes: 91 additions & 0 deletions src/components/common/Carousel/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import React, { useEffect, useRef, useState } from 'react';
import { CarouselArrowType, CarouselOverflowType } from '@src/lib/types/universal';
import { S } from './style';

interface CarouselProps {
itemWidth: number;
stride?: number;
leftArrowType?: CarouselArrowType;
rightArrowType?: CarouselArrowType;
overflowType?: CarouselOverflowType;
children: JSX.Element[];
}

const SWIPE_THRESHOLD = 50;

export default function Carousel({
itemWidth,
stride = 1,
leftArrowType = CarouselArrowType.External,
rightArrowType = CarouselArrowType.External,
overflowType = CarouselOverflowType.Blur,
children,
}: CarouselProps) {
const [currentIndex, setCurrentIndex] = useState(0);
const [startX, setStartX] = useState(0);
const wrapperRef = useRef<HTMLDivElement>(null);

useEffect(() => {
setCurrentIndex(0);
}, [stride, itemWidth]);

const handlePrev = () => {
setCurrentIndex(Math.max(0, currentIndex - stride));
};

const handleNext = () => {
setCurrentIndex(Math.min(children.length - 1, currentIndex + stride));
};

const handleTouchStart = (e: React.TouchEvent<HTMLDivElement>) => {
setStartX(e.touches[0].clientX);
};

const handleTouchEnd = (e: React.TouchEvent<HTMLDivElement>) => {
const endX = e.changedTouches[0].clientX;
const deltaX = startX - endX;

if (deltaX > SWIPE_THRESHOLD) {
handleNext();
} else if (deltaX < -SWIPE_THRESHOLD) {
handlePrev();
}
};

const translateX = -currentIndex * itemWidth;

return (
<S.Wrapper ref={wrapperRef}>
{overflowType === CarouselOverflowType.Blur && (
<>
<S.LeftBlur />
<S.RightBlur />
</>
)}
{currentIndex !== 0 && <S.LeftArrow type={leftArrowType} onClick={handlePrev} />}
<S.CarouselViewport>
<S.CarouselWrapper
translateX={translateX}
itemWidth={itemWidth}
itemCount={children.length}
onTouchStart={handleTouchStart}
onTouchEnd={handleTouchEnd}
>
{children}
</S.CarouselWrapper>
</S.CarouselViewport>
{currentIndex !== children.length - stride && (
<S.RightArrow type={rightArrowType} onClick={handleNext} />
)}
<S.DotWrapper>
{Array.from({ length: Math.ceil(children.length / stride) }).map((dot, index) => (
<S.Dot
key={index}
onClick={() => setCurrentIndex(index * stride)}
selected={index === Math.floor(currentIndex / stride)}
/>
))}
</S.DotWrapper>
</S.Wrapper>
);
}
114 changes: 114 additions & 0 deletions src/components/common/Carousel/style.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import styled from '@emotion/styled';
import { colors } from '@sopt-makers/colors';
import arrowLeft from '@src/assets/icons/arrow_left_28x28.svg';
import arrowRight from '@src/assets/icons/arrow_right_28x28.svg';
import { HideScrollbar } from '@src/lib/styles/scrollbar';
import { CarouselArrowType } from '@src/lib/types/universal';

const Wrapper = styled(HideScrollbar)`
width: 100%;
position: relative;
`;

const Arrow = styled.div<{ type: CarouselArrowType }>`
${({ type }) => type === CarouselArrowType.None && 'display: hidden;'}
position: absolute;
width: 40px;
height: 40px;
border-radius: 20px;
background-color: ${colors.gray600};
color: white;
z-index: 2;
top: 50%;
transform: translateY(-50%);
cursor: pointer;
background-repeat: no-repeat;
background-position: center;
`;

const LeftArrow = styled(Arrow)<{ type: CarouselArrowType }>`
left: -50px;
background-image: url(${arrowLeft});
`;

const RightArrow = styled(Arrow)<{ type: CarouselArrowType }>`
right: -50px;
background-image: url(${arrowRight});
`;

const CarouselWrapper = styled.div<{
translateX: number;
itemWidth: number;
itemCount: number;
}>`
width: ${({ itemWidth, itemCount }) => itemWidth * itemCount}px;
display: grid;
grid-template-columns: ${({ itemWidth, itemCount }) => `repeat(${itemCount}, ${itemWidth}px)`};
transition: transform 0.5s ease-in-out;
transform: ${({ translateX }) => `translateX(${translateX}px)`};
`;

const CarouselViewport = styled.div`
width: 100%;
`;

const Blur = styled.div`
z-index: 2;
position: absolute;
height: 100%;
width: calc(50vw - 50%);
top: 0;
background: linear-gradient(
to right,
transparent 10px,
${colors.background} 50px,
${colors.background}
);
`;

const LeftBlur = styled(Blur)`
left: calc(50% - 50vw);
transform: rotate(180deg);
`;

const RightBlur = styled(Blur)`
right: calc(50% - 50vw);
`;

const DotWrapper = styled.div`
margin-top: 24px;
display: flex;
justify-content: center;
gap: 12px;
`;

const Dot = styled.div<{ selected: boolean }>`
position: relative;
width: 8px;
height: 8px;
background-color: ${({ selected }) => (selected ? colors.white : colors.gray800)};
border-radius: 50%;
cursor: pointer;
::before {
content: '';
position: absolute;
top: -4px;
left: -4px;
right: -4px;
bottom: -4px;
border-radius: 50%;
}
`;

export const S = {
Wrapper,
LeftArrow,
RightArrow,
CarouselWrapper,
CarouselViewport,
LeftBlur,
RightBlur,
DotWrapper,
Dot,
};
Loading

0 comments on commit b2e1936

Please sign in to comment.