mirror of
https://github.com/TheRealOwenRees/echecsfrance.git
synced 2026-07-23 04:26:57 +00:00
Feature/date picker (#238)
- Date range picker for tournaments. - Resets date when closing picker - tRPC and Jotai update
This commit is contained in:
+8
-6
@@ -19,15 +19,13 @@
|
|||||||
"@next/bundle-analyzer": "^14.0.4",
|
"@next/bundle-analyzer": "^14.0.4",
|
||||||
"@tanstack/react-query": "^4.35.0",
|
"@tanstack/react-query": "^4.35.0",
|
||||||
"@trivago/prettier-plugin-sort-imports": "^4.2.0",
|
"@trivago/prettier-plugin-sort-imports": "^4.2.0",
|
||||||
"@trpc/client": "^10.38.1",
|
"@trpc/client": "^10.45.0",
|
||||||
"@trpc/react-query": "^10.38.1",
|
"@trpc/react-query": "^10.45.0",
|
||||||
"@trpc/server": "^10.38.1",
|
"@trpc/server": "^10.45.0",
|
||||||
"@types/node": "^20.8.4",
|
"@types/node": "^20.8.4",
|
||||||
"@types/react": "^18.2.27",
|
|
||||||
"@types/react-dom": "^18.2.18",
|
|
||||||
"autoprefixer": "^10.4.16",
|
"autoprefixer": "^10.4.16",
|
||||||
"date-fns": "^2.30.0",
|
"date-fns": "^2.30.0",
|
||||||
"jotai": "^2.2.2",
|
"jotai": "^2.6.1",
|
||||||
"leaflet": "^1.9.4",
|
"leaflet": "^1.9.4",
|
||||||
"leaflet-defaulticon-compatibility": "^0.1.2",
|
"leaflet-defaulticon-compatibility": "^0.1.2",
|
||||||
"leaflet.markercluster": "^1.5.3",
|
"leaflet.markercluster": "^1.5.3",
|
||||||
@@ -39,6 +37,7 @@
|
|||||||
"next-pwa": "^5.6.0",
|
"next-pwa": "^5.6.0",
|
||||||
"postcss": "8.4.33",
|
"postcss": "8.4.33",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
|
"react-date-range": "^1.4.0",
|
||||||
"react-datepicker": "^4.18.0",
|
"react-datepicker": "^4.18.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-hook-form": "^7.46.1",
|
"react-hook-form": "^7.46.1",
|
||||||
@@ -64,6 +63,9 @@
|
|||||||
"@types/leaflet.markercluster": "^1.5.1",
|
"@types/leaflet.markercluster": "^1.5.1",
|
||||||
"@types/lodash": "^4.14.195",
|
"@types/lodash": "^4.14.195",
|
||||||
"@types/nodemailer": "^6.4.8",
|
"@types/nodemailer": "^6.4.8",
|
||||||
|
"@types/react": "^18.2.27",
|
||||||
|
"@types/react-date-range": "^1.4.9",
|
||||||
|
"@types/react-dom": "^18.2.18",
|
||||||
"@types/react-datepicker": "^4.15.0",
|
"@types/react-datepicker": "^4.15.0",
|
||||||
"@types/react-input-mask": "^3.0.2",
|
"@types/react-input-mask": "^3.0.2",
|
||||||
"eslint": "^8.54.0",
|
"eslint": "^8.54.0",
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useAtom, useAtomValue } from "jotai";
|
||||||
|
import { DateRange } from "react-date-range";
|
||||||
|
import "react-date-range/dist/styles.css";
|
||||||
|
import "react-date-range/dist/theme/default.css";
|
||||||
|
|
||||||
|
import { dateRangeAtom, maxDateAtom } from "@/atoms";
|
||||||
|
import { DatePickerDirection } from "@/types";
|
||||||
|
|
||||||
|
const DatePicker = ({
|
||||||
|
datePickerDirection,
|
||||||
|
}: {
|
||||||
|
datePickerDirection: DatePickerDirection;
|
||||||
|
}) => {
|
||||||
|
const [dateRange, setDateRange] = useAtom(dateRangeAtom);
|
||||||
|
const maxDate = useAtomValue(maxDateAtom);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DateRange
|
||||||
|
onChange={(item) => setDateRange([item.selection])}
|
||||||
|
minDate={new Date()}
|
||||||
|
maxDate={maxDate}
|
||||||
|
months={2}
|
||||||
|
moveRangeOnFirstSelection={true}
|
||||||
|
preventSnapRefocus={true}
|
||||||
|
ranges={dateRange}
|
||||||
|
direction={datePickerDirection}
|
||||||
|
showDateDisplay={false}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DatePicker;
|
||||||
@@ -1,15 +1,18 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useEffect } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
|
|
||||||
import { useAtom, useAtomValue, useSetAtom } from "jotai";
|
import { useAtom, useAtomValue, useSetAtom } from "jotai";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import { FaExternalLinkAlt } from "react-icons/fa";
|
import { BsCalendar2Date } from "react-icons/bs";
|
||||||
import { FaTrophy } from "react-icons/fa";
|
import { FaExternalLinkAlt, FaTrophy } from "react-icons/fa";
|
||||||
import { Tooltip } from "react-tooltip";
|
import { Tooltip } from "react-tooltip";
|
||||||
import { twMerge } from "tailwind-merge";
|
import { twMerge } from "tailwind-merge";
|
||||||
|
|
||||||
|
import DatePicker from "@/app/[locale]/tournaments/DatePicker";
|
||||||
import {
|
import {
|
||||||
|
datePickerIsOpenAtom,
|
||||||
|
dateRangeAtom,
|
||||||
debouncedHoveredListIdAtom,
|
debouncedHoveredListIdAtom,
|
||||||
debouncedHoveredMapIdAtom,
|
debouncedHoveredMapIdAtom,
|
||||||
filteredTournamentsListAtom,
|
filteredTournamentsListAtom,
|
||||||
@@ -20,6 +23,8 @@ import {
|
|||||||
import ScrollToTopButton from "@/components/ScrollToTopButton";
|
import ScrollToTopButton from "@/components/ScrollToTopButton";
|
||||||
import SearchBar from "@/components/SearchBar";
|
import SearchBar from "@/components/SearchBar";
|
||||||
import { useBreakpoint } from "@/hooks/tailwind";
|
import { useBreakpoint } from "@/hooks/tailwind";
|
||||||
|
import useDatePickerWidth from "@/hooks/useDatePickerWidth";
|
||||||
|
import { DatePickerDirection } from "@/types";
|
||||||
|
|
||||||
import TimeControlFilters from "./TimeControlFilters";
|
import TimeControlFilters from "./TimeControlFilters";
|
||||||
|
|
||||||
@@ -27,14 +32,26 @@ const TournamentTable = () => {
|
|||||||
const t = useTranslations("Tournaments");
|
const t = useTranslations("Tournaments");
|
||||||
const at = useTranslations("App");
|
const at = useTranslations("App");
|
||||||
|
|
||||||
|
const isLg = useBreakpoint("lg");
|
||||||
|
const datePickerRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
const filteredTournaments = useAtomValue(filteredTournamentsListAtom);
|
const filteredTournaments = useAtomValue(filteredTournamentsListAtom);
|
||||||
|
|
||||||
const [syncVisible, setSyncVisible] = useAtom(syncVisibleAtom);
|
const [syncVisible, setSyncVisible] = useAtom(syncVisibleAtom);
|
||||||
const [normsOnly, setNormsOnly] = useAtom(normsOnlyAtom);
|
const [normsOnly, setNormsOnly] = useAtom(normsOnlyAtom);
|
||||||
const hoveredMapId = useAtomValue(hoveredMapIdAtom);
|
const hoveredMapId = useAtomValue(hoveredMapIdAtom);
|
||||||
const debouncedHoveredMapId = useAtomValue(debouncedHoveredMapIdAtom);
|
const debouncedHoveredMapId = useAtomValue(debouncedHoveredMapIdAtom);
|
||||||
const setHoveredListId = useSetAtom(debouncedHoveredListIdAtom);
|
const setHoveredListId = useSetAtom(debouncedHoveredListIdAtom);
|
||||||
|
|
||||||
const isLg = useBreakpoint("lg");
|
const setDateRange = useSetAtom(dateRangeAtom);
|
||||||
|
const [datePickerIsOpen, setDatePickerIsOpen] = useAtom(datePickerIsOpenAtom);
|
||||||
|
const [dateDirectionState, setDateDirectionState] =
|
||||||
|
useState<DatePickerDirection>("horizontal");
|
||||||
|
const datePickerColour = datePickerIsOpen
|
||||||
|
? "text-primary-600"
|
||||||
|
: "text-gray-500";
|
||||||
|
|
||||||
|
useDatePickerWidth({ datePickerRef, setDateDirectionState });
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isLg || debouncedHoveredMapId === null) return;
|
if (!isLg || debouncedHoveredMapId === null) return;
|
||||||
@@ -45,14 +62,31 @@ const TournamentTable = () => {
|
|||||||
tournamentRow?.scrollIntoView({ behavior: "smooth" });
|
tournamentRow?.scrollIntoView({ behavior: "smooth" });
|
||||||
}, [debouncedHoveredMapId, isLg]);
|
}, [debouncedHoveredMapId, isLg]);
|
||||||
|
|
||||||
|
const handleDatePickerClick = () => {
|
||||||
|
// reset date range today -> max date
|
||||||
|
setDateRange([
|
||||||
|
{
|
||||||
|
startDate: new Date(),
|
||||||
|
endDate: undefined,
|
||||||
|
key: "selection",
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
setDatePickerIsOpen(!datePickerIsOpen);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section
|
<section
|
||||||
className="grid w-full auto-rows-max pb-20 lg:col-start-2 lg:col-end-3 lg:h-content lg:overflow-y-scroll lg:pb-0"
|
className="grid w-full auto-rows-max pb-20 lg:col-start-2 lg:col-end-3 lg:h-content lg:overflow-y-scroll lg:pb-0"
|
||||||
id="listing"
|
id="listing"
|
||||||
>
|
>
|
||||||
<div className="z-10 flex w-full flex-wrap items-center justify-between gap-3 p-3">
|
<div className="z-10 flex w-full flex-wrap items-center justify-start gap-3 p-3">
|
||||||
<SearchBar />
|
<SearchBar />
|
||||||
|
|
||||||
|
<BsCalendar2Date
|
||||||
|
className={`cursor-pointer text-3xl ${datePickerColour}`}
|
||||||
|
onClick={handleDatePickerClick}
|
||||||
|
/>
|
||||||
|
|
||||||
<div className="flex flex-col gap-0 text-gray-900 dark:text-white">
|
<div className="flex flex-col gap-0 text-gray-900 dark:text-white">
|
||||||
<label>
|
<label>
|
||||||
<input
|
<input
|
||||||
@@ -80,6 +114,12 @@ const TournamentTable = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="flex justify-center" ref={datePickerRef}>
|
||||||
|
{datePickerIsOpen && (
|
||||||
|
<DatePicker datePickerDirection={dateDirectionState} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
<ScrollToTopButton />
|
<ScrollToTopButton />
|
||||||
|
|
||||||
<div className="overflow-x-scroll">
|
<div className="overflow-x-scroll">
|
||||||
|
|||||||
@@ -1,4 +1,11 @@
|
|||||||
import { differenceInDays, isSameDay, parse } from "date-fns";
|
import {
|
||||||
|
differenceInDays,
|
||||||
|
formatISO,
|
||||||
|
isSameDay,
|
||||||
|
parse,
|
||||||
|
setDefaultOptions,
|
||||||
|
} from "date-fns";
|
||||||
|
import { fr } from "date-fns/locale";
|
||||||
import { groupBy } from "lodash";
|
import { groupBy } from "lodash";
|
||||||
import { unstable_cache } from "next/cache";
|
import { unstable_cache } from "next/cache";
|
||||||
|
|
||||||
@@ -9,6 +16,8 @@ import { errorLog } from "@/utils/logger";
|
|||||||
|
|
||||||
import TournamentsDisplay from "./TournamentsDisplay";
|
import TournamentsDisplay from "./TournamentsDisplay";
|
||||||
|
|
||||||
|
setDefaultOptions({ locale: fr });
|
||||||
|
|
||||||
const getTournaments = async () => {
|
const getTournaments = async () => {
|
||||||
try {
|
try {
|
||||||
const client = await clientPromise;
|
const client = await clientPromise;
|
||||||
@@ -129,6 +138,7 @@ const getTournaments = async () => {
|
|||||||
town: t.town,
|
town: t.town,
|
||||||
department: t.department,
|
department: t.department,
|
||||||
date: t.date,
|
date: t.date,
|
||||||
|
isoDate: formatISO(date),
|
||||||
url: t.url,
|
url: t.url,
|
||||||
timeControl,
|
timeControl,
|
||||||
latLng: { lat: t.coordinates[0], lng: t.coordinates[1] },
|
latLng: { lat: t.coordinates[0], lng: t.coordinates[1] },
|
||||||
|
|||||||
+31
-6
@@ -1,10 +1,14 @@
|
|||||||
|
import { formatISO, setDefaultOptions } from "date-fns";
|
||||||
|
import { fr } from "date-fns/locale";
|
||||||
import { atom } from "jotai";
|
import { atom } from "jotai";
|
||||||
import { LatLngBounds, LatLngLiteral } from "leaflet";
|
import { LatLngBounds } from "leaflet";
|
||||||
|
|
||||||
import { Club, TimeControl, Tournament } from "@/types";
|
import { Club, TimeControl, Tournament } from "@/types";
|
||||||
import atomWithDebounce from "@/utils/atomWithDebounce";
|
import atomWithDebounce from "@/utils/atomWithDebounce";
|
||||||
import { normalizedContains } from "@/utils/string";
|
import { normalizedContains } from "@/utils/string";
|
||||||
|
|
||||||
|
setDefaultOptions({ locale: fr });
|
||||||
|
|
||||||
export const burgerMenuIsOpenAtom = atom(false);
|
export const burgerMenuIsOpenAtom = atom(false);
|
||||||
export const mapBoundsAtom = atom<LatLngBounds | null>(null);
|
export const mapBoundsAtom = atom<LatLngBounds | null>(null);
|
||||||
export const syncVisibleAtom = atom(true);
|
export const syncVisibleAtom = atom(true);
|
||||||
@@ -20,11 +24,6 @@ export const otherAtom = atom(true);
|
|||||||
|
|
||||||
export const clubsAtom = atom<Club[]>([]);
|
export const clubsAtom = atom<Club[]>([]);
|
||||||
|
|
||||||
export const franceCenterAtom = atom<LatLngLiteral>({
|
|
||||||
lat: 47.0844,
|
|
||||||
lng: 2.3964,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const {
|
export const {
|
||||||
currentValueAtom: hoveredMapIdAtom,
|
currentValueAtom: hoveredMapIdAtom,
|
||||||
debouncedValueAtom: debouncedHoveredMapIdAtom,
|
debouncedValueAtom: debouncedHoveredMapIdAtom,
|
||||||
@@ -41,8 +40,17 @@ export const filteredTournamentsByTimeControlAtom = atom((get) => {
|
|||||||
const blitz = get(blitzAtom);
|
const blitz = get(blitzAtom);
|
||||||
const other = get(otherAtom);
|
const other = get(otherAtom);
|
||||||
|
|
||||||
|
const dateRange = get(dateRangeAtom);
|
||||||
|
const startDate = formatISO(dateRange[0].startDate);
|
||||||
|
const endDate =
|
||||||
|
dateRange[0].endDate !== undefined
|
||||||
|
? formatISO(dateRange[0].endDate)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
return tournaments.filter(
|
return tournaments.filter(
|
||||||
(tournament) =>
|
(tournament) =>
|
||||||
|
tournament.isoDate >= startDate &&
|
||||||
|
(endDate === undefined || tournament.isoDate <= endDate) &&
|
||||||
!tournament.pending &&
|
!tournament.pending &&
|
||||||
tournament.status === "scheduled" &&
|
tournament.status === "scheduled" &&
|
||||||
((tournament.timeControl === TimeControl.Classic && classic) ||
|
((tournament.timeControl === TimeControl.Classic && classic) ||
|
||||||
@@ -102,3 +110,20 @@ export const filteredClubsListAtom = atom((get) => {
|
|||||||
// Filter by those in the current map bounds
|
// Filter by those in the current map bounds
|
||||||
return clubs.filter((club) => mapBounds.contains(club.latLng));
|
return clubs.filter((club) => mapBounds.contains(club.latLng));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Date picker atoms
|
||||||
|
export const datePickerIsOpenAtom = atom(false);
|
||||||
|
|
||||||
|
export const maxDateAtom = atom((get) => {
|
||||||
|
const tournaments = get(tournamentsAtom);
|
||||||
|
const dateTimestamps = tournaments.map((t) => new Date(t.isoDate).getTime());
|
||||||
|
return new Date(Math.max(...dateTimestamps));
|
||||||
|
});
|
||||||
|
|
||||||
|
export const dateRangeAtom = atom<any>([
|
||||||
|
{
|
||||||
|
startDate: new Date(),
|
||||||
|
endDate: undefined,
|
||||||
|
key: "selection",
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import { Dispatch, RefObject, SetStateAction, useEffect } from "react";
|
||||||
|
|
||||||
|
import { DatePickerDirection } from "@/types";
|
||||||
|
|
||||||
|
const useDatePickerWidth = ({
|
||||||
|
datePickerRef,
|
||||||
|
setDateDirectionState,
|
||||||
|
}: {
|
||||||
|
datePickerRef: RefObject<HTMLDivElement>;
|
||||||
|
setDateDirectionState: Dispatch<SetStateAction<DatePickerDirection>>;
|
||||||
|
}) => {
|
||||||
|
useEffect(() => {
|
||||||
|
const updateDatePickerDirection = () => {
|
||||||
|
const datePickerWidth = datePickerRef.current?.offsetWidth ?? 0;
|
||||||
|
const isLg = datePickerWidth >= 680;
|
||||||
|
isLg
|
||||||
|
? setDateDirectionState("horizontal")
|
||||||
|
: setDateDirectionState("vertical");
|
||||||
|
};
|
||||||
|
updateDatePickerDirection();
|
||||||
|
|
||||||
|
window.addEventListener("resize", updateDatePickerDirection);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener("resize", updateDatePickerDirection);
|
||||||
|
};
|
||||||
|
}, [datePickerRef, setDateDirectionState]);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useDatePickerWidth;
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||||
|
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||||
|
<svg fill="#000000" height="800px" width="800px" version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
viewBox="0 0 29.125 29.125" xml:space="preserve">
|
||||||
|
<g>
|
||||||
|
<path d="M21.703,6.148c1.119,0,2.021-0.898,2.021-2.016V2.02c0-1.12-0.902-2.02-2.021-2.02c-1.114,0-2.018,0.9-2.018,2.02v2.113
|
||||||
|
C19.686,5.25,20.589,6.148,21.703,6.148z"/>
|
||||||
|
<path d="M28.883,3.498h-4.069v1.023c0,1.699-1.379,3.078-3.074,3.078c-1.699,0-3.076-1.379-3.076-3.078V3.498h-8.202v1.023
|
||||||
|
c0,1.699-1.378,3.078-3.075,3.078c-1.7,0-3.074-1.379-3.074-3.078V3.498L0.209,3.445v25.678l2.052,0.002h24.603l2.051-0.008
|
||||||
|
L28.883,3.498z M26.863,27.08H2.261V10.674h24.603V27.08H26.863z"/>
|
||||||
|
<path d="M7.352,6.148c1.119,0,2.021-0.898,2.021-2.016V2.02C9.372,0.9,8.471,0,7.352,0C6.237,0,5.334,0.9,5.334,2.02v2.113
|
||||||
|
C5.334,5.25,6.237,6.148,7.352,6.148z"/>
|
||||||
|
<path d="M7.798,21.752c0.422,0.23,1.381,0.619,2.346,0.619c1.224,0,1.843-0.588,1.843-1.334c0-1.006-0.996-1.457-2.028-1.457H8.995
|
||||||
|
v-1.691h0.928c0.792,0,1.785-0.311,1.785-1.164c0-0.605-0.483-1.055-1.488-1.055c-0.823,0-1.688,0.357-2.095,0.607l-0.481-1.705
|
||||||
|
c0.609-0.389,1.8-0.762,3.12-0.762c2.142,0,3.33,1.131,3.33,2.51c0,1.072-0.601,1.926-1.845,2.342v0.033
|
||||||
|
c1.211,0.217,2.189,1.133,2.189,2.465c0,1.768-1.566,3.07-4.127,3.07c-1.305,0-2.403-0.34-2.991-0.699L7.798,21.752z"/>
|
||||||
|
<polygon points="19.001,16.076 18.973,16.076 17.05,16.99 16.661,15.221 19.344,13.982 21.282,13.982 21.282,24.062 19.001,24.062
|
||||||
|
"/>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.8 KiB |
@@ -1,6 +1,5 @@
|
|||||||
import { initTRPC } from "@trpc/server";
|
import { initTRPC } from "@trpc/server";
|
||||||
import superjson from "superjson";
|
import superjson from "superjson";
|
||||||
import { z } from "zod";
|
|
||||||
|
|
||||||
const t = initTRPC.create({
|
const t = initTRPC.create({
|
||||||
transformer: superjson,
|
transformer: superjson,
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ export type Tournament = {
|
|||||||
url: string;
|
url: string;
|
||||||
timeControl: TimeControl;
|
timeControl: TimeControl;
|
||||||
date: string;
|
date: string;
|
||||||
|
isoDate: string;
|
||||||
latLng: LatLngLiteral;
|
latLng: LatLngLiteral;
|
||||||
norm: boolean;
|
norm: boolean;
|
||||||
pending: boolean;
|
pending: boolean;
|
||||||
@@ -78,6 +79,8 @@ export type ResponseMessage = {
|
|||||||
message: string;
|
message: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type DatePickerDirection = "horizontal" | "vertical";
|
||||||
|
|
||||||
export type ScrollableElement = Window | HTMLElement;
|
export type ScrollableElement = Window | HTMLElement;
|
||||||
|
|
||||||
// Prettify takes a type as its argument and returns a new type that has the same properties as the original type,
|
// Prettify takes a type as its argument and returns a new type that has the same properties as the original type,
|
||||||
|
|||||||
Reference in New Issue
Block a user