Skip to content
17 changes: 17 additions & 0 deletions src/components/Container/BevelScrollContainer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import type { PropsWithChildren } from "react";
import { twMerge } from "tailwind-merge";

export default function BevelScrollContainer({
children,
className,
}: PropsWithChildren<{ className?: string }>) {
return (
<div className="bevel-pressed h-full w-full overflow-hidden p-1">
<div
className={twMerge("scrollbar bg-text-invert h-full w-full overflow-y-auto p-3", className)}
>
{children}
</div>
</div>
);
}
7 changes: 4 additions & 3 deletions src/features/friends/list/List.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Home, UserMinus2 } from "lucide-react";
import { useEffect, useState } from "react";

import Button from "@/components/Button/Button";
import BevelScrollContainer from "@/components/Container/BevelScrollContainer";
import { useAuthStore } from "@/stores/useAuthStore";
import { useWindowStore } from "@/stores/useWindowStore";

Expand Down Expand Up @@ -49,8 +50,8 @@ export default function List() {
return (
<div className="flex min-h-0 flex-1 flex-col gap-1 p-4">
<span className="pl-1">친구 목록 {friends.length}</span>
<div className="bevel-pressed flex min-h-0 flex-1 flex-col overflow-hidden px-[3px]">
<div className="bg-text-invert scrollbar flex min-h-0 flex-1 flex-col gap-2 overflow-y-auto p-3 pr-0">
<div className="flex min-h-0 flex-1">
<BevelScrollContainer>
<ul className="flex flex-col gap-2">
{friends.map((friend) => {
const profile =
Expand Down Expand Up @@ -95,7 +96,7 @@ export default function List() {
);
})}
</ul>
</div>
</BevelScrollContainer>
</div>
</div>
);
Expand Down
7 changes: 4 additions & 3 deletions src/features/friends/request/Request.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useEffect, useState } from "react";

import Button from "@/components/Button/Button";
import BevelScrollContainer from "@/components/Container/BevelScrollContainer";
import { useAuthStore } from "@/stores/useAuthStore";

