From eea0832a42eb418576da60ecb872f072d5539f79 Mon Sep 17 00:00:00 2001 From: monoid Date: Tue, 1 Apr 2025 02:25:28 +0900 Subject: [PATCH] global navigation bar --- src/App.tsx | 2 + src/Header.tsx | 269 +++++++++++++++++++++++++++++++++++++++++++++++++ src/index.css | 4 + src/util/cn.ts | 4 +- 4 files changed, 277 insertions(+), 2 deletions(-) create mode 100644 src/Header.tsx diff --git a/src/App.tsx b/src/App.tsx index b1d60d3..1fca0ce 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -4,6 +4,7 @@ import CommentHeader from './CommentHeader'; import { Footer } from './Footer'; import { GalleryContent } from './Gallery'; import { GalleryTitleHeader } from './GalleryTitleHeader'; +import { GlobalNavigationBar } from './Header'; import { GalleryTable, TableRowData } from './table'; @@ -163,6 +164,7 @@ const comments: SubCommentData[] = [ function App() { return (
+
= ({ items, isVisible, widthClass, leftClass }) => ( +
+ + +
+); + +// Helper component for Nav Items with Dropdowns +const NavItemWithDropdown: React.FC = ({ title, items, widthClass, leftClass }) => { + const [isDropdownVisible, setIsDropdownVisible] = useState(false); + const timeoutRef = useRef(null); + + const showDropdown = () => { + if (timeoutRef.current) clearTimeout(timeoutRef.current); + setIsDropdownVisible(true); + }; + + const hideDropdown = () => { + timeoutRef.current = setTimeout(() => { + setIsDropdownVisible(false); + }, 150); + }; + + return ( +
  • + + {title} + + {title === '갤러리' && } + +
  • + ); +}; + +// Helper component for More Button Dropdown +const MoreButtonDropdown: React.FC> = ({ items, widthClass, leftClass }) => { + const [isDropdownVisible, setIsDropdownVisible] = useState(false); + const timeoutRef = useRef(null); + + const showDropdown = () => { + if (timeoutRef.current) clearTimeout(timeoutRef.current); + setIsDropdownVisible(true); + }; + + const hideDropdown = () => { + timeoutRef.current = setTimeout(() => { + setIsDropdownVisible(false); + }, 150); + }; + + return ( +
  • + + +
  • + ); +}; + +// Main Ticker component +const InfoTicker: React.FC = () => { + const tickerItems: TickerItem[] = [ + { id: 1, text: '디시 로터리 응모', href: '#', highlightClass: '' }, + { id: 2, text: '어제 %HIGHLIGHT%개 게시글 등록', href: '#', highlight: '911,981', highlightClass: 'text-custom-yellow-light' }, + { id: 3, text: '어제 %HIGHLIGHT%개 댓글 등록', href: '#', highlight: '2,527,905', highlightClass: 'text-custom-cyan' }, + { id: 4, text: '총 갤러리 수 %HIGHLIGHT%개', href: '#', highlight: '80,516', highlightClass: 'text-custom-pink' }, + ]; + + const [currentItemIndex, setCurrentItemIndex] = useState(0); + const [animating, setAnimating] = useState(false); + + useEffect(() => { + const intervalId = setInterval(() => { + setAnimating(true); + + const animTimeout = setTimeout(() => { + setAnimating(false); + setCurrentItemIndex((prevIndex) => (prevIndex + 1) % tickerItems.length); + }, 500); // Animation duration + + return () => clearTimeout(animTimeout); + }, 3000); + + return () => clearInterval(intervalId); + }, [tickerItems.length]); + + const currentItem = tickerItems[currentItemIndex]; + const nextItemIndex = (currentItemIndex + 1) % tickerItems.length; + const nextItem = tickerItems[nextItemIndex]; + + const renderTickerItem = (item: TickerItem) => { + const parts = item.text.split('%HIGHLIGHT%'); + return ( + + {parts[0]} + {item.highlight && ( + + {` ${item.highlight} `} + + )} + {parts[1]} + + ); + }; + + return ( +
    +
    +
    + {renderTickerItem(currentItem)} +
    +
    +
    + {renderTickerItem(nextItem)} +
    +
    +
    +
    + ); +}; + +// Main Navigation Bar Component +export function GlobalNavigationBar(): JSX.Element { + const galleryItems: DropdownItem[] = [ + { name: '게임', href: '#' }, { name: '연예/방송', href: '#' }, { name: '스포츠', href: '#' }, + { name: '교육/금융/IT', href: '#' }, { name: '여행/음식/생물', href: '#' }, { name: '취미/생활', href: '#' }, + ]; + + const moreItems: DropdownItem[] = [ + { name: '디시뉴스', href: '#' }, { name: '디시게임', href: '#' }, { name: '디시위키', href: '#' }, + { name: '이벤트', href: '#' }, { name: '디시콘', href: '#' }, + ]; + + return ( + + ); +} diff --git a/src/index.css b/src/index.css index 9375590..9d61b92 100644 --- a/src/index.css +++ b/src/index.css @@ -19,6 +19,10 @@ --color-custom-dropdown-title-bg: rgb(243 247 255); --color-custom-dropdown-bg: rgb(243 243 243); --color-custom-dropdown-text: rgb(85 85 85); + --color-custom-yellow: rgb(255 237 68); + --color-custom-yellow-light: rgb(255 255 153); + --color-custom-cyan: rgb(134 225 240); + --color-custom-pink: rgb(255 187 238); --font-apple: -apple-system, BlinkMacSystemFont, "Apple SD Gothic Neo", "Malgun Gothic", "맑은 고딕", arial, 굴림, Gulim, sans-serif; --font-apple-dotum: -apple-system, BlinkMacSystemFont, "Apple SD Gothic Neo", "Malgun Gothic", "맑은 고딕", arial, Dotum, 돋움, sans-serif; diff --git a/src/util/cn.ts b/src/util/cn.ts index 33dd0c4..54da2c1 100644 --- a/src/util/cn.ts +++ b/src/util/cn.ts @@ -1,6 +1,6 @@ -import { clsx } from "clsx"; +import { ClassValue, clsx } from "clsx"; import { twMerge } from "tailwind-merge"; -export function cn(...inputs: (string | undefined | false)[]) { +export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); } \ No newline at end of file