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
+12 -12
View File
@@ -7,7 +7,7 @@ import { Plus, X } from 'lucide-react';
import { widgetRegistry } from '../widget-registry';
import { WidgetPicker } from './WidgetPicker';
type Position = { x: number; y: number };
type Position = { x: number; y: number }; // percentages (0100)
type WidgetInstance = {
id: string;
@@ -69,8 +69,8 @@ type DragState = {
startY: number;
originX: number;
originY: number;
widgetW: number;
widgetH: number;
widgetWPct: number;
widgetHPct: number;
panelW: number;
panelH: number;
};
@@ -91,8 +91,8 @@ const DraggableWidget = ({ instance, panelRef, onMove, onRemove }: DraggableWidg
startY: ev.clientY,
originX: instance.position.x,
originY: instance.position.y,
widgetW: widgetRect.width,
widgetH: widgetRect.height,
widgetWPct: (widgetRect.width / panelRect.width) * 100,
widgetHPct: (widgetRect.height / panelRect.height) * 100,
panelW: panelRect.width,
panelH: panelRect.height,
};
@@ -119,11 +119,11 @@ const DraggableWidget = ({ instance, panelRef, onMove, onRemove }: DraggableWidg
return;
}
const d = dragRef.current;
const rawX = d.originX + (ev.clientX - d.startX);
const rawY = d.originY + (ev.clientY - d.startY);
const deltaXPct = ((ev.clientX - d.startX) / d.panelW) * 100;
const deltaYPct = ((ev.clientY - d.startY) / d.panelH) * 100;
onMove({
x: Math.max(0, Math.min(rawX, d.panelW - d.widgetW)),
y: Math.max(0, Math.min(rawY, d.panelH - d.widgetH)),
x: Math.max(0, Math.min(d.originX + deltaXPct, 100 - d.widgetWPct)),
y: Math.max(0, Math.min(d.originY + deltaYPct, 100 - d.widgetHPct)),
});
},
[onMove, panelRef, endDrag],
@@ -144,7 +144,7 @@ const DraggableWidget = ({ instance, panelRef, onMove, onRemove }: DraggableWidg
<div
ref={containerRef}
className="absolute group"
style={{ left: instance.position.x, top: instance.position.y }}
style={{ left: `${instance.position.x}%`, top: `${instance.position.y}%` }}
onPointerDown={onPointerDown}
onPointerMove={onPointerMove}
onPointerUp={onPointerUp}
@@ -194,8 +194,8 @@ export const WidgetPanel = ({ panelId }: { panelId: string }) => {
const addWidget = useCallback((widgetType: string) => {
nextId.current++;
const id = `widget-${nextId.current}`;
const offset = (nextId.current % 5) * 30;
setInstances((prev) => [...prev, { id, widgetType, position: { x: 20 + offset, y: 20 + offset } }]);
const offset = (nextId.current % 5) * 5;
setInstances((prev) => [...prev, { id, widgetType, position: { x: 2 + offset, y: 2 + offset } }]);
setShowPicker(false);
}, []);
@@ -1,54 +0,0 @@
import { Link } from 'react-router';
import { LayoutGrid, ArrowRight } from 'lucide-react';
import { useQuery } from '@tanstack/react-query';
import { useClient } from 'hooks/useClient';
import { useAuth } from 'hooks/useAuth';
import type { WorkspaceDefinition } from '@/components/Workspace';
import { Widget } from '../Widget';
export const Workspaces = () => {
const client = useClient();
const { isAuthenticated } = useAuth();
const { data: state = {} } = useQuery<Record<string, unknown>>({
queryKey: ['USER_STATE'],
enabled: isAuthenticated,
queryFn: () => client.get('/user/state'),
staleTime: Infinity,
});
const workspaces = (state.workspaces ?? []) as WorkspaceDefinition[];
return (
<Widget title="Workspaces">
<div className="px-2 pb-3 max-h-52 overflow-y-auto">
{workspaces.length === 0 ? (
<div className="flex flex-col items-center gap-2 py-4 text-muted-foreground">
<LayoutGrid className="h-5 w-5" />
<p className="text-xs">No workspaces</p>
<Link to="/workspaces" className="text-xs text-primary hover:underline">
Create one
</Link>
</div>
) : (
<ul className="space-y-0.5">
{workspaces.map((ws) => (
<li key={ws.id} className="flex items-center gap-2 px-2 py-1.5 rounded-md hover:bg-muted/50 group">
<Link to={`/workspaces/${ws.id}`} className="flex items-center gap-2 flex-1 min-w-0">
<LayoutGrid className="h-4 w-4 shrink-0 text-muted-foreground" />
<span className="text-sm truncate">{ws.name}</span>
</Link>
<Link
to={`/workspaces/${ws.id}`}
className="shrink-0 p-1 rounded text-muted-foreground/40 md:opacity-0 md:group-hover:opacity-100 hover:text-primary transition-opacity"
>
<ArrowRight className="h-3.5 w-3.5" />
</Link>
</li>
))}
</ul>
)}
</div>
</Widget>
);
};
+1 -3
View File
@@ -1,9 +1,7 @@
import { Clock as ClockIcon, LayoutGrid } from 'lucide-react';
import { Clock as ClockIcon } from 'lucide-react';
import type { AppRegistryEntry } from '@/components/Workspace';
import { Clock } from './Clock/index';
import { Workspaces } from './Workspaces/index';
export const widgetRegistry: Record<string, AppRegistryEntry> = {
'clock': { name: 'Clock', icon: ClockIcon, component: () => <Clock /> },
'workspaces': { name: 'Workspaces', icon: LayoutGrid, component: () => <Workspaces /> },
};