mirror of
https://github.com/dpup/meshstream.git
synced 2026-03-28 17:42:37 +01:00
Mobile layout tweaks
This commit is contained in:
@@ -58,7 +58,7 @@ export const Map: React.FC<MapProps> = ({
|
||||
|
||||
const mapContainerClasses = flush
|
||||
? `w-full h-full overflow-hidden relative ${className}`
|
||||
: `${className} relative overflow-hidden rounded-lg border border-neutral-700 bg-neutral-800/50`;
|
||||
: `${className} relative overflow-hidden rounded-xl border border-neutral-700 bg-neutral-800/50`;
|
||||
|
||||
if (!apiKeyAvailable) {
|
||||
return (
|
||||
|
||||
@@ -116,14 +116,18 @@ export const Nav: React.FC<NavProps> = ({ connectionStatus }) => {
|
||||
return (
|
||||
<>
|
||||
{/* Mobile header (always visible on mobile) */}
|
||||
<div className="md:hidden fixed top-0 left-0 right-0 z-20 border-neutral-700 border-b shadow-inner bg-neutral-800/40">
|
||||
<div className="md:hidden fixed top-0 left-0 right-0 z-20 bg-neutral-800/40">
|
||||
<div className="flex items-center justify-between p-3">
|
||||
<div className="flex items-center">
|
||||
<Link
|
||||
to="/home"
|
||||
className="flex items-center"
|
||||
onClick={() => isMobileView && setIsMobileMenuOpen(false)}
|
||||
>
|
||||
<div className="bg-pink-400 rounded-md mr-3 p-1.5 flex items-center justify-center">
|
||||
<Layers className="h-5 w-5 text-neutral-800" />
|
||||
</div>
|
||||
<h1 className="text-xl font-thin tracking-wide text-neutral-100">{SITE_TITLE}</h1>
|
||||
</div>
|
||||
</Link>
|
||||
<Button
|
||||
variant="ghost"
|
||||
icon={isMobileMenuOpen ? X : Menu}
|
||||
@@ -161,12 +165,12 @@ export const Nav: React.FC<NavProps> = ({ connectionStatus }) => {
|
||||
{/* Desktop sidebar (hidden on mobile) */}
|
||||
<aside className="hidden md:flex w-64 text-neutral-100 h-screen fixed left-0 top-0 flex-col">
|
||||
{/* Logo and title section */}
|
||||
<div className="p-4 flex items-center">
|
||||
<Link to="/home" className="p-4 flex items-center hover:bg-neutral-800/50 transition-colors">
|
||||
<div className="bg-pink-400 rounded-md mr-3 p-1.5 flex items-center justify-center">
|
||||
<Layers className="h-5 w-5 text-neutral-800" />
|
||||
</div>
|
||||
<h1 className="text-xl font-thin tracking-wide">{SITE_TITLE}</h1>
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
<Separator className="mt-1" />
|
||||
|
||||
|
||||
@@ -154,7 +154,7 @@ export const PacketList: React.FC = () => {
|
||||
return (
|
||||
<div className="flex flex-col h-full">
|
||||
<div className="sticky top-0 z-10">
|
||||
<div className="flex justify-between items-center mb-2">
|
||||
<div className="flex flex-col md:flex-row space-y-2 md:space-y-0 justify-between md:items-center mb-2">
|
||||
<div className="flex items-center space-x-3">
|
||||
{/* Show buffered count when paused */}
|
||||
{streamPaused && bufferedPackets.length > 0 && (
|
||||
@@ -240,44 +240,40 @@ export const PacketList: React.FC = () => {
|
||||
)}
|
||||
|
||||
{/* Pagination */}
|
||||
{totalPages > 1 && (
|
||||
<>
|
||||
<Separator className="mx-0 mt-4" />
|
||||
<div className="flex justify-between items-center text-sm py-2 bg-neutral-800/50 sticky bottom-0">
|
||||
<div className="text-sm text-neutral-400 px-2">
|
||||
Page {currentPage} of {totalPages}
|
||||
</div>
|
||||
<Separator className="mx-0 mt-4" />
|
||||
<div className="flex justify-between items-center text-sm sticky bottom-0">
|
||||
<div className="text-sm text-neutral-400 px-2">
|
||||
Page {currentPage} of {totalPages}
|
||||
</div>
|
||||
|
||||
<div className="flex items-center space-x-3">
|
||||
<Button
|
||||
onClick={() =>
|
||||
setCurrentPage(currentPage > 1 ? currentPage - 1 : 1)
|
||||
}
|
||||
disabled={currentPage === 1}
|
||||
icon={ChevronLeft}
|
||||
variant="secondary"
|
||||
size="md"
|
||||
>
|
||||
Previous
|
||||
</Button>
|
||||
<div className="flex items-center space-x-3">
|
||||
<Button
|
||||
onClick={() =>
|
||||
setCurrentPage(currentPage > 1 ? currentPage - 1 : 1)
|
||||
}
|
||||
disabled={currentPage === 1}
|
||||
icon={ChevronLeft}
|
||||
variant="secondary"
|
||||
size="md"
|
||||
>
|
||||
Previous
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
onClick={() =>
|
||||
setCurrentPage(
|
||||
currentPage < totalPages ? currentPage + 1 : totalPages
|
||||
)
|
||||
}
|
||||
disabled={currentPage === totalPages}
|
||||
icon={ChevronRight}
|
||||
variant="secondary"
|
||||
size="md"
|
||||
>
|
||||
Next
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<Button
|
||||
onClick={() =>
|
||||
setCurrentPage(
|
||||
currentPage < totalPages ? currentPage + 1 : totalPages
|
||||
)
|
||||
}
|
||||
disabled={currentPage === totalPages}
|
||||
icon={ChevronRight}
|
||||
variant="secondary"
|
||||
size="md"
|
||||
>
|
||||
Next
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -2,7 +2,8 @@ import React from "react";
|
||||
import { Battery, MapPin, Thermometer, Network, BoomBox } from "lucide-react";
|
||||
import { Counter } from "../Counter";
|
||||
import { NodeData } from "../../store/slices/aggregatorSlice";
|
||||
import { getActivityLevel, getNodeColors, ActivityLevel } from "../../lib/activity";
|
||||
import { getActivityLevel, getNodeColors } from "../../lib/activity";
|
||||
import { cn } from "@/lib/cn";
|
||||
|
||||
export interface MeshCardProps {
|
||||
type: "node" | "gateway";
|
||||
@@ -45,17 +46,6 @@ export const MeshCard: React.FC<MeshCardProps> = ({
|
||||
const activityLevel = getActivityLevel(lastHeard, type === "gateway");
|
||||
const colors = getNodeColors(activityLevel, type === "gateway");
|
||||
|
||||
// Get card style based on activity
|
||||
const getCardStyle = () => {
|
||||
if (activityLevel === ActivityLevel.RECENT) {
|
||||
return "bg-neutral-800 hover:bg-neutral-700";
|
||||
} else if (activityLevel === ActivityLevel.ACTIVE) {
|
||||
return "bg-neutral-800/80 hover:bg-neutral-700/80";
|
||||
} else {
|
||||
return "bg-neutral-800/50 hover:bg-neutral-800";
|
||||
}
|
||||
};
|
||||
|
||||
// Get icon style based on activity
|
||||
const getIconStyle = () => {
|
||||
return colors.background + " " + colors.textClass;
|
||||
@@ -69,10 +59,10 @@ export const MeshCard: React.FC<MeshCardProps> = ({
|
||||
return (
|
||||
<div
|
||||
onClick={handleClick}
|
||||
className={`flex items-center p-2 rounded-lg ${getCardStyle()} ${onClick ? "cursor-pointer transition-colors" : ""}`}
|
||||
className={cn('flex items-center p-2 rounded-lg bg-neutral-700/50 hover:bg-neutral-700', {"cursor-pointer transition-colors" : !!onClick})}
|
||||
>
|
||||
<div className="mr-2">
|
||||
<div className={`p-1.5 rounded-full ${getIconStyle()}`}>
|
||||
<div className={cn('p-1.5 rounded-full', getIconStyle())}>
|
||||
{getIcon()}
|
||||
</div>
|
||||
</div>
|
||||
@@ -98,7 +88,7 @@ export const MeshCard: React.FC<MeshCardProps> = ({
|
||||
</div>
|
||||
<div className="text-xs text-neutral-400 flex items-center">
|
||||
<span
|
||||
className={`inline-block w-1.5 h-1.5 rounded-full mr-1 ${getStatusDotStyle()}`}
|
||||
className={cn('inline-block w-1.5 h-1.5 rounded-full mr-1', getStatusDotStyle())}
|
||||
></span>
|
||||
{timeString}
|
||||
</div>
|
||||
|
||||
@@ -609,8 +609,8 @@ function getMarkerIcon(node: MapNode): MarkerIconConfig {
|
||||
path: google.maps.SymbolPath.CIRCLE,
|
||||
scale: 12,
|
||||
fillColor: colors.fill,
|
||||
fillOpacity: 0.8,
|
||||
fillOpacity: 1,
|
||||
strokeColor: colors.stroke,
|
||||
strokeWeight: 4,
|
||||
strokeWeight: 2,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -31,8 +31,6 @@ export const NodeList: React.FC = () => {
|
||||
navigate({ to: "/node/$nodeId", params: { nodeId: nodeId.toString(16) } });
|
||||
};
|
||||
|
||||
// Instead of early return, we'll handle the empty state in the JSX
|
||||
|
||||
return (
|
||||
<div className="space-y-1">
|
||||
<div className="flex justify-between items-center mb-2">
|
||||
|
||||
@@ -293,10 +293,17 @@ export const MapReportPacket: React.FC<MapReportPacketProps> = ({ packet }) => {
|
||||
)}
|
||||
</div>
|
||||
{/* Position information */}
|
||||
<div className="grid grid-cols-2 gap-3 mt-3 text-sm">
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3 mt-3 text-sm">
|
||||
<KeyValuePair
|
||||
label="Coordinates"
|
||||
value={`${center.latitude.toFixed(6)}, ${center.longitude.toFixed(6)}`}
|
||||
label="Latitude"
|
||||
value={center.latitude.toFixed(6)}
|
||||
monospace={true}
|
||||
inset={true}
|
||||
/>
|
||||
|
||||
<KeyValuePair
|
||||
label="Longitude"
|
||||
value={center.longitude.toFixed(6)}
|
||||
monospace={true}
|
||||
inset={true}
|
||||
/>
|
||||
|
||||
@@ -35,7 +35,7 @@ export const PositionPacket: React.FC<PositionPacketProps> = ({ packet }) => {
|
||||
>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div>
|
||||
<KeyValueGrid>
|
||||
<KeyValueGrid className="grid-cols-2">
|
||||
<KeyValuePair
|
||||
label="Latitude"
|
||||
value={latitude !== undefined ? latitude.toFixed(6) : 'N/A'}
|
||||
|
||||
@@ -49,7 +49,7 @@ export const WaypointPacket: React.FC<WaypointPacketProps> = ({ packet }) => {
|
||||
<div className="text-base text-neutral-300">{waypoint.name}</div>
|
||||
</div>
|
||||
)}
|
||||
<KeyValueGrid>
|
||||
<KeyValueGrid className="grid-cols-1 sm:grid-cols-2">
|
||||
{latitude !== undefined && (
|
||||
<KeyValuePair label="Latitude" value={latitude.toFixed(6)} vertical monospace />
|
||||
)}
|
||||
|
||||
@@ -94,8 +94,22 @@ export const KeyValueGrid: React.FC<{
|
||||
columns?: number;
|
||||
className?: string;
|
||||
}> = ({ children, columns = 2, className }) => {
|
||||
// Generate the appropriate grid columns class based on the columns prop
|
||||
let gridColsClass = 'grid-cols-2'; // Default
|
||||
|
||||
if (className && className.includes('grid-cols-')) {
|
||||
// If className already includes a grid-cols class, don't add another one
|
||||
gridColsClass = '';
|
||||
} else if (columns === 1) {
|
||||
gridColsClass = 'grid-cols-1';
|
||||
} else if (columns === 3) {
|
||||
gridColsClass = 'grid-cols-3';
|
||||
} else if (columns === 4) {
|
||||
gridColsClass = 'grid-cols-4';
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={cn(`grid grid-cols-${columns} gap-x-6 gap-y-3`, className)}>
|
||||
<div className={cn(`grid ${gridColsClass} gap-x-6 gap-y-3`, className)}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -24,7 +24,7 @@ function MapPage() {
|
||||
|
||||
return (
|
||||
<PageWrapper>
|
||||
<div className="flex flex-col h-[calc(100vh-4rem)]">
|
||||
<div className="flex flex-col h-[calc(100vh-7rem)] md:h-[calc(100vh-5rem)]">
|
||||
<div className="flex-1 flex flex-col">
|
||||
<NetworkMap
|
||||
fullHeight
|
||||
@@ -32,7 +32,7 @@ function MapPage() {
|
||||
onAutoZoomChange={setAutoZoomEnabled}
|
||||
/>
|
||||
|
||||
<div className="mt-2 bg-neutral-800/50 rounded-lg p-2 text-xs flex items-center justify-between effect-inset">
|
||||
<div className="mt-2 rounded-lg p-2 text-xs flex items-center justify-between effect-inset">
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className={`inline-flex items-center px-2 py-0.5 rounded ${getNodeColors(ActivityLevel.RECENT, false).textClass} ${getNodeColors(ActivityLevel.RECENT, false).background}`}>
|
||||
|
||||
Reference in New Issue
Block a user