Skip to content

Commit

Permalink
Fix legends, add dataset info
Browse files Browse the repository at this point in the history
  • Loading branch information
barbara-chaves committed Jun 19, 2024
1 parent 5176d32 commit b4bc8d4
Show file tree
Hide file tree
Showing 17 changed files with 1,735 additions and 202 deletions.
5 changes: 5 additions & 0 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"deck.gl": "^9.0.17",
"express": "^4.19.2",
"jotai": "^2.8.2",
"lodash-es": "^4.17.21",
"lucide-react": "^0.378.0",
"mapbox-gl": "^3.3.0",
"next": "14.2.3",
Expand All @@ -57,6 +58,9 @@
"react": "^18",
"react-dom": "^18",
"react-map-gl": "^7.1.7",
"react-markdown": "^9.0.1",
"rehype-raw": "^7.0.0",
"remark-gfm": "^4.0.0",
"rooks": "^7.14.1",
"tailwind-merge": "^2.3.0",
"tailwindcss-animate": "^1.0.7",
Expand All @@ -66,6 +70,7 @@
"@types/color": "^3.0.6",
"@types/d3-dsv": "^3",
"@types/express": "^4",
"@types/lodash-es": "^4",
"@types/mapbox-gl": "^3",
"@types/node": "^20",
"@types/react": "^18",
Expand Down
79 changes: 47 additions & 32 deletions client/src/components/map/legends/content/rangeland.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"use client";
import { useMemo } from "react";
import { LegendComponent } from "../../types";
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";
Expand All @@ -6,8 +7,10 @@ import { ChevronDownIcon } from "lucide-react";
import { RANGELAND_BIOMES, RANGELAND_ECOREGIONS } from "@/containers/datasets/constants";
import { useGetRangelands } from "@/types/generated/rangeland";
import { useSyncRangelandRegions, useSyncRangelandType } from "@/store/map";
import { useTranslations } from "@/i18n";

const RangelandLegend = () => {
const t = useTranslations();
const [rangelandType] = useSyncRangelandType();
const [rangelandRegions] = useSyncRangelandRegions();

Expand Down Expand Up @@ -48,39 +51,51 @@ const RangelandLegend = () => {
: [];
}, [rangelandRegions, rangelandsData, rangelandType]);

const subtitle = useMemo(() => {
if (rangelandType === RANGELAND_BIOMES) {
return t("Types");
}
if (rangelandType === RANGELAND_ECOREGIONS) {
return t("Ecoregions");
}
}, [rangelandType]);

return (
<ul className="space-y-2">
{items?.map((i) => (
<li key={i.name} className="flex gap-4">
{i.items?.length ? (
<Collapsible>
<CollapsibleTrigger className="group flex gap-2.5">
<ChevronDownIcon className="h-4 w-4 flex-shrink-0 opacity-50 group-data-[state=open]:rotate-180" />
{!!i.color && <CircleLegend className="h-3.5 w-3.5" colors={[i.color]} />}
<span className="text-start text-xs font-light">{i.name}</span>
</CollapsibleTrigger>
<CollapsibleContent asChild>
<ul className="space-y-1.5 pl-12 pt-2">
{i.items.map((subItem) => (
<li key={subItem.name} className="flex gap-2.5">
{!!subItem.color && (
<CircleLegend className="h-3.5 w-3.5" colors={[subItem.color]} />
)}
<span className="text-xs font-light">{subItem.name}</span>
</li>
))}
</ul>
</CollapsibleContent>
</Collapsible>
) : (
<>
{!!i.color && <CircleLegend colors={[i.color]} />}
<span className="text-xs font-light">{i.name}</span>
</>
)}
</li>
))}
</ul>
<div className="space-y-2">
{!!subtitle && <p className="text-xs">{subtitle}</p>}
<ul className="space-y-2">
{items?.map((i) => (
<li key={i.name} className="flex gap-4">
{i.items?.length ? (
<Collapsible>
<CollapsibleTrigger className="group flex gap-2.5">
<ChevronDownIcon className="h-4 w-4 flex-shrink-0 opacity-50 group-data-[state=open]:rotate-180" />
{!!i.color && <CircleLegend className="h-3.5 w-3.5" colors={[i.color]} />}
<span className="text-start text-xs font-light">{i.name}</span>
</CollapsibleTrigger>
<CollapsibleContent asChild>
<ul className="space-y-1.5 pl-12 pt-2">
{i.items.map((subItem) => (
<li key={subItem.name} className="flex gap-2.5">
{!!subItem.color && (
<CircleLegend className="h-3.5 w-3.5" colors={[subItem.color]} />
)}
<span className="text-xs font-light">{subItem.name}</span>
</li>
))}
</ul>
</CollapsibleContent>
</Collapsible>
) : (
<>
{!!i.color && <CircleLegend colors={[i.color]} />}
<span className="text-xs font-light">{i.name}</span>
</>
)}
</li>
))}
</ul>
</div>
);
};

