mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-23 13:51:14 -05:00
Reverted to commit 10950a4034
This commit is contained in:
@@ -97,10 +97,6 @@ export function FormerNamesEditor({ names, onChange, currentName }: FormerNamesE
|
|||||||
<DatePicker
|
<DatePicker
|
||||||
date={name.date_changed ? new Date(name.date_changed) : undefined}
|
date={name.date_changed ? new Date(name.date_changed) : undefined}
|
||||||
onSelect={(date) => updateName(actualIndex, 'date_changed', date || undefined)}
|
onSelect={(date) => updateName(actualIndex, 'date_changed', date || undefined)}
|
||||||
placeholder="Select or type date"
|
|
||||||
allowTextEntry={true}
|
|
||||||
fromYear={1800}
|
|
||||||
toYear={new Date().getFullYear()}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -186,8 +186,6 @@ export function ReviewForm({
|
|||||||
placeholder="When did you visit?"
|
placeholder="When did you visit?"
|
||||||
disableFuture={true}
|
disableFuture={true}
|
||||||
fromYear={1950}
|
fromYear={1950}
|
||||||
allowTextEntry={true}
|
|
||||||
dateFormat="yyyy-MM-dd"
|
|
||||||
/>
|
/>
|
||||||
<p className="text-xs text-muted-foreground">
|
<p className="text-xs text-muted-foreground">
|
||||||
Select the date of your visit to help others understand when this experience occurred.
|
Select the date of your visit to help others understand when this experience occurred.
|
||||||
|
|||||||
@@ -1,73 +0,0 @@
|
|||||||
import * as React from "react";
|
|
||||||
import { CaptionProps, useNavigation } from "react-day-picker";
|
|
||||||
import { ChevronLeft, ChevronRight } from "lucide-react";
|
|
||||||
import { Button } from "@/components/ui/button";
|
|
||||||
import { YearGridSelector } from "@/components/ui/year-grid-selector";
|
|
||||||
import { cn } from "@/lib/utils";
|
|
||||||
|
|
||||||
interface CustomCaptionProps extends CaptionProps {
|
|
||||||
fromYear?: number;
|
|
||||||
toYear?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function CustomCalendarCaption({
|
|
||||||
displayMonth,
|
|
||||||
fromYear = 1800,
|
|
||||||
toYear = new Date().getFullYear() + 10,
|
|
||||||
}: CustomCaptionProps) {
|
|
||||||
const { goToMonth, nextMonth, previousMonth, currentMonth } = useNavigation();
|
|
||||||
|
|
||||||
const handleYearSelect = (year: number) => {
|
|
||||||
const newDate = new Date(displayMonth);
|
|
||||||
newDate.setFullYear(year);
|
|
||||||
goToMonth(newDate);
|
|
||||||
};
|
|
||||||
|
|
||||||
const monthName = displayMonth.toLocaleString("en-US", { month: "long" });
|
|
||||||
const selectedYear = displayMonth.getFullYear();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="flex justify-between items-center px-2 py-2">
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
className="h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100"
|
|
||||||
onClick={() => previousMonth && goToMonth(previousMonth)}
|
|
||||||
disabled={!previousMonth}
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
<ChevronLeft className="h-4 w-4" />
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<span className="text-sm font-medium">{monthName}</span>
|
|
||||||
<YearGridSelector
|
|
||||||
selectedYear={selectedYear}
|
|
||||||
onYearSelect={handleYearSelect}
|
|
||||||
fromYear={fromYear}
|
|
||||||
toYear={toYear}
|
|
||||||
>
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
className={cn(
|
|
||||||
"h-7 px-2 text-sm font-medium hover:bg-accent",
|
|
||||||
"focus:bg-accent focus:text-accent-foreground"
|
|
||||||
)}
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
{selectedYear}
|
|
||||||
</Button>
|
|
||||||
</YearGridSelector>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
className="h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100"
|
|
||||||
onClick={() => nextMonth && goToMonth(nextMonth)}
|
|
||||||
disabled={!nextMonth}
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
<ChevronRight className="h-4 w-4" />
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -4,20 +4,13 @@ import { DayPicker } from "react-day-picker";
|
|||||||
|
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { buttonVariants } from "@/components/ui/button";
|
import { buttonVariants } from "@/components/ui/button";
|
||||||
import { CustomCalendarCaption } from "@/components/ui/calendar-custom-caption";
|
|
||||||
|
|
||||||
export type CalendarProps = React.ComponentProps<typeof DayPicker> & {
|
export type CalendarProps = React.ComponentProps<typeof DayPicker>;
|
||||||
enableYearGrid?: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
function Calendar({ className, classNames, showOutsideDays = true, enableYearGrid = false, ...props }: CalendarProps) {
|
function Calendar({ className, classNames, showOutsideDays = true, ...props }: CalendarProps) {
|
||||||
const captionLayout = enableYearGrid ? undefined : (props.captionLayout || "dropdown-buttons");
|
|
||||||
return (
|
return (
|
||||||
<DayPicker
|
<DayPicker
|
||||||
showOutsideDays={showOutsideDays}
|
showOutsideDays={showOutsideDays}
|
||||||
captionLayout={captionLayout}
|
|
||||||
fromYear={props.fromYear || 1800}
|
|
||||||
toYear={props.toYear || new Date().getFullYear() + 10}
|
|
||||||
className={cn("p-3", className)}
|
className={cn("p-3", className)}
|
||||||
classNames={{
|
classNames={{
|
||||||
months: "flex flex-col sm:flex-row space-y-4 sm:space-x-4 sm:space-y-0",
|
months: "flex flex-col sm:flex-row space-y-4 sm:space-x-4 sm:space-y-0",
|
||||||
@@ -49,15 +42,6 @@ function Calendar({ className, classNames, showOutsideDays = true, enableYearGri
|
|||||||
...classNames,
|
...classNames,
|
||||||
}}
|
}}
|
||||||
components={{
|
components={{
|
||||||
Caption: enableYearGrid
|
|
||||||
? (captionProps) => (
|
|
||||||
<CustomCalendarCaption
|
|
||||||
{...captionProps}
|
|
||||||
fromYear={props.fromYear || 1800}
|
|
||||||
toYear={props.toYear || new Date().getFullYear() + 10}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
: undefined,
|
|
||||||
IconLeft: ({ ..._props }) => <ChevronLeft className="h-4 w-4" />,
|
IconLeft: ({ ..._props }) => <ChevronLeft className="h-4 w-4" />,
|
||||||
IconRight: ({ ..._props }) => <ChevronRight className="h-4 w-4" />,
|
IconRight: ({ ..._props }) => <ChevronRight className="h-4 w-4" />,
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -1,79 +0,0 @@
|
|||||||
import * as React from "react";
|
|
||||||
import { format, parse, isValid } from "date-fns";
|
|
||||||
import { cn } from "@/lib/utils";
|
|
||||||
import { DatePicker, DatePickerProps } from "./date-picker";
|
|
||||||
|
|
||||||
interface DateInputWithFeedbackProps extends DatePickerProps {
|
|
||||||
showFeedback?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function DateInputWithFeedback({
|
|
||||||
showFeedback = true,
|
|
||||||
onSelect,
|
|
||||||
...props
|
|
||||||
}: DateInputWithFeedbackProps) {
|
|
||||||
const [parseStatus, setParseStatus] = React.useState<{
|
|
||||||
isValid: boolean;
|
|
||||||
message: string;
|
|
||||||
parsed?: Date;
|
|
||||||
}>({ isValid: true, message: "" });
|
|
||||||
|
|
||||||
const parseDate = (input: string): Date | null => {
|
|
||||||
if (!input) return null;
|
|
||||||
|
|
||||||
// Try ISO format: 2005-06-15
|
|
||||||
let parsed = parse(input, "yyyy-MM-dd", new Date());
|
|
||||||
if (isValid(parsed)) return parsed;
|
|
||||||
|
|
||||||
// Try US format: 06/15/2005
|
|
||||||
parsed = parse(input, "MM/dd/yyyy", new Date());
|
|
||||||
if (isValid(parsed)) return parsed;
|
|
||||||
|
|
||||||
// Try European format: 15/06/2005
|
|
||||||
parsed = parse(input, "dd/MM/yyyy", new Date());
|
|
||||||
if (isValid(parsed)) return parsed;
|
|
||||||
|
|
||||||
// Try short format: 6/15/05
|
|
||||||
parsed = parse(input, "M/d/yy", new Date());
|
|
||||||
if (isValid(parsed)) return parsed;
|
|
||||||
|
|
||||||
// Try short year format: 2005-6-15
|
|
||||||
parsed = parse(input, "yyyy-M-d", new Date());
|
|
||||||
if (isValid(parsed)) return parsed;
|
|
||||||
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSelect = (date: Date | undefined) => {
|
|
||||||
if (date) {
|
|
||||||
setParseStatus({
|
|
||||||
isValid: true,
|
|
||||||
message: `✓ ${format(date, "PPP")}`,
|
|
||||||
parsed: date
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
setParseStatus({ isValid: true, message: "" });
|
|
||||||
}
|
|
||||||
onSelect?.(date);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="space-y-1">
|
|
||||||
<DatePicker
|
|
||||||
allowTextEntry
|
|
||||||
onSelect={handleSelect}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
{showFeedback && parseStatus.message && (
|
|
||||||
<p className={cn(
|
|
||||||
"text-xs",
|
|
||||||
parseStatus.isValid
|
|
||||||
? "text-green-600 dark:text-green-400"
|
|
||||||
: "text-amber-600 dark:text-amber-400"
|
|
||||||
)}>
|
|
||||||
{parseStatus.message}
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,10 +1,9 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { format, parse, isValid } from "date-fns";
|
import { format } from "date-fns";
|
||||||
import { CalendarIcon } from "lucide-react";
|
import { CalendarIcon } from "lucide-react";
|
||||||
|
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Input } from "@/components/ui/input";
|
|
||||||
import { Calendar } from "@/components/ui/calendar";
|
import { Calendar } from "@/components/ui/calendar";
|
||||||
import {
|
import {
|
||||||
Popover,
|
Popover,
|
||||||
@@ -22,9 +21,6 @@ export interface DatePickerProps {
|
|||||||
disablePast?: boolean;
|
disablePast?: boolean;
|
||||||
fromYear?: number;
|
fromYear?: number;
|
||||||
toYear?: number;
|
toYear?: number;
|
||||||
allowTextEntry?: boolean;
|
|
||||||
dateFormat?: string;
|
|
||||||
enableYearGrid?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function DatePicker({
|
export function DatePicker({
|
||||||
@@ -37,66 +33,12 @@ export function DatePicker({
|
|||||||
disablePast = false,
|
disablePast = false,
|
||||||
fromYear,
|
fromYear,
|
||||||
toYear,
|
toYear,
|
||||||
allowTextEntry = false,
|
|
||||||
dateFormat = "yyyy-MM-dd",
|
|
||||||
enableYearGrid = false,
|
|
||||||
}: DatePickerProps) {
|
}: DatePickerProps) {
|
||||||
const [open, setOpen] = React.useState(false);
|
const [open, setOpen] = React.useState(false);
|
||||||
const [textInput, setTextInput] = React.useState("");
|
|
||||||
const [isTyping, setIsTyping] = React.useState(false);
|
|
||||||
|
|
||||||
const parseDate = (input: string): Date | null => {
|
|
||||||
if (!input) return null;
|
|
||||||
|
|
||||||
// Try ISO format: 2005-06-15
|
|
||||||
let parsed = parse(input, "yyyy-MM-dd", new Date());
|
|
||||||
if (isValid(parsed)) return parsed;
|
|
||||||
|
|
||||||
// Try US format: 06/15/2005
|
|
||||||
parsed = parse(input, "MM/dd/yyyy", new Date());
|
|
||||||
if (isValid(parsed)) return parsed;
|
|
||||||
|
|
||||||
// Try European format: 15/06/2005
|
|
||||||
parsed = parse(input, "dd/MM/yyyy", new Date());
|
|
||||||
if (isValid(parsed)) return parsed;
|
|
||||||
|
|
||||||
// Try short format: 6/15/05
|
|
||||||
parsed = parse(input, "M/d/yy", new Date());
|
|
||||||
if (isValid(parsed)) return parsed;
|
|
||||||
|
|
||||||
// Try short year format: 2005-6-15
|
|
||||||
parsed = parse(input, "yyyy-M-d", new Date());
|
|
||||||
if (isValid(parsed)) return parsed;
|
|
||||||
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleTextChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
||||||
const value = e.target.value;
|
|
||||||
setTextInput(value);
|
|
||||||
setIsTyping(true);
|
|
||||||
|
|
||||||
const parsed = parseDate(value);
|
|
||||||
if (parsed) {
|
|
||||||
onSelect?.(parsed);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleBlur = () => {
|
|
||||||
setIsTyping(false);
|
|
||||||
if (date) {
|
|
||||||
setTextInput(format(date, dateFormat));
|
|
||||||
} else {
|
|
||||||
setTextInput("");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSelect = (selectedDate: Date | undefined) => {
|
const handleSelect = (selectedDate: Date | undefined) => {
|
||||||
onSelect?.(selectedDate);
|
onSelect?.(selectedDate);
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
if (selectedDate) {
|
|
||||||
setTextInput(format(selectedDate, dateFormat));
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const getDisabledDates = (date: Date) => {
|
const getDisabledDates = (date: Date) => {
|
||||||
@@ -106,49 +48,6 @@ export function DatePicker({
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (allowTextEntry) {
|
|
||||||
return (
|
|
||||||
<div className="relative flex gap-1">
|
|
||||||
<Input
|
|
||||||
type="text"
|
|
||||||
value={isTyping ? textInput : (date ? format(date, dateFormat) : "")}
|
|
||||||
onChange={handleTextChange}
|
|
||||||
onFocus={() => setIsTyping(true)}
|
|
||||||
onBlur={handleBlur}
|
|
||||||
placeholder={placeholder}
|
|
||||||
disabled={disabled}
|
|
||||||
className={cn("flex-1", className)}
|
|
||||||
/>
|
|
||||||
<Popover open={open} onOpenChange={setOpen}>
|
|
||||||
<PopoverTrigger asChild>
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
size="icon"
|
|
||||||
disabled={disabled}
|
|
||||||
className="shrink-0"
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
<CalendarIcon className="h-4 w-4" />
|
|
||||||
</Button>
|
|
||||||
</PopoverTrigger>
|
|
||||||
<PopoverContent className="w-auto p-0" align="start">
|
|
||||||
<Calendar
|
|
||||||
mode="single"
|
|
||||||
selected={date}
|
|
||||||
onSelect={handleSelect}
|
|
||||||
disabled={getDisabledDates}
|
|
||||||
initialFocus
|
|
||||||
className="p-3 pointer-events-auto"
|
|
||||||
fromYear={fromYear}
|
|
||||||
toYear={toYear}
|
|
||||||
enableYearGrid={enableYearGrid}
|
|
||||||
/>
|
|
||||||
</PopoverContent>
|
|
||||||
</Popover>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Popover open={open} onOpenChange={setOpen}>
|
<Popover open={open} onOpenChange={setOpen}>
|
||||||
<PopoverTrigger asChild>
|
<PopoverTrigger asChild>
|
||||||
@@ -160,7 +59,6 @@ export function DatePicker({
|
|||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
type="button"
|
|
||||||
>
|
>
|
||||||
<CalendarIcon className="mr-2 h-4 w-4" />
|
<CalendarIcon className="mr-2 h-4 w-4" />
|
||||||
<span className="truncate">
|
<span className="truncate">
|
||||||
@@ -178,7 +76,6 @@ export function DatePicker({
|
|||||||
className="p-3 pointer-events-auto"
|
className="p-3 pointer-events-auto"
|
||||||
fromYear={fromYear}
|
fromYear={fromYear}
|
||||||
toYear={toYear}
|
toYear={toYear}
|
||||||
enableYearGrid={enableYearGrid}
|
|
||||||
/>
|
/>
|
||||||
</PopoverContent>
|
</PopoverContent>
|
||||||
</Popover>
|
</Popover>
|
||||||
|
|||||||
@@ -128,7 +128,6 @@ export function FlexibleDateInput({
|
|||||||
disablePast={disablePast}
|
disablePast={disablePast}
|
||||||
fromYear={fromYear}
|
fromYear={fromYear}
|
||||||
toYear={toYear}
|
toYear={toYear}
|
||||||
allowTextEntry={true}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -1,176 +0,0 @@
|
|||||||
import * as React from "react";
|
|
||||||
import { ChevronLeft, ChevronRight } from "lucide-react";
|
|
||||||
import { cn } from "@/lib/utils";
|
|
||||||
import { Button } from "@/components/ui/button";
|
|
||||||
import {
|
|
||||||
Popover,
|
|
||||||
PopoverContent,
|
|
||||||
PopoverTrigger,
|
|
||||||
} from "@/components/ui/popover";
|
|
||||||
|
|
||||||
interface YearGridSelectorProps {
|
|
||||||
selectedYear: number;
|
|
||||||
onYearSelect: (year: number) => void;
|
|
||||||
fromYear?: number;
|
|
||||||
toYear?: number;
|
|
||||||
children: React.ReactNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function YearGridSelector({
|
|
||||||
selectedYear,
|
|
||||||
onYearSelect,
|
|
||||||
fromYear = 1800,
|
|
||||||
toYear = new Date().getFullYear() + 10,
|
|
||||||
children,
|
|
||||||
}: YearGridSelectorProps) {
|
|
||||||
const [open, setOpen] = React.useState(false);
|
|
||||||
const [view, setView] = React.useState<"year" | "decade">("year");
|
|
||||||
const [startYear, setStartYear] = React.useState(() => {
|
|
||||||
// Start from the decade containing the selected year
|
|
||||||
return Math.floor(selectedYear / 10) * 10;
|
|
||||||
});
|
|
||||||
|
|
||||||
const handleYearSelect = (year: number) => {
|
|
||||||
onYearSelect(year);
|
|
||||||
setOpen(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleDecadeSelect = (decade: number) => {
|
|
||||||
setStartYear(decade);
|
|
||||||
setView("year");
|
|
||||||
};
|
|
||||||
|
|
||||||
const navigateYears = (direction: "prev" | "next") => {
|
|
||||||
setStartYear((prev) => prev + (direction === "next" ? 12 : -12));
|
|
||||||
};
|
|
||||||
|
|
||||||
const navigateDecades = (direction: "prev" | "next") => {
|
|
||||||
setStartYear((prev) => prev + (direction === "next" ? 120 : -120));
|
|
||||||
};
|
|
||||||
|
|
||||||
const renderYearGrid = () => {
|
|
||||||
const years: number[] = [];
|
|
||||||
for (let i = 0; i < 12; i++) {
|
|
||||||
const year = startYear + i;
|
|
||||||
if (year >= fromYear && year <= toYear) {
|
|
||||||
years.push(year);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const endYear = startYear + 11;
|
|
||||||
const currentYear = new Date().getFullYear();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="p-3 space-y-3">
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
size="icon"
|
|
||||||
className="h-7 w-7"
|
|
||||||
onClick={() => navigateYears("prev")}
|
|
||||||
disabled={startYear <= fromYear}
|
|
||||||
>
|
|
||||||
<ChevronLeft className="h-4 w-4" />
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
className="text-sm font-medium hover:bg-accent"
|
|
||||||
onClick={() => setView("decade")}
|
|
||||||
>
|
|
||||||
{startYear} - {endYear}
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
size="icon"
|
|
||||||
className="h-7 w-7"
|
|
||||||
onClick={() => navigateYears("next")}
|
|
||||||
disabled={endYear >= toYear}
|
|
||||||
>
|
|
||||||
<ChevronRight className="h-4 w-4" />
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<div className="grid grid-cols-4 gap-1">
|
|
||||||
{years.map((year) => (
|
|
||||||
<Button
|
|
||||||
key={year}
|
|
||||||
variant={year === selectedYear ? "default" : "ghost"}
|
|
||||||
className={cn(
|
|
||||||
"h-9 w-full text-sm font-normal",
|
|
||||||
year === currentYear && year !== selectedYear && "border border-primary"
|
|
||||||
)}
|
|
||||||
onClick={() => handleYearSelect(year)}
|
|
||||||
>
|
|
||||||
{year}
|
|
||||||
</Button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const renderDecadeGrid = () => {
|
|
||||||
const decades: number[] = [];
|
|
||||||
const decadeStart = Math.floor(startYear / 100) * 100;
|
|
||||||
for (let i = 0; i < 12; i++) {
|
|
||||||
const decade = decadeStart + i * 10;
|
|
||||||
if (decade >= Math.floor(fromYear / 10) * 10 && decade <= Math.ceil(toYear / 10) * 10) {
|
|
||||||
decades.push(decade);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const decadeEnd = decadeStart + 110;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="p-3 space-y-3">
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
size="icon"
|
|
||||||
className="h-7 w-7"
|
|
||||||
onClick={() => navigateDecades("prev")}
|
|
||||||
disabled={decadeStart <= Math.floor(fromYear / 100) * 100}
|
|
||||||
>
|
|
||||||
<ChevronLeft className="h-4 w-4" />
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
className="text-sm font-medium hover:bg-accent"
|
|
||||||
onClick={() => setView("year")}
|
|
||||||
>
|
|
||||||
{decadeStart}s - {decadeEnd}s
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
size="icon"
|
|
||||||
className="h-7 w-7"
|
|
||||||
onClick={() => navigateDecades("next")}
|
|
||||||
disabled={decadeEnd >= Math.ceil(toYear / 100) * 100}
|
|
||||||
>
|
|
||||||
<ChevronRight className="h-4 w-4" />
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<div className="grid grid-cols-3 gap-2">
|
|
||||||
{decades.map((decade) => (
|
|
||||||
<Button
|
|
||||||
key={decade}
|
|
||||||
variant="ghost"
|
|
||||||
className="h-10 w-full text-sm font-normal"
|
|
||||||
onClick={() => handleDecadeSelect(decade)}
|
|
||||||
>
|
|
||||||
{decade}s
|
|
||||||
</Button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Popover open={open} onOpenChange={setOpen}>
|
|
||||||
<PopoverTrigger asChild>{children}</PopoverTrigger>
|
|
||||||
<PopoverContent className="w-auto p-0" align="start">
|
|
||||||
{view === "year" ? renderYearGrid() : renderDecadeGrid()}
|
|
||||||
</PopoverContent>
|
|
||||||
</Popover>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user