Add badge indicator on layers icon. (#894)
* feat: indicate number of layers enabled. * Update packages/web/src/components/PageComponents/Map/Tools/MapLayerTool.tsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * fix: reduced code duplication in layers component * fixed unread message bubble --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -6,7 +6,7 @@ import {
|
|||||||
} from "@components/UI/Popover.tsx";
|
} from "@components/UI/Popover.tsx";
|
||||||
import { cn } from "@core/utils/cn.ts";
|
import { cn } from "@core/utils/cn.ts";
|
||||||
import { LayersIcon } from "lucide-react";
|
import { LayersIcon } from "lucide-react";
|
||||||
import type { ReactNode } from "react";
|
import { type ReactNode, useMemo } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
export interface VisibilityState {
|
export interface VisibilityState {
|
||||||
@@ -62,13 +62,36 @@ export function MapLayerTool({
|
|||||||
}: MapLayerToolProps): ReactNode {
|
}: MapLayerToolProps): ReactNode {
|
||||||
const { t } = useTranslation("map");
|
const { t } = useTranslation("map");
|
||||||
|
|
||||||
|
const enabledCount = useMemo(() => {
|
||||||
|
return Object.values(visibilityState).filter(Boolean).length;
|
||||||
|
}, [visibilityState]);
|
||||||
|
|
||||||
|
const handleCheckboxChange = (key: keyof VisibilityState) => {
|
||||||
|
setVisibilityState({
|
||||||
|
...visibilityState,
|
||||||
|
[key]: !visibilityState[key],
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const layers = useMemo(
|
||||||
|
() => [
|
||||||
|
{ key: "nodeMarkers", label: t("layerTool.nodeMarkers") },
|
||||||
|
{ key: "waypoints", label: t("layerTool.waypoints") },
|
||||||
|
{ key: "directNeighbors", label: t("layerTool.directNeighbors") },
|
||||||
|
{ key: "remoteNeighbors", label: t("layerTool.remoteNeighbors") },
|
||||||
|
{ key: "positionPrecision", label: t("layerTool.positionPrecision") },
|
||||||
|
// { key: "traceroutes", label: t("layerTool.traceroutes") },
|
||||||
|
],
|
||||||
|
[t],
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Popover>
|
<Popover>
|
||||||
<PopoverTrigger asChild>
|
<PopoverTrigger asChild>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className={cn(
|
className={cn(
|
||||||
"rounded align-center",
|
"relative rounded align-center",
|
||||||
"w-[29px] px-1 py-1 shadow-l outline-[2px] outline-stone-600/20",
|
"w-[29px] px-1 py-1 shadow-l outline-[2px] outline-stone-600/20",
|
||||||
"bg-stone-50 hover:bg-stone-200 dark:bg-stone-200 dark:hover:bg-stone-300 ",
|
"bg-stone-50 hover:bg-stone-200 dark:bg-stone-200 dark:hover:bg-stone-300 ",
|
||||||
"text-slate-600 hover:text-slate-700 active:bg-slate-300",
|
"text-slate-600 hover:text-slate-700 active:bg-slate-300",
|
||||||
@@ -77,6 +100,21 @@ export function MapLayerTool({
|
|||||||
aria-label={t("mapMenu.layersAria")}
|
aria-label={t("mapMenu.layersAria")}
|
||||||
>
|
>
|
||||||
<LayersIcon className="w-[21px]" />
|
<LayersIcon className="w-[21px]" />
|
||||||
|
{enabledCount > 0 && (
|
||||||
|
<span
|
||||||
|
className={cn(
|
||||||
|
"absolute -bottom-2 -right-2",
|
||||||
|
"min-w-4 h-4 px-[3px]",
|
||||||
|
"rounded-full text-[10px] leading-4",
|
||||||
|
"bg-blue-500 text-white",
|
||||||
|
"flex items-center justify-center",
|
||||||
|
"ring-2 ring-white dark:ring-stone-200",
|
||||||
|
)}
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
{enabledCount}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</button>
|
</button>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<PopoverContent
|
<PopoverContent
|
||||||
@@ -85,50 +123,14 @@ export function MapLayerTool({
|
|||||||
align="end"
|
align="end"
|
||||||
sideOffset={7}
|
sideOffset={7}
|
||||||
>
|
>
|
||||||
|
{layers.map(({ key, label }) => (
|
||||||
<CheckboxItem
|
<CheckboxItem
|
||||||
label={t("layerTool.nodeMarkers")}
|
key={key}
|
||||||
checked={visibilityState.nodeMarkers}
|
label={label}
|
||||||
onChange={(checked) => {
|
checked={visibilityState[key as keyof VisibilityState]}
|
||||||
setVisibilityState({ ...visibilityState, nodeMarkers: checked });
|
onChange={() => handleCheckboxChange(key as keyof VisibilityState)}
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<CheckboxItem
|
|
||||||
label={t("layerTool.waypoints")}
|
|
||||||
checked={visibilityState.waypoints}
|
|
||||||
onChange={(checked) => {
|
|
||||||
setVisibilityState({ ...visibilityState, waypoints: checked });
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<CheckboxItem
|
|
||||||
label={t("layerTool.directNeighbors")}
|
|
||||||
checked={visibilityState.directNeighbors}
|
|
||||||
onChange={(checked) => {
|
|
||||||
setVisibilityState({
|
|
||||||
...visibilityState,
|
|
||||||
directNeighbors: checked,
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<CheckboxItem
|
|
||||||
label={t("layerTool.remoteNeighbors")}
|
|
||||||
checked={visibilityState.remoteNeighbors}
|
|
||||||
onChange={(checked) => {
|
|
||||||
setVisibilityState({
|
|
||||||
...visibilityState,
|
|
||||||
remoteNeighbors: checked,
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<CheckboxItem
|
|
||||||
label={t("layerTool.positionPrecision")}
|
|
||||||
checked={visibilityState.positionPrecision}
|
|
||||||
onChange={(checked) => {
|
|
||||||
setVisibilityState({
|
|
||||||
...visibilityState,
|
|
||||||
positionPrecision: checked,
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
|
))}
|
||||||
{/*<CheckboxItem
|
{/*<CheckboxItem
|
||||||
key="traceroutes"
|
key="traceroutes"
|
||||||
label={t("layerTool.traceroutes")}
|
label={t("layerTool.traceroutes")}
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ export const SidebarButton = ({
|
|||||||
"flex-shrink-0",
|
"flex-shrink-0",
|
||||||
"transition-opacity duration-300 ease-in-out",
|
"transition-opacity duration-300 ease-in-out",
|
||||||
isButtonCollapsed ? "opacity-0 invisible" : "opacity-100 visible",
|
isButtonCollapsed ? "opacity-0 invisible" : "opacity-100 visible",
|
||||||
isDirty ? "bg-sky-500" : "bg-red-600",
|
isDirty ? "bg-sky-500" : "bg-blue-500",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{count}
|
{count}
|
||||||
|
|||||||
Reference in New Issue
Block a user