Skip to content

Commit e112c53

Browse files
authored
Merge pull request #377 from widgetify-app/explorer-sidebar-redesign
Explorer sidebar redesign
2 parents 527cbb1 + 1edc64f commit e112c53

1 file changed

Lines changed: 52 additions & 79 deletions

File tree

src/layouts/explorer/explorer.tsx

Lines changed: 52 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { useGetContents } from '@/services/hooks/content/get-content.hook'
22
import { useRef, useState, useEffect } from 'react'
33
import Analytics from '@/analytics'
44
import { getFaviconFromUrl } from '@/common/utils/icon'
5-
import { HiOutlineLightBulb } from 'react-icons/hi2'
65

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

2928
useEffect(() => {
29+
if (!catalogData?.contents || !scrollContainerRef.current) return
30+
3031
const observerOptions = {
3132
root: scrollContainerRef.current,
32-
rootMargin: '-10% 0px -80% 0px',
33-
threshold: 0,
33+
rootMargin: '0px 0px -40% 0px',
34+
threshold: 0.1,
3435
}
3536

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

4445
const observer = new IntersectionObserver(observerCallback, observerOptions)
45-
Object.values(categoryRefs.current).forEach((div) => {
46+
47+
const currentRefs = categoryRefs.current
48+
Object.values(currentRefs).forEach((div) => {
4649
if (div) observer.observe(div)
4750
})
51+
4852
return () => observer.disconnect()
49-
}, [catalogData])
53+
}, [catalogData?.contents])
5054

5155
const scrollToCategory = (id: string) => {
5256
setActiveCategory(id)
53-
categoryRefs.current[id]?.scrollIntoView({
54-
behavior: 'smooth',
55-
block: 'start',
56-
})
57+
const element = categoryRefs.current[id]
58+
if (element) {
59+
element.scrollIntoView({
60+
behavior: 'smooth',
61+
block: 'start',
62+
})
63+
}
5764
Analytics.event('explorer_click_category')
5865
}
5966

