220 lines
9.1 KiB
TypeScript
220 lines
9.1 KiB
TypeScript
import { cn } from "@/utils/class-merge";
|
||
import { ClockIcon } from "@heroicons/react/24/outline";
|
||
import { FunctionComponent } from "preact";
|
||
import { Dispatch, StateUpdater, useEffect, useState } from "preact/hooks";
|
||
import { Calendar, CalendarPassThroughMethodOptions } from "primereact/calendar";
|
||
import { FormEvent } from "primereact/ts-helpers";
|
||
import Button from "./ui/Button";
|
||
import ModalWindow, { ModalWindowProps } from "./ui/Modal";
|
||
|
||
interface ModalCalendarProps extends ModalWindowProps {
|
||
isOpen?: boolean;
|
||
setIsOpen?: Dispatch<StateUpdater<boolean>>;
|
||
onClose?: () => void;
|
||
value?: Date;
|
||
onChange?: (e: FormEvent<Date>) => void;
|
||
}
|
||
|
||
const TRANSITIONS = {
|
||
overlay: {
|
||
timeout: 150,
|
||
cn: {
|
||
enter: "opacity-0 scale-75",
|
||
enterActive: "opacity-100 !scale-100 transition-[transform,opacity] duration-150 ease-in",
|
||
exit: "opacity-100",
|
||
exitActive: "!opacity-0 transition-opacity duration-150 ease-linear",
|
||
},
|
||
},
|
||
};
|
||
|
||
const ModalCalendar: FunctionComponent<ModalCalendarProps> = ({
|
||
isOpen,
|
||
setIsOpen,
|
||
onClose,
|
||
onChange,
|
||
value,
|
||
...rest
|
||
}) => {
|
||
const [showTime, setShowTime] = useState(false);
|
||
const [minDate, setMinDate] = useState(new Date());
|
||
useEffect(() => {
|
||
const interval = setInterval(() => {
|
||
setMinDate(new Date());
|
||
}, 1000);
|
||
return () => clearInterval(interval);
|
||
}, []);
|
||
return (
|
||
<ModalWindow
|
||
{...rest}
|
||
isOpen={isOpen}
|
||
setIsOpen={setIsOpen}
|
||
onClose={() => {
|
||
onClose!();
|
||
setShowTime(false);
|
||
}}
|
||
className="md:h-[40rem] md:w-[30rem]"
|
||
>
|
||
<div class="w-full flex-1 self-start">
|
||
<Calendar
|
||
inline
|
||
onChange={onChange}
|
||
value={value}
|
||
hourFormat="24"
|
||
minDate={minDate}
|
||
showTime={showTime}
|
||
pt={{
|
||
root: ({ props }: CalendarPassThroughMethodOptions) => ({
|
||
className: cn("inline-flex w-full relative", {
|
||
"opacity-60 select-none pointer-events-none cursor-default": props.disabled,
|
||
}),
|
||
}),
|
||
input: ({ props }: CalendarPassThroughMethodOptions) => ({
|
||
root: {
|
||
className: cn(
|
||
"font-sans text-base text-gray-600 bg-white p-3 border border-gray-300 transition-colors duration-200 appearance-none",
|
||
"hover:border-blue-500",
|
||
{
|
||
"rounded-lg": !props.showIcon,
|
||
"border-r-0 rounded-l-lg": props.showIcon,
|
||
}
|
||
),
|
||
},
|
||
}),
|
||
dropdownButton: {
|
||
root: ({ props }: CalendarPassThroughMethodOptions) => ({
|
||
className: cn({ "rounded-l-none": props.icon }),
|
||
}),
|
||
},
|
||
panel: ({ props }: CalendarPassThroughMethodOptions) => ({
|
||
className: cn("bg-[rgba(251,194,199,0.38)]", "min-w-full", {
|
||
"shadow-md border-0 absolute": !props.inline,
|
||
"inline-block overflow-x-auto border border-gray-300 p-2 rounded-lg": props.inline,
|
||
}),
|
||
}),
|
||
header: {
|
||
className: cn(
|
||
"flex items-center justify-between",
|
||
"p-2 text-gray-700 bg-[rgba(251,194,199,0.38)] font-semibold m-0 border-b border-gray-300 rounded-t-lg"
|
||
),
|
||
},
|
||
previousButton: {
|
||
className: cn(
|
||
"flex items-center justify-center cursor-pointer overflow-hidden relative",
|
||
"w-8 h-8 text-gray-600 border-0 bg-transparent rounded-full transition-colors duration-200 ease-in-out",
|
||
"hover:text-gray-700 hover:border-transparent hover:bg-gray-200 "
|
||
),
|
||
},
|
||
title: { className: "leading-8 mx-auto" },
|
||
monthTitle: {
|
||
className: cn("text-gray-700 transition duration-200 font-semibold p-2", "mr-2", "hover:text-blue-500"),
|
||
},
|
||
yearTitle: {
|
||
className: cn("text-gray-700 transition duration-200 font-semibold p-2", "hover:text-blue-500"),
|
||
},
|
||
nextButton: {
|
||
className: cn(
|
||
"flex items-center justify-center cursor-pointer overflow-hidden relative",
|
||
"w-8 h-8 text-gray-600 border-0 bg-transparent rounded-full transition-colors duration-200 ease-in-out",
|
||
"hover:text-gray-700 hover:border-transparent hover:bg-gray-200 "
|
||
),
|
||
},
|
||
table: {
|
||
className: cn("border-collapse w-full", "my-2"),
|
||
},
|
||
tableHeaderCell: { className: "p-2" },
|
||
weekDay: { className: "text-gray-600 " },
|
||
day: { className: "p-2" },
|
||
dayLabel: ({ context }: CalendarPassThroughMethodOptions) => ({
|
||
className: cn(
|
||
"w-6 h-6 rounded-full transition-shadow duration-200 border-transparent border",
|
||
"flex items-center justify-center mx-auto overflow-hidden relative",
|
||
"focus:outline-none focus:outline-offset-0 focus:shadow-[0_0_0_0.2rem_rgba(191,219,254,1)] ",
|
||
{
|
||
"opacity-60 cursor-default": context.disabled,
|
||
"cursor-pointer": !context.disabled,
|
||
},
|
||
{
|
||
"text-gray-600 bg-transparent hover:bg-gray-200 ": !context.selected && !context.disabled,
|
||
"text-blue-700 bg-blue-100 hover:bg-blue-200": context.selected && !context.disabled,
|
||
}
|
||
),
|
||
}),
|
||
monthPicker: { className: "my-2" },
|
||
month: ({ context }: CalendarPassThroughMethodOptions) => ({
|
||
className: cn(
|
||
"w-1/3 inline-flex items-center justify-center cursor-pointer overflow-hidden relative",
|
||
"p-2 transition-shadow duration-200 rounded-lg",
|
||
"focus:outline-none focus:outline-offset-0 focus:shadow-[0_0_0_0.2rem_rgba(191,219,254,1)] ",
|
||
{
|
||
"text-gray-600 bg-transparent hover:bg-gray-200 ": !context.selected && !context.disabled,
|
||
"text-blue-700 bg-blue-100 hover:bg-blue-200": context.selected && !context.disabled,
|
||
}
|
||
),
|
||
}),
|
||
yearPicker: {
|
||
className: cn("my-2"),
|
||
},
|
||
year: ({ context }: CalendarPassThroughMethodOptions) => ({
|
||
className: cn(
|
||
"w-1/2 inline-flex items-center justify-center cursor-pointer overflow-hidden relative",
|
||
"p-2 transition-shadow duration-200 rounded-lg",
|
||
"focus:outline-none focus:outline-offset-0 focus:shadow-[0_0_0_0.2rem_rgba(191,219,254,1)] ",
|
||
{
|
||
"text-gray-600 bg-transparent hover:bg-gray-200 ": !context.selected && !context.disabled,
|
||
"text-blue-700 bg-blue-100 hover:bg-blue-200": context.selected && !context.disabled,
|
||
}
|
||
),
|
||
}),
|
||
timePicker: {
|
||
className: cn("flex justify-center items-center", "border-t-1 border-solid border-gray-300"),
|
||
},
|
||
separatorContainer: { className: "flex items-center flex-col px-2" },
|
||
separator: { className: "text-xl" },
|
||
hourPicker: { className: "flex items-center flex-col px-2" },
|
||
minutePicker: { className: "flex items-center flex-col px-2" },
|
||
ampmPicker: { className: "flex items-center flex-col px-2" },
|
||
incrementButton: {
|
||
className: cn(
|
||
"flex items-center justify-center cursor-pointer overflow-hidden relative",
|
||
"w-8 h-8 text-gray-600 border-0 bg-transparent rounded-full transition-colors duration-200 ease-in-out",
|
||
"hover:text-gray-700 hover:border-transparent hover:bg-gray-200 "
|
||
),
|
||
},
|
||
decrementButton: {
|
||
className: cn(
|
||
"flex items-center justify-center cursor-pointer overflow-hidden relative",
|
||
"w-8 h-8 text-gray-600 border-0 bg-transparent rounded-full transition-colors duration-200 ease-in-out",
|
||
"hover:text-gray-700 hover:border-transparent hover:bg-gray-200 "
|
||
),
|
||
},
|
||
groupContainer: { className: "flex" },
|
||
group: {
|
||
className: cn(
|
||
"flex-1",
|
||
"border-l border-gray-300 pr-0.5 pl-0.5 pt-0 pb-0",
|
||
"first:pl-0 first:border-l-0"
|
||
),
|
||
},
|
||
transition: TRANSITIONS.overlay,
|
||
}}
|
||
/>
|
||
<p class={cn("mt-2 text-center text-xl font-semibold", { hidden: !value })}>
|
||
{value && Intl.DateTimeFormat("ru-RU", { day: "2-digit", month: "2-digit", year: "numeric" }).format(value)}
|
||
</p>
|
||
</div>
|
||
<Button
|
||
className="flex w-full flex-row items-center justify-center gap-2 self-end"
|
||
onClick={() => {
|
||
setShowTime(!showTime);
|
||
}}
|
||
>
|
||
<ClockIcon class="h-8" /> <p>Время</p>
|
||
</Button>
|
||
</ModalWindow>
|
||
);
|
||
};
|
||
|
||
ModalCalendar.displayName = "AHModalCalendar";
|
||
|
||
export default ModalCalendar;
|