feat: implement sidebar docked call panel with participant management and sync status handling
This commit is contained in:
@@ -28,10 +28,16 @@ interface DragPosition {
|
||||
|
||||
/** Threshold in pixels for docking detection */
|
||||
const DOCK_THRESHOLD = 50;
|
||||
/** Combined width of space panel (68px) + room list panel (256px) for dock detection */
|
||||
const ROOM_LIST_EDGE = 324;
|
||||
|
||||
/** Dock zone type */
|
||||
type DockZone = 'none' | 'right' | 'sidebar';
|
||||
|
||||
/** Custom hook for draggable functionality with viewport constraints and docking detection */
|
||||
function useDraggable(
|
||||
onDockRequest?: () => void,
|
||||
onDockRight?: () => void,
|
||||
onDockSidebar?: () => void,
|
||||
onUndockRequest?: () => void,
|
||||
isDocked?: boolean,
|
||||
pendingDrag?: PendingDragState | null,
|
||||
@@ -39,7 +45,7 @@ function useDraggable(
|
||||
) {
|
||||
const [position, setPosition] = useState<DragPosition>({ x: 0, y: 0 });
|
||||
const [isDragging, setIsDragging] = useState(false);
|
||||
const [isNearDockZone, setIsNearDockZone] = useState(false);
|
||||
const [nearDockZone, setNearDockZone] = useState<DockZone>('none');
|
||||
const dragRef = useRef<HTMLDivElement>(null);
|
||||
const dragStartRef = useRef<{ x: number; y: number; startX: number; startY: number } | null>(null);
|
||||
|
||||
@@ -145,9 +151,20 @@ function useDraggable(
|
||||
const handleMouseMove = (e: MouseEvent) => {
|
||||
if (!dragStartRef.current || !dragRef.current) return;
|
||||
|
||||
// Check if near right edge for docking
|
||||
// Check dock zones
|
||||
const nearRightEdge = e.clientX > window.innerWidth - DOCK_THRESHOLD;
|
||||
setIsNearDockZone(nearRightEdge && !isDocked);
|
||||
const nearSidebarZone = e.clientX < ROOM_LIST_EDGE + DOCK_THRESHOLD &&
|
||||
e.clientY > window.innerHeight - 150;
|
||||
|
||||
if (!isDocked) {
|
||||
if (nearSidebarZone) {
|
||||
setNearDockZone('sidebar');
|
||||
} else if (nearRightEdge) {
|
||||
setNearDockZone('right');
|
||||
} else {
|
||||
setNearDockZone('none');
|
||||
}
|
||||
}
|
||||
|
||||
// If docked and dragging away from edge, trigger undock
|
||||
if (isDocked) {
|
||||
@@ -191,12 +208,20 @@ function useDraggable(
|
||||
};
|
||||
|
||||
const handleMouseUp = (e: MouseEvent) => {
|
||||
// Check if should dock on mouse release
|
||||
if (!isDocked && e.clientX > window.innerWidth - DOCK_THRESHOLD) {
|
||||
onDockRequest?.();
|
||||
// Check which dock zone to use
|
||||
const nearRightEdge = e.clientX > window.innerWidth - DOCK_THRESHOLD;
|
||||
const nearSidebarZone = e.clientX < ROOM_LIST_EDGE + DOCK_THRESHOLD &&
|
||||
e.clientY > window.innerHeight - 150;
|
||||
|
||||
if (!isDocked) {
|
||||
if (nearSidebarZone) {
|
||||
onDockSidebar?.();
|
||||
} else if (nearRightEdge) {
|
||||
onDockRight?.();
|
||||
}
|
||||
}
|
||||
setIsDragging(false);
|
||||
setIsNearDockZone(false);
|
||||
setNearDockZone('none');
|
||||
dragStartRef.current = null;
|
||||
};
|
||||
|
||||
@@ -207,9 +232,9 @@ function useDraggable(
|
||||
document.removeEventListener('mousemove', handleMouseMove);
|
||||
document.removeEventListener('mouseup', handleMouseUp);
|
||||
};
|
||||
}, [isDragging, position, isDocked, onDockRequest, onUndockRequest]);
|
||||
}, [isDragging, position, isDocked, onDockRight, onDockSidebar, onUndockRequest]);
|
||||
|
||||
return { position, isDragging, isNearDockZone, handleMouseDown, dragRef };
|
||||
return { position, isDragging, nearDockZone, handleMouseDown, dragRef };
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -687,6 +712,7 @@ export function CallOverlay() {
|
||||
|
||||
// Docking state
|
||||
const [isDocked, setIsDocked] = useSetting(settingsAtom, 'isCallPanelDocked');
|
||||
const [, setDockPosition] = useSetting(settingsAtom, 'callPanelDockPosition');
|
||||
|
||||
// Pending drag state from undocking
|
||||
const [pendingDrag, setPendingDrag] = useAtom(pendingUndockDragAtom);
|
||||
@@ -695,16 +721,23 @@ export function CallOverlay() {
|
||||
setPendingDrag(null);
|
||||
}, [setPendingDrag]);
|
||||
|
||||
const handleDockRequest = useCallback(() => {
|
||||
const handleDockRight = useCallback(() => {
|
||||
setDockPosition('right');
|
||||
setIsDocked(true);
|
||||
}, [setIsDocked]);
|
||||
}, [setIsDocked, setDockPosition]);
|
||||
|
||||
const handleDockSidebar = useCallback(() => {
|
||||
setDockPosition('sidebar');
|
||||
setIsDocked(true);
|
||||
}, [setIsDocked, setDockPosition]);
|
||||
|
||||
const handleUndockRequest = useCallback(() => {
|
||||
setIsDocked(false);
|
||||
}, [setIsDocked]);
|
||||
|
||||
const { position, isDragging, isNearDockZone, handleMouseDown, dragRef } = useDraggable(
|
||||
handleDockRequest,
|
||||
const { position, isDragging, nearDockZone, handleMouseDown, dragRef } = useDraggable(
|
||||
handleDockRight,
|
||||
handleDockSidebar,
|
||||
handleUndockRequest,
|
||||
isDocked,
|
||||
pendingDrag,
|
||||
@@ -1047,8 +1080,9 @@ export function CallOverlay() {
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Dock indicator shown when dragging near the right edge */}
|
||||
{isNearDockZone && !isDocked && <div className={css.DockIndicator} />}
|
||||
{/* Dock indicators shown when dragging near dock zones */}
|
||||
{nearDockZone === 'right' && !isDocked && <div className={css.DockIndicator} />}
|
||||
{nearDockZone === 'sidebar' && !isDocked && <div className={css.SidebarDockIndicator} />}
|
||||
<div
|
||||
ref={setContainerRef}
|
||||
className={containerClass}
|
||||
|
||||
Reference in New Issue
Block a user