Skip to content

Commit

Permalink
feat(client): Add contextual panel
Browse files Browse the repository at this point in the history
  • Loading branch information
clementprdhomme committed Oct 28, 2024
1 parent c517683 commit d32d4b1
Show file tree
Hide file tree
Showing 11 changed files with 247 additions and 25 deletions.
26 changes: 26 additions & 0 deletions client/src/components/map/controls/contextual-layers.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import ContextualLayersPanel from "@/components/panels/contextual-layers";
import { Button } from "@/components/ui/button";
import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet";
import LayersIcon from "@/svgs/layers.svg";

const ContextualLayersControls = () => {
return (
<Sheet>
<SheetTrigger asChild>
<Button type="button" variant="yellow" className="w-8 font-sans xl:w-auto">
<LayersIcon aria-hidden className="xl:!size-5" />
<span className="sr-only xl:not-sr-only">Contextual layers</span>
</Button>
</SheetTrigger>
<SheetContent
side="right"
variant="light"
className="bottom-[68px] h-[calc(100%_-_68px)] xl:bottom-0 xl:h-full"
>
<ContextualLayersPanel />
</SheetContent>
</Sheet>
);
};

export default ContextualLayersControls;
14 changes: 10 additions & 4 deletions client/src/components/map/controls/index.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
import ContextualLayersControls from "./contextual-layers";
import MapSettingsControls from "./map-settings";
import ZoomControls from "./zoom";

const Controls = () => {
return (
<div className="absolute bottom-10 right-5 z-10 flex flex-col gap-2 xl:bottom-6 xl:right-10">
<ZoomControls />
<MapSettingsControls />
</div>
<>
<div className="absolute bottom-20 right-5 z-10 leading-none xl:bottom-auto xl:right-10 xl:top-6">
<ContextualLayersControls />
</div>
<div className="absolute bottom-10 right-5 z-10 flex flex-col gap-2 xl:bottom-6 xl:right-10">
<ZoomControls />
<MapSettingsControls />
</div>
</>
);
};

