Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions apps/www/src/content/docs/components/calendar/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,14 @@ import { Calendar, RangePicker, DatePicker } from '@raystack/apsara'

## RangePicker Props

The RangePicker supports customizing the popover behavior using the `popoverProps` prop.

<auto-type-table path="./props.ts" name="RangePickerProps" />

## DatePicker Props

The DatePicker supports customizing the popover behavior using the `popoverProps` prop.

<auto-type-table path="./props.ts" name="DatePickerProps" />

## Examples
Expand Down
7 changes: 7 additions & 0 deletions apps/www/src/content/docs/components/calendar/props.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { InputFieldProps } from '../input-field/props';
import { PopoverContentProps } from '../popover/props';

export interface CalendarProps {
/** Number of months to display */
Expand Down Expand Up @@ -74,6 +75,9 @@ export interface RangePickerProps {
* If not provided, uses the local timezone.
*/
timeZone?: string;

/** Props for customizing the popover */
popoverProps?: PopoverContentProps;
}

export interface DatePickerProps {
Expand Down Expand Up @@ -114,4 +118,7 @@ export interface DatePickerProps {
* If not provided, uses the local timezone.
*/
timeZone?: string;

/** Props for customizing the popover */
popoverProps?: PopoverContentProps;
}
2 changes: 1 addition & 1 deletion packages/raystack/components/calendar/calendar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ function DropDown({

useEffect(() => {
if (open && onDropdownOpen) onDropdownOpen();
}, [open]);
}, [open, onDropdownOpen]);

function handleChange(value: string) {
if (onChange) {
Expand Down
32 changes: 18 additions & 14 deletions packages/raystack/components/calendar/date-picker.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
'use client';

import { CalendarIcon } from '@radix-ui/react-icons';
import { cx } from 'class-variance-authority';
import dayjs from 'dayjs';
import customParseFormat from 'dayjs/plugin/customParseFormat';
import { useCallback, useEffect, useRef, useState } from 'react';
import { PropsBase, PropsSingleRequired } from 'react-day-picker';

import { InputField } from '../input-field';
import { InputFieldProps } from '../input-field/input-field';
import { Popover } from '../popover';
import { PopoverContentProps } from '../popover/popover';
import { Calendar } from './calendar';
import styles from './calendar.module.css';

dayjs.extend(customParseFormat);

interface DatePickerProps {
side?: 'top' | 'right' | 'bottom' | 'left';
dateFormat?: string;
inputFieldProps?: InputFieldProps;
calendarProps?: PropsSingleRequired & PropsBase;
Expand All @@ -26,18 +26,19 @@ interface DatePickerProps {
| ((props: { selectedDate: string }) => React.ReactNode);
showCalendarIcon?: boolean;
timeZone?: string;
popoverProps?: PopoverContentProps;
}

export function DatePicker({
side = 'top',
dateFormat = 'DD/MM/YYYY',
inputFieldProps,
calendarProps,
value = new Date(),
onSelect = () => {},
children,
showCalendarIcon = true,
timeZone
timeZone,
popoverProps
}: DatePickerProps) {
const [showCalendar, setShowCalendar] = useState(false);
const [selectedDate, setSelectedDate] = useState(value);
Expand All @@ -55,19 +56,22 @@ export function DatePicker({
selectedDateRef.current = selectedDate;
}, [selectedDate]);

function isElementOutside(el: HTMLElement) {
const isElementOutside = useCallback((el: HTMLElement) => {
return (
!isDropdownOpenRef.current && // Month and Year dropdown from Date picker
!inputFieldRef.current?.contains(el) && // InputField
!contentRef.current?.contains(el)
);
}

const handleMouseDown = useCallback((event: MouseEvent) => {
const el = event.target as HTMLElement | null;
if (el && isElementOutside(el)) removeEventListeners();
}, []);

const handleMouseDown = useCallback(
(event: MouseEvent) => {
const el = event.target as HTMLElement | null;
if (el && isElementOutside(el)) removeEventListeners();
},
[isElementOutside]
);

function registerEventListeners() {
isInputFieldFocused.current = true;
document.addEventListener('mouseup', handleMouseDown);
Expand Down Expand Up @@ -184,17 +188,17 @@ export function DatePicker({
) : children ? (
<div>{children}</div>
) : (
defaultTrigger
<div>{defaultTrigger}</div>
);

return (
<Popover open={showCalendar} onOpenChange={onOpenChange}>
<Popover.Trigger asChild>{trigger}</Popover.Trigger>

<Popover.Content
side={side}
className={styles.calendarPopover}
ref={contentRef}
{...popoverProps}
className={cx(styles.calendarPopover, popoverProps?.className)}
side={popoverProps?.side ?? 'top'}
>
<Calendar
required={true}
Expand Down
14 changes: 10 additions & 4 deletions packages/raystack/components/calendar/range-picker.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
'use client';

import { CalendarIcon } from '@radix-ui/react-icons';
import { cx } from 'class-variance-authority';
import dayjs from 'dayjs';
import { useCallback, useMemo, useState } from 'react';
import { DateRange, PropsBase, PropsRangeRequired } from 'react-day-picker';
import { Flex } from '../flex';
import { InputField } from '../input-field';
import { InputFieldProps } from '../input-field/input-field';
import { Popover } from '../popover';
import { PopoverContentProps } from '../popover/popover';
import { Calendar } from './calendar';
import styles from './calendar.module.css';

interface RangePickerProps {
side?: 'top' | 'right' | 'bottom' | 'left';
dateFormat?: string;
inputFieldsProps?: { startDate?: InputFieldProps; endDate?: InputFieldProps };
calendarProps?: PropsRangeRequired & PropsBase;
Expand All @@ -26,12 +27,12 @@ interface RangePickerProps {
showCalendarIcon?: boolean;
footer?: React.ReactNode;
timeZone?: string;
popoverProps?: PopoverContentProps;
}

type RangeFields = keyof DateRange;

export function RangePicker({
side = 'top',
dateFormat = 'DD/MM/YYYY',
inputFieldsProps = {},
calendarProps,
Expand All @@ -45,7 +46,8 @@ export function RangePicker({
children,
showCalendarIcon = true,
footer,
timeZone
timeZone,
popoverProps
}: RangePickerProps) {
const [showCalendar, setShowCalendar] = useState(false);
const [currentRangeField, setCurrentRangeField] =
Expand Down Expand Up @@ -166,7 +168,11 @@ export function RangePicker({
return (
<Popover open={showCalendar} onOpenChange={setShowCalendar}>
<Popover.Trigger asChild>{trigger}</Popover.Trigger>
<Popover.Content side={side} className={styles.calendarPopover}>
<Popover.Content
{...popoverProps}
className={cx(styles.calendarPopover, popoverProps?.className)}
side={popoverProps?.side ?? 'top'}
>
<Calendar
showOutsideDays={false}
numberOfMonths={2}
Expand Down
2 changes: 1 addition & 1 deletion packages/raystack/components/popover/popover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import styles from './popover.module.css';

const popoverContent = cva(styles.popover);

interface PopoverContentProps
export interface PopoverContentProps
extends React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content> {
ariaLabel?: string;
}
Expand Down