import { acceptFriendRequest, getFriendRequests, rejectFriendRequest } from "../api/friends";
Expand Down Expand Up @@ -52,8 +53,8 @@ export default function Request() {
return (
<div className="flex min-h-0 flex-1 flex-col gap-1 p-4">
<span className="pl-1">친구 신청 {requests.length}</span>
<div className="bevel-pressed flex min-h-0 flex-1 flex-col overflow-hidden px-[3px]">
<div className="bg-text-invert scrollbar flex min-h-0 flex-1 flex-col gap-2 overflow-y-auto p-3 pr-0">
<div className="flex min-h-0 flex-1">
<BevelScrollContainer>
<ul className="flex flex-col gap-2">
{requests.map((request) => (
<li key={request.id}>
Expand Down Expand Up @@ -86,7 +87,7 @@ export default function Request() {
</li>
))}
</ul>
</div>
</BevelScrollContainer>
</div>
</div>
);
Expand Down
9 changes: 5 additions & 4 deletions src/features/friends/search/Search.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useEffect, useState } from "react";

import Button from "@/components/Button/Button";
import BevelScrollContainer from "@/components/Container/BevelScrollContainer";
import Input from "@/components/Input/Input";
import { useAuthStore } from "@/stores/useAuthStore";
import { useWindowStore } from "@/stores/useWindowStore";
Expand Down Expand Up @@ -68,9 +69,9 @@ export default function Search() {
{hasSearched && (
<div className="mt-4 flex min-h-0 flex-1 flex-col gap-1">
<span className="pl-1">검색 결과 {results.length}</span>
<div className="bevel-pressed flex min-h-0 flex-1 overflow-hidden px-[3px]">
<div className="bg-text-invert scrollbar flex min-h-0 flex-1 flex-col gap-2 overflow-y-auto p-3 pr-0">
<ul>
<div className="flex min-h-0 flex-1">
<BevelScrollContainer>
<ul className="flex flex-col gap-2">
{results.map((Profile) => (
<li key={Profile.auth_id}>
<div className="flex items-center gap-3 p-2 pl-2 select-none">
Expand Down Expand Up @@ -100,7 +101,7 @@ export default function Search() {
</li>
))}
</ul>
</div>
</BevelScrollContainer>
</div>
</div>
)}
Expand Down
89 changes: 43 additions & 46 deletions src/features/minihome/gallery/GalleryList.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import Button from "@/components/Button/Button";
import BevelScrollContainer from "@/components/Container/BevelScrollContainer";

import type { GalleryImage } from "./types/gallery.types";

Expand Down Expand Up @@ -43,53 +44,49 @@ export default function GalleryList({
const statusContent = renderStatus();

return (
// 베벨 테두리 래퍼
<div className="bevel-pressed h-full w-full overflow-hidden p-1">
{/* 내부 컨텐츠 */}
<div className="scrollbar bg-text-invert h-full w-full overflow-y-auto px-4 pt-4 pb-4">
{/* 헤더 */}
<div className="flex flex-wrap items-center justify-between gap-3">
<h1>사진첩</h1>
<div className="flex flex-col items-end gap-1">
{isMine && (
<Button composition="textOnly" onClick={onRequestUpload}>
업로드
</Button>
)}
{statusContent && (
<div className="[&>p]:mt-0 [&>p]:text-right [&>p]:text-xs">{statusContent}</div>
)}
</div>
<BevelScrollContainer>
{/* 헤더 */}
<div className="flex flex-wrap items-center justify-between gap-3">
<h1>사진첩</h1>
<div className="flex flex-col items-end gap-1">
{isMine && (
<Button composition="textOnly" onClick={onRequestUpload}>
업로드
</Button>
)}
{statusContent && (
<div className="[&>p]:mt-0 [&>p]:text-right [&>p]:text-xs">{statusContent}</div>
)}
</div>

{hasImages ? (
/* 이미지 그리드 영역 */
<div className="mt-2 grid grid-cols-4 gap-1">
{images.map((image) => (
<button
key={image.id}
type="button"
className="bevel-default aspect-square w-full cursor-pointer overflow-hidden p-1 transition hover:opacity-90"
onClick={() => onSelectImage(image.id)}
>
<div className="h-full w-full">
{image.image_url ? (
<img
src={image.image_url}
alt={image.caption ?? "갤러리 이미지"}
className="h-full w-full object-cover"
/>
) : (
<div className="text-muted flex h-full w-full items-center justify-center text-xs">
이미지 호출 실패
</div>
)}
</div>
</button>
))}
</div>
) : null}
</div>
</div>

{hasImages ? (
/* 이미지 그리드 영역 */
<div className="mt-2 grid grid-cols-4 gap-1">
{images.map((image) => (
<button
key={image.id}
type="button"
className="bevel-default aspect-square w-full cursor-pointer overflow-hidden p-1 transition hover:opacity-90"
onClick={() => onSelectImage(image.id)}
>
<div className="h-full w-full">
{image.image_url ? (
<img
src={image.image_url}
alt={image.caption ?? "갤러리 이미지"}
className="h-full w-full object-cover"
/>
) : (
<div className="text-muted flex h-full w-full items-center justify-center text-xs">
이미지 호출 실패
</div>
)}
</div>
</button>
))}
</div>
) : null}
</BevelScrollContainer>
);
}
59 changes: 31 additions & 28 deletions src/features/minihome/guestbook/GuestBook.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useEffect, useState } from "react";

import BevelScrollContainer from "@/components/Container/BevelScrollContainer";
import { useAuthStore } from "@/stores/useAuthStore";

import {
Expand Down Expand Up @@ -126,34 +127,36 @@ export default function GuestBook({ ownerId }: { ownerId: string | undefined })
{!isMine && <EntryTextArea onSubmit={handleEntry} />}
<div className="flex min-h-0 flex-1 flex-col gap-1">
<span className="p">전체 방명록 {data.length}</span>
<div className="bevel-pressed bg-text-invert scrollbar flex min-h-0 flex-1 flex-col gap-2 overflow-y-auto p-3">
<ul className="flex flex-col gap-2">
{data.map((entry) => (
<li key={entry.id}>
<Entry
id={entry.id}
author={entry.author}
created_at={entry.created_at}
content={entry.content}
canDelete={isMine || entry.author?.auth_id === user?.id}
onDelete={() => handleEntryDelete(entry.id)}
>
{entry.comments.length > 0 ? (
<Comment
id={entry.comments[0].id}
commenter={entry.comments[0].commenter}
created_at={entry.comments[0].created_at}
content={entry.comments[0].content}
canDelete={isMine}
onDelete={() => handleReplyDelete(entry.id, entry.comments[0].id)}
/>
) : (
isMine && <CommentInput entryId={entry.id} onSubmit={handleReplyWrite} />
)}
</Entry>
</li>
))}
</ul>
<div className="flex min-h-0 flex-1">
<BevelScrollContainer>
<ul className="flex flex-col gap-2">
{data.map((entry) => (
<li key={entry.id}>
<Entry
id={entry.id}
author={entry.author}
created_at={entry.created_at}
content={entry.content}
canDelete={isMine || entry.author?.auth_id === user?.id}
onDelete={() => handleEntryDelete(entry.id)}
>
{entry.comments.length > 0 ? (
<Comment
id={entry.comments[0].id}
commenter={entry.comments[0].commenter}
created_at={entry.comments[0].created_at}
content={entry.comments[0].content}
canDelete={isMine}
onDelete={() => handleReplyDelete(entry.id, entry.comments[0].id)}
/>
) : (
isMine && <CommentInput entryId={entry.id} onSubmit={handleReplyWrite} />
)}
</Entry>
</li>
))}
</ul>
</BevelScrollContainer>
</div>
</div>
</div>
Expand Down
7 changes: 4 additions & 3 deletions src/features/minihome/post/list/PostList.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useEffect, useState } from "react";

import BevelScrollContainer from "@/components/Container/BevelScrollContainer";
import { useAuthUser } from "@/hooks/useAuthUser";
import type { PostWithCounts } from "@/types/post.types";
import supabase from "@/utils/supabase";
Expand Down Expand Up @@ -69,8 +70,8 @@ export default function PostList({ onPostClick, ownerId }: PostListProps) {
}

return (
<div className="bevel-pressed bg-text-invert flex min-h-0 flex-1 flex-col overflow-hidden py-2.5">
<div className="scrollbar flex min-h-0 flex-col gap-2 overflow-y-auto pl-4">
<BevelScrollContainer>
<div className="flex min-h-0 flex-1 flex-col gap-2">
{posts.length > 0 ? (
posts.map((post) => (
<PostItem
Expand All @@ -84,6 +85,6 @@ export default function PostList({ onPostClick, ownerId }: PostListProps) {
<div className="py-4 text-center opacity-60">작성된 게시글이 없습니다.</div>
)}
</div>
</div>
</BevelScrollContainer>
);
}