Skip to content

Commit

Permalink
map settings and URL management
Browse files Browse the repository at this point in the history
  • Loading branch information
andresgnlez committed Sep 28, 2023
1 parent 8095eb0 commit 74ef090
Show file tree
Hide file tree
Showing 27 changed files with 1,153 additions and 11 deletions.
2 changes: 1 addition & 1 deletion client/components.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils"
"utils": "@/lib/classnames"
}
}
2 changes: 1 addition & 1 deletion client/e2e/example.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ test("has title", async ({ page }) => {
await page.goto(`/`);

// Expect a title "to contain" a substring.
await expect(page).toHaveTitle(/Create Next App/);
await expect(page).toHaveTitle(/CCSA/);
});

// test("has title", async ({ page }) => {
Expand Down
6 changes: 6 additions & 0 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@
"postinstall": "cd .. && husky install client/.husky"
},
"dependencies": {
"@radix-ui/react-checkbox": "1.0.4",
"@radix-ui/react-label": "2.0.2",
"@radix-ui/react-popover": "1.0.7",
"@radix-ui/react-radio-group": "1.1.3",
"@radix-ui/react-tooltip": "1.0.7",
"@tanstack/react-query": "4.35.3",
"@typescript-eslint/eslint-plugin": "6.7.3",
Expand All @@ -29,6 +33,8 @@
"react-dom": "18.2.0",
"react-icons": "4.11.0",
"react-map-gl": "7.1.6",
"recoil": "0.7.7",
"recoil-sync": "0.2.0",
"rooks": "7.14.1",
"tailwind-merge": "1.14.0",
"tailwindcss-animate": "1.0.7",
Expand Down
Binary file added client/public/images/map/light.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added client/public/images/map/satellite.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
29 changes: 25 additions & 4 deletions client/src/app/layout-providers.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,42 @@
"use client";

import { PropsWithChildren, useState } from "react";
import { PropsWithChildren, useCallback, useState } from "react";

import { MapProvider } from "react-map-gl";

import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { RecoilRoot } from "recoil";

import { RecoilURLSyncNext } from "@/lib/recoil";
import type { Deserialize, Serialize } from "@/lib/recoil";
// import RecoilDevTools from "@/lib/recoil/devtools";

import { TooltipProvider } from "@/components/ui/tooltip";

export default function LayoutProviders({ children }: PropsWithChildren) {
const [queryClient] = useState(() => new QueryClient());
const serialize: Serialize = useCallback((x) => {
return x === undefined ? "" : JSON.stringify(x);
}, []);

//Demo of custom deserialization
const deserialize: Deserialize = useCallback((x: string) => {
return JSON.parse(x);
}, []);

return (
<QueryClientProvider client={queryClient}>
<MapProvider>
<TooltipProvider>{children}</TooltipProvider>
</MapProvider>
<RecoilRoot>
<RecoilURLSyncNext
location={{ part: "queryParams" }}
serialize={serialize}
deserialize={deserialize}
>
<MapProvider>
<TooltipProvider>{children}</TooltipProvider>
</MapProvider>
</RecoilURLSyncNext>
</RecoilRoot>
</QueryClientProvider>
);
}
2 changes: 1 addition & 1 deletion client/src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import LayoutProviders from "@/app/layout-providers";
const inter = Inter({ subsets: ["latin"] });

export const metadata: Metadata = {
title: "Create Next App",
title: "CCSA",
description: "Generated by create next app",
};

Expand Down
67 changes: 67 additions & 0 deletions client/src/components/map/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,70 @@ export const DEFAULT_VIEW_STATE: Partial<ViewState> = {
latitude: 0,
longitude: 0,
};

export const BASEMAPS = [
{
label: "Light",
value: "basemap-light",
preview: `/images/map/light.jpeg`,
settings: {
labels: "labels-dark",
boundaries: "boundaries-dark",
roads: "roads-dark",
},
},
{
label: "Satellite",
value: "basemap-satellite",
preview: `/images/map/satellite.jpeg`,
settings: {
labels: "labels-light",
boundaries: "boundaries-light",
roads: "roads-light",
},
},
];

export const LABELS = [
{
id: "1059d2b8cfa87b8d894b5373ea556666",
label: "Dark labels",
slug: "labels-dark",
},
{
id: "5924e7eeda116f817dd89f1d8d418721",
label: "Light labels",
slug: "labels-light",
},
{
id: "asdfasdfasdfasdf",
label: "No labels",
slug: "labels-none",
},
];

export const BOUNDARIES = [
{
id: "ae861f3122c21ad7754e66d3cead38e6",
label: "Dark boundaries",
slug: "boundaries-dark",
},
{
id: "31b240eba06a254ade36f1dde6a3c07e",
label: "Light boundaries",
slug: "boundaries-light",
},
];

export const ROADS = [
{
id: "4e240a8b884456747dcd07d41b4d5543",
label: "Dark roads",
slug: "roads-dark",
},
{
id: "edb80ef589e776ec6c2568b2fc6ad74c",
label: "Light roads",
slug: "roads-light",
},
];
62 changes: 62 additions & 0 deletions client/src/components/map/controls/settings/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
"use client";

import { FC, HTMLAttributes, PropsWithChildren } from "react";

import { PopoverArrow } from "@radix-ui/react-popover";
import { TooltipPortal } from "@radix-ui/react-tooltip";
import { LuSettings } from "react-icons/lu";

import { cn } from "@/lib/classnames";

import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { Tooltip, TooltipArrow, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";

import { CONTROL_BUTTON_STYLES } from "../constants";

interface SettingsControlProps {
className?: HTMLAttributes<HTMLDivElement>["className"];
}

export const SettingsControl: FC<PropsWithChildren<SettingsControlProps>> = ({
className,
children,
}: PropsWithChildren<SettingsControlProps>) => {
return (
<div className={cn("flex flex-col space-y-0.5", className)}>
<Popover>
<Tooltip>
<PopoverTrigger asChild>
<TooltipTrigger asChild>
<button
className={cn({
[CONTROL_BUTTON_STYLES.default]: true,
[CONTROL_BUTTON_STYLES.hover]: true,
[CONTROL_BUTTON_STYLES.active]: true,
})}
aria-label="Map settings"
type="button"
>
<LuSettings className="h-full w-full" />
</button>
</TooltipTrigger>
</PopoverTrigger>

<TooltipPortal>
<TooltipContent side="left" align="center">
<div className="text-xxs">Map settings</div>

<TooltipArrow className="fill-white" width={10} height={5} />
</TooltipContent>
</TooltipPortal>

<PopoverContent side="left" align="start">
{children}
<PopoverArrow className="fill-white" width={10} height={5} />
</PopoverContent>
</Tooltip>
</Popover>
</div>
);
};

export default SettingsControl;
2 changes: 1 addition & 1 deletion client/src/components/map/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ export const Map: FC<CustomMapProps> = ({
mapboxAccessToken={env.NEXT_PUBLIC_MAPBOX_API_TOKEN}
onMove={handleMapMove}
onLoad={handleMapLoad}
mapStyle="mapbox://styles/marxan/ckn4fr7d71qg817kgd9vuom4s"
mapStyle="mapbox://styles/layer-manager/clj8fgofm000t01pjcu21agsd"
{...mapboxProps}
{...localViewState}
>
Expand Down
29 changes: 29 additions & 0 deletions client/src/components/ui/checkbox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
"use client";

import * as React from "react";

import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
import { LuCheck } from "react-icons/lu";

import { cn } from "@/lib/classnames";

const Checkbox = React.forwardRef<
React.ElementRef<typeof CheckboxPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>
>(({ className, ...props }, ref) => (
<CheckboxPrimitive.Root
ref={ref}
className={cn(
"peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
className,
)}
{...props}
>
<CheckboxPrimitive.Indicator className={cn("flex items-center justify-center text-current")}>
<LuCheck className="h-4 w-4" />
</CheckboxPrimitive.Indicator>
</CheckboxPrimitive.Root>
));
Checkbox.displayName = CheckboxPrimitive.Root.displayName;

export { Checkbox };
22 changes: 22 additions & 0 deletions client/src/components/ui/label.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
"use client";

import * as React from "react";

import * as LabelPrimitive from "@radix-ui/react-label";
import { cva, type VariantProps } from "class-variance-authority";

import { cn } from "@/lib/classnames";

const labelVariants = cva(
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
);

const Label = React.forwardRef<
React.ElementRef<typeof LabelPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> & VariantProps<typeof labelVariants>
>(({ className, ...props }, ref) => (
<LabelPrimitive.Root ref={ref} className={cn(labelVariants(), className)} {...props} />
));
Label.displayName = LabelPrimitive.Root.displayName;

export { Label };
32 changes: 32 additions & 0 deletions client/src/components/ui/popover.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
"use client";

import * as React from "react";

import * as PopoverPrimitive from "@radix-ui/react-popover";

import { cn } from "@/lib/classnames";

const Popover = PopoverPrimitive.Root;

const PopoverTrigger = PopoverPrimitive.Trigger;

const PopoverContent = React.forwardRef<
React.ElementRef<typeof PopoverPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>
>(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
<PopoverPrimitive.Portal>
<PopoverPrimitive.Content
ref={ref}
align={align}
sideOffset={sideOffset}
className={cn(
"z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className,
)}
{...props}
/>
</PopoverPrimitive.Portal>
));
PopoverContent.displayName = PopoverPrimitive.Content.displayName;

export { Popover, PopoverTrigger, PopoverContent };
39 changes: 39 additions & 0 deletions client/src/components/ui/radio-group.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
"use client";

import * as React from "react";

import * as RadioGroupPrimitive from "@radix-ui/react-radio-group";
import { LuCircle } from "react-icons/lu";

import { cn } from "@/lib/classnames";

const RadioGroup = React.forwardRef<
React.ElementRef<typeof RadioGroupPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Root>
>(({ className, ...props }, ref) => {
return <RadioGroupPrimitive.Root className={cn("grid gap-2", className)} {...props} ref={ref} />;
});
RadioGroup.displayName = RadioGroupPrimitive.Root.displayName;

const RadioGroupItem = React.forwardRef<
React.ElementRef<typeof RadioGroupPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Item>
>(({ className, ...props }, ref) => {
return (
<RadioGroupPrimitive.Item
ref={ref}
className={cn(
"aspect-square h-4 w-4 rounded-full border border-primary text-primary ring-offset-background focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
className,
)}
{...props}
>
<RadioGroupPrimitive.Indicator className="flex items-center justify-center">
<LuCircle className="h-2.5 w-2.5 fill-current text-current" />
</RadioGroupPrimitive.Indicator>
</RadioGroupPrimitive.Item>
);
});
RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName;

export { RadioGroup, RadioGroupItem };
10 changes: 9 additions & 1 deletion client/src/containers/home/index.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
"use client";

import MapSettingsManagerPanel from "@/containers/home/map-settings";
import MapSettingsManager from "@/containers/home/map-settings/manager";

import Map from "@/components/map";
import Controls from "@/components/map/controls";
import SettingsControl from "@/components/map/controls/settings";
import ZoomControl from "@/components/map/controls/zoom";

const Home = (): JSX.Element => {
return (
<div className="h-screen w-full">
<Map>
<Controls>
<Controls className="absolute right-6 top-4">
<ZoomControl />
<SettingsControl>
<MapSettingsManagerPanel />
</SettingsControl>
</Controls>
<MapSettingsManager />
</Map>
</div>
);
Expand Down
Loading

0 comments on commit 74ef090

Please sign in to comment.