Expand Down
12 changes: 7 additions & 5 deletions client/src/components/map/legends/header/buttons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import Slider from "@/components/ui/slider";
import { Dialog, DialogContent, DialogHeader, DialogTrigger } from "@/components/ui/dialog";
import EyeOffIcon from "@/svgs/eye-off.svg";
import EyeIcon from "@/svgs/eye.svg";
import { ReactNode } from "react";
import { DialogProps } from "@radix-ui/react-dialog";

type LayerVisibilityProps = {
onChangeVisibility: (v: boolean) => void;
Expand Down Expand Up @@ -47,19 +49,19 @@ const LayerOpacity = ({ opacity = 1, onChangeOpacity }: LayerOpacityProps) => (
</Popover>
);

type LayerInfoProps = {
info?: string;
type LayerInfoProps = DialogProps & {
info?: ReactNode;
title?: string;
};

const LayerInfo = ({ info, title }: LayerInfoProps) => (
<Dialog>
const LayerInfo = ({ info, title, ...props }: LayerInfoProps) => (
<Dialog {...props}>
<DialogTrigger disabled={!info} className="h-min">
<CircleHelpIcon className="h-5 w-5 stroke-foreground" />
</DialogTrigger>
<DialogContent>
<DialogHeader>{title}</DialogHeader>
<p className="text-sm">{info}</p>
<div className="text-sm">{info}</div>
</DialogContent>
</Dialog>
);
Expand Down
2 changes: 1 addition & 1 deletion client/src/components/map/legends/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const Legend = ({ children }: PropsWithChildren) => {
return (
<div>
<ScrollAreaPrimitive.Root type="scroll" className="relative overflow-hidden">
<ScrollAreaPrimitive.Viewport className="max-h-48 w-full">
<ScrollAreaPrimitive.Viewport className="max-h-[200px] w-full">
{children}
</ScrollAreaPrimitive.Viewport>
<ScrollBar />
Expand Down
2 changes: 1 addition & 1 deletion client/src/components/ui/dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const DialogOverlay = React.forwardRef<
<DialogPrimitive.Overlay
ref={ref}
className={cn(
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/80",
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50",
className,
)}
{...props}
Expand Down
38 changes: 38 additions & 0 deletions client/src/components/ui/rich-text.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import Markdown from "react-markdown";

import rehypeRaw from "rehype-raw";
import remarkGfm from "remark-gfm";

import { cn } from "@/lib/utils";
import { omit } from "lodash-es";

type RichTextProps = {
children: string | undefined | null;
className?: string;
};

const RichText = ({ children, className }: RichTextProps) => {
return (
<Markdown
components={{
a: (props) => (
<a {...omit(props, "node")} target="_blank" className="underline">
{props.children}
</a>
),
ol: (props) => (
<ol {...omit(props, "node")} className="ml-4 list-decimal">
{props.children}
</ol>
),
}}
className={cn("space-y-2", className)}
remarkPlugins={[[remarkGfm, { singleTilde: false }]]}
rehypePlugins={[rehypeRaw]}
>
{children}
</Markdown>
);
};

export default RichText;
2 changes: 1 addition & 1 deletion client/src/containers/datasets/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { useGetLocalizedList } from "@/lib/localized-query";

const Datasets = () => {
const datasetsListQuery = useGetDatasets({
populate: ["layers", "layers.layer", "sources"],
populate: ["layers", "layers.layer", "sources", "citations"],
sort: "id:asc",
});

Expand Down
40 changes: 40 additions & 0 deletions client/src/containers/datasets/info.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
"use client";
import RichText from "@/components/ui/rich-text";
import { useTranslations } from "@/i18n";
import { DefaultCitationsComponent } from "@/types/generated/strapi.schemas";

type DatsetInfoProps = {
info?: string;
citations?: DefaultCitationsComponent[];
};

const DatasetInfo = ({ citations, info }: DatsetInfoProps) => {
const t = useTranslations();
return (
<div className="space-y-4">
<div>
<RichText>{info}</RichText>
</div>
{!!citations?.length && (
<div className="space-y-2 text-sm">
<h3 className="font-medium">{t("Dataset reference")}</h3>
{citations.map((citation) => (
<div className="text-xs" key={citation.id}>
<h4>{citation?.name}</h4>
<a
href={citation?.url}
target="_blank"
rel="noopener noreferrer"
className="underline"
>
{citation?.url}
</a>
</div>
))}
</div>
)}
</div>
);
};

export default DatasetInfo;
14 changes: 9 additions & 5 deletions client/src/containers/datasets/item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ import { DatasetListResponseDataItem } from "@/types/generated/strapi.schemas";

import { useSyncDatasets, useSyncLayers, useSyncLayersSettings } from "@/store/map";
import { Switch } from "@/components/ui/switch";
import CitationsIcon from "@/svgs/citations.svg";
// import CitationsIcon from "@/svgs/citations.svg";
import { cn } from "@/lib/utils";
import { useTranslations } from "next-intl";
import { RANGELAND_DATASET_SLUG } from "./constants";
import { useMemo } from "react";
import { LayerInfo, LayerVisibility } from "@/components/map/legends/header/buttons";
import GroupDataset from "./components/group";
import TemporalDataset from "./components/temporal";
import DatasetInfo from "./info";

type DatasetsItemProps = DatasetListResponseDataItem & {
className?: string;
Expand Down Expand Up @@ -102,7 +103,10 @@ const DatasetsItem = ({ attributes, className }: DatasetsItemProps) => {
</div>

<div className="mt-px flex gap-2">
<LayerInfo />
<LayerInfo
title={attributes?.title}
info={<DatasetInfo citations={attributes?.citations} info={attributes?.info} />}
/>
<LayerVisibility
visible={datasetVisibility}
onChangeVisibility={handleChangeVisibility}
Expand All @@ -119,17 +123,17 @@ const DatasetsItem = ({ attributes, className }: DatasetsItemProps) => {
)}
{attributes?.type === "Temporal" && <TemporalDataset layers={attributes?.layers} />}
<div className="flex items-center gap-2">
{!!attributes?.sources?.[0]?.url && (
{!!attributes?.sources?.url && (
<a
href={attributes?.sources?.[0].url}
href={attributes?.sources?.url}
target="_blank"
rel="noopener noreferrer"
className="text-xs uppercase text-foreground underline underline-offset-2"
>
{t("data source")}
</a>
)}
<CitationsIcon className="h-5 w-5" />
{/* <CitationsIcon className="h-5 w-5" /> */}
</div>
</div>
);
Expand Down
2 changes: 1 addition & 1 deletion client/src/containers/map/legends/item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ const LegendItem = ({ dataset }: LegendItemProps) => {
visible={settings.visibility}
opacity={settings.opacity}
title={datasetData?.data?.attributes?.title}
subtitle={datasetLayer?.name}
// subtitle={datasetLayer?.name}
info={datasetLayer?.layer?.data?.attributes?.description}
setOpacity={(o) => setLayerSettings("opacity", o)}
setVisibility={(v) => setLayerSettings("visibility", v)}
Expand Down
12 changes: 9 additions & 3 deletions client/src/types/generated/strapi.schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1802,6 +1802,7 @@ export type DatasetType = (typeof DatasetType)[keyof typeof DatasetType];
export const DatasetType = {
Group: "Group",
Temporal: "Temporal",
Simple: "Simple",
} as const;

export type DatasetLocalizations = {
Expand All @@ -1824,12 +1825,13 @@ export interface Dataset {
createdAt?: string;
createdBy?: DatasetCreatedBy;
description?: string;
info?: string;
layers: DefaultLayerComponent[];
locale?: string;
localizations?: DatasetLocalizations;
publishedAt?: string;
slug?: string;
sources?: DefaultSourceComponent[];
sources?: DefaultSourceComponent;
title: string;
type: DatasetType;
updatedAt?: string;
Expand Down Expand Up @@ -1901,15 +1903,17 @@ export type DatasetRequestDataType =
export const DatasetRequestDataType = {
Group: "Group",
Temporal: "Temporal",
Simple: "Simple",
} as const;

export type DatasetRequestData = {
citations?: DefaultCitationsComponent[];
description?: string;
info?: string;
layers: DefaultLayerComponent[];
locale?: string;
slug?: string;
sources?: DefaultSourceComponent[];
sources?: DefaultSourceComponent;
title: string;
type: DatasetRequestDataType;
};
Expand All @@ -1925,15 +1929,17 @@ export type DatasetLocalizationRequestType =
export const DatasetLocalizationRequestType = {
Group: "Group",
Temporal: "Temporal",
Simple: "Simple",
} as const;

export interface DatasetLocalizationRequest {
citations?: DefaultCitationsComponent[];
description?: string;
info?: string;
layers: DefaultLayerComponent[];
locale: string;
slug?: string;
sources?: DefaultSourceComponent[];
sources?: DefaultSourceComponent;
title: string;
type: DatasetLocalizationRequestType;
}
Expand Down
Loading

0 comments on commit b4bc8d4

Please sign in to comment.