Skip to content

Commit

Permalink
refactor: 맛집 상세페이지 이미지 캐러셀 적용 (#695)
Browse files Browse the repository at this point in the history
* feat: 대체 이미지 생성

* feat: 맛집 상세페이지 이미지 캐러셀 적용

* refactor: useOnClickBlock 고도화

- click 이벤트 핸들러 이벤트 파라미터 콜백에 전달

* fix: 워터마크 드래그시 클릭되는 오류 개선
  • Loading branch information
shackstack authored Apr 5, 2024
1 parent 225285b commit c558300
Show file tree
Hide file tree
Showing 7 changed files with 70 additions and 15 deletions.
2 changes: 1 addition & 1 deletion frontend/.vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@

"stylelint.config": null,
"stylelint.validate": ["css", "scss", "typescript", "typescriptreact"],
"cSpell.words": ["JSESSION", "tanstack", "zustand"]
"cSpell.words": ["JSESSION", "tanstack", "webp", "zustand"]
}
2 changes: 1 addition & 1 deletion frontend/.webpack/webpack.common.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ module.exports = {
sideEffects: true,
},
{
test: /\.(jpg|jpeg|gif|png|ico)?$/,
test: /\.(jpg|jpeg|gif|png|ico|webp)?$/,
type: 'asset',
generator: {
filename: 'images/[name][ext]',
Expand Down
1 change: 1 addition & 0 deletions frontend/src/@types/global.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

declare module '*.png';
declare module '*.woff2';
declare module '*.webp';
declare module '*.svg' {
import * as React from 'react';

Expand Down
Binary file added frontend/src/assets/images/no-image.webp
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useState } from 'react';
import { styled, css } from 'styled-components';
import useOnClickBlock from '~/hooks/useOnClickBlock';
import { BORDER_RADIUS, FONT_SIZE, paintSkeleton } from '~/styles/common';
import { getImgUrl } from '~/utils/image';

Expand All @@ -12,13 +13,14 @@ interface WaterMarkImageProps {

function WaterMarkImage({ waterMark, imageUrl, type, sns }: WaterMarkImageProps) {
const [isImageLoading, setIsImageLoading] = useState<boolean>(true);
const register = useOnClickBlock({
callback: e => {
e.stopPropagation();

const onClickWaterMark = (e: React.MouseEvent) => {
e.stopPropagation();

if (sns === 'INSTAGRAM') window.open(`https://www.instagram.com/${waterMark.substring(1)}`, '_blank');
if (sns === 'YOUTUBE') window.open(`https://www.youtube.com/${waterMark}`, '_blank');
};
if (sns === 'INSTAGRAM') window.open(`https://www.instagram.com/${waterMark.substring(1)}`, '_blank');
if (sns === 'YOUTUBE') window.open(`https://www.youtube.com/${waterMark}`, '_blank');
},
});

return (
<StyledWaterMarkImage type={type} isImageLoading={isImageLoading}>
Expand All @@ -33,7 +35,7 @@ function WaterMarkImage({ waterMark, imageUrl, type, sns }: WaterMarkImageProps)
/>
</picture>
{waterMark && (
<StyledWaterMark onClick={onClickWaterMark} aria-hidden="true">
<StyledWaterMark {...register} aria-hidden="true">
{waterMark}
</StyledWaterMark>
)}
Expand Down
8 changes: 4 additions & 4 deletions frontend/src/hooks/useOnClickBlock.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useCallback, useState } from 'react';
import { MouseEvent, useCallback, useState } from 'react';

interface UseOnClickBlockProps {
callback: () => void;
callback: (e?: MouseEvent) => void;
}

const useOnClickBlock = ({ callback }: UseOnClickBlockProps) => {
Expand All @@ -15,9 +15,9 @@ const useOnClickBlock = ({ callback }: UseOnClickBlockProps) => {
setIsDragging(true);
}, []);

const handleClick = () => {
const handleClick = (e: MouseEvent) => {
if (isDragging) return;
callback();
callback(e);
};

return { onMouseDown: handleMouseDown, onMouseMove: handleMouseMove, onClick: handleClick };
Expand Down
56 changes: 54 additions & 2 deletions frontend/src/pages/RestaurantDetailPage/ImageViewer.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,32 @@
import { useQuery } from '@tanstack/react-query';
import Slider from 'react-slick';
import styled from 'styled-components';
import { RestaurantData } from '~/@types/api.types';
import { getRestaurantDetail } from '~/api/restaurant';
import ImageCarousel from '~/components/@common/ImageCarousel';
import ImageGrid from '~/components/@common/ImageGrid';
import WaterMarkImage from '~/components/@common/WaterMarkImage';
import useMediaQuery from '~/hooks/useMediaQuery';
import Next from '~/assets/icons/arrow/next.svg';
import Prev from '~/assets/icons/arrow/prev.svg';
import { BORDER_RADIUS } from '~/styles/common';
import noImageUrl from '~/assets/images/no-image.webp';

interface ImageViewerProps {
restaurantId: string;
celebId: string;
}

const sliderProps = {
arrows: true,
dots: true,
infinite: true,
speed: 1000,
slidesToShow: 2,
slidesToScroll: 2,
nextArrow: <Next />,
prevArrow: <Prev />,
};

function ImageViewer({ restaurantId, celebId }: ImageViewerProps) {
const { isMobile } = useMediaQuery();

Expand All @@ -23,7 +40,42 @@ function ImageViewer({ restaurantId, celebId }: ImageViewerProps) {

if (isMobile) return <ImageCarousel type="list" images={images} showWaterMark />;

return <ImageGrid images={images.map(({ name: url, author, sns }) => ({ waterMark: author, url, sns }))} />;
if (images.length === 1) {
const { name: url, author, sns } = images[0];

return (
<Slider {...sliderProps}>
<SlideItem>
<WaterMarkImage type="list" imageUrl={url} waterMark={author} sns={sns} />
</SlideItem>
<SlideItem>
<StyledNoImage src={noImageUrl} />
</SlideItem>
</Slider>
);
}

return (
<Slider {...sliderProps}>
{images.map(({ name: url, author, sns }) => (
<SlideItem key={url}>
<WaterMarkImage type="list" imageUrl={url} waterMark={author} sns={sns} />
</SlideItem>
))}
</Slider>
);
}

export default ImageViewer;

const SlideItem = styled.li`
padding: 1.2rem;
`;

const StyledNoImage = styled.img`
width: 100%;
object-fit: cover;
border-radius: ${BORDER_RADIUS.md};
`;

0 comments on commit c558300

Please sign in to comment.