diff --git a/.changeset/neat-zebras-yell.md b/.changeset/neat-zebras-yell.md new file mode 100644 index 00000000..a3172588 --- /dev/null +++ b/.changeset/neat-zebras-yell.md @@ -0,0 +1,11 @@ +--- +"@offer-ui/react": patch +--- + +- Carousel + - onClick, 화살표 클릭시에는 동작하지 않도록 수정 + - tablet, mobile에서 인디케이터 클릭시 동작하지 않는 이슈 수정 + - src -> url로 변경 +- ImageModal + - 처음에 이미지 위치 잡지 못하는 이슈 수정 + - src -> url로 변경 diff --git a/packages/react/src/components/Carousel/Carousel.stories.tsx b/packages/react/src/components/Carousel/Carousel.stories.tsx index 9cad2d8b..9d3d22f8 100644 --- a/packages/react/src/components/Carousel/Carousel.stories.tsx +++ b/packages/react/src/components/Carousel/Carousel.stories.tsx @@ -14,9 +14,9 @@ export default meta export const Default: StoryObj = { args: { images: [ - { id: 1, src: 'https://picsum.photos/400' }, - { id: 2, src: 'https://picsum.photos/400' }, - { id: 3, src: 'https://picsum.photos/400' } + { id: 1, url: 'https://picsum.photos/400' }, + { id: 2, url: 'https://picsum.photos/400' }, + { id: 3, url: 'https://picsum.photos/400' } ], isArrow: true, name: 'products', diff --git a/packages/react/src/components/Carousel/index.tsx b/packages/react/src/components/Carousel/index.tsx index 8f76b65a..651e2383 100644 --- a/packages/react/src/components/Carousel/index.tsx +++ b/packages/react/src/components/Carousel/index.tsx @@ -5,11 +5,20 @@ import { useMedia } from '@offer-ui/hooks/useMedia' import type { ForwardedRef, HTMLAttributes, TouchEventHandler } from 'react' import { forwardRef, useEffect, useState } from 'react' +type ImageInfo = { + id: number + url: string +} + export type CarouselProps = { /** Carousel 컴포넌트에 들어갈 이미지들을 정합니다. - * @type { src: string, id: number } [] + * @type ImageInfo [] + */ + images: ImageInfo[] + /** Carousel 에서 처음에 보여줄 이미지를 선택합니다. + * @type number */ - images: { src: string; id: number }[] + selectedIndex: number /** Carousel 컴포넌트에 화살표의 유무를 정합니다. * @type boolean */ @@ -23,9 +32,13 @@ export type CarouselProps = { */ name: string /** Carousel 내에 Image 클릭시 실행할 함수를 지정합니다. + * @type (): void | undefined + */ + onClickImage?(): void + /** Carousel 내에 Indicator가 변할 때 실행할 함수를 지정합니다. * @type (index: number): void | undefined */ - onClick?(index: number): void + onChangeIndicator?(index: number): void } & HTMLAttributes type SliderProps = { @@ -54,12 +67,21 @@ const FULL_SCREEN_WIDTH = 100 const USER_DRAG_LENGTH = 100 export const Carousel = forwardRef(function Carousel( - { images = [], isArrow, size = 687, name, onClick, ...props }: CarouselProps, + { + images = [], + isArrow, + size = 687, + selectedIndex, + name, + onClickImage, + onChangeIndicator, + ...props + }: CarouselProps, ref: ForwardedRef ) { const { desktop } = useMedia() const carouselWidthSize = desktop ? size : FULL_SCREEN_WIDTH - const [currentIndex, setCurrentIndex] = useState(0) + const [currentIndex, setCurrentIndex] = useState(selectedIndex) const [startClientX, setStartClientX] = useState(0) const [endClientX, setEndClientX] = useState(0) const [cursorOn, setCursorOn] = useState(false) @@ -70,13 +92,16 @@ export const Carousel = forwardRef(function Carousel( const handleIndicator = (idx: number): void => { setCurrentIndex(idx) + onChangeIndicator?.(idx) } const handleOffset: HandleOffset = navType => { const { LEFT } = NAV_TYPE const goPrev = navType === LEFT + const nextIndex = goPrev ? currentIndex - 1 : currentIndex + 1 - setCurrentIndex(goPrev ? currentIndex - 1 : currentIndex + 1) + setCurrentIndex(nextIndex) + onChangeIndicator?.(nextIndex) } const handleTouchStart: TouchEventHandler = e => { @@ -118,23 +143,21 @@ export const Carousel = forwardRef(function Carousel( onTouchStart={handleTouchStart}> onClick?.(currentIndex)}> + onClick={(): void => onClickImage?.()}> {images.map(image => { return ( ) })} {isArrow && hasImages && ( <> - {isFirstImage ? ( -
- ) : ( + {!isFirstImage && ( { @@ -143,9 +166,7 @@ export const Carousel = forwardRef(function Carousel( )} - {isLastImage ? ( -
- ) : ( + {!isLastImage && ( { @@ -248,25 +269,13 @@ const StyledImage = styled(Image)` } ` -export const StyledArrowBox = styled.div` - position: absolute; - top: 0; - width: 100%; - height: 100%; - display: flex; - justify-content: space-between; - align-items: center; - ${({ theme }): string => theme.mediaQuery.tablet} { - display: none; - } -` - const StyledRightArrow = styled.button` position: absolute; top: 50%; right: 0; width: 40px; height: 60px; + z-index: ${({ theme }): number => theme.zIndex.common}; border: none; transform: translate(0, -50%); background-color: ${({ theme }): string => theme.colors.white}; @@ -279,6 +288,8 @@ const StyledLeftArrow = styled.button` left: 0; width: 40px; height: 60px; + + z-index: ${({ theme }): number => theme.zIndex.common}; border: none; transform: translate(0, -50%); background-color: ${({ theme }): string => theme.colors.white}; diff --git a/packages/react/src/components/ImageModal/ImageModal.stories.tsx b/packages/react/src/components/ImageModal/ImageModal.stories.tsx index 354b54c9..b0758879 100644 --- a/packages/react/src/components/ImageModal/ImageModal.stories.tsx +++ b/packages/react/src/components/ImageModal/ImageModal.stories.tsx @@ -16,15 +16,15 @@ export const Default: StoryObj = { images: [ { id: 1, - src: 'errorImage' + url: 'errorImage' }, { id: 2, - src: 'http://placekitten.com/500/600' + url: 'http://placekitten.com/500/600' }, { id: 3, - src: 'http://placekitten.com/400/800' + url: 'http://placekitten.com/400/800' } ], name: 'cat-detail' diff --git a/packages/react/src/components/ImageModal/index.tsx b/packages/react/src/components/ImageModal/index.tsx index cd4bac89..c5484412 100644 --- a/packages/react/src/components/ImageModal/index.tsx +++ b/packages/react/src/components/ImageModal/index.tsx @@ -10,7 +10,7 @@ import { createPortal } from 'react-dom' type ImageInfo = { id: number - src: string + url: string } export type ImageModalProps = { /** @@ -20,14 +20,14 @@ export type ImageModalProps = { isOpen?: boolean /** * ImageModal에 띄울 이미지들을 정합니다. - * @type IconType + * @type ImageInfo */ images: ImageInfo[] /** * ImageModal의 처음 띄워줄 이미지 index를 지정합니다. * @type number | undefined */ - initIndex?: number + selectedIndex?: number /** * ImageModal의 이름을 정합니다. * @type string @@ -77,17 +77,17 @@ const calculateSizeRate = (width: number, height: number): number => export const ImageModal = forwardRef(function ImageModal( { - onClose, - initIndex = 0, + selectedIndex = 0, images = [], isOpen = false, name, + onClose, ...props }: ImageModalProps, ref: ForwardedRef ) { const imagesInfo = useRef([]) - const [currentIndex, setCurrentIndex] = useState(initIndex) + const [currentIndex, setCurrentIndex] = useState(selectedIndex) const startClientX = useRef(null) const topElement = useRef(null) const hasImages = images.length > 0 @@ -110,20 +110,20 @@ export const ImageModal = forwardRef(function ImageModal( }, [images]) useEffect(() => { - isOpen && setCurrentIndex(initIndex) - }, [isOpen]) + setCurrentIndex(selectedIndex) + }, [isOpen, selectedIndex]) const getImagesInfo = async (): Promise => { - const fulfilledImages = images.map(({ src, id }) => { + const fulfilledImages = images.map(({ url, id }) => { const image = new Image() - image.src = src + image.src = url return new Promise(resolve => { image.onload = (): void => { resolve({ height: image.height, id, - src, + url, width: (image.width * DEFAULT_RESIZE_IMAGE.HEIGHT) / image.height }) } @@ -132,7 +132,7 @@ export const ImageModal = forwardRef(function ImageModal( resolve({ height: DEFAULT_RESIZE_IMAGE.HEIGHT, id, - src: null, + url: null, width: DEFAULT_RESIZE_IMAGE.WIDTH }) } @@ -161,7 +161,7 @@ export const ImageModal = forwardRef(function ImageModal( } return sumImageWidth - }, [currentIndex]) + }, [currentIndex, imagesInfo.current, selectedIndex]) const handleClickIndicator = (idx: number): void => { setCurrentIndex(idx) @@ -211,7 +211,7 @@ export const ImageModal = forwardRef(function ImageModal( ref={ref} currentIndex={currentIndex} currentTranslateX={currentTranslateX}> - {imagesInfo.current.map(({ src, id, width, height }) => ( + {imagesInfo.current.map(({ url, id, width, height }) => ( ))} @@ -273,15 +273,15 @@ const StyledImageContainer = styled.div` ${({ theme }): string => theme.mediaQuery.mobile} { transform: translate(0, 0); gap: 0; - transform: ${({ currentIndex }): SerializedStyles => - css`translate(-${currentIndex * 100}vw, 0);`}; + transform: ${({ currentIndex }): string => + `translate(-${currentIndex * 100}vw, 0)`}; } ${({ theme }): string => theme.mediaQuery.tablet} { transform: translate(0, 0); gap: 0; - transform: ${({ currentIndex }): SerializedStyles => - css`translate(-${currentIndex * 100}vw, 0);`}; + transform: ${({ currentIndex }): string => + `translate(-${currentIndex * 100}vw, 0)`}; } ` diff --git a/packages/react/src/styles/themes/zIndex.ts b/packages/react/src/styles/themes/zIndex.ts index 0fba8b99..9d284a93 100644 --- a/packages/react/src/styles/themes/zIndex.ts +++ b/packages/react/src/styles/themes/zIndex.ts @@ -1,6 +1,7 @@ export type ZIndex = typeof zIndex export const zIndex = { + common: 100, modal: 300, modalIcon: 400, selectbox: 200