Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 52 additions & 79 deletions src/layouts/explorer/explorer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { useGetContents } from '@/services/hooks/content/get-content.hook'
import { useRef, useState, useEffect } from 'react'
import Analytics from '@/analytics'
import { getFaviconFromUrl } from '@/common/utils/icon'
import { HiOutlineLightBulb } from 'react-icons/hi2'

interface LinkItem {
name: string
Expand All @@ -27,10 +26,12 @@ export function ExplorerContent() {
const scrollContainerRef = useRef<HTMLDivElement>(null)

useEffect(() => {
if (!catalogData?.contents || !scrollContainerRef.current) return

const observerOptions = {
root: scrollContainerRef.current,
rootMargin: '-10% 0px -80% 0px',
threshold: 0,
rootMargin: '0px 0px -40% 0px',
threshold: 0.1,
}

const observerCallback = (entries: IntersectionObserverEntry[]) => {
Expand All @@ -42,43 +43,48 @@ export function ExplorerContent() {
}

const observer = new IntersectionObserver(observerCallback, observerOptions)
Object.values(categoryRefs.current).forEach((div) => {

const currentRefs = categoryRefs.current
Object.values(currentRefs).forEach((div) => {
if (div) observer.observe(div)
})

return () => observer.disconnect()
}, [catalogData])
}, [catalogData?.contents])

const scrollToCategory = (id: string) => {
setActiveCategory(id)
categoryRefs.current[id]?.scrollIntoView({
behavior: 'smooth',
block: 'start',
})
const element = categoryRefs.current[id]
if (element) {
element.scrollIntoView({
behavior: 'smooth',
block: 'start',
})
}
Analytics.event('explorer_click_category')
}

return (
<div className="flex flex-row w-full h-screen overflow-hidden">
<aside className="flex-col items-center hidden w-24 gap-3 py-2 md:flex border-base-300 bg-content bg-glass lg:mt-3 rounded-t-2xl rounded-b-2xl h-[calc(100vh-10rem)]">
<div className="flex flex-col items-center w-full gap-3 px-2 overflow-x-hidden overflow-y-auto scrollbar-none">
<aside className="flex-col items-center hidden w-20 gap-3 py-4 md:flex bg-white/[0.02] backdrop-blur-sm border border-white/[0.08] rounded-[2.5rem] lg:mt-4 h-fit max-h-[calc(100vh-160px)] sticky top-4">
<div className="flex flex-col items-center w-full gap-2 px-2 py-2 overflow-x-hidden overflow-y-auto scrollbar-none">
{catalogData?.contents?.map((cat: CategoryItem) => (
<button
key={cat.id}
onClick={() => scrollToCategory(cat.id)}
className={`relative group flex flex-col items-center justify-center w-full py-3 px-2 rounded-2xl transition-all duration-300 cursor-pointer ${
className={`relative group flex flex-col items-center justify-center w-14 min-h-14 max-h-14 rounded-[1.5rem] transition-all duration-500 cursor-pointer border ${
activeCategory === cat.id
? 'bg-primary/80 text-white shadow-sm shadow-primary/20'
: 'bg-base-200/60 hover:bg-base-300 text-base-content/70 hover:scale-102'
? 'bg-primary text-white shadow-md shadow-primary/40 scale-110 border-primary/50'
: 'bg-white/[0.03] border-white/5 text-base-content/60 hover:bg-primary/10 hover:text-primary hover:scale-105 hover:border-primary/30'
}`}
>
{activeCategory === cat.id && (
<div className="absolute w-1 h-10 rounded-r-full -left-2 bg-primary" />
)}

<div
className={`absolute w-1 h-8 rounded-r-full -left-2 bg-primary transition-all duration-500 ${activeCategory === cat.id ? 'opacity-100 scale-y-100' : 'opacity-0 scale-y-0'}`}
/>
{cat.icon ? (
<img
src={cat.icon}
className={`w-8 h-8 mb-2 object-contain transition-transform ${
className={`w-7 h-7 object-contain transition-transform ${
activeCategory === cat.id
? 'scale-110'
: 'opacity-70 group-hover:opacity-100'
Expand All @@ -87,7 +93,7 @@ export function ExplorerContent() {
/>
) : (
<div
className={`w-8 h-8 mb-2 rounded-xl flex items-center justify-center text-lg font-bold transition-all ${
className={`w-7 h-7 rounded-xl flex items-center justify-center text-sm font-bold transition-all ${
activeCategory === cat.id
? 'bg-white/20'
: 'bg-base-content/10 group-hover:bg-base-content/20'
Expand All @@ -96,17 +102,6 @@ export function ExplorerContent() {
{cat.category.substring(0, 1)}
</div>
)}

<span
className={`text-[9px] font-bold text-center leading-tight transition-all line-clamp-2 ${
activeCategory === cat.id
? 'opacity-100'
: 'opacity-60 group-hover:opacity-90'
}`}
>
{cat.category}
</span>

<div className="absolute z-50 px-3 py-2 ml-4 text-xs font-semibold transition-opacity rounded-lg shadow-xl opacity-0 pointer-events-none left-full bg-neutral text-neutral-content group-hover:opacity-100 whitespace-nowrap">
{cat.category}
<div className="absolute -translate-y-1/2 border-4 border-transparent right-full top-1/2 border-r-neutral" />
Expand All @@ -116,16 +111,16 @@ export function ExplorerContent() {
</div>
</aside>

<div className="flex flex-col flex-1 w-full h-full gap-3 px-2 py-3 overflow-hidden md:px-6">
<div className="flex flex-col w-full h-full gap-3 px-2 py-3 overflow-hidden md:px-24">
<div className="md:hidden sticky top-0 z-50 flex items-center w-full gap-2 p-1.5 overflow-x-auto bg-base-100/80 backdrop-blur-xl rounded-2xl border border-white/10 shadow-lg no-scrollbar flex-nowrap">
{catalogData?.contents?.map((cat: CategoryItem) => (
<button
key={cat.id}
onClick={() => scrollToCategory(cat.id)}
className={`flex items-center gap-2 px-4 py-2 text-[10px] font-bold whitespace-nowrap rounded-xl transition-all shrink-0 ${
activeCategory === cat.id
? 'bg-primary text-white shadow-md'
: 'bg-base-200/50'
? 'bg-primary text-white shadow-md scale-105'
: 'bg-base-200/50 hover:bg-primary/10 hover:text-primary'
}`}
>
{cat.icon && (
Expand All @@ -136,13 +131,13 @@ export function ExplorerContent() {
))}
</div>

<div className="grid w-full h-full grid-cols-1 gap-6 overflow-hidden lg:grid-cols-12">
<div className="flex flex-col h-full gap-4 overflow-hidden lg:col-span-8">
<div className="grid w-full h-full grid-cols-1 gap-6 overflow-hidden lg:grid-cols-1">
<div className="flex flex-col h-full gap-4 overflow-hidden lg:col-span-1">
<div
ref={scrollContainerRef}
className="flex-1 pb-10 pr-1 overflow-y-auto scrollbar-none scroll-smooth"
>
<div className="grid grid-cols-1 gap-4 pb-10 md:grid-cols-2">
<div className="grid max-w-5xl grid-cols-1 gap-4 pb-[50vh] mx-auto md:grid-cols-2">
{catalogData?.contents?.map(
(category: CategoryItem, index: number) => (
<div
Expand All @@ -151,10 +146,21 @@ export function ExplorerContent() {
ref={(el) => {
categoryRefs.current[category.id] = el
}}
className={`relative overflow-hidden border scroll-mt-2 bg-content bg-glass border-base-300 rounded-3xl transition-all duration-300 ${
index % 3 === 0
? 'md:col-span-2'
: 'md:col-span-1'
className={`relative overflow-hidden border scroll-mt-4 bg-content bg-glass border-base-300 rounded-3xl transition-all duration-300 ${
index === 0
? 'md:col-span-2' // آیتم اول همیشه تمام عرض
: (
index ===
catalogData.contents
.length -
1 &&
catalogData.contents
.length %
2 ===
0
)
? 'md:col-span-2' // اگر تعداد کل زوج بود (مثل ۶ تا)، آخری تمام عرض شود تا جای خالی پر شود
: 'md:col-span-1'
}`}
>
{category.banner && (
Expand All @@ -172,7 +178,6 @@ export function ExplorerContent() {
/>
</div>
)}

<div className="p-5">
<div className="flex items-center gap-4 mb-6">
<div className="flex items-center gap-2.5">
Expand All @@ -191,13 +196,8 @@ export function ExplorerContent() {
</div>
<div className="flex-1 h-px bg-linear-to-r from-base-content/10 to-transparent" />
</div>

<div
className={`grid gap-y-6 gap-x-2 ${
index % 3 === 0
? 'grid-cols-4 sm:grid-cols-6 lg:grid-cols-8'
: 'grid-cols-3 sm:grid-cols-4'
}`}
className={`grid gap-y-6 gap-x-2 grid-cols-3 sm:grid-cols-5`}
>
{category.links?.map((link, idx) => (
<a
Expand All @@ -207,10 +207,10 @@ export function ExplorerContent() {
rel="noopener noreferrer"
className="flex flex-col items-center gap-3 group/item active:scale-95"
>
<div className="relative flex items-center justify-center w-12 h-12 transition-all duration-300 bg-base-200/40 rounded-2xl group-hover/item:bg-primary/10">
<div className="relative flex items-center justify-center w-12 h-12 transition-all duration-500 bg-base-200/40 rounded-2xl group-hover/item:bg-primary/20 group-hover/item:shadow-lg group-hover/item:shadow-primary/20 group-hover/item:-translate-y-1.5 border border-transparent group-hover/item:border-primary/20">
{link.badge && (
<span
className="absolute -top-1 -right-1 z-20 px-1.5 py-0.5 rounded-lg text-[8px] font-black border border-white/10"
className="absolute -top-1 -right-1 z-20 px-1.5 py-0.5 rounded-lg text-[8px] font-black border border-white/10 shadow-sm"
style={{
backgroundColor:
link.badgeColor ||
Expand All @@ -228,11 +228,11 @@ export function ExplorerContent() {
link.url
)
}
className="object-contain w-6 h-6 transition-transform rounded group-hover/item:scale-110"
className="object-contain w-6 h-6 transition-all duration-500 rounded group-hover/item:scale-110 group-hover/item:brightness-110"
alt={link.name}
/>
</div>
<span className="text-[10px] font-medium tracking-tighter text-center truncate w-full opacity-50 group-hover/item:opacity-100 transition-opacity">
<span className="text-[10px] font-semibold tracking-tighter text-center truncate w-full opacity-40 group-hover/item:opacity-100 group-hover/item:text-primary transition-all duration-300">
{link.name}
</span>
</a>
Expand All @@ -245,33 +245,6 @@ export function ExplorerContent() {
</div>
</div>
</div>
<div className="hidden h-full pb-4 space-y-4 overflow-y-auto lg:block lg:col-span-4 scrollbar-none">
<div className="sticky top-0 flex flex-col gap-4">
<a
href="https://feedback.widgetify.ir"
target="_blank"
rel="noopener noreferrer"
className="relative flex flex-col items-center justify-center p-5 overflow-hidden text-center transition-all duration-500 border border-dashed group bg-content bg-glass border-base-300 rounded-3xl hover:border-primary/50 hover:bg-primary/5 min-h-20"
>
<div className="p-3 mb-3 transition-all duration-500 rounded-2xl bg-base-200/50 text-base-content/40 group-hover:scale-110 group-hover:rotate-12 group-hover:text-warning group-hover:bg-warning/10">
<HiOutlineLightBulb
size={28}
className="transition-transform duration-500"
/>
</div>

<h3 className="font-bold tracking-tight transition-colors duration-300 text-muted group-hover:text-primary">
چیزی جا انداختیم؟
</h3>

<p className="mt-1 text-[10px] font-medium opacity-40 transition-opacity duration-300 group-hover:opacity-100">
بهمون بگو تا اضافه‌اش کنیم
</p>

<div className="absolute bottom-0 left-1/2 -translate-x-1/2 w-0 h-0.5 bg-primary/30 transition-all duration-500 group-hover:w-1/3" />
</a>
</div>
</div>
</div>
</div>
</div>
Expand Down