Make portal mobile-friendly with floating buttons and slide-over panels
Replace fixed sidebar with floating action buttons (menu + add node) in bottom-left corner. Sidebar and node panel now slide in as overlay drawers with backdrop dismiss, capped width for small screens, and slide animations.
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useState } from 'react';
|
||||
import { useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
import { api } from '../api';
|
||||
import type { NodeWithConnections } from '../types';
|
||||
|
||||
export default function NodePanel({
|
||||
nodeId,
|
||||
@@ -25,17 +24,9 @@ export default function NodePanel({
|
||||
const [editing, setEditing] = useState<string | null>(null);
|
||||
const [editValue, setEditValue] = useState('');
|
||||
|
||||
useEffect(() => {
|
||||
const handler = (e: KeyboardEvent) => { if (e.key === 'Escape') onClose(); };
|
||||
window.addEventListener('keydown', handler);
|
||||
return () => window.removeEventListener('keydown', handler);
|
||||
}, [onClose]);
|
||||
|
||||
if (isLoading || !node) {
|
||||
return (
|
||||
<div className="w-80 bg-gray-900 border-l border-gray-800 p-4 text-gray-400">
|
||||
Loading...
|
||||
</div>
|
||||
<div className="h-full bg-gray-900 p-4 text-gray-400">Loading...</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -85,7 +76,7 @@ export default function NodePanel({
|
||||
);
|
||||
|
||||
return (
|
||||
<aside className="w-80 bg-gray-900 border-l border-gray-800 flex flex-col h-full overflow-y-auto">
|
||||
<aside className="h-full bg-gray-900 border-l border-gray-800 flex flex-col overflow-y-auto">
|
||||
<div className="p-4 border-b border-gray-800 flex justify-between items-start">
|
||||
<div>
|
||||
<span className="text-[10px] uppercase tracking-wide text-gray-500">{node.kind}</span>
|
||||
|
||||
@@ -9,18 +9,23 @@ export default function Sidebar({
|
||||
selectedId,
|
||||
onSelect,
|
||||
onAddNode,
|
||||
onClose,
|
||||
}: {
|
||||
selectedId: string | null;
|
||||
onSelect: (id: string) => void;
|
||||
onAddNode: () => void;
|
||||
onClose: () => void;
|
||||
}) {
|
||||
const [kindFilter, setKindFilter] = useState('');
|
||||
const { data: nodes, isLoading } = useNodes(kindFilter ? { kind: kindFilter } : undefined);
|
||||
|
||||
return (
|
||||
<aside className="w-72 bg-gray-900 border-r border-gray-800 flex flex-col h-full">
|
||||
<aside className="h-full bg-gray-900 border-r border-gray-800 flex flex-col">
|
||||
<div className="p-4 border-b border-gray-800">
|
||||
<h1 className="text-lg font-bold text-white mb-3">Cortex Portal</h1>
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<h1 className="text-lg font-bold text-white">Cortex Portal</h1>
|
||||
<button onClick={onClose} className="text-gray-500 hover:text-white text-xl leading-none">×</button>
|
||||
</div>
|
||||
<SearchBar onSelect={onSelect} />
|
||||
<div className="flex gap-1 mt-3 flex-wrap">
|
||||
{KINDS.map((k) => (
|
||||
|
||||
Reference in New Issue
Block a user