File size: 3,815 Bytes
cf86710 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 | import { format, isValid, parse } from "date-fns";
import React, { useEffect, useId, useRef, useState } from "react";
import { DayPicker } from "react-day-picker";
export function Dialog() {
const dialogRef = useRef<HTMLDialogElement>(null);
const dialogId = useId();
const headerId = useId();
// Hold the month in state to control the calendar when the input changes
const [month, setMonth] = useState(new Date());
// Hold the selected date in state
const [selectedDate, setSelectedDate] = useState<Date | undefined>(undefined);
// Hold the input value in state
const [inputValue, setInputValue] = useState("");
// Hold the dialog visibility in state
const [isDialogOpen, setIsDialogOpen] = useState(false);
// Function to toggle the dialog visibility
const toggleDialog = () => setIsDialogOpen(!isDialogOpen);
const calendarLabel = `Calendar, ${format(month, "MMMM yyyy")}`;
// Hook to handle the body scroll behavior and focus trapping. You may want to
// use your own trapping library as the body.style overflow will break the
// scroll position.
useEffect(() => {
if (!dialogRef.current) return;
if (isDialogOpen) {
dialogRef.current.showModal();
} else {
dialogRef.current.close();
}
}, [isDialogOpen]);
/**
* Function to handle the DayPicker select event: update the input value and
* the selected date, and set the month.
*/
const handleDayPickerSelect = (date: Date | undefined) => {
if (!date) {
setInputValue("");
setSelectedDate(undefined);
} else {
setSelectedDate(date);
setInputValue(format(date, "MM/dd/yyyy"));
}
dialogRef.current?.close();
};
/**
* Handle the input change event: parse the input value to a date, update the
* selected date and set the month.
*/
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setInputValue(e.target.value); // keep the input value in sync
const parsedDate = parse(e.target.value, "MM/dd/yyyy", new Date());
if (isValid(parsedDate)) {
setSelectedDate(parsedDate);
setMonth(parsedDate);
} else {
setSelectedDate(undefined);
}
};
return (
<div>
<label htmlFor="date-input">
<strong>Pick a Date: </strong>
</label>
<input
style={{ fontSize: "inherit" }}
id="date-input"
type="text"
value={inputValue}
placeholder={"MM/dd/yyyy"}
onChange={handleInputChange}
/>{" "}
<button
type="button"
style={{ fontSize: "inherit" }}
onClick={toggleDialog}
aria-controls={dialogId}
aria-haspopup="dialog"
aria-expanded={isDialogOpen}
aria-label="Open calendar to choose booking date"
>
📆
</button>
<p aria-live="assertive" aria-atomic="true">
{selectedDate !== undefined
? selectedDate.toDateString()
: "Please type or pick a date"}
</p>
<dialog
ref={dialogRef}
id={dialogId}
aria-modal
aria-labelledby={headerId}
onClose={() => setIsDialogOpen(false)}
>
{isDialogOpen && (
<>
<h2 id={headerId}>Choose a date</h2>
<DayPicker
defaultMonth={selectedDate || month}
onMonthChange={setMonth}
autoFocus
role="application"
aria-label={calendarLabel}
mode="single"
selected={selectedDate}
onSelect={handleDayPickerSelect}
footer={
selectedDate !== undefined && (
<>Selected: {selectedDate.toDateString()}</>
)
}
/>
</>
)}
</dialog>
</div>
);
}
|