xtablo-source/ui/src/ui-library/date-range-picker.tsx
Arthur Belleville 374a1ec4b8
Fix lint
2025-10-10 11:10:28 +02:00

150 lines
4.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React from "react";
import {
DateRangePicker as AriaDateRangePicker,
DateRangePickerProps as AriaDateRangePickerProps,
DateRangePickerStateContext,
DateValue,
Group,
useLocale,
} from "react-aria-components";
import { twMerge } from "tailwind-merge";
import { Button } from "./button";
import { DateInput } from "./date-field";
import { Dialog } from "./dialog";
import { CalendarIcon } from "./icons";
import { Popover } from "./popover";
import { RangeCalendar } from "./range-calendar";
import { composeTailwindRenderProps, inputField } from "./utils";
export interface DateRangePickerProps<T extends DateValue> extends AriaDateRangePickerProps<T> {}
export function DateRangePicker<T extends DateValue>({ ...props }: DateRangePickerProps<T>) {
return (
<AriaDateRangePicker
{...props}
className={composeTailwindRenderProps(props.className, inputField)}
/>
);
}
export function DateRangePickerInput() {
const { locale } = useLocale();
const state = React.useContext(DateRangePickerStateContext);
const formattedValue = state?.formatValue(locale, {});
return (
<>
<Group
data-ui="control"
className={({ isFocusWithin }) =>
twMerge(
"[&:has([aria-valuetext=Empty]:) w-full",
"grid grid-cols-[max-content_16px_max-content_1fr] items-center",
"group border-input relative rounded-md border",
"group-data-invalid:border-destructive",
"[&:has(_input[data-disabled=true])]:border-border/50",
"[&:has([data-ui=date-segment][aria-readonly])]:bg-zinc-50",
"dark:[&:has([data-ui=date-segment][aria-readonly])]:bg-white/10",
formattedValue ? "min-w-60" : "min-w-[278px]",
isFocusWithin && "border-ring ring-ring group-data-invalid:border-ring ring-1"
)
}
>
<DateInput
slot="start"
className={[
"flex min-w-fit border-none focus-within:ring-0",
"[&:has([data-ui=date-segment][aria-readonly])]:bg-transparent",
"dark:[&:has([data-ui=date-segment][aria-readonly])]:bg-transparent",
].join(" ")}
/>
<span
aria-hidden="true"
className="text-muted place-self-center group-data-disabled:opacity-50"
>
</span>
<DateInput
slot="end"
className={[
"flex min-w-fit flex-1 border-none opacity-100 focus-within:ring-0",
"[&:has([data-ui=date-segment][aria-readonly])]:bg-transparent",
"dark:[&:has([data-ui=date-segment][aria-readonly])]:bg-transparent",
].join(" ")}
/>
<Button
variant="plain"
isIconOnly
size="sm"
className="text-muted group-hover:text-foreground me-1 justify-self-end focus-visible:-outline-offset-1"
>
<CalendarIcon />
</Button>
</Group>
<Popover placement="bottom" className="rounded-xl">
<Dialog>
<RangeCalendar />
</Dialog>
</Popover>
</>
);
}
export function DateRangePickerButton({
className,
children,
}: {
className?: string;
children?: React.ReactNode;
}) {
const { locale } = useLocale();
const state = React.useContext(DateRangePickerStateContext);
const formattedValue = state?.formatValue(locale, {});
return (
<>
<Group data-ui="control">
<Button
variant="outline"
className={twMerge("border-input w-full min-w-64 px-0 font-normal sm:px-0", className)}
>
<div
className={twMerge(
"grid w-full items-center",
formattedValue ? "grid grid-cols-[1fr_16px_1fr_36px]" : "grid-cols-[1fr_36px]"
)}
>
{formattedValue ? (
<>
<span className="min-w-fit px-3 text-base/6 sm:text-sm/6">
{formattedValue.start}
</span>
<span
aria-hidden="true"
className="text-muted place-self-center group-data-disabled:opacity-50"
>
</span>
<span className="min-w-fit px-3 text-base/6 sm:text-sm/6">
{formattedValue.end}
</span>
</>
) : (
<span className="text-muted justify-self-start px-3">{children}</span>
)}
<CalendarIcon className="text-muted group-hover:text-foreground place-self-center" />
</div>
</Button>
<DateInput slot="start" aria-hidden className="hidden" />
<DateInput slot="end" aria-hidden className="hidden" />
</Group>
<Popover placement="bottom" className="rounded-xl">
<Dialog>
<RangeCalendar />
</Dialog>
</Popover>
</>
);
}