Minor style fixes
This commit is contained in:
+126
-109
@@ -46,26 +46,34 @@ const CollapseToggleButton = () => {
|
|||||||
aria-label={buttonLabel}
|
aria-label={buttonLabel}
|
||||||
onClick={toggleSidebar}
|
onClick={toggleSidebar}
|
||||||
className={cn(
|
className={cn(
|
||||||
'absolute top-20 right-0 z-10 p-0.5 rounded-full transform translate-x-1/2',
|
"absolute top-20 right-0 z-10 p-0.5 rounded-full transform translate-x-1/2",
|
||||||
'transition-colors duration-300 ease-in-out',
|
"transition-colors duration-300 ease-in-out",
|
||||||
'border border-slate-300 dark:border-slate-200',
|
"border border-slate-300 dark:border-slate-200",
|
||||||
'text-slate-500 dark:text-slate-200 hover:text-slate-400 dark:hover:text-slate-400',
|
"text-slate-500 dark:text-slate-200 hover:text-slate-400 dark:hover:text-slate-400",
|
||||||
'focus:outline-none focus:ring-2 focus:ring-accent transition-transform'
|
"focus:outline-none focus:ring-2 focus:ring-accent transition-transform bg-background-primary",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<CircleChevronLeft
|
<CircleChevronLeft
|
||||||
size={24}
|
size={24}
|
||||||
className={cn(
|
className={cn(
|
||||||
'transition-transform duration-300 ease-in-out',
|
"transition-transform duration-300 ease-in-out",
|
||||||
isCollapsed && 'rotate-180'
|
isCollapsed && "rotate-180",
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export const Sidebar = ({ children }: SidebarProps) => {
|
export const Sidebar = ({ children }: SidebarProps) => {
|
||||||
const { hardware, getNode, getNodesLength, metadata, activePage, setActivePage, setDialogOpen } = useDevice();
|
const {
|
||||||
|
hardware,
|
||||||
|
getNode,
|
||||||
|
getNodesLength,
|
||||||
|
metadata,
|
||||||
|
activePage,
|
||||||
|
setActivePage,
|
||||||
|
setDialogOpen,
|
||||||
|
} = useDevice();
|
||||||
const { setCommandPaletteOpen } = useAppStore();
|
const { setCommandPaletteOpen } = useAppStore();
|
||||||
const myNode = getNode(hardware.myNodeNum);
|
const myNode = getNode(hardware.myNodeNum);
|
||||||
const { isCollapsed } = useSidebar();
|
const { isCollapsed } = useSidebar();
|
||||||
@@ -86,19 +94,18 @@ export const Sidebar = ({ children }: SidebarProps) => {
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
'relative border-slate-300 dark:border-slate-700',
|
"relative border-slate-300 dark:border-slate-700",
|
||||||
'transition-all duration-300 ease-in-out flex-shrink-0',
|
"transition-all duration-300 ease-in-out flex-shrink-0",
|
||||||
isCollapsed ? 'w-24' : 'w-46 lg:w-64'
|
isCollapsed ? "w-24" : "w-46 lg:w-64",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<CollapseToggleButton />
|
<CollapseToggleButton />
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
'h-14 flex mt-2 gap-2 items-center flex-shrink-0 transition-all duration-300 ease-in-out',
|
"h-14 flex mt-2 gap-2 items-center flex-shrink-0 transition-all duration-300 ease-in-out",
|
||||||
'border-b-[0.5px] border-slate-300 dark:border-slate-700',
|
"border-b-[0.5px] border-slate-300 dark:border-slate-700",
|
||||||
isCollapsed && 'justify-center px-0'
|
isCollapsed && "justify-center px-0",
|
||||||
|
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
@@ -108,11 +115,11 @@ export const Sidebar = ({ children }: SidebarProps) => {
|
|||||||
/>
|
/>
|
||||||
<h2
|
<h2
|
||||||
className={cn(
|
className={cn(
|
||||||
'text-xl font-semibold text-gray-800 dark:text-gray-100 whitespace-nowrap',
|
"text-xl font-semibold text-gray-800 dark:text-gray-100 whitespace-nowrap",
|
||||||
'transition-all duration-300 ease-in-out',
|
"transition-all duration-300 ease-in-out",
|
||||||
isCollapsed
|
isCollapsed
|
||||||
? 'opacity-0 max-w-0 invisible ml-0'
|
? "opacity-0 max-w-0 invisible ml-0"
|
||||||
: 'opacity-100 max-w-xs visible ml-2'
|
: "opacity-100 max-w-xs visible ml-2",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
Meshtastic
|
Meshtastic
|
||||||
@@ -136,106 +143,116 @@ export const Sidebar = ({ children }: SidebarProps) => {
|
|||||||
))}
|
))}
|
||||||
</SidebarSection>
|
</SidebarSection>
|
||||||
|
|
||||||
<div className={cn(
|
<div
|
||||||
'flex-1 min-h-0',
|
className={cn(
|
||||||
isCollapsed && 'overflow-hidden'
|
"flex-1 min-h-0",
|
||||||
)}
|
isCollapsed && "overflow-hidden",
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="pt-4 border-t-[0.5px] bg-background-primary border-slate-300 dark:border-slate-700 flex-shrink-0">
|
<div className="pt-4 border-t-[0.5px] bg-background-primary border-slate-300 dark:border-slate-700 flex-shrink-0">
|
||||||
{myNode === undefined ? (
|
{myNode === undefined
|
||||||
<div className="flex flex-col items-center justify-center py-6">
|
? (
|
||||||
<Spinner />
|
<div className="flex flex-col items-center justify-center py-6">
|
||||||
<Subtle
|
<Spinner />
|
||||||
className={cn(
|
<Subtle
|
||||||
'mt-4 transition-opacity duration-300',
|
|
||||||
isCollapsed ? 'opacity-0 invisible' : 'opacity-100 visible'
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
Loading...
|
|
||||||
</Subtle>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<div
|
|
||||||
className={cn(
|
|
||||||
'flex place-items-center gap-2',
|
|
||||||
isCollapsed && 'justify-center'
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<Avatar
|
|
||||||
text={myNode.user?.shortName ?? myNode.num.toString()}
|
|
||||||
className={cn("flex-shrink-0 ml-2",
|
|
||||||
isCollapsed && "ml-0",
|
|
||||||
)}
|
|
||||||
size="sm"
|
|
||||||
/>
|
|
||||||
<p
|
|
||||||
className={cn(
|
className={cn(
|
||||||
'max-w-[20ch] text-wrap text-sm font-medium',
|
"mt-4 transition-opacity duration-300",
|
||||||
'transition-all duration-300 ease-in-out overflow-hidden',
|
isCollapsed ? "opacity-0 invisible" : "opacity-100 visible",
|
||||||
isCollapsed
|
|
||||||
? 'opacity-0 max-w-0 invisible'
|
|
||||||
: 'opacity-100 max-w-full visible'
|
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{myNode.user?.longName}
|
Loading...
|
||||||
</p>
|
</Subtle>
|
||||||
</div>
|
</div>
|
||||||
|
)
|
||||||
<div
|
: (
|
||||||
className={cn(
|
<>
|
||||||
'flex flex-col gap-0.5 ml-2 mt-2',
|
<div
|
||||||
'transition-all duration-300 ease-in-out',
|
className={cn(
|
||||||
isCollapsed
|
"flex place-items-center gap-2",
|
||||||
? 'opacity-0 max-w-0 h-0 invisible'
|
isCollapsed && "justify-center",
|
||||||
: 'opacity-100 max-w-xs h-auto visible'
|
)}
|
||||||
)}
|
|
||||||
>
|
|
||||||
<div className="inline-flex gap-2">
|
|
||||||
<BatteryStatus deviceMetrics={myNode.deviceMetrics} />
|
|
||||||
</div>
|
|
||||||
<div className="inline-flex gap-2">
|
|
||||||
<ZapIcon size={18} className="text-gray-500 dark:text-gray-400 w-4 flex-shrink-0" />
|
|
||||||
<Subtle>{myNode.deviceMetrics?.voltage?.toPrecision(3) ?? "UNK"} volts</Subtle>
|
|
||||||
</div>
|
|
||||||
<div className="inline-flex gap-2">
|
|
||||||
<CpuIcon size={18} className="text-gray-500 dark:text-gray-400 w-4 flex-shrink-0" />
|
|
||||||
<Subtle>v{myMetadata?.firmwareVersion ?? "UNK"}</Subtle>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className={cn(
|
|
||||||
'flex items-center flex-shrink-0 ml-2',
|
|
||||||
'transition-all duration-300 ease-in-out',
|
|
||||||
isCollapsed
|
|
||||||
? 'opacity-0 max-w-0 invisible pointer-events-none'
|
|
||||||
: 'opacity-100 max-w-xs visible'
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
aria-label="Edit device name"
|
|
||||||
className="p-1 rounded transition-colors hover:text-accent"
|
|
||||||
onClick={() => setDialogOpen("deviceName", true)}
|
|
||||||
>
|
>
|
||||||
<PenLine size={22} />
|
<Avatar
|
||||||
</button>
|
text={myNode.user?.shortName ?? myNode.num.toString()}
|
||||||
<ThemeSwitcher />
|
className={cn("flex-shrink-0 ml-2", isCollapsed && "ml-0")}
|
||||||
<button
|
size="sm"
|
||||||
type="button"
|
/>
|
||||||
className="transition-all hover:text-accent"
|
<p
|
||||||
onClick={() => setCommandPaletteOpen(true)}
|
className={cn(
|
||||||
>
|
"max-w-[20ch] text-wrap text-sm font-medium",
|
||||||
<SearchIcon />
|
"transition-all duration-300 ease-in-out overflow-hidden",
|
||||||
</button>
|
isCollapsed
|
||||||
</div>
|
? "opacity-0 max-w-0 invisible"
|
||||||
|
: "opacity-100 max-w-full visible",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{myNode.user?.longName}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
</>
|
<div
|
||||||
)}
|
className={cn(
|
||||||
|
"flex flex-col gap-0.5 ml-2 mt-2",
|
||||||
|
"transition-all duration-300 ease-in-out",
|
||||||
|
isCollapsed
|
||||||
|
? "opacity-0 max-w-0 h-0 invisible"
|
||||||
|
: "opacity-100 max-w-xs h-auto visible",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className="inline-flex gap-2">
|
||||||
|
<BatteryStatus deviceMetrics={myNode.deviceMetrics} />
|
||||||
|
</div>
|
||||||
|
<div className="inline-flex gap-2">
|
||||||
|
<ZapIcon
|
||||||
|
size={18}
|
||||||
|
className="text-gray-500 dark:text-gray-400 w-4 flex-shrink-0"
|
||||||
|
/>
|
||||||
|
<Subtle>
|
||||||
|
{myNode.deviceMetrics?.voltage?.toPrecision(3) ?? "UNK"}
|
||||||
|
{" "}
|
||||||
|
volts
|
||||||
|
</Subtle>
|
||||||
|
</div>
|
||||||
|
<div className="inline-flex gap-2">
|
||||||
|
<CpuIcon
|
||||||
|
size={18}
|
||||||
|
className="text-gray-500 dark:text-gray-400 w-4 flex-shrink-0"
|
||||||
|
/>
|
||||||
|
<Subtle>v{myMetadata?.firmwareVersion ?? "UNK"}</Subtle>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"flex items-center flex-shrink-0 ml-2",
|
||||||
|
"transition-all duration-300 ease-in-out",
|
||||||
|
isCollapsed
|
||||||
|
? "opacity-0 max-w-0 invisible pointer-events-none"
|
||||||
|
: "opacity-100 max-w-xs visible",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
aria-label="Edit device name"
|
||||||
|
className="p-1 rounded transition-colors hover:text-accent"
|
||||||
|
onClick={() => setDialogOpen("deviceName", true)}
|
||||||
|
>
|
||||||
|
<PenLine size={22} />
|
||||||
|
</button>
|
||||||
|
<ThemeSwitcher />
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="transition-all hover:text-accent"
|
||||||
|
onClick={() => setCommandPaletteOpen(true)}
|
||||||
|
>
|
||||||
|
<SearchIcon />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useState, useEffect, useId } from "react";
|
import { useEffect, useId, useState } from "react";
|
||||||
import { Check } from "lucide-react";
|
import { Check } from "lucide-react";
|
||||||
import { Label } from "@components/UI/Label.tsx";
|
import { Label } from "@components/UI/Label.tsx";
|
||||||
import { cn } from "@core/utils/cn.ts";
|
import { cn } from "@core/utils/cn.ts";
|
||||||
@@ -65,13 +65,15 @@ export function Checkbox({
|
|||||||
role="presentation"
|
role="presentation"
|
||||||
className={cn(
|
className={cn(
|
||||||
"w-6 h-6 border-2 border-gray-500 rounded-md flex items-center justify-center",
|
"w-6 h-6 border-2 border-gray-500 rounded-md flex items-center justify-center",
|
||||||
disabled ? "opacity-50 cursor-not-allowed" : "cursor-pointer focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2",
|
disabled
|
||||||
isChecked ? "" : ""
|
? "opacity-50 cursor-not-allowed"
|
||||||
|
: "cursor-pointer focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2",
|
||||||
|
isChecked ? "" : "",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{isChecked && (
|
{isChecked && (
|
||||||
<div className="animate-fade-in scale-100 opacity-100">
|
<div className="animate-fade-in scale-100 opacity-100">
|
||||||
<Check className="w-4 h-4 text-slate-900 dark:text-slate-200" />
|
<Check className="w-4 h-4" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -85,7 +87,7 @@ export function Checkbox({
|
|||||||
className={cn(
|
className={cn(
|
||||||
"text-gray-900 dark:text-gray-900",
|
"text-gray-900 dark:text-gray-900",
|
||||||
disabled ? "opacity-50 cursor-not-allowed" : "cursor-pointer",
|
disabled ? "opacity-50 cursor-not-allowed" : "cursor-pointer",
|
||||||
labelClassName
|
labelClassName,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
@@ -95,4 +97,4 @@ export function Checkbox({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,27 +16,31 @@ export const SidebarSection = ({
|
|||||||
}: SidebarSectionProps) => {
|
}: SidebarSectionProps) => {
|
||||||
const { isCollapsed } = useSidebar();
|
const { isCollapsed } = useSidebar();
|
||||||
return (
|
return (
|
||||||
<div className={cn(
|
<div
|
||||||
"py-2",
|
className={cn(
|
||||||
isCollapsed ? 'px-0' : 'px-4',
|
"py-2",
|
||||||
className,
|
isCollapsed ? "px-0" : "px-4",
|
||||||
)}>
|
className,
|
||||||
|
)}
|
||||||
<Heading as="h3" className={cn(
|
>
|
||||||
'mb-2',
|
<Heading
|
||||||
'uppercase tracking-wider text-md',
|
as="h3"
|
||||||
'transition-all duration-300 ease-in-out',
|
className={cn(
|
||||||
'whitespace-nowrap overflow-hidden',
|
"mb-2",
|
||||||
isCollapsed
|
"uppercase tracking-wider text-md",
|
||||||
? 'opacity-0 max-w-0 h-0 invisible px-0 mb-0'
|
"transition-all duration-300 ease-in-out",
|
||||||
: 'opacity-100 max-w-xs h-auto visible px-1 mb-1'
|
"whitespace-nowrap overflow-hidden",
|
||||||
)}>
|
isCollapsed
|
||||||
|
? "opacity-0 max-w-0 h-0 invisible px-0 mb-0"
|
||||||
|
: "opacity-100 max-w-xs h-auto visible px-1 mb-1",
|
||||||
|
)}
|
||||||
|
>
|
||||||
{label}
|
{label}
|
||||||
</Heading>
|
</Heading>
|
||||||
|
|
||||||
<div className="space-y-0.5">
|
<div className="space-y-1">
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user