6067
return (
6168
<div className="flex flex-row w-full h-screen overflow-hidden">
62-
<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)]">
63-
<div className="flex flex-col items-center w-full gap-3 px-2 overflow-x-hidden overflow-y-auto scrollbar-none">
69+
<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">
70+
<div className="flex flex-col items-center w-full gap-2 px-2 py-2 overflow-x-hidden overflow-y-auto scrollbar-none">
6471
{catalogData?.contents?.map((cat: CategoryItem) => (
6572
<button
6673
key={cat.id}
6774
onClick={() => scrollToCategory(cat.id)}
68-
className={`relative group flex flex-col items-center justify-center w-full py-3 px-2 rounded-2xl transition-all duration-300 cursor-pointer ${
75+
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 ${
6976
activeCategory === cat.id
70-
? 'bg-primary/80 text-white shadow-sm shadow-primary/20'
71-
: 'bg-base-200/60 hover:bg-base-300 text-base-content/70 hover:scale-102'
77+
? 'bg-primary text-white shadow-md shadow-primary/40 scale-110 border-primary/50'
78+
: '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'
7279
}`}
7380
>
74-
{activeCategory === cat.id && (
75-
<div className="absolute w-1 h-10 rounded-r-full -left-2 bg-primary" />
76-
)}
77-
81+
<div
82+
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'}`}
83+
/>
7884
{cat.icon ? (
7985
<img
8086
src={cat.icon}
81-
className={`w-8 h-8 mb-2 object-contain transition-transform ${
87+
className={`w-7 h-7 object-contain transition-transform ${
8288
activeCategory === cat.id
8389
? 'scale-110'
8490
: 'opacity-70 group-hover:opacity-100'
@@ -87,7 +93,7 @@ export function ExplorerContent() {
8793
/>
8894
) : (
8995
<div
90-
className={`w-8 h-8 mb-2 rounded-xl flex items-center justify-center text-lg font-bold transition-all ${
96+
className={`w-7 h-7 rounded-xl flex items-center justify-center text-sm font-bold transition-all ${
9197
activeCategory === cat.id
9298
? 'bg-white/20'
9399
: 'bg-base-content/10 group-hover:bg-base-content/20'
@@ -96,17 +102,6 @@ export function ExplorerContent() {
96102
{cat.category.substring(0, 1)}
97103
</div>
98104
)}
99-
100-
<span
101-
className={`text-[9px] font-bold text-center leading-tight transition-all line-clamp-2 ${
102-
activeCategory === cat.id
103-
? 'opacity-100'
104-
: 'opacity-60 group-hover:opacity-90'
105-
}`}
106-
>
107-
{cat.category}
108-
</span>
109-
110105
<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">
111106
{cat.category}
112107
<div className="absolute -translate-y-1/2 border-4 border-transparent right-full top-1/2 border-r-neutral" />
@@ -116,16 +111,16 @@ export function ExplorerContent() {
116111
</div>
117112
</aside>
118113

119-
<div className="flex flex-col flex-1 w-full h-full gap-3 px-2 py-3 overflow-hidden md:px-6">
114+
<div className="flex flex-col w-full h-full gap-3 px-2 py-3 overflow-hidden md:px-24">
120115
<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">
121116
{catalogData?.contents?.map((cat: CategoryItem) => (
122117
<button
123118
key={cat.id}
124119
onClick={() => scrollToCategory(cat.id)}
125120
className={`flex items-center gap-2 px-4 py-2 text-[10px] font-bold whitespace-nowrap rounded-xl transition-all shrink-0 ${
126121
activeCategory === cat.id
127-
? 'bg-primary text-white shadow-md'
128-
: 'bg-base-200/50'
122+
? 'bg-primary text-white shadow-md scale-105'
123+
: 'bg-base-200/50 hover:bg-primary/10 hover:text-primary'
129124
}`}
130125
>
131126
{cat.icon && (
@@ -136,13 +131,13 @@ export function ExplorerContent() {
136131
))}
137132
</div>
138133

139-
<div className="grid w-full h-full grid-cols-1 gap-6 overflow-hidden lg:grid-cols-12">
140-
<div className="flex flex-col h-full gap-4 overflow-hidden lg:col-span-8">
134+
<div className="grid w-full h-full grid-cols-1 gap-6 overflow-hidden lg:grid-cols-1">
135+
<div className="flex flex-col h-full gap-4 overflow-hidden lg:col-span-1">
141136
<div
142137
ref={scrollContainerRef}
143138
className="flex-1 pb-10 pr-1 overflow-y-auto scrollbar-none scroll-smooth"
144139
>
145-
<div className="grid grid-cols-1 gap-4 pb-10 md:grid-cols-2">
140+
<div className="grid max-w-5xl grid-cols-1 gap-4 pb-[50vh] mx-auto md:grid-cols-2">
146141
{catalogData?.contents?.map(
147142
(category: CategoryItem, index: number) => (
148143
<div
@@ -151,10 +146,21 @@ export function ExplorerContent() {
151146
ref={(el) => {
152147
categoryRefs.current[category.id] = el
153148
}}
154-
className={`relative overflow-hidden border scroll-mt-2 bg-content bg-glass border-base-300 rounded-3xl transition-all duration-300 ${
155-
index % 3 === 0
156-
? 'md:col-span-2'
157-
: 'md:col-span-1'
149+
className={`relative overflow-hidden border scroll-mt-4 bg-content bg-glass border-base-300 rounded-3xl transition-all duration-300 ${
150+
index === 0
151+
? 'md:col-span-2' // آیتم اول همیشه تمام عرض
152+
: (
153+
index ===
154+
catalogData.contents
155+
.length -
156+
1 &&
157+
catalogData.contents
158+
.length %
159+
2 ===
160+
0
161+
)
162+
? 'md:col-span-2' // اگر تعداد کل زوج بود (مثل ۶ تا)، آخری تمام عرض شود تا جای خالی پر شود
163+
: 'md:col-span-1'
158164
}`}
159165
>
160166
{category.banner && (
@@ -172,7 +178,6 @@ export function ExplorerContent() {
172178
/>
173179
</div>
174180
)}
175-
176181
<div className="p-5">
177182
<div className="flex items-center gap-4 mb-6">
178183
<div className="flex items-center gap-2.5">
@@ -191,13 +196,8 @@ export function ExplorerContent() {
191196
</div>
192197
<div className="flex-1 h-px bg-linear-to-r from-base-content/10 to-transparent" />
193198
</div>
194-
195199
<div
196-
className={`grid gap-y-6 gap-x-2 ${
197-
index % 3 === 0
198-
? 'grid-cols-4 sm:grid-cols-6 lg:grid-cols-8'
199-
: 'grid-cols-3 sm:grid-cols-4'
200-
}`}
200+
className={`grid gap-y-6 gap-x-2 grid-cols-3 sm:grid-cols-5`}
201201
>
202202
{category.links?.map((link, idx) => (
203203
<a
@@ -207,10 +207,10 @@ export function ExplorerContent() {
207207
rel="noopener noreferrer"
208208
className="flex flex-col items-center gap-3 group/item active:scale-95"
209209
>
210-
<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">
210+
<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">
211211
{link.badge && (
212212
<span
213-
className="absolute -top-1 -right-1 z-20 px-1.5 py-0.5 rounded-lg text-[8px] font-black border border-white/10"
213+
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"
214214
style={{
215215
backgroundColor:
216216
link.badgeColor ||
@@ -228,11 +228,11 @@ export function ExplorerContent() {
228228
link.url
229229
)
230230
}
231-
className="object-contain w-6 h-6 transition-transform rounded group-hover/item:scale-110"
231+
className="object-contain w-6 h-6 transition-all duration-500 rounded group-hover/item:scale-110 group-hover/item:brightness-110"
232232
alt={link.name}
233233
/>
234234
</div>
235-
<span className="text-[10px] font-medium tracking-tighter text-center truncate w-full opacity-50 group-hover/item:opacity-100 transition-opacity">
235+
<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">
236236
{link.name}
237237
</span>
238238
</a>
@@ -245,33 +245,6 @@ export function ExplorerContent() {
245245
</div>
246246
</div>
247247
</div>
248-
<div className="hidden h-full pb-4 space-y-4 overflow-y-auto lg:block lg:col-span-4 scrollbar-none">
249-
<div className="sticky top-0 flex flex-col gap-4">
250-
<a
251-
href="https://feedback.widgetify.ir"
252-
target="_blank"
253-
rel="noopener noreferrer"
254-
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"
255-
>
256-
<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">
257-
<HiOutlineLightBulb
258-
size={28}
259-
className="transition-transform duration-500"
260-
/>
261-
</div>
262-
263-
<h3 className="font-bold tracking-tight transition-colors duration-300 text-muted group-hover:text-primary">
264-
چیزی جا انداختیم؟
265-
</h3>
266-
267-
<p className="mt-1 text-[10px] font-medium opacity-40 transition-opacity duration-300 group-hover:opacity-100">
268-
بهمون بگو تا اضافه‌اش کنیم
269-
</p>
270-
271-
<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" />
272-
</a>
273-
</div>
274-
</div>
275248
</div>
276249
</div>
277250
</div>

0 commit comments

Comments
 (0)