Expand Down
18 changes: 3 additions & 15 deletions client/src/components/map/controls/zoom.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,12 @@ const ZoomControls = () => {
const onClickZoomOut = useCallback(() => map?.zoomOut(), [map]);

return (
<div className="flex flex-col gap-px">
<Button
type="button"
variant="yellow"
size="icon"
className="hidden xl:inline-flex"
onClick={onClickZoomIn}
>
<div className="hidden flex-col gap-px xl:flex">
<Button type="button" variant="yellow" size="icon" onClick={onClickZoomIn}>
<span className="sr-only">Zoom in</span>
<PlusIcon aria-hidden />
</Button>
<Button
type="button"
variant="yellow"
size="icon"
className="hidden xl:inline-flex"
onClick={onClickZoomOut}
>
<Button type="button" variant="yellow" size="icon" onClick={onClickZoomOut}>
<span className="sr-only">Zoom out</span>
<MinusIcon aria-hidden />
</Button>
Expand Down
81 changes: 81 additions & 0 deletions client/src/components/panels/contextual-layers/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
"use client";

import { Button, buttonVariants } from "@/components/ui/button";
import { SheetClose, SheetHeader, SheetTitle } from "@/components/ui/sheet";
import { Skeleton } from "@/components/ui/skeleton";
import useDatasetsBySubTopic from "@/hooks/use-datasets-by-sub-topic";
import XMarkIcon from "@/svgs/xmark.svg";

import Item from "./item";

const ContextualLayersPanel = () => {
const { data, isLoading } = useDatasetsBySubTopic("contextual");

return (
<>
<SheetHeader className="flex items-center justify-between">
<SheetTitle
className={buttonVariants({ variant: "yellow", className: "text-base xl:sr-only" })}
>
Contextual layers
</SheetTitle>
<SheetClose asChild>
<Button
type="button"
variant="yellow"
className="w-8 font-sans text-base transition-transform duration-500 ease-out xl:relative xl:w-auto xl:gap-5 xl:pr-10 xl:group-data-[state=open]:-translate-x-12"
>
<XMarkIcon aria-hidden className="!size-5 xl:!size-6" />
<span className="sr-only">Close</span>
<span aria-hidden className="hidden xl:inline">
Contextual layers
</span>
</Button>
</SheetClose>
</SheetHeader>

<p className="mt-11">
Introduction lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit
amet, consectetu elit.
</p>

{isLoading && (
<>
<Skeleton className="mt-6 h-6 w-1/2" />
<Skeleton className="mt-2 h-6 w-1/3" />
<Skeleton className="mt-2 h-6 w-2/3" />
<Skeleton className="mt-6 h-6 w-1/2" />
<Skeleton className="mt-2 h-6 w-1/3" />
<Skeleton className="mt-2 h-6 w-2/3" />
<Skeleton className="mt-6 h-6 w-1/2" />
<Skeleton className="mt-2 h-6 w-1/3" />
<Skeleton className="mt-2 h-6 w-2/3" />
</>
)}

{!isLoading && data.length === 0 && (
<div className="py-11 text-center font-semibold">Data not available</div>
)}

{!isLoading && data.length > 0 && (
<div className="mt-6 flex flex-col gap-2">
{data.map(({ subTopic, datasets }) => (
<Item
key={subTopic}
name={subTopic}
layers={datasets
.filter((dataset) => dataset.layers.length > 0)
.map((dataset) => ({
// Assuming the dataset has just one layer, which is currently the case
id: dataset.layers[0].id!,
name: dataset.layers[0].attributes!.name!,
}))}
/>
))}
</div>
)}
</>
);
};

export default ContextualLayersPanel;
23 changes: 23 additions & 0 deletions client/src/components/panels/contextual-layers/item.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Dataset, Layer } from "@/types/generated/strapi.schemas";

interface ItemProps {
name: Dataset["name"];
layers: { id: number; name: Layer["name"] }[];
}

const Item = ({ name, layers }: ItemProps) => {
return (
<div>
<div className="border-b border-casper-blue-400/50 py-2 uppercase">{name}</div>
<ul className="py-2">
{layers.map((layer) => (
<li key={layer.id} className="py-2">
<span className="text-xl">{layer.name}</span>
</li>
))}
</ul>
</div>
);
};

export default Item;
2 changes: 1 addition & 1 deletion client/src/components/ui/button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const buttonVariants = cva(
"bg-neutral-900 text-neutral-50 hover:bg-neutral-900/90 ring-offset-white focus-visible:ring-neutral-950",
},
size: {
default: "h-10 px-4 py-2",
default: "h-8 w-auto xl:h-10 px-4 xl:py-2",
icon: "h-8 w-8 xl:h-10 xl:w-10",
auto: "",
},
Expand Down
19 changes: 14 additions & 5 deletions client/src/components/ui/sheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,20 +30,25 @@ const SheetOverlay = React.forwardRef<
SheetOverlay.displayName = SheetPrimitive.Overlay.displayName;

const sheetVariants = cva(
"fixed z-10 bg-rhino-blue-900 text-white p-5 transition ease-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-500 data-[state=open]:duration-500 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-neutral-950",
"group fixed z-10 p-5 transition ease-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-500 data-[state=open]:duration-500 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-neutral-950",
{
variants: {
side: {
top: "inset-x-0 top-0 border-t border-t-white/30 data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top",
bottom:
"inset-x-0 bottom-0 border-b border-b-white/30 data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom",
left: "inset-y-0 left-0 h-full w-3/4 border-l border-l-white/30 data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm",
left: "inset-y-0 left-0 h-full w-full border-l border-l-white/30 data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:w-[400px]",
right:
"inset-y-0 right-0 h-full w-3/4 border-r border-r-white/30 data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm",
"inset-y-0 right-0 h-full w-full border-r border-r-white/30 data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:w-[400px]",
},
variant: {
default: "bg-rhino-blue-900 text-white",
light: "bg-white text-rhino-blue-950",
},
},
defaultVariants: {
side: "right",
variant: "default",
},
},
);
Expand All @@ -55,9 +60,13 @@ interface SheetContentProps
const SheetContent = React.forwardRef<
React.ElementRef<typeof SheetPrimitive.Content>,
SheetContentProps
>(({ side = "right", className, children, ...props }, ref) => (
>(({ side = "right", variant = "default", className, children, ...props }, ref) => (
<SheetPortal>
<SheetPrimitive.Content ref={ref} className={cn(sheetVariants({ side }), className)} {...props}>
<SheetPrimitive.Content
ref={ref}
className={cn(sheetVariants({ side, variant }), className)}
{...props}
>
{children}
</SheetPrimitive.Content>
</SheetPortal>
Expand Down
7 changes: 7 additions & 0 deletions client/src/components/ui/skeleton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { cn } from "@/lib/utils";

function Skeleton({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
return <div className={cn("animate-pulse bg-casper-blue-50", className)} {...props} />;
}

export { Skeleton };
72 changes: 72 additions & 0 deletions client/src/hooks/use-datasets-by-sub-topic.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { useGetDatasets } from "@/types/generated/dataset";
import { DatasetLayersDataItem } from "@/types/generated/strapi.schemas";

type DatasetsBySubTopic = {
subTopic: string;
datasets: { id: number; name: string; layers: DatasetLayersDataItem[] }[];
};

export default function useDatasetsBySubTopic(topicSlug: string, layersFields = ["name"]) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore-error
const { data, isLoading } = useGetDatasets<DatasetsBySubTopic[]>(
{
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore-error
fields: ["name"],
populate: {
sub_topic: {
fields: ["name"],
},
layers: {
fields: layersFields,
},
},
filters: {
topic: {
slug: {
$eq: topicSlug,
},
},
},
sort: "sub_topic.name",
},
{
query: {
placeholderData: { data: [] },
select: (data) => {
const res: DatasetsBySubTopic[] = [];

if (!data?.data) {
return res;
}

let currentIndex = -1;
let currentSubTopic = null;

for (const item of data.data) {
const subTopic = item.attributes!.sub_topic!.data!.attributes!.name!;
const dataset = item.attributes!.name;
const layers = item.attributes!.layers!.data!;

if (currentSubTopic === null || currentSubTopic !== subTopic) {
currentSubTopic = subTopic;
currentIndex++;
res[currentIndex] = { subTopic: currentSubTopic, datasets: [] };
}

res[currentIndex].datasets.push({
id: item.id!,
name: dataset,
layers,
});
}

return res;
},
},
},
);

return { data, isLoading };
}
5 changes: 5 additions & 0 deletions client/src/svgs/layers.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions client/src/svgs/xmark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit d32d4b1

Please sign in to comment.