-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: create new detail page layout #25
- Loading branch information
Showing
48 changed files
with
997 additions
and
397 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
export { SearchButton } from './search/search-button'; | ||
export { InfoButton } from './info/info-button'; | ||
export { ItemButton } from './item/item-button'; | ||
export type { BaseButtonProps, ItemButtonProps } from '@/types/button.d'; |
27 changes: 27 additions & 0 deletions
27
app/details/components/layouts/buttons/info/info-button.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import Image from 'next/image'; | ||
import { ItemButtonProps } from '@/types/button.d'; | ||
|
||
export function InfoButton({ item, className }: ItemButtonProps) { | ||
return ( | ||
<div className={`relative ${className}`}> | ||
<div className="absolute -top-8 left-1/2 -translate-x-1/2 bg-white/80 border border-white/50 backdrop-blur-md px-3 py-1.5 rounded-full flex items-center gap-2 whitespace-nowrap"> | ||
{item.info.imageUrl && ( | ||
<div className="relative w-3.5 h-3.5 rounded-full overflow-hidden"> | ||
<Image | ||
src={item.info.imageUrl} | ||
alt={item.info.name} | ||
fill | ||
className="object-cover" | ||
/> | ||
</div> | ||
)} | ||
<span className="text-[10px] text-black/60 font-semibold"> | ||
{item.info.brands?.[0].replace(/_/g, ' ').toUpperCase()} | ||
</span> | ||
</div> | ||
<div className="w-4 h-4 rounded-full border border-white/80 flex items-center justify-center group-hover:border-white/100 transition-colors duration-200"> | ||
<div className="w-2 h-2 bg-white/80 rounded-full group-hover:bg-white/100 transition-colors duration-200" /> | ||
</div> | ||
</div> | ||
); | ||
} |
27 changes: 27 additions & 0 deletions
27
app/details/components/layouts/buttons/item/item-button.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import { ItemButtonProps } from '@/types/button.d'; | ||
import { SearchButton } from '../search/search-button'; | ||
import { InfoButton } from '../info/info-button'; | ||
import { ItemDetailPopup } from '../../popup/item-detail-popup'; | ||
|
||
interface ItemButtonContainerProps extends ItemButtonProps { | ||
variant?: 'default' | 'search'; | ||
} | ||
|
||
export function ItemButton({ | ||
item, | ||
isActive, | ||
variant = 'default', | ||
position = 'right', | ||
className, | ||
}: ItemButtonContainerProps) { | ||
return ( | ||
<div className={`relative ${className}`}> | ||
{variant === 'search' ? ( | ||
<SearchButton /> | ||
) : ( | ||
<InfoButton item={item} isActive={isActive} /> | ||
)} | ||
<ItemDetailPopup item={item} isVisible={isActive} position={position} /> | ||
</div> | ||
); | ||
} |
16 changes: 16 additions & 0 deletions
16
app/details/components/layouts/buttons/search/search-button.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import SearchIcon from '@mui/icons-material/Search'; | ||
import AddIcon from '@mui/icons-material/Add'; | ||
import { BaseButtonProps } from '@/types/button.d'; | ||
|
||
export function SearchButton({ className }: BaseButtonProps) { | ||
return ( | ||
<div className={`absolute top-5 flex flex-col items-center gap-1 ${className}`}> | ||
<div className="bg-black/60 backdrop-blur-md px-3 py-1 rounded-full flex items-center gap-2 whitespace-nowrap"> | ||
<SearchIcon className="w-4 h-4 text-white/80" /> | ||
</div> | ||
<div className="w-4 h-4 rounded-full bg-black/60 flex items-center justify-center transition-colors duration-200"> | ||
<AddIcon className="w-3 h-3 text-white/80" /> | ||
</div> | ||
</div> | ||
); | ||
} |
19 changes: 0 additions & 19 deletions
19
app/details/components/layouts/header/featured-image-header.tsx
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import { Button } from '@/components/ui/Button'; | ||
import { | ||
pretendardBold, | ||
pretendardSemiBold, | ||
pretendardRegular, | ||
} from '@/lib/constants/fonts'; | ||
import FavoriteBorderIcon from '@mui/icons-material/FavoriteBorder'; | ||
import Link from 'next/link'; | ||
|
||
interface HeaderProps { | ||
title: string | null; | ||
description: string | null; | ||
} | ||
|
||
export default function Header({ title, description }: HeaderProps) { | ||
return ( | ||
<div className="flex flex-col items-center mb-16"> | ||
<h1 | ||
className={`${pretendardBold.className} text-2xl md:text-5xl text-white mb-6`} | ||
title={title ?? undefined} | ||
> | ||
뉴진스 다니엘 코디 | ||
</h1> | ||
<p | ||
className={`${pretendardRegular.className} text-md md:text-lg text-white/50 max-w-[60%] line-clamp-1 mb-6`} | ||
title={description ?? undefined} | ||
> | ||
{description} | ||
</p> | ||
{/* badge 컴포넌트 */} | ||
<button | ||
className={`w-fit m-2 p-2 text-sm | ||
md:text-base cursor-pointer text-white hover:bg-white/10 transition-colors flex items-center gap-2 ${pretendardBold.className}`} | ||
> | ||
<Link | ||
href="/" | ||
className=" bg-white/20 rounded-full w-6 h-6 md:w-8 md:h-8 flex items-center justify-center text-sm md:text-base" | ||
></Link> | ||
<p | ||
className={`${pretendardRegular.className} text-white/70 text-sm md:text-base`} | ||
> | ||
뉴진스_민지 | ||
</p> | ||
</button> | ||
</div> | ||
); | ||
} |
28 changes: 28 additions & 0 deletions
28
app/details/components/layouts/image-section/main-image.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { ReactNode, memo } from 'react'; | ||
import Image from 'next/image'; | ||
|
||
interface MainImageProps { | ||
imageUrl: string; | ||
onTouch: () => void; | ||
children: ReactNode; | ||
} | ||
|
||
function MainImageComponent({ imageUrl, onTouch, children }: MainImageProps) { | ||
return ( | ||
<div className="relative aspect-[3/4]" onClick={onTouch}> | ||
<Image | ||
src={imageUrl} | ||
alt="Featured fashion" | ||
fill | ||
className="object-cover" | ||
priority | ||
sizes="650px" | ||
/> | ||
<div className="absolute inset-0 z-10"> | ||
{children} | ||
</div> | ||
</div> | ||
); | ||
} | ||
|
||
export const MainImage = memo(MainImageComponent); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
'use client'; | ||
|
||
import { useState, useCallback, memo } from 'react'; | ||
import { DetailPageState } from '@/types/model.d'; | ||
import { ImagePopup } from '../popup/popup'; | ||
import { ImageList } from '../list'; | ||
import { MainImage } from './main-image'; | ||
|
||
interface ImageViewProps { | ||
detailPageState: DetailPageState; | ||
imageUrl: string; | ||
} | ||
|
||
function ImageViewComponent({ detailPageState, imageUrl }: ImageViewProps) { | ||
const [currentIndex, setCurrentIndex] = useState<number | null>(null); | ||
const [isTouch, setIsTouch] = useState(false); | ||
const [hoveredItem, setHoveredItem] = useState<number | null>(null); | ||
|
||
const handleTouch = useCallback(() => { | ||
setIsTouch(prev => !prev); | ||
}, []); | ||
|
||
const handleSetCurrentIndex = useCallback((index: number | null) => { | ||
setCurrentIndex(index); | ||
}, []); | ||
|
||
const handleSetHoveredItem = useCallback((index: number | null) => { | ||
setHoveredItem(index); | ||
}, []); | ||
|
||
return ( | ||
<section className="flex flex-row w-[1000px] mx-auto"> | ||
<div className="w-[650px]"> | ||
<MainImage | ||
imageUrl={imageUrl} | ||
onTouch={handleTouch} | ||
> | ||
<ImagePopup | ||
detailPageState={detailPageState} | ||
currentIndex={currentIndex} | ||
isTouch={isTouch} | ||
hoveredItem={hoveredItem} | ||
setHoveredItem={handleSetHoveredItem} | ||
/> | ||
</MainImage> | ||
</div> | ||
<div className="w-[320px] ml-[30px]"> | ||
<ImageList | ||
detailPageState={detailPageState} | ||
currentIndex={currentIndex} | ||
setCurrentIndex={handleSetCurrentIndex} | ||
/> | ||
</div> | ||
</section> | ||
); | ||
} | ||
|
||
export const ImageView = memo(ImageViewComponent); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import { DetailPageState } from '@/types/model.d'; | ||
import { ListHeader } from './list-header'; | ||
import { ListProgress } from './list-progress'; | ||
import { ListItems } from './list-items'; | ||
import { createNoImageItem } from './no-image'; | ||
import { useState, useCallback } from 'react'; | ||
|
||
interface ImageListProps { | ||
detailPageState: DetailPageState; | ||
currentIndex: number | null; | ||
setCurrentIndex: (index: number | null) => void; | ||
} | ||
|
||
export function ImageList({ | ||
detailPageState, | ||
currentIndex, | ||
setCurrentIndex, | ||
}: ImageListProps) { | ||
const [hoveredItem, setHoveredItem] = useState<number | null>(null); | ||
const items = [...(detailPageState.itemList ?? []), createNoImageItem()]; | ||
const currentProgress = currentIndex !== null ? currentIndex + 1 : 0; | ||
|
||
const handleHover = useCallback((index: number | null) => { | ||
setHoveredItem(index); | ||
}, []); | ||
|
||
return ( | ||
<div className="flex flex-col h-full"> | ||
<ListHeader /> | ||
<ListItems | ||
items={items} | ||
currentIndex={currentIndex} | ||
setCurrentIndex={setCurrentIndex} | ||
hoveredItem={hoveredItem} | ||
setHoveredItem={handleHover} | ||
/> | ||
<ListProgress total={items.length} current={currentProgress} /> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import { useState } from 'react'; | ||
|
||
type Category = 'ALL' | 'FASHION' | 'INTERIOR'; | ||
|
||
interface ListHeaderProps { | ||
onCategoryChange?: (category: Category) => void; | ||
} | ||
|
||
export function ListHeader({ onCategoryChange }: ListHeaderProps) { | ||
const [activeCategory, setActiveCategory] = useState<Category>('ALL'); | ||
|
||
const handleCategoryClick = (category: Category) => { | ||
setActiveCategory(category); | ||
onCategoryChange?.(category); | ||
}; | ||
|
||
return ( | ||
<div className="flex flex-col mb-3"> | ||
<div className="flex items-center h-[40px] border-b border-white/10"> | ||
{(['ALL', 'FASHION', 'INTERIOR'] as Category[]).map((category) => ( | ||
<button | ||
key={category} | ||
onClick={() => handleCategoryClick(category)} | ||
className={`px-4 h-full text-xs font-medium transition-colors relative | ||
${ | ||
activeCategory === category | ||
? 'text-white after:absolute after:bottom-[-1px] after:left-0 after:w-full after:h-[1px] after:bg-white' | ||
: 'text-white/40 hover:text-white/60' | ||
}`} | ||
> | ||
{category} | ||
</button> | ||
))} | ||
</div> | ||
<div className="mt-4"> | ||
<div className="text-sm font-bold text-white/60 uppercase">FASHION</div> | ||
</div> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import Image from 'next/image'; | ||
import { HoverItem } from '@/types/model.d'; | ||
|
||
interface ListItemImageProps { | ||
item: HoverItem; | ||
isActive: boolean; | ||
} | ||
|
||
export function ListItemImage({ item, isActive }: ListItemImageProps) { | ||
const imageSize = 90; | ||
|
||
if (!item.info.imageUrl) { | ||
return ( | ||
<div | ||
className="relative bg-white/10 flex items-center justify-center shrink-0" | ||
style={{ width: imageSize, height: imageSize }} | ||
> | ||
<div className="text-xs text-white/40 uppercase"> | ||
{item.info.category} | ||
</div> | ||
</div> | ||
); | ||
} | ||
|
||
return ( | ||
<div | ||
className="relative bg-white/10 shrink-0" | ||
style={{ width: imageSize, height: imageSize }} | ||
> | ||
<Image | ||
src={item.info.imageUrl} | ||
alt={item.info.name} | ||
width={imageSize} | ||
height={imageSize} | ||
className="w-full h-full object-cover" | ||
priority | ||
unoptimized | ||
/> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { HoverItem } from '@/types/model.d'; | ||
|
||
interface ListItemInfoProps { | ||
item: HoverItem; | ||
isActive: boolean; | ||
} | ||
|
||
export function ListItemInfo({ item, isActive }: ListItemInfoProps) { | ||
return ( | ||
<div className="flex flex-col justify-center flex-1 min-w-0"> | ||
<div className="text-xs text-white/40 mb-1"> | ||
{item.info.category?.toUpperCase()} | ||
</div> | ||
<div className="text-xs text-white/60 mb-1"> | ||
{item.info.brands?.[0].replace(/_/g, ' ').toUpperCase()} | ||
</div> | ||
<div className="text-sm text-white truncate"> | ||
{item.info.name} | ||
</div> | ||
</div> | ||
); | ||
} |
Oops, something went wrong.