Skip to content

Commit

Permalink
feat(client): Allow playing layer animation
Browse files Browse the repository at this point in the history
  • Loading branch information
clementprdhomme committed Nov 13, 2024
1 parent f69a833 commit de8056b
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 42 deletions.
137 changes: 95 additions & 42 deletions client/src/components/dataset-card/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
"use client";

import { getMonth } from "date-fns";
import { format } from "date-fns/format";
import Link from "next/link";
import { useCallback, useMemo, useState } from "react";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import * as React from "react";

import { Button } from "@/components/ui/button";
Expand All @@ -22,6 +23,8 @@ import { cn } from "@/lib/utils";
import CalendarDaysIcon from "@/svgs/calendar-days.svg";
import ChevronDownIcon from "@/svgs/chevron-down.svg";
import DownloadIcon from "@/svgs/download.svg";
import PauseIcon from "@/svgs/pause.svg";
import PlayIcon from "@/svgs/play.svg";
import { DatasetLayersDataItem } from "@/types/generated/strapi.schemas";
import { LayerParamsConfig } from "@/types/layer";

Expand Down Expand Up @@ -60,6 +63,10 @@ const DatasetCard = ({ id, name, defaultLayerId, layers }: DatasetCardProps) =>
const [selectedLayerId, setSelectedLayerId] = useState(defaultSelectedLayerId);
const [selectedReturnPeriod, setSelectedReturnPeriod] = useState(defaultSelectedReturnPeriod);
const [selectedDate, setSelectedDate] = useState(defaultSelectedDate);
const [isAnimated, setIsAnimated] = useState(false);
const animationIntervalRef = useRef<NodeJS.Timeout | null>(null);
// Date that was selected before the animation is played
const dateBeforeAnimationRef = useRef<string | null>(null);

const selectedLayer = useMemo(
() => layers.find(({ id }) => id === selectedLayerId),
Expand Down Expand Up @@ -168,17 +175,44 @@ const DatasetCard = ({ id, name, defaultLayerId, layers }: DatasetCardProps) =>
addLayer(selectedLayerId, { ["return-period"]: returnPeriod, date });
}
},
[
selectedLayerId,
setSelectedReturnPeriod,
isDatasetActive,
addLayer,
updateLayer,
layers,
layersConfiguration,
],
[selectedLayerId, isDatasetActive, addLayer, updateLayer, layers, layersConfiguration],
);

const onToggleAnimation = useCallback(() => {
const newIsAnimated = !isAnimated;

if (newIsAnimated) {
dateBeforeAnimationRef.current = selectedDate !== undefined ? selectedDate : null;
} else {
dateBeforeAnimationRef.current = null;
}

setIsAnimated(newIsAnimated);
}, [selectedDate, isAnimated, setIsAnimated]);

// When the layer is animated, show each month of the year in a loop
useEffect(() => {
if (isAnimated && selectedDate !== undefined && selectedLayerId !== undefined) {
animationIntervalRef.current = setInterval(() => {
const date = format(
new Date(selectedDate).setMonth((getMonth(selectedDate) + 1) % 12),
"yyyy-MM-dd",
);

setSelectedDate(date);
updateLayer(selectedLayerId, { date });
}, 500);
} else if (animationIntervalRef.current !== null) {
clearInterval(animationIntervalRef.current);
}

return () => {
if (animationIntervalRef.current !== null) {
clearInterval(animationIntervalRef.current);
}
};
}, [selectedLayerId, selectedDate, isAnimated, setSelectedDate, updateLayer]);

return (
<div className="p-4 border-image-[url(/assets/images/border-image.svg)] border-slice-10 border-image-width-2.5 border-outset-[5px] border-repeat-round">
<div className="flex items-start justify-between gap-4">
Expand Down Expand Up @@ -245,39 +279,58 @@ const DatasetCard = ({ id, name, defaultLayerId, layers }: DatasetCardProps) =>
)}
{selectedDate !== undefined && dateRange !== undefined && isDatasetActive && (
<div className="flex items-center justify-between gap-4">
<Label htmlFor={`dataset-${id}-date`} className="shrink-0 text-xs font-medium">
Displayed on map
</Label>
<Popover>
<PopoverTrigger asChild>
<Button
id={`dataset-${id}-date`}
type="button"
variant="yellow"
className="group flex-grow justify-between px-3 xl:h-auto xl:py-1.5"
>
<CalendarDaysIcon aria-hidden />
{format(selectedDate, "MMMM, yyyy")}
<ChevronDownIcon
className="ml-auto group-data-[state=open]:rotate-180"
aria-hidden
/>
</Button>
</PopoverTrigger>
<PopoverContent
side="bottom"
align="end"
sideOffset={2}
className="w-[var(--radix-popover-trigger-width)]"
<Button
type="button"
variant="ghost"
size="auto"
className="hidden h-6 w-6 rounded-full border border-rhino-blue-950 hover:border-rhino-blue-800 hover:text-rhino-blue-800 lg:inline-flex"
aria-pressed={isAnimated}
onClick={onToggleAnimation}
>
<span className="sr-only">Play layer animation</span>
{!isAnimated && <PlayIcon className="!size-4 transition-colors" aria-hidden />}
{isAnimated && <PauseIcon className="!size-4 transition-colors" aria-hidden />}
</Button>
<div className="flex w-full items-center gap-2 lg:w-auto">
<Label
htmlFor={`dataset-${id}-date`}
className={cn({
"shrink-0 text-xs font-medium": true,
"pointer-events-none opacity-60": isAnimated,
})}
>
<MonthPicker
selected={selectedDate}
minDate={dateRange[0]}
maxDate={dateRange[1]}
onSelect={onChangeSelectedDate}
/>
</PopoverContent>
</Popover>
Displayed on map
</Label>
<Popover>
<PopoverTrigger asChild>
<Button
id={`dataset-${id}-date`}
type="button"
variant="yellow"
className="group/month-picker max-w-[220px] flex-grow justify-between px-3 disabled:bg-rhino-blue-50 disabled:text-rhino-blue-950/60 disabled:opacity-100 lg:flex-grow-0 xl:h-auto xl:py-1.5"
disabled={isAnimated}
>
<CalendarDaysIcon aria-hidden />
{format(
isAnimated ? dateBeforeAnimationRef.current! : selectedDate,
"MMMM, yyyy",
)}
<ChevronDownIcon
className="ml-auto group-data-[state=open]/month-picker:rotate-180"
aria-hidden
/>
</Button>
</PopoverTrigger>
<PopoverContent side="bottom" align="end" sideOffset={2} className="w-[220px]">
<MonthPicker
selected={isAnimated ? dateBeforeAnimationRef.current! : selectedDate}
minDate={dateRange[0]}
maxDate={dateRange[1]}
onSelect={onChangeSelectedDate}
/>
</PopoverContent>
</Popover>
</div>
</div>
)}
</div>
Expand Down
4 changes: 4 additions & 0 deletions client/src/svgs/pause.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions client/src/svgs/play.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions client/tailwind.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const config: Config = {
"50": "#f3f5fb",
"400": "#86a0d4",
"500": "#6982c8",
"800": "#424B8B",
"900": "#38406e",
"950": "#262a45",
},
Expand Down

0 comments on commit de8056b

Please sign in to comment.