Workspaces left panel in homepage
This commit is contained in:
@@ -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 (0–100)
|
||||
|
||||
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,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 /> },
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user