Skip to content

Commit

Permalink
Client/feature/south sudan (#57)
Browse files Browse the repository at this point in the history
* Update timeline legend componenmt and add header to legends
  • Loading branch information
barbara-chaves committed Apr 26, 2024
1 parent 2c9fa63 commit a98f883
Show file tree
Hide file tree
Showing 9 changed files with 122 additions and 141 deletions.
4 changes: 3 additions & 1 deletion client/src/components/map/legend/item-types/basic/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,21 @@ import React from 'react';
import { cn } from '@/lib/classnames';

import { LegendTypeProps } from '../../types';
import LegendHeader from '../header';

export const LegendTypeBasic: React.FC<LegendTypeProps> = ({
className = '',
items = [],
title,
info,
}) => {
return (
<div
className={cn({
[className]: !!className,
})}
>
{title && <p className="mb-2 text-sm text-white">{title}</p>}
<LegendHeader title={title} info={info} />
<ul className="flex w-full flex-wrap items-center gap-3">
{items.map(({ value, color }) => (
<li key={`${value}`} className="flex items-center gap-x-1 text-white">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,21 @@ import React from 'react';
import { cn } from '@/lib/classnames';

import { LegendTypeProps } from '../../types';
import LegendHeader from '../header';

export const LegendTypeChoropleth: React.FC<LegendTypeProps> = ({
className = '',
items,
title,
info,
}) => {
return (
<div
className={cn('font-open', {
[className]: !!className,
})}
>
{title && <p className="mb-2 text-sm text-white">{title}</p>}
<LegendHeader title={title} info={info} />
<ul className="flex w-full">
{items.map(({ color, value }) => (
<li
Expand Down
10 changes: 8 additions & 2 deletions client/src/components/map/legend/item-types/gradient/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,21 @@ import React from 'react';
import { cn } from '@/lib/classnames';

import { LegendTypeProps } from '../../types';
import LegendHeader from '../header';

export const LegendTypeGradient: React.FC<LegendTypeProps> = ({ className = '', items, title }) => {
export const LegendTypeGradient: React.FC<LegendTypeProps> = ({
className = '',
items,
title,
info,
}) => {
return (
<div
className={cn({
[className || '']: !!className,
})}
>
{title && <p className="mb-2 text-sm text-white">{title}</p>}
<LegendHeader title={title} info={info} />
<div
className="min-w-72 flex h-3"
style={{
Expand Down
36 changes: 36 additions & 0 deletions client/src/components/map/legend/item-types/header.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { PopoverClose } from '@radix-ui/react-popover';
import { Info, X } from 'lucide-react';

import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';

type LegendHeaderProps = {
title?: string;
info?: string;
};

const LegendHeader = ({ title, info }: LegendHeaderProps) => {
return (
<div className="font-open-sans mb-4 flex items-center justify-between gap-2 text-white">
{!!title && <div className="text-sm font-semibold text-white">{title}</div>}
{!!info && (
<Popover>
<PopoverTrigger>
<Info className="h-4 w-4 stroke-white" />
</PopoverTrigger>

<PopoverContent className="border-none bg-[#003247] text-xs text-white">
<div className="mb-2 flex justify-between text-sm font-semibold">
{title}
<PopoverClose>
<X className="h-4 w-4 stroke-white" />
</PopoverClose>
</div>
{info}
</PopoverContent>
</Popover>
)}
</div>
);
};

export default LegendHeader;
116 changes: 45 additions & 71 deletions client/src/components/map/legend/item-types/timeline/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,7 @@ import { layersAtom, layersSettingsAtom, timelineAtom } from '@/store/map';
import { LegendTypeTimelineProps } from '@/components/map/legend/types';
import { Button } from '@/components/ui/button';

const width = 200;
const height = 12;
const textMarginY = -5;
import LegendHeader from '../header';

export const LegendTypeTimeline: React.FC<LegendTypeTimelineProps> = ({
start,
Expand All @@ -36,6 +34,7 @@ export const LegendTypeTimeline: React.FC<LegendTypeTimelineProps> = ({
labels,
title,
animationInterval = 1000,
info,
...props
}) => {
const intervalRef = useRef<NodeJS.Timer>();
Expand Down Expand Up @@ -136,6 +135,7 @@ export const LegendTypeTimeline: React.FC<LegendTypeTimelineProps> = ({
})) || []
);
}, [start, end, dateType, interval, format, labels]);
const thumbLabelRef = useRef<HTMLDivElement>(null);

const handlePlay = useCallback(() => {
clearInterval(intervalRef.current);
Expand Down Expand Up @@ -175,27 +175,16 @@ export const LegendTypeTimeline: React.FC<LegendTypeTimelineProps> = ({

const maxValue = TIMELINE?.length - 1 || 1;
const minValue = 0;
const yearScale = useCallback(
(year: number) => (year / (maxValue - minValue)) * (width - 2),
[maxValue, minValue]
);

const currValueText = useRef<SVGTextElement>(null);
const firstValueText = useRef<SVGTextElement>(null);
// If the layer is not the first one in the timeline, don't render the component
if (firstTimelineLayer !== layerId) {
return null;
}
const currTextSize = currValueText.current?.getBBox().width || 0;
const firstTextSize = currValueText.current?.getBBox().width || 0;
const currYearX = yearScale(value) - currTextSize / 2;
const minYearX = Math.max(-firstTextSize / 2, -15);
const maxYearX = width - Math.min(firstTextSize / 2, 25) - 5;

return (
<div style={props?.style} className="z-30 mt-4">
<div>{!!title && <div className="mb-4 text-sm text-white">{title}</div>}</div>
<div className="flex items-center justify-between gap-8">
<LegendHeader title={title} info={info} />
<div className="flex items-center gap-8">
<Button
variant="default"
className="relative z-50 flex h-10 w-10 shrink-0 items-center justify-center rounded-full px-0 py-0 hover:bg-white"
Expand All @@ -209,66 +198,51 @@ export const LegendTypeTimeline: React.FC<LegendTypeTimelineProps> = ({
</Button>

<Root
max={(TIMELINE?.length || 1) - 1}
min={0}
max={maxValue}
min={minValue}
value={[frame]}
onValueChange={([v]) => onChangeFrame(v)}
className="-translate-x- relative mr-2 flex w-[200px] touch-none select-none items-center"
className="relative flex w-[200px] touch-none select-none flex-col justify-end"
>
<Track className="w-full justify-between">
<svg width={width} height={height} className="w-full cursor-pointer overflow-visible">
{/* Min value text */}
<text
x={minYearX}
y={textMarginY}
className={cn(
'font-open-sans fill-white text-xs',
currYearX - minYearX > firstTextSize ? 'opacity-100' : 'opacity-25'
)}
ref={firstValueText}
>
{TIMELINE[0]?.label}
</text>

{/* Years lines */}
{TIMELINE?.map(({ value }) => {
const position = value % 10 === 0 ? 0 : 4;
return (
<line
key={value}
x1={yearScale(value)}
x2={yearScale(value)}
y1={position}
y2={height}
strokeWidth={2}
className="stroke-gray-400"
/>
);
<div
style={{ marginTop: thumbLabelRef?.current?.clientHeight }}
className="mb-2 flex justify-between"
>
<div
className={cn('font-open-sans text-xs leading-none text-white', {
'text-gray-400': value === minValue,
})}

{/* Max value text */}
<text
className={cn(
'font-open-sans max-w-[50px] fill-white text-xs',
maxYearX - currYearX > firstTextSize ? 'opacity-100' : 'opacity-25'
)}
x={maxYearX}
y={textMarginY}
ref={currValueText}
>
{TIMELINE?.[TIMELINE.length - 1]?.label}
</text>
{/* Current value text */}
<text
x={currYearX}
y={textMarginY - 10}
className="font-open-sans fill-white text-sm font-bold"
>
{TIMELINE?.[value]?.label}
</text>
</svg>
>
{TIMELINE?.[minValue]?.label}
</div>
<div
className={cn('font-open-sans text-xs leading-none text-white', {
'text-gray-400': value === maxValue,
})}
>
{TIMELINE?.[maxValue]?.label}
</div>
</div>
<Track className="flex h-3 w-full justify-between">
{TIMELINE?.map((t, index) => (
<div key={index} className="h-full w-0.5 bg-gray-400" />
))}
</Track>
<Thumb className="translate-y block h-5 w-0 -translate-x-px cursor-pointer border-r-[2px] bg-white" />
<Thumb className="block h-6 w-0.5 cursor-pointer bg-white">
<span
className={cn(
'font-open-sans block w-max text-xs font-semibold leading-none text-white',
`translate-y-[calc(-16px-100%)]`,
{
'-translate-x-full': value === maxValue,
'-translate-x-1/2': value !== maxValue && value !== minValue,
}
)}
ref={thumbLabelRef}
>
{TIMELINE?.[value]?.label}
</span>
</Thumb>
</Root>
</div>
</div>
Expand Down
21 changes: 13 additions & 8 deletions client/src/components/map/legend/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,17 +99,23 @@ export interface SortableItemProps extends PropsWithChildren {
sortable: Sortable;
}

export interface LegendTypeProps {
export interface Legend {
className?: string;
title?: string;
style?: CSSProperties;
info?: string;
type: LegendType;
}

export interface LegendTypeProps extends Legend {
items: Array<{
value: string;
color: string;
}>;
title?: string;
}

export interface LegendTypeTimelineProps {
className?: string;
export interface LegendTypeTimelineProps extends Legend {
description?: string;
id: number;
layerId: number;
Expand All @@ -119,30 +125,29 @@ export interface LegendTypeTimelineProps {
dateType?: 'year' | 'month' | 'day' | 'hour' | 'minute' | 'second';
format?: string;
animationInterval?: number;
style?: CSSProperties;
labels?: string[];
title?: string;
}

export interface LegendMatrixIntersectionsProps {
export interface LegendMatrixIntersectionsProps extends Legend {
intersections: Array<{
id: number;
color: string;
}>;
}

export interface LegendTypeSwitchProps {
export interface LegendTypeSwitchProps extends Legend {
layerId: number;
param: string;
layerTitle: string;
style?: CSSProperties;
color?: string;
title?: string;
}

type ItemLegends = Extract<LegendType, 'basic' | 'choropleth' | 'gradient'>;

export type LegendTypesProps<T> = T extends ItemLegends
? LegendTypeProps
: T extends 'basic' | 'choropleth' | 'gradient'
? LegendTypeProps
: T extends 'timeline'
? LegendTypeTimelineProps
Expand Down
1 change: 0 additions & 1 deletion client/src/containers/map/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,6 @@ export default function MapContainer() {
}, [tmpBbox]); // eslint-disable-line react-hooks/exhaustive-deps

const handleMapViewStateChange = useCallback(() => {
console.log('viewStateChange');
if (map) {
const b = map
.getBounds()
Expand Down

0 comments on commit a98f883

Please sign in to comment.