11 changed files with 243 additions and 77 deletions
@ -0,0 +1,18 @@ |
|||||
|
import type React from "react"; |
||||
|
|
||||
|
export const Card = ({ |
||||
|
children, |
||||
|
className, |
||||
|
...rest |
||||
|
}: JSX.IntrinsicElements["div"]): JSX.Element => { |
||||
|
return ( |
||||
|
<div |
||||
|
className={`flex overflow-hidden rounded-2xl bg-white text-sm text-black shadow-md ${ |
||||
|
className ?? "" |
||||
|
}`}
|
||||
|
{...rest} |
||||
|
> |
||||
|
{children} |
||||
|
</div> |
||||
|
); |
||||
|
}; |
||||
@ -0,0 +1,16 @@ |
|||||
|
import type React from "react"; |
||||
|
|
||||
|
export const Mono = ({ |
||||
|
children, |
||||
|
className, |
||||
|
...rest |
||||
|
}: JSX.IntrinsicElements["span"]): JSX.Element => { |
||||
|
return ( |
||||
|
<span |
||||
|
className={`font-mono text-sm text-slate-500 ${className ?? ""}`} |
||||
|
{...rest} |
||||
|
> |
||||
|
{children} |
||||
|
</span> |
||||
|
); |
||||
|
}; |
||||
@ -0,0 +1,30 @@ |
|||||
|
import type React from "react"; |
||||
|
|
||||
|
import { BoltIcon } from "@heroicons/react/24/outline"; |
||||
|
|
||||
|
import { Card } from "../Card.js"; |
||||
|
|
||||
|
export interface BatteryWidgetProps { |
||||
|
batteryLevel: number; |
||||
|
voltage: number; |
||||
|
} |
||||
|
|
||||
|
export const BatteryWidget = ({ |
||||
|
batteryLevel, |
||||
|
voltage, |
||||
|
}: BatteryWidgetProps): JSX.Element => { |
||||
|
return ( |
||||
|
<Card> |
||||
|
<div className="flex w-20 bg-slate-700 p-3"> |
||||
|
<BoltIcon className="m-auto h-12 text-white" /> |
||||
|
</div> |
||||
|
<div className="w-full"> |
||||
|
<div className="flex h-8 bg-slate-100"> |
||||
|
<span className="m-auto text-lg font-medium">Power</span> |
||||
|
</div> |
||||
|
<span>{batteryLevel}</span> |
||||
|
<span>{voltage}</span> |
||||
|
</div> |
||||
|
</Card> |
||||
|
); |
||||
|
}; |
||||
@ -1,11 +1,38 @@ |
|||||
import type React from "react"; |
import type React from "react"; |
||||
|
|
||||
export interface NodeInfoWidgetProps {} |
import type { Protobuf } from "@meshtastic/meshtasticjs"; |
||||
|
|
||||
export const NodeInfoWidget = ({}: NodeInfoWidgetProps): JSX.Element => { |
import { Card } from "../Card.js"; |
||||
|
|
||||
|
export interface NodeInfoWidgetProps { |
||||
|
hardware: Protobuf.MyNodeInfo; |
||||
|
} |
||||
|
|
||||
|
export const NodeInfoWidget = ({ |
||||
|
hardware, |
||||
|
}: NodeInfoWidgetProps): JSX.Element => { |
||||
return ( |
return ( |
||||
<div className="mb-4 flex flex-col space-y-3 rounded-2xl bg-[#f9e3aa] p-6 text-sm text-black"> |
<Card className="flex-col"> |
||||
node info |
<div className="flex h-8 bg-slate-100"> |
||||
</div> |
<span className="m-auto text-lg font-medium">Information</span> |
||||
|
</div> |
||||
|
<div className="flex flex-col gap-2 p-3"> |
||||
|
<dl className="mt-2 border-b border-gray-200"> |
||||
|
<div className="flex justify-between py-1 text-sm font-medium"> |
||||
|
<dt className="text-gray-500">Firmware version</dt> |
||||
|
<dd className="cursor-pointer whitespace-nowrap text-gray-900 hover:text-orange-400 hover:underline"> |
||||
|
{hardware.firmwareVersion} |
||||
|
</dd> |
||||
|
</div> |
||||
|
</dl> |
||||
|
<div className="flex justify-between py-1 text-sm font-medium"> |
||||
|
<dt className="text-gray-500">Bitrate</dt> |
||||
|
<dd className="whitespace-nowrap text-gray-900"> |
||||
|
{hardware.bitrate.toFixed(2)} |
||||
|
<span className="font-mono text-sm text-slate-500 ">bps</span> |
||||
|
</dd> |
||||
|
</div> |
||||
|
</div> |
||||
|
</Card> |
||||
); |
); |
||||
}; |
}; |
||||
|
|||||
@ -1,11 +1,55 @@ |
|||||
import type React from "react"; |
import type React from "react"; |
||||
|
|
||||
export interface PeersWidgetProps {} |
import { base16 } from "rfc4648"; |
||||
|
|
||||
export const PeersWidget = ({}: PeersWidgetProps): JSX.Element => { |
import { Hashicon } from "@emeraldpay/hashicon-react"; |
||||
|
import { EllipsisHorizontalCircleIcon } from "@heroicons/react/24/outline"; |
||||
|
import type { Protobuf } from "@meshtastic/meshtasticjs"; |
||||
|
|
||||
|
import { Card } from "../Card.js"; |
||||
|
import { IconButton } from "../IconButton.js"; |
||||
|
import { Mono } from "../Mono.js"; |
||||
|
|
||||
|
export interface PeersWidgetProps { |
||||
|
peers: Protobuf.NodeInfo[]; |
||||
|
} |
||||
|
|
||||
|
export const PeersWidget = ({ peers }: PeersWidgetProps): JSX.Element => { |
||||
return ( |
return ( |
||||
<div className="mb-4 flex flex-col space-y-3 rounded-2xl bg-[#f9e3aa] p-6 text-sm text-black"> |
<Card> |
||||
Peers |
<div className="flex w-full flex-col gap-1"> |
||||
</div> |
<div className="flex h-8 bg-slate-100"> |
||||
|
<span className="m-auto text-lg font-medium">Peers</span> |
||||
|
</div> |
||||
|
<div className="p-4"> |
||||
|
{peers.map((peer) => ( |
||||
|
<div |
||||
|
className="flex gap-2 rounded-md p-2 hover:bg-slate-100" |
||||
|
key={peer.num} |
||||
|
> |
||||
|
<span className="my-auto shrink-0"> |
||||
|
<Hashicon value={peer.num.toString()} size={28} /> |
||||
|
</span> |
||||
|
<div className="flex flex-col"> |
||||
|
<span className="font-medium">{peer.user?.longName}</span> |
||||
|
<Mono> |
||||
|
{base16 |
||||
|
.stringify(peer.user?.macaddr ?? []) |
||||
|
.match(/.{1,2}/g) |
||||
|
?.join(":") ?? ""} |
||||
|
</Mono> |
||||
|
</div> |
||||
|
<div className="my-auto ml-auto"> |
||||
|
<IconButton |
||||
|
variant="secondary" |
||||
|
size="sm" |
||||
|
icon={<EllipsisHorizontalCircleIcon className="h-4" />} |
||||
|
/> |
||||
|
</div> |
||||
|
</div> |
||||
|
))} |
||||
|
</div> |
||||
|
</div> |
||||
|
</Card> |
||||
); |
); |
||||
}; |
}; |
||||
|
|||||
@ -1,11 +1,25 @@ |
|||||
import type React from "react"; |
import type React from "react"; |
||||
|
|
||||
export interface PositionWidgetProps {} |
import { MapPinIcon } from "@heroicons/react/24/outline"; |
||||
|
|
||||
export const PositionWidget = ({}: PositionWidgetProps): JSX.Element => { |
import { Card } from "../Card.js"; |
||||
|
|
||||
|
export interface PositionWidgetProps { |
||||
|
grid: string; |
||||
|
} |
||||
|
|
||||
|
export const PositionWidget = ({ grid }: PositionWidgetProps): JSX.Element => { |
||||
return ( |
return ( |
||||
<div className="mb-4 flex flex-col space-y-3 rounded-2xl bg-[#f9e3aa] p-6 text-sm text-black"> |
<Card> |
||||
position |
<div className="flex w-20 bg-teal-600 p-3"> |
||||
</div> |
<MapPinIcon className="m-auto h-12 text-white" /> |
||||
|
</div> |
||||
|
<div className="flex w-full flex-col"> |
||||
|
<div className="flex h-8 bg-slate-100"> |
||||
|
<span className="m-auto text-lg font-medium">Position</span> |
||||
|
</div> |
||||
|
<span className="m-auto text-lg">{grid}</span> |
||||
|
</div> |
||||
|
</Card> |
||||
); |
); |
||||
}; |
}; |
||||
|
|||||
Loading…
Reference in new issue