Workspaces left panel in homepage

This commit is contained in:
2026-02-18 20:00:42 +00:00
parent 7092c2665a
commit 72bca6cd42
8 changed files with 85 additions and 257 deletions
@@ -1,4 +1,4 @@
import { useRef, useState } from 'react';
import { useEffect, useRef, useState } from 'react';
import { Link, useLocation } from 'react-router';
import type { LucideIcon } from 'lucide-react';
@@ -19,6 +19,7 @@ const ICON_GAP = 24;
const DOCK_PADDING = 12;
const MAX_SCALE = 1.5;
const MAX_DISTANCE = 150;
const BOTTOM_THRESHOLD = 100;
const getScale = (mouseX: number | null, iconCenterX: number) => {
if (mouseX === null) return 1;
@@ -29,25 +30,39 @@ const getScale = (mouseX: number | null, iconCenterX: number) => {
export const Dock = ({ items, className }: DockProps) => {
const [mouseX, setMouseX] = useState<number | null>(null);
const [visible, setVisible] = useState(false);
const dockRef = useRef<HTMLDivElement | null>(null);
const location = useLocation();
const isActive = (to: string) => location.pathname.startsWith(to);
const handleMouseMove = (ev: React.MouseEvent) => {
const rect = dockRef.current?.getBoundingClientRect();
if (rect) setMouseX(ev.clientX - rect.left);
};
useEffect(() => {
const handleMouseMove = (ev: MouseEvent) => {
const distFromBottom = window.innerHeight - ev.clientY;
if (distFromBottom <= BOTTOM_THRESHOLD) {
setVisible(true);
const rect = dockRef.current?.getBoundingClientRect();
if (rect) setMouseX(ev.clientX - rect.left);
} else {
setVisible(false);
setMouseX(null);
}
};
const handleMouseLeave = () => setMouseX(null);
document.addEventListener('mousemove', handleMouseMove);
return () => document.removeEventListener('mousemove', handleMouseMove);
}, []);
return (
<div
ref={dockRef}
onMouseMove={handleMouseMove}
onMouseLeave={handleMouseLeave}
className={`fixed bottom-4 left-1/2 -translate-x-1/2 z-5 items-end gap-2 md:gap-6 px-2 py-1.5 md:px-3 md:py-2 rounded-2xl border backdrop-blur-xl shadow-lg ${className ?? 'flex'}`}
style={{ backgroundColor: 'transparent', borderColor: 'transparent' }}
className={`fixed left-1/2 z-5 items-end gap-2 md:gap-6 px-2 py-1.5 md:px-3 md:py-2 rounded-2xl border backdrop-blur-xl shadow-lg transition-transform duration-300 ease-in-out ${className ?? 'flex'}`}
style={{
backgroundColor: 'var(--dock-bg)',
borderColor: 'var(--dock-border)',
bottom: '16px',
transform: `translateX(-50%) translateY(${visible ? '0' : 'calc(100% + 24px)'})`,
}}
>
{items.map((item, index) => {
const iconCenter = DOCK_PADDING + index * (ICON_SIZE + ICON_GAP) + ICON_SIZE / 2;
@@ -74,11 +89,11 @@ export const Dock = ({ items, className }: DockProps) => {
<div
className="w-10 h-10 md:w-12 md:h-12 rounded-xl flex items-center justify-center transition-all"
style={{
background: active ? `${item.color}55` : `${item.color}30`,
boxShadow: active ? `0 0 12px ${item.color}30` : 'none',
background: item.color,
boxShadow: active ? `0 0 12px ${item.color}40` : 'none',
}}
>
<item.icon className="h-5 w-5 md:h-6 md:w-6" style={{ color: active ? item.color : `${item.color}cc` }} />
<item.icon className="h-5 w-5 md:h-6 md:w-6 text-white" />
</div>
{active && (
<div className="absolute -bottom-1.5 w-1.5 h-1.5 rounded-full" style={{ background: item.color }} />