Add disclosure to widgets
This commit is contained in:
@@ -7,7 +7,7 @@ export const Card = ({
|
|||||||
}: JSX.IntrinsicElements["div"]): JSX.Element => {
|
}: JSX.IntrinsicElements["div"]): JSX.Element => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`flex overflow-hidden rounded-2xl bg-white text-sm text-black shadow-md ${
|
className={`flex overflow-hidden rounded-md bg-white text-sm text-black shadow-md ${
|
||||||
className ?? ""
|
className ?? ""
|
||||||
}`}
|
}`}
|
||||||
{...rest}
|
{...rest}
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import type React from "react";
|
||||||
|
|
||||||
|
import { Disclosure } from "@headlessui/react";
|
||||||
|
import { ChevronDownIcon, ChevronUpIcon } from "@heroicons/react/24/outline";
|
||||||
|
|
||||||
|
export interface DropdownProps {
|
||||||
|
title: string;
|
||||||
|
icon: JSX.Element;
|
||||||
|
children: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Dropdown = ({
|
||||||
|
title,
|
||||||
|
icon,
|
||||||
|
children,
|
||||||
|
}: DropdownProps): JSX.Element => {
|
||||||
|
return (
|
||||||
|
<Disclosure defaultOpen>
|
||||||
|
{({ open }) => (
|
||||||
|
<>
|
||||||
|
<Disclosure.Button className="flex h-8 justify-between bg-slate-100 px-2 hover:bg-slate-200">
|
||||||
|
<div className="my-auto flex gap-2 text-slate-700">
|
||||||
|
<div className="my-auto">{icon}</div>
|
||||||
|
<span className="text-lg font-medium">{title}</span>
|
||||||
|
</div>
|
||||||
|
<div className="my-auto text-slate-600">
|
||||||
|
{open ? (
|
||||||
|
<ChevronUpIcon className="h-5" />
|
||||||
|
) : (
|
||||||
|
<ChevronDownIcon className="h-5" />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</Disclosure.Button>
|
||||||
|
<Disclosure.Panel>{children}</Disclosure.Panel>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Disclosure>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -1,8 +1,10 @@
|
|||||||
import type React from "react";
|
import type React from "react";
|
||||||
|
|
||||||
import { BoltIcon } from "@heroicons/react/24/outline";
|
import { Battery100Icon, BoltIcon } from "@heroicons/react/24/outline";
|
||||||
|
|
||||||
import { Card } from "../Card.js";
|
import { Card } from "../Card.js";
|
||||||
|
import { Dropdown } from "../Dropdown.js";
|
||||||
|
import { Mono } from "../Mono.js";
|
||||||
|
|
||||||
export interface BatteryWidgetProps {
|
export interface BatteryWidgetProps {
|
||||||
batteryLevel: number;
|
batteryLevel: number;
|
||||||
@@ -14,17 +16,22 @@ export const BatteryWidget = ({
|
|||||||
voltage,
|
voltage,
|
||||||
}: BatteryWidgetProps): JSX.Element => {
|
}: BatteryWidgetProps): JSX.Element => {
|
||||||
return (
|
return (
|
||||||
<Card>
|
<Card className="flex-col">
|
||||||
<div className="flex w-20 bg-slate-700 p-3">
|
<Dropdown title="Position" icon={<BoltIcon className="h-4" />}>
|
||||||
<BoltIcon className="m-auto h-12 text-white" />
|
<div className="flex">
|
||||||
</div>
|
<div className="flex w-20 bg-slate-700 p-3">
|
||||||
<div className="w-full">
|
<Battery100Icon className="m-auto h-12 text-white" />
|
||||||
<div className="flex h-8 bg-slate-100">
|
</div>
|
||||||
<span className="m-auto text-lg font-medium">Power</span>
|
<span className="m-auto text-lg">
|
||||||
|
{batteryLevel}
|
||||||
|
<Mono>%</Mono>
|
||||||
|
</span>
|
||||||
|
<span className="m-auto text-lg">
|
||||||
|
{voltage}
|
||||||
|
<Mono>v</Mono>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<span>{batteryLevel}</span>
|
</Dropdown>
|
||||||
<span>{voltage}</span>
|
|
||||||
</div>
|
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
import React, { useEffect } from "react";
|
import React, { useEffect } from "react";
|
||||||
|
|
||||||
import { useDevice } from "@core/providers/useDevice.js";
|
import { useDevice } from "@core/providers/useDevice.js";
|
||||||
|
import { AdjustmentsHorizontalIcon } from "@heroicons/react/24/outline";
|
||||||
|
|
||||||
import { Button } from "../Button.js";
|
import { Button } from "../Button.js";
|
||||||
import { Card } from "../Card.js";
|
import { Card } from "../Card.js";
|
||||||
|
import { Dropdown } from "../Dropdown.js";
|
||||||
|
|
||||||
export const ConfiguringWidget = (): JSX.Element => {
|
export const ConfiguringWidget = (): JSX.Element => {
|
||||||
const {
|
const {
|
||||||
@@ -36,43 +38,45 @@ export const ConfiguringWidget = (): JSX.Element => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Card className="flex-col">
|
<Card className="flex-col">
|
||||||
<div className="flex h-8 bg-slate-100">
|
<Dropdown
|
||||||
<span className="m-auto text-lg font-medium">Power</span>
|
title="Config Status"
|
||||||
</div>
|
icon={<AdjustmentsHorizontalIcon className="h-4" />}
|
||||||
<div className="flex flex-col gap-2 p-3">
|
>
|
||||||
<ol className="flex flex-col gap-3 overflow-hidden">
|
<div className="flex flex-col gap-2 p-3">
|
||||||
<StatusIndicator
|
<ol className="flex flex-col gap-3 overflow-hidden">
|
||||||
title="Device Info"
|
<StatusIndicator
|
||||||
current={hardware.myNodeNum ? 1 : 0}
|
title="Device Info"
|
||||||
total={0}
|
current={hardware.myNodeNum ? 1 : 0}
|
||||||
/>
|
total={0}
|
||||||
<StatusIndicator title="Peers" current={nodes.length} total={0} />
|
/>
|
||||||
<StatusIndicator
|
<StatusIndicator title="Peers" current={nodes.length} total={0} />
|
||||||
title="Device Config"
|
<StatusIndicator
|
||||||
current={Object.keys(config).length - 1}
|
title="Device Config"
|
||||||
total={6}
|
current={Object.keys(config).length - 1}
|
||||||
/>
|
total={6}
|
||||||
<StatusIndicator
|
/>
|
||||||
title="Module Config"
|
<StatusIndicator
|
||||||
current={Object.keys(moduleConfig).length - 1}
|
title="Module Config"
|
||||||
total={6}
|
current={Object.keys(moduleConfig).length - 1}
|
||||||
/>
|
total={6}
|
||||||
<StatusIndicator
|
/>
|
||||||
title="Channels"
|
<StatusIndicator
|
||||||
current={channels.length}
|
title="Channels"
|
||||||
total={hardware.maxChannels ?? 0}
|
current={channels.length}
|
||||||
/>
|
total={hardware.maxChannels ?? 0}
|
||||||
</ol>
|
/>
|
||||||
<Button
|
</ol>
|
||||||
variant="secondary"
|
<Button
|
||||||
size="sm"
|
variant="secondary"
|
||||||
onClick={() => {
|
size="sm"
|
||||||
void connection?.configure();
|
onClick={() => {
|
||||||
}}
|
void connection?.configure();
|
||||||
>
|
}}
|
||||||
Retry
|
>
|
||||||
</Button>
|
Retry
|
||||||
</div>
|
</Button>
|
||||||
|
</div>
|
||||||
|
</Dropdown>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
import type React from "react";
|
import type React from "react";
|
||||||
|
|
||||||
|
import { InformationCircleIcon } from "@heroicons/react/24/outline";
|
||||||
import type { Protobuf } from "@meshtastic/meshtasticjs";
|
import type { Protobuf } from "@meshtastic/meshtasticjs";
|
||||||
|
|
||||||
import { Card } from "../Card.js";
|
import { Card } from "../Card.js";
|
||||||
|
import { Dropdown } from "../Dropdown.js";
|
||||||
|
|
||||||
export interface NodeInfoWidgetProps {
|
export interface NodeInfoWidgetProps {
|
||||||
hardware: Protobuf.MyNodeInfo;
|
hardware: Protobuf.MyNodeInfo;
|
||||||
@@ -13,26 +15,28 @@ export const NodeInfoWidget = ({
|
|||||||
}: NodeInfoWidgetProps): JSX.Element => {
|
}: NodeInfoWidgetProps): JSX.Element => {
|
||||||
return (
|
return (
|
||||||
<Card className="flex-col">
|
<Card className="flex-col">
|
||||||
<div className="flex h-8 bg-slate-100">
|
<Dropdown
|
||||||
<span className="m-auto text-lg font-medium">Information</span>
|
title="Information"
|
||||||
</div>
|
icon={<InformationCircleIcon className="h-4" />}
|
||||||
<div className="flex flex-col gap-2 p-3">
|
>
|
||||||
<dl className="mt-2 border-b border-gray-200">
|
<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">
|
<div className="flex justify-between py-1 text-sm font-medium">
|
||||||
<dt className="text-gray-500">Firmware version</dt>
|
<dt className="text-gray-500">Bitrate</dt>
|
||||||
<dd className="cursor-pointer whitespace-nowrap text-gray-900 hover:text-orange-400 hover:underline">
|
<dd className="whitespace-nowrap text-gray-900">
|
||||||
{hardware.firmwareVersion}
|
{hardware.bitrate.toFixed(2)}
|
||||||
|
<span className="font-mono text-sm text-slate-500 ">bps</span>
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</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>
|
||||||
</div>
|
</Dropdown>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,10 +3,14 @@ import type React from "react";
|
|||||||
import { base16 } from "rfc4648";
|
import { base16 } from "rfc4648";
|
||||||
|
|
||||||
import { Hashicon } from "@emeraldpay/hashicon-react";
|
import { Hashicon } from "@emeraldpay/hashicon-react";
|
||||||
import { EllipsisHorizontalCircleIcon } from "@heroicons/react/24/outline";
|
import {
|
||||||
|
EllipsisHorizontalCircleIcon,
|
||||||
|
UserGroupIcon,
|
||||||
|
} from "@heroicons/react/24/outline";
|
||||||
import type { Protobuf } from "@meshtastic/meshtasticjs";
|
import type { Protobuf } from "@meshtastic/meshtasticjs";
|
||||||
|
|
||||||
import { Card } from "../Card.js";
|
import { Card } from "../Card.js";
|
||||||
|
import { Dropdown } from "../Dropdown.js";
|
||||||
import { IconButton } from "../IconButton.js";
|
import { IconButton } from "../IconButton.js";
|
||||||
import { Mono } from "../Mono.js";
|
import { Mono } from "../Mono.js";
|
||||||
|
|
||||||
@@ -16,12 +20,9 @@ export interface PeersWidgetProps {
|
|||||||
|
|
||||||
export const PeersWidget = ({ peers }: PeersWidgetProps): JSX.Element => {
|
export const PeersWidget = ({ peers }: PeersWidgetProps): JSX.Element => {
|
||||||
return (
|
return (
|
||||||
<Card>
|
<Card className="flex-col">
|
||||||
<div className="flex w-full flex-col gap-1">
|
<Dropdown title="Peers" icon={<UserGroupIcon className="h-4" />}>
|
||||||
<div className="flex h-8 bg-slate-100">
|
<div className="p-3">
|
||||||
<span className="m-auto text-lg font-medium">Peers</span>
|
|
||||||
</div>
|
|
||||||
<div className="p-4">
|
|
||||||
{peers.map((peer) => (
|
{peers.map((peer) => (
|
||||||
<div
|
<div
|
||||||
className="flex gap-2 rounded-md p-2 hover:bg-slate-100"
|
className="flex gap-2 rounded-md p-2 hover:bg-slate-100"
|
||||||
@@ -49,7 +50,7 @@ export const PeersWidget = ({ peers }: PeersWidgetProps): JSX.Element => {
|
|||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Dropdown>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import type React from "react";
|
|||||||
import { MapPinIcon } from "@heroicons/react/24/outline";
|
import { MapPinIcon } from "@heroicons/react/24/outline";
|
||||||
|
|
||||||
import { Card } from "../Card.js";
|
import { Card } from "../Card.js";
|
||||||
|
import { Dropdown } from "../Dropdown.js";
|
||||||
|
|
||||||
export interface PositionWidgetProps {
|
export interface PositionWidgetProps {
|
||||||
grid: string;
|
grid: string;
|
||||||
@@ -10,16 +11,15 @@ export interface PositionWidgetProps {
|
|||||||
|
|
||||||
export const PositionWidget = ({ grid }: PositionWidgetProps): JSX.Element => {
|
export const PositionWidget = ({ grid }: PositionWidgetProps): JSX.Element => {
|
||||||
return (
|
return (
|
||||||
<Card>
|
<Card className="flex-col">
|
||||||
<div className="flex w-20 bg-teal-600 p-3">
|
<Dropdown title="Position" icon={<MapPinIcon className="h-4" />}>
|
||||||
<MapPinIcon className="m-auto h-12 text-white" />
|
<div className="flex">
|
||||||
</div>
|
<div className="flex w-20 bg-teal-600 p-3">
|
||||||
<div className="flex w-full flex-col">
|
<MapPinIcon className="m-auto h-12 text-white" />
|
||||||
<div className="flex h-8 bg-slate-100">
|
</div>
|
||||||
<span className="m-auto text-lg font-medium">Position</span>
|
<span className="m-auto text-lg">{grid}</span>
|
||||||
</div>
|
</div>
|
||||||
<span className="m-auto text-lg">{grid}</span>
|
</Dropdown>
|
||||||
</div>
|
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user