extract props
This commit is contained in:
		
							parent
							
								
									7afb21f1f0
								
							
						
					
					
						commit
						4203f06af0
					
				
					 7 changed files with 260 additions and 190 deletions
				
			
		
							
								
								
									
										161
									
								
								src/App.tsx
									
										
									
									
									
								
							
							
						
						
									
										161
									
								
								src/App.tsx
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -1,8 +1,9 @@
 | 
			
		|||
import './App.css'
 | 
			
		||||
import { AnnoymousNickNameProvider } from './AuthorData';
 | 
			
		||||
import { CommentInput, CommentItem, CommentListContainer, CommentPagination, PostListControls, SubCommentData } from './Comment';
 | 
			
		||||
import CommentHeader from './CommentHeader';
 | 
			
		||||
import { Footer } from './Footer';
 | 
			
		||||
import { GalleryContent } from './Gallery';
 | 
			
		||||
import { GalleryContent, GalleryContentHeader } from './Gallery';
 | 
			
		||||
import { GalleryTitleHeader } from './GalleryTitleHeader';
 | 
			
		||||
import { GlobalNavigationBar, Header, VisitHistory } from './Header';
 | 
			
		||||
import { LoginBox } from './Sidebar';
 | 
			
		||||
| 
						 | 
				
			
			@ -106,7 +107,6 @@ const tableData: TableRowData[] = [
 | 
			
		|||
    views: "",
 | 
			
		||||
    recommendations: "",
 | 
			
		||||
    isNews: true,
 | 
			
		||||
    titleLinkUrl: '#', // Add actual URL
 | 
			
		||||
  },
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -164,66 +164,105 @@ const comments: SubCommentData[] = [
 | 
			
		|||
 | 
			
		||||
function App() {
 | 
			
		||||
  return (
 | 
			
		||||
    <div>
 | 
			
		||||
      <Header />
 | 
			
		||||
      <GlobalNavigationBar />
 | 
			
		||||
      <VisitHistory />
 | 
			
		||||
      <div className='relative w-[1450px] mx-auto'>
 | 
			
		||||
        <main
 | 
			
		||||
          className='w-[1160px] m-[20px_auto_0]'
 | 
			
		||||
        >
 | 
			
		||||
          <section className='' >
 | 
			
		||||
            <GalleryTitleHeader />
 | 
			
		||||
            <div
 | 
			
		||||
              className='border-custom-blue-dark border w-[1158px]'
 | 
			
		||||
            />
 | 
			
		||||
            <GalleryContent />
 | 
			
		||||
            <CommentHeader />
 | 
			
		||||
            <CommentListContainer>
 | 
			
		||||
              <CommentItem comment={{
 | 
			
		||||
                id: 1,
 | 
			
		||||
                author: { type: "nickname", nickname: "동아리망했다" },
 | 
			
		||||
                text: '너 지금 상현이햄을 ■■라고',
 | 
			
		||||
                timestamp: '03.31 17:44:25',
 | 
			
		||||
                showDelete: true,
 | 
			
		||||
              }} />
 | 
			
		||||
              <CommentItem comment={{
 | 
			
		||||
                id: 2,
 | 
			
		||||
                author: { type: "IP", ip: "218.144" },
 | 
			
		||||
                text: '그냥 미국인인데 조센징들은 왜 조선계라고 못 넣어서 안달일까',
 | 
			
		||||
                timestamp: '03.31 18:16:45',
 | 
			
		||||
                showDelete: true,
 | 
			
		||||
                subComments: comments,
 | 
			
		||||
              }} />
 | 
			
		||||
              <CommentItem comment={{
 | 
			
		||||
                id: 3,
 | 
			
		||||
                author: { type: "IP", ip: "123.245" },
 | 
			
		||||
                text: 'aaa',
 | 
			
		||||
                timestamp: '03.31 18:16:45',
 | 
			
		||||
                showDelete: true,
 | 
			
		||||
              }} />
 | 
			
		||||
            </CommentListContainer>
 | 
			
		||||
            <CommentPagination currentPage={1} maxPage={2} />
 | 
			
		||||
            <CommentInput />
 | 
			
		||||
            <PostListControls owner />
 | 
			
		||||
            <div className='flex justify-between'>
 | 
			
		||||
              <div style={{
 | 
			
		||||
                width: "840px",
 | 
			
		||||
              }}>
 | 
			
		||||
                <GalleryTable data={tableData} />
 | 
			
		||||
                <PostListControls />
 | 
			
		||||
    <AnnoymousNickNameProvider value="썬갤러">
 | 
			
		||||
      <div>
 | 
			
		||||
        <Header />
 | 
			
		||||
        <GlobalNavigationBar />
 | 
			
		||||
        <VisitHistory recentVisits={[
 | 
			
		||||
          {
 | 
			
		||||
            id: 1,
 | 
			
		||||
            name: "워썬더",
 | 
			
		||||
            isMinor: true,
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            id: 2,
 | 
			
		||||
            name: "배틀그라운드",
 | 
			
		||||
            isMinor: false,
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            id: 3,
 | 
			
		||||
            name: "리그오브레전드",
 | 
			
		||||
            isMinor: false,
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            id: 4,
 | 
			
		||||
            name: "발로란트",
 | 
			
		||||
            isMinor: false,
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            id: 5,
 | 
			
		||||
            name: "오버워치",
 | 
			
		||||
            isMinor: true,
 | 
			
		||||
          }
 | 
			
		||||
        ]} />
 | 
			
		||||
        <div className='relative w-[1450px] mx-auto'>
 | 
			
		||||
          <main
 | 
			
		||||
            className='w-[1160px] m-[20px_auto_0]'
 | 
			
		||||
          >
 | 
			
		||||
            <section className='' >
 | 
			
		||||
              <GalleryTitleHeader title='워썬더 갤러리' />
 | 
			
		||||
              <div
 | 
			
		||||
                className='border-custom-blue-dark border w-[1158px]'
 | 
			
		||||
              />
 | 
			
		||||
              <GalleryContentHeader kind='일반'
 | 
			
		||||
                title={'아 e글 유파들이 고도 왜 안올리는지 알았다'}
 | 
			
		||||
                author={{
 | 
			
		||||
                  type: "IP",
 | 
			
		||||
                  ip: "183.96",
 | 
			
		||||
                }}
 | 
			
		||||
                date='2025.02.04 21:32:47'
 | 
			
		||||
                views={1234}
 | 
			
		||||
                recommendations={12}
 | 
			
		||||
                commentCount={9}
 | 
			
		||||
              />
 | 
			
		||||
              <GalleryContent />
 | 
			
		||||
              <CommentHeader commentCount={9} />
 | 
			
		||||
              <CommentListContainer>
 | 
			
		||||
                <CommentItem comment={{
 | 
			
		||||
                  id: 1,
 | 
			
		||||
                  author: { type: "nickname", nickname: "동아리망했다" },
 | 
			
		||||
                  text: '너 지금 상현이햄을 ■■라고',
 | 
			
		||||
                  timestamp: '03.31 17:44:25',
 | 
			
		||||
                  showDelete: true,
 | 
			
		||||
                }} />
 | 
			
		||||
                <CommentItem comment={{
 | 
			
		||||
                  id: 2,
 | 
			
		||||
                  author: { type: "IP", ip: "218.144" },
 | 
			
		||||
                  text: '그냥 미국인인데 조센징들은 왜 조선계라고 못 넣어서 안달일까',
 | 
			
		||||
                  timestamp: '03.31 18:16:45',
 | 
			
		||||
                  showDelete: true,
 | 
			
		||||
                  subComments: comments,
 | 
			
		||||
                }} />
 | 
			
		||||
                <CommentItem comment={{
 | 
			
		||||
                  id: 3,
 | 
			
		||||
                  author: { type: "IP", ip: "123.245" },
 | 
			
		||||
                  text: 'aaa',
 | 
			
		||||
                  timestamp: '03.31 18:16:45',
 | 
			
		||||
                  showDelete: true,
 | 
			
		||||
                }} />
 | 
			
		||||
              </CommentListContainer>
 | 
			
		||||
              <CommentPagination currentPage={1} maxPage={2} />
 | 
			
		||||
              <CommentInput />
 | 
			
		||||
              <PostListControls owner />
 | 
			
		||||
              <div className='flex justify-between'>
 | 
			
		||||
                <div style={{
 | 
			
		||||
                  width: "840px",
 | 
			
		||||
                }}>
 | 
			
		||||
                  <GalleryTable data={tableData} />
 | 
			
		||||
                  <PostListControls />
 | 
			
		||||
                </div>
 | 
			
		||||
                <div style={{
 | 
			
		||||
                  width: "300px",
 | 
			
		||||
                }}>
 | 
			
		||||
                  <LoginBox />
 | 
			
		||||
                </div>
 | 
			
		||||
              </div>
 | 
			
		||||
              <div style={{
 | 
			
		||||
                width: "300px",
 | 
			
		||||
              }}>
 | 
			
		||||
                <LoginBox />
 | 
			
		||||
              </div>
 | 
			
		||||
            </div>
 | 
			
		||||
          </section>
 | 
			
		||||
        </main>
 | 
			
		||||
      </div>
 | 
			
		||||
      <Footer />
 | 
			
		||||
    </div>
 | 
			
		||||
            </section>
 | 
			
		||||
          </main>
 | 
			
		||||
        </div>
 | 
			
		||||
        <Footer />
 | 
			
		||||
      </div >
 | 
			
		||||
    </AnnoymousNickNameProvider>
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										89
									
								
								src/AuthorData.tsx
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								src/AuthorData.tsx
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,89 @@
 | 
			
		|||
import { createContext, useContext, useMemo } from "react";
 | 
			
		||||
 | 
			
		||||
export type AuthorData = {
 | 
			
		||||
    // 운영자
 | 
			
		||||
    type: "operator";
 | 
			
		||||
} | {
 | 
			
		||||
    // 고닉
 | 
			
		||||
    // 중복 불가능 닉네임
 | 
			
		||||
    type: "nickname";
 | 
			
		||||
    nickname: string;
 | 
			
		||||
    // 파딱, 주딱 구분을 위한 userType
 | 
			
		||||
    userType?: "manager" | "submanager"; // Optional, if applicable
 | 
			
		||||
} | {
 | 
			
		||||
    // 유동닉
 | 
			
		||||
    type: "IP";
 | 
			
		||||
    // IP 주소
 | 
			
		||||
    ip: string;
 | 
			
		||||
 | 
			
		||||
    tempNickname?: string; // Optional, if applicable
 | 
			
		||||
} | {
 | 
			
		||||
    // 반유동닉
 | 
			
		||||
    // 중복 가능 닉네임
 | 
			
		||||
    type: "semi-nickname";
 | 
			
		||||
    nickname: string;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const NicknameImagePath = {
 | 
			
		||||
    "주딱": "/fix_managernik.gif",
 | 
			
		||||
    "파딱": "/fix_sub_managernik.gif",
 | 
			
		||||
    "반유동": "/nik.gif",
 | 
			
		||||
    "default": "/fix_nik.gif"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const AnnoymousNickName = createContext("ㅇㅇ");
 | 
			
		||||
export const AnnoymousNickNameProvider = AnnoymousNickName.Provider;
 | 
			
		||||
 | 
			
		||||
export function NickName({ author } : {
 | 
			
		||||
    author: AuthorData,
 | 
			
		||||
}) {
 | 
			
		||||
    const defaultNickname = useContext(AnnoymousNickName);
 | 
			
		||||
 | 
			
		||||
    const nickname = useMemo(() => {
 | 
			
		||||
        switch (author.type) {
 | 
			
		||||
            case "nickname":
 | 
			
		||||
                return author.nickname;
 | 
			
		||||
            case "semi-nickname":
 | 
			
		||||
                return author.nickname;
 | 
			
		||||
            case "IP":
 | 
			
		||||
                return author.tempNickname ?? defaultNickname;
 | 
			
		||||
            case "operator":
 | 
			
		||||
                return "운영자";
 | 
			
		||||
            default:
 | 
			
		||||
                return defaultNickname;
 | 
			
		||||
        }
 | 
			
		||||
    }, [author, defaultNickname])
 | 
			
		||||
    
 | 
			
		||||
    return <>
 | 
			
		||||
        {/* Author Name Span */}
 | 
			
		||||
        <span className="inline-block max-w-[81%] align-top text-ellipsis overflow-hidden whitespace-nowrap">
 | 
			
		||||
            {/* Inner em/span for potential finer control if needed */}
 | 
			
		||||
            <em className="not-italic leading-[13px]">
 | 
			
		||||
                {nickname}
 | 
			
		||||
            </em>
 | 
			
		||||
        </span>
 | 
			
		||||
        {/* Author IP */}
 | 
			
		||||
        {author?.type === "IP" && (
 | 
			
		||||
            <span className="font-tahoma text-[11px] text-custom-gray-medium tracking-[-0.05em] ml-[3px]">
 | 
			
		||||
                ({author.ip})
 | 
			
		||||
            </span>
 | 
			
		||||
        )}
 | 
			
		||||
        {/* Author Icon Placeholder */}
 | 
			
		||||
        {author?.type !== "IP" && (
 | 
			
		||||
            <a className="text-custom-gray-dark ml-0.5 inline-block">
 | 
			
		||||
                {/* Replace with actual icon component or img tag */}
 | 
			
		||||
                <img
 | 
			
		||||
                    alt="icon"
 | 
			
		||||
                    src={
 | 
			
		||||
                        author?.type === "nickname" ? (
 | 
			
		||||
                            author?.userType === "manager" ? NicknameImagePath["주딱"] :
 | 
			
		||||
                                author?.userType === "submanager" ? NicknameImagePath["파딱"] :
 | 
			
		||||
                                    NicknameImagePath["default"]
 | 
			
		||||
                        ) : NicknameImagePath["반유동"]
 | 
			
		||||
                    }
 | 
			
		||||
                    className="align-middle cursor-pointer w-3 h-3" // Example size
 | 
			
		||||
                />
 | 
			
		||||
            </a>
 | 
			
		||||
        )}
 | 
			
		||||
    </>
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
import { Separator } from "./Separator";
 | 
			
		||||
import { AuthorData } from "./table";
 | 
			
		||||
import { AuthorData } from './AuthorData';
 | 
			
		||||
import { cn } from "./util/cn";
 | 
			
		||||
import React, { useState } from 'react';
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,15 +1,37 @@
 | 
			
		|||
import { Separator } from "./Separator"
 | 
			
		||||
import { AuthorData, NickName } from './AuthorData';
 | 
			
		||||
 | 
			
		||||
function GalleryContentHeader() {
 | 
			
		||||
export interface GalleryContentHeaderProps {
 | 
			
		||||
    title: string;
 | 
			
		||||
    kind: string;
 | 
			
		||||
    author: AuthorData;
 | 
			
		||||
    date: string;
 | 
			
		||||
    views?: number;
 | 
			
		||||
    recommendations?: number;
 | 
			
		||||
    /**
 | 
			
		||||
     * Number of comments on the post
 | 
			
		||||
     */
 | 
			
		||||
    commentCount?: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function GalleryContentHeader({
 | 
			
		||||
    title,
 | 
			
		||||
    kind,
 | 
			
		||||
    author,
 | 
			
		||||
    date,
 | 
			
		||||
    views = 0,
 | 
			
		||||
    recommendations = 0,
 | 
			
		||||
    commentCount = 0,
 | 
			
		||||
}: GalleryContentHeaderProps) {
 | 
			
		||||
    return (
 | 
			
		||||
        <header>
 | 
			
		||||
            {/* Outer container with margin, padding, and bottom border */}
 | 
			
		||||
            <div className="mt-4 mb-[29px] pb-[11px] border-b border-solid border-gray-200">
 | 
			
		||||
                {/* Post Title */}
 | 
			
		||||
                <h3 className="px-0.5 mb-[7px] text-sm font-bold">
 | 
			
		||||
                    <span> [일반] </span>
 | 
			
		||||
                    <span> [{kind}] </span>
 | 
			
		||||
                    <span>
 | 
			
		||||
                        아 e글 유파들이 고도 왜 안올리는지 알았다
 | 
			
		||||
                        {title}
 | 
			
		||||
                    </span>
 | 
			
		||||
                </h3>
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -19,16 +41,10 @@ function GalleryContentHeader() {
 | 
			
		|||
                >
 | 
			
		||||
                    {/* Left section: Author, IP, Timestamp */}
 | 
			
		||||
                    <div>
 | 
			
		||||
                        <span>
 | 
			
		||||
                            {/* author name */}
 | 
			
		||||
                            <em className="not-italic"> 썬갤러 </em> {/* Adjusted em styling for clarity */}
 | 
			
		||||
                        </span>
 | 
			
		||||
                        <span className="font-tahoma text-[11px] text-gray-500 ml-1">
 | 
			
		||||
                            (110.15)
 | 
			
		||||
                        </span>
 | 
			
		||||
                        <NickName author={author} />
 | 
			
		||||
                        <span className="cursor-default">
 | 
			
		||||
                            <Separator />
 | 
			
		||||
                            2025.02.04 21:32:47
 | 
			
		||||
                            {date}
 | 
			
		||||
                        </span>
 | 
			
		||||
                    </div>
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -37,11 +53,11 @@ function GalleryContentHeader() {
 | 
			
		|||
                        className="pr-[7px]"
 | 
			
		||||
                    >
 | 
			
		||||
                        <span className="cursor-default">
 | 
			
		||||
                            조회 65
 | 
			
		||||
                            조회 {views}
 | 
			
		||||
                        </span>
 | 
			
		||||
                        <Separator />
 | 
			
		||||
                        <span className="cursor-default">
 | 
			
		||||
                            추천 0
 | 
			
		||||
                            추천 {recommendations}
 | 
			
		||||
                        </span>
 | 
			
		||||
                        <Separator />
 | 
			
		||||
                        <span className="cursor-default">
 | 
			
		||||
| 
						 | 
				
			
			@ -50,7 +66,7 @@ function GalleryContentHeader() {
 | 
			
		|||
                                className="inline-block h-5 leading-5  px-2.5  bg-gray-200 
 | 
			
		||||
                             text-gray-700 border border-gray-300 rounded-full
 | 
			
		||||
                              hover:bg-gray-300 hover:border-gray-400 text-xs">
 | 
			
		||||
                                댓글 13
 | 
			
		||||
                                댓글 {commentCount}
 | 
			
		||||
                            </a>
 | 
			
		||||
                        </span>
 | 
			
		||||
                    </div>
 | 
			
		||||
| 
						 | 
				
			
			@ -76,16 +92,7 @@ function GalleryRecommendation({
 | 
			
		|||
                        pt-[19px] w-fit box-content">
 | 
			
		||||
                <div className="flex items-center justify-center overflow-hidden pb-2">
 | 
			
		||||
                        <div className="flex justify-end overflow-hidden w-[139px] mb-0.5">
 | 
			
		||||
                                <div
 | 
			
		||||
                                        style={{
 | 
			
		||||
                                                width: "67px",
 | 
			
		||||
                                                paddingTop: "10px",
 | 
			
		||||
                                                paddingLeft: "11px",
 | 
			
		||||
                                                textAlign: "center",
 | 
			
		||||
                                                fontWeight: "bold",
 | 
			
		||||
                                                color: "rgb(85, 85, 85)"
 | 
			
		||||
                                        }}
 | 
			
		||||
                                >
 | 
			
		||||
                                <div className="text-custom-dropdown-text text-center pt-[10px] pl-[11px] w-[67px] font-bold">
 | 
			
		||||
                                        <p className="text-base leading-[22px] text-[#d31900] font-bold"
 | 
			
		||||
                                        >{recommendCount}</p>
 | 
			
		||||
                                        <p
 | 
			
		||||
| 
						 | 
				
			
			@ -108,16 +115,12 @@ function GalleryRecommendation({
 | 
			
		|||
                                >
 | 
			
		||||
                                        <em
 | 
			
		||||
                                                style={{
 | 
			
		||||
                                                        backgroundColor: "transparent",
 | 
			
		||||
                                                        backgroundImage:
 | 
			
		||||
                                                                'url("/sp_image.png")',
 | 
			
		||||
                                                        backgroundRepeat: "no-repeat",
 | 
			
		||||
                                                        backgroundPositionX: "0px",
 | 
			
		||||
                                                        backgroundPositionY: "-315px",
 | 
			
		||||
                                                        display: "inline-block",
 | 
			
		||||
                                                        width: "56px",
 | 
			
		||||
                                                        height: "56px",
 | 
			
		||||
                                                }}
 | 
			
		||||
                                                className="bg-sp-img inline-block"
 | 
			
		||||
                                        ></em>
 | 
			
		||||
                                </button>
 | 
			
		||||
                        </div>
 | 
			
		||||
| 
						 | 
				
			
			@ -276,23 +279,18 @@ export function GalleryContent() {
 | 
			
		|||
    return <article
 | 
			
		||||
        className="text-custom-gray-dark text-[13px] font-apple"
 | 
			
		||||
    >
 | 
			
		||||
        <GalleryContentHeader />
 | 
			
		||||
        <div style={{ lineHeight: "22px" }}>
 | 
			
		||||
            <div style={{ marginBottom: "50px" }}>
 | 
			
		||||
                <div
 | 
			
		||||
                    style={{
 | 
			
		||||
                        overflow: "hidden",
 | 
			
		||||
                        position: "relative"
 | 
			
		||||
                    }}
 | 
			
		||||
                >
 | 
			
		||||
                <div className="overflow-hidden relative">
 | 
			
		||||
                    <div>
 | 
			
		||||
                        <span style={{ marginLeft: "10px" }}>
 | 
			
		||||
                        <span className="ml-[10px]">
 | 
			
		||||
                            <img
 | 
			
		||||
                                style={{
 | 
			
		||||
                                    maxWidth: "100%",
 | 
			
		||||
                                    width: "550px",
 | 
			
		||||
                                    height: "350px",
 | 
			
		||||
                                }}
 | 
			
		||||
                                alt="alt"
 | 
			
		||||
                            />
 | 
			
		||||
                        </span>
 | 
			
		||||
                    </div>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,13 +1,24 @@
 | 
			
		|||
import { Separator } from "./Separator"
 | 
			
		||||
 | 
			
		||||
export function GalleryTitleHeader() {
 | 
			
		||||
interface GalleryTitleHeaderProps {
 | 
			
		||||
    title?: string;
 | 
			
		||||
    relatedGalleriesCount?: {
 | 
			
		||||
        current: number;
 | 
			
		||||
        total: number;
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function GalleryTitleHeader({
 | 
			
		||||
    title = "워썬더 갤러리",
 | 
			
		||||
    relatedGalleriesCount = { current: 2, total: 8 },
 | 
			
		||||
}: GalleryTitleHeaderProps = {}) {
 | 
			
		||||
    return (
 | 
			
		||||
        <header className="bg-transparent h-[37px] mb-[3px] pt-[4px] text-gray-500">
 | 
			
		||||
            <div className="flex justify-between items-center">
 | 
			
		||||
                <div className="flex items-center">
 | 
			
		||||
                    <h2 className="mt-[-2px] mr-[6px] ml-[2px] text-[24px] max-w-[420px] font-[nanumGothic] tracking-[-1px] m-[2px_8px_0_3px] overflow-hidden whitespace-nowrap text-ellipsis text-custom-blue-dark">
 | 
			
		||||
                        <a className="text-custom-blue-dark font-bold">
 | 
			
		||||
                            워썬더 갤러리
 | 
			
		||||
                            {title}
 | 
			
		||||
                            <div className="bg-sp-img bg-no-repeat inline-block align-top ml-[4px] w-[22px] h-[22px] bg-[-195px_-844px] mt-[3px]">
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </a>
 | 
			
		||||
| 
						 | 
				
			
			@ -26,14 +37,20 @@ export function GalleryTitleHeader() {
 | 
			
		|||
                    </div>
 | 
			
		||||
                    <Separator />
 | 
			
		||||
                    <button className="cursor-pointer align-top font-sans">
 | 
			
		||||
                        연관 갤러리(2/8)
 | 
			
		||||
                        연관 갤러리({relatedGalleriesCount.current}/{relatedGalleriesCount.total})
 | 
			
		||||
                        <span className="mr-[2px] ml-[2px] hidden">
 | 
			
		||||
                        </span>
 | 
			
		||||
                        <em className="bg-sp-img bg-no-repeat inline-block w-[9px] h-[5px] bg-[-115px_-43px] align-[1px] ml-[2px]">
 | 
			
		||||
                        </em>
 | 
			
		||||
                    </button>
 | 
			
		||||
                    <Separator />
 | 
			
		||||
                    <button className="cursor-pointer align-top">
 | 
			
		||||
                    <button className="cursor-pointer align-top"
 | 
			
		||||
                        onClick={() => {
 | 
			
		||||
                            prompt("해당 글의 주소입니다.\nCtrl+C를 눌러 클립보드로 복사하세요.",
 | 
			
		||||
                                window.location.href
 | 
			
		||||
                            )
 | 
			
		||||
                        }}
 | 
			
		||||
                    >
 | 
			
		||||
                        갤주소 복사
 | 
			
		||||
                    </button>
 | 
			
		||||
                    <Separator />
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -375,17 +375,20 @@ export function Header({
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
export function VisitHistory() {
 | 
			
		||||
export function VisitHistory({
 | 
			
		||||
    recentVisits,
 | 
			
		||||
}: {
 | 
			
		||||
    recentVisits?: {
 | 
			
		||||
        id: number;
 | 
			
		||||
        name: string;
 | 
			
		||||
        isMinor?: boolean;
 | 
			
		||||
    }[];
 | 
			
		||||
}) {
 | 
			
		||||
    recentVisits = recentVisits ?? [];
 | 
			
		||||
    // --- State for Interactivity (Example - not fully implemented in static version) ---
 | 
			
		||||
    const [isDropdownOpen, setIsDropdownOpen] = useState(false); // To control the main dropdown visibility
 | 
			
		||||
    const [activeTab, setActiveTab] = useState('recent'); // 'recent' or 'favorites' for the dropdown tabs
 | 
			
		||||
 | 
			
		||||
    // Dummy data matching the HTML snippet
 | 
			
		||||
    const recentVisits = [
 | 
			
		||||
        { id: 1, name: '장르소설', isMinor: true },
 | 
			
		||||
        { id: 2, name: '실시간 베스트', isMinor: false },
 | 
			
		||||
        // Add more items as needed
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <div className="w-[1160px] mx-auto relative"> {/* Added relative positioning for children */}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,27 +1,6 @@
 | 
			
		|||
import { AuthorData, NickName } from './AuthorData';
 | 
			
		||||
import { cn } from './util/cn';
 | 
			
		||||
 | 
			
		||||
export type AuthorData = {
 | 
			
		||||
    // 운영자
 | 
			
		||||
    type: "operator"
 | 
			
		||||
} | {
 | 
			
		||||
    // 고닉
 | 
			
		||||
    // 중복 불가능 닉네임
 | 
			
		||||
    type: "nickname",
 | 
			
		||||
    nickname: string,
 | 
			
		||||
    // 파딱, 주딱 구분을 위한 userType
 | 
			
		||||
    userType?: "manager" | "submanager" // Optional, if applicable
 | 
			
		||||
} | {
 | 
			
		||||
    // 유동닉
 | 
			
		||||
    type: "IP",
 | 
			
		||||
    // IP 주소
 | 
			
		||||
    ip: string,
 | 
			
		||||
} | {
 | 
			
		||||
    // 반유동닉
 | 
			
		||||
    // 중복 가능 닉네임
 | 
			
		||||
    type: "semi-nickname",
 | 
			
		||||
    nickname: string,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// --- Data Interface (Remains the same) ---
 | 
			
		||||
export interface TableRowData {
 | 
			
		||||
    id: string | number;
 | 
			
		||||
| 
						 | 
				
			
			@ -33,19 +12,7 @@ export interface TableRowData {
 | 
			
		|||
    | "icon_ad" | "icon_dctrend";
 | 
			
		||||
 | 
			
		||||
    // e.g., "운영자", "고닉", "반유동", "유동닉"
 | 
			
		||||
    author?: {
 | 
			
		||||
        type: "operator"
 | 
			
		||||
    } | {
 | 
			
		||||
        type: "nickname",
 | 
			
		||||
        nickname: string,
 | 
			
		||||
        userType?: "manager" | "submanager" // Optional, if applicable
 | 
			
		||||
    } | {
 | 
			
		||||
        type: "IP",
 | 
			
		||||
        ip: string,
 | 
			
		||||
    } | {
 | 
			
		||||
        type: "semi-nickname",
 | 
			
		||||
        nickname: string,
 | 
			
		||||
    }
 | 
			
		||||
    author?: AuthorData;
 | 
			
		||||
 | 
			
		||||
    date: string;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -56,7 +23,6 @@ export interface TableRowData {
 | 
			
		|||
    isAdOrSurvey?: boolean;
 | 
			
		||||
    isNews?: boolean; // Handle the last row type specifically if needed
 | 
			
		||||
    titleLinkUrl?: string; // Optional URL for title
 | 
			
		||||
    authorLinkUrl?: string; // Optional URL for author
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// --- Child Component: TableRow ---
 | 
			
		||||
| 
						 | 
				
			
			@ -64,12 +30,6 @@ interface TableRowProps {
 | 
			
		|||
    rowData: TableRowData;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const NicknameImagePath = {
 | 
			
		||||
    "주딱": "/fix_managernik.gif",
 | 
			
		||||
    "파딱": "/fix_sub_managernik.gif",
 | 
			
		||||
    "반유동": "/nik.gif",
 | 
			
		||||
    "default": "/fix_nik.gif"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function TableRow({ rowData }: TableRowProps) {
 | 
			
		||||
    const {
 | 
			
		||||
| 
						 | 
				
			
			@ -85,7 +45,6 @@ export function TableRow({ rowData }: TableRowProps) {
 | 
			
		|||
        isAdOrSurvey,
 | 
			
		||||
        isNews,
 | 
			
		||||
        titleLinkUrl = "#",
 | 
			
		||||
        authorLinkUrl = "#",
 | 
			
		||||
        author,
 | 
			
		||||
    } = rowData;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -139,44 +98,9 @@ export function TableRow({ rowData }: TableRowProps) {
 | 
			
		|||
            <td className={cn(
 | 
			
		||||
                tdCenterClasses, // Base style for author cell
 | 
			
		||||
                'text-[13px]', // Author cell often uses 13px base font size
 | 
			
		||||
                authorLinkUrl !== '#' ? 'cursor-pointer' : 'cursor-default', // Conditional cursor
 | 
			
		||||
            )}>
 | 
			
		||||
                {author?.type === "operator" ? (
 | 
			
		||||
                    <b className="font-bold">운영자</b>
 | 
			
		||||
                ) : (
 | 
			
		||||
                    <>
 | 
			
		||||
                        {/* Author Name Span */}
 | 
			
		||||
                        <span className="inline-block max-w-[81%] align-top text-ellipsis overflow-hidden whitespace-nowrap">
 | 
			
		||||
                            {/* Inner em/span for potential finer control if needed */}
 | 
			
		||||
                            <em className="not-italic leading-[13px]">
 | 
			
		||||
                                {author?.type === "nickname" ? author?.nickname : "ㅇㅇ"}
 | 
			
		||||
                            </em>
 | 
			
		||||
                        </span>
 | 
			
		||||
                        {/* Author IP */}
 | 
			
		||||
                        {author?.type === "IP" && (
 | 
			
		||||
                            <span className="font-tahoma text-[11px] text-custom-gray-medium tracking-[-0.05em] ml-[3px]">
 | 
			
		||||
                                ({author.ip})
 | 
			
		||||
                            </span>
 | 
			
		||||
                        )}
 | 
			
		||||
                        {/* Author Icon Placeholder */}
 | 
			
		||||
                        {author?.type !== "IP" && (
 | 
			
		||||
                            <a href={authorLinkUrl} className="text-custom-gray-dark ml-0.5 inline-block">
 | 
			
		||||
                                {/* Replace with actual icon component or img tag */}
 | 
			
		||||
                                <img
 | 
			
		||||
                                    alt="icon"
 | 
			
		||||
                                    src={
 | 
			
		||||
                                        author?.type === "nickname" ? (
 | 
			
		||||
                                            author?.userType === "manager" ? NicknameImagePath["주딱"] :
 | 
			
		||||
                                                author?.userType === "submanager" ? NicknameImagePath["파딱"] :
 | 
			
		||||
                                                    NicknameImagePath["default"]
 | 
			
		||||
                                        ) : NicknameImagePath["반유동"]
 | 
			
		||||
                                    }
 | 
			
		||||
                                    className="align-middle cursor-pointer w-3 h-3" // Example size
 | 
			
		||||
                                />
 | 
			
		||||
                            </a>
 | 
			
		||||
                        )}
 | 
			
		||||
                    </>
 | 
			
		||||
                )}
 | 
			
		||||
                {author && <NickName author={author} />}
 | 
			
		||||
                {variant === "icon_dctrend" && "디시트렌드"}
 | 
			
		||||
            </td>
 | 
			
		||||
            <td className={tdCenterClasses}>{date}</td>
 | 
			
		||||
            <td className={tdCenterClasses}>{views}</td>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		
		Reference in a new issue