mirror of
https://github.com/TheRealOwenRees/echecsfrance.git
synced 2026-07-23 04:26:57 +00:00
* Mutual Highlighting (#41) * Tidy up * Delete unused API route
This commit is contained in:
@@ -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
|
||||
};
|
||||
}
|
||||
@@ -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();
|
||||
};
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user