Mutual Highlighting (#41) (#55)

* Mutual Highlighting (#41)

* Tidy up

* Delete unused API route
This commit is contained in:
Timothy Armes
2023-07-05 13:55:56 +02:00
committed by GitHub
parent 96fda01929
commit 1715b1646b
18 changed files with 451 additions and 272 deletions
+66
View File
@@ -0,0 +1,66 @@
import { atom, SetStateAction } from "jotai";
export default function atomWithDebounce<T>(
initialValue: T,
delayMilliseconds = 500,
shouldDebounceOnReset = false
) {
const prevTimeoutAtom = atom<ReturnType<typeof setTimeout> | undefined>(
undefined
);
// DO NOT EXPORT currentValueAtom as using this atom to set state can cause
// inconsistent state between currentValueAtom and debouncedValueAtom
const _currentValueAtom = atom(initialValue);
const isDebouncingAtom = atom(false);
const debouncedValueAtom = atom(
initialValue,
(get, set, update: SetStateAction<T>) => {
clearTimeout(get(prevTimeoutAtom));
const prevValue = get(_currentValueAtom);
const nextValue =
typeof update === "function"
? (update as (prev: T) => T)(prevValue)
: update;
const onDebounceStart = () => {
set(_currentValueAtom, nextValue);
set(isDebouncingAtom, true);
};
const onDebounceEnd = () => {
set(debouncedValueAtom, nextValue);
set(isDebouncingAtom, false);
};
onDebounceStart();
if (!shouldDebounceOnReset && nextValue === initialValue) {
onDebounceEnd();
return;
}
const nextTimeoutId = setTimeout(() => {
onDebounceEnd();
}, delayMilliseconds);
// set previous timeout atom in case it needs to get cleared
set(prevTimeoutAtom, nextTimeoutId);
}
);
// exported atom setter to clear timeout if needed
const clearTimeoutAtom = atom(null, (get, set, _arg) => {
clearTimeout(get(prevTimeoutAtom));
set(isDebouncingAtom, false);
});
return {
currentValueAtom: atom((get) => get(_currentValueAtom)),
isDebouncingAtom,
clearTimeoutAtom,
debouncedValueAtom
};
}
-30
View File
@@ -1,30 +0,0 @@
import { Db } from "mongodb";
/**
* Converts date from string into a date to allow ordering
*/
export const dateOrderingFrance = async (db: Db) => {
return await db
.collection("tournaments")
.aggregate([
{
$addFields: {
dateParts: {
$dateFromString: {
dateString: "$date",
format: "%d/%m/%Y",
},
},
},
},
{
$sort: {
dateParts: 1,
},
},
{
$unset: "dateParts",
},
])
.toArray();
};
-89
View File
@@ -1,89 +0,0 @@
import { TournamentDataProps, Tournament } from "@/types";
import L, { LatLngLiteral } from "leaflet";
import {
LayerGroup,
LayersControl,
Marker,
Popup,
MarkerProps,
} from "react-leaflet";
import { useTranslations } from "next-intl";
const coordinateRandomisation = (lat: number, lng: number): LatLngLiteral => {
const randomisation = () => Math.random() * (-0.01 - 0.01) + 0.01;
return {
lat: lat + randomisation(),
lng: lng + randomisation(),
};
};
type TournamentMarkerProps = {
tournament: Tournament;
colour: string;
} & Omit<MarkerProps, "position">;
export const TournamentMarker = ({
tournament,
colour,
...markerProps
}: TournamentMarkerProps) => {
const t = useTranslations("Tournaments");
const iconOptions = new L.Icon({
iconUrl: `/images/leaflet/marker-icon-2x-${colour}.png`,
shadowUrl: "/images/leaflet/marker-shadow.png",
iconSize: [25, 41],
iconAnchor: [12, 41],
popupAnchor: [1, -34],
shadowSize: [41, 41],
});
return (
<Marker
position={coordinateRandomisation(
tournament.coordinates[0],
tournament.coordinates[1]
)}
icon={iconOptions}
{...markerProps}
>
<Popup>
<p>
{tournament.date}
<br />
<a href={tournament.url} target="_blank" rel="noopener noreferrer">
{tournament.tournament}
</a>
</p>
{t("approx")}
</Popup>
</Marker>
);
};
export const createLayerGroups = (
timeControl: string,
colour: string,
{ tournamentData }: TournamentDataProps
) => {
const filteredTournaments = tournamentData.filter(
(t) => t.time_control === timeControl
);
const layerGroup = filteredTournaments.map((tournament) => {
return (
<TournamentMarker
key={tournament._id}
tournament={tournament}
colour={colour}
riseOnHover
/>
);
});
return (
<LayersControl.Overlay checked name={timeControl}>
<LayerGroup>{layerGroup}</LayerGroup>
</LayersControl.Overlay>
);
};