Skip to content

Commit

Permalink
feat(DateRangePicker): supports placing predefined ranges on the left (
Browse files Browse the repository at this point in the history
  • Loading branch information
simonguo committed Aug 26, 2022
1 parent 73cc856 commit 8df4a61
Show file tree
Hide file tree
Showing 10 changed files with 369 additions and 131 deletions.
4 changes: 4 additions & 0 deletions docs/pages/_common/types/range.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,9 @@ interface Range {
label: React.ReactNode;
value: Date | ((date: Date) => Date);
closeOverlay?: boolean;

// Sets the position where the predefined range is displayed, the default is bottom.
// Only supported on DateRangePicker。
placement?: 'bottom' | 'left';
}
```
Original file line number Diff line number Diff line change
@@ -1,30 +1,134 @@
<!--start-code-->

```js
import { DateRangePicker } from 'rsuite';
import { DateRangePicker, Stack } from 'rsuite';
import subDays from 'date-fns/subDays';
import startOfWeek from 'date-fns/startOfWeek';
import endOfWeek from 'date-fns/endOfWeek';
import addDays from 'date-fns/addDays';
import startOfMonth from 'date-fns/startOfMonth';
import endOfMonth from 'date-fns/endOfMonth';
import addMonths from 'date-fns/addMonths';

const predefinedRanges = [
{
label: 'Today',
value: [new Date(), new Date()],
placement: 'left'
},
{
label: 'Yesterday',
value: [addDays(new Date(), -1), addDays(new Date(), -1)],
placement: 'left'
},
{
label: 'This week',
value: [startOfWeek(new Date()), endOfWeek(new Date())],
placement: 'left'
},
{
label: 'Last 7 days',
value: [subDays(new Date(), 6), new Date()],
placement: 'left'
},
{
label: 'Last 30 days',
value: [subDays(new Date(), 29), new Date()],
placement: 'left'
},
{
label: 'This month',
value: [startOfMonth(new Date()), new Date()],
placement: 'left'
},
{
label: 'Last month',
value: [startOfMonth(addMonths(new Date(), -1)), endOfMonth(addMonths(new Date(), -1))],
placement: 'left'
},
{
label: 'This year',
value: [new Date(new Date().getFullYear(), 0, 1), new Date()],
placement: 'left'
},
{
label: 'Last year',
value: [new Date(new Date().getFullYear() - 1, 0, 1), new Date(new Date().getFullYear(), 0, 0)],
placement: 'left'
},
{
label: 'All time',
value: [new Date(new Date().getFullYear() - 1, 0, 1), new Date()],
placement: 'left'
},
{
label: 'Last week',
closeOverlay: false,
value: value => {
const [start = new Date()] = value || [];
return [
addDays(startOfWeek(start, { weekStartsOn: 0 }), -7),
addDays(endOfWeek(start, { weekStartsOn: 0 }), -7)
];
},
appearance: 'default'
},
{
label: 'Next week',
closeOverlay: false,
value: value => {
const [start = new Date()] = value || [];
return [
addDays(startOfWeek(start, { weekStartsOn: 0 }), 7),
addDays(endOfWeek(start, { weekStartsOn: 0 }), 7)
];
},
appearance: 'default'
}
];

const predefinedBottomRanges = [
{
label: 'Today',
value: [new Date(), new Date()]
},
{
label: 'Yesterday',
value: [addDays(new Date(), -1), addDays(new Date(), -1)]
},
{
label: 'This week',
value: [startOfWeek(new Date()), endOfWeek(new Date())]
},
{
label: 'Last 7 days',
value: [subDays(new Date(), 6), new Date()]
},
{
label: 'Last 30 days',
value: [subDays(new Date(), 29), new Date()]
}
];

const App = () => (
<div className="field">
<Stack direction="column" spacing={8} alignItems="flex-start">
<DateRangePicker
ranges={predefinedBottomRanges}
placeholder="Placement defaults to bottom"
style={{ width: 300 }}
/>
<DateRangePicker
ranges={predefinedRanges}
placeholder="Placement left"
style={{ width: 300 }}
/>
<DateRangePicker
ranges={[
{
label: 'Yesterday',
value: [addDays(new Date(), -1), addDays(new Date(), -1)]
},
{
label: 'Today',
value: [new Date(), new Date()]
},
{
label: 'Last 7 days ( closeOverlay:false )',
value: [subDays(new Date(), 6), new Date()],
closeOverlay: false
}
]}
ranges={predefinedRanges}
showOneCalendar
placeholder="One calendar"
style={{ width: 300 }}
/>
</div>
</Stack>
);

ReactDOM.render(<App />, document.getElementById('root'));
Expand Down
22 changes: 20 additions & 2 deletions docs/pages/components/date-range-picker/index.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,32 @@
import React from 'react';
import { DateRangePicker, Button, Divider } from 'rsuite';
import { DateRangePicker, Button, Divider, Stack } from 'rsuite';
import DefaultPage from '@/components/Page';
import startOfWeek from 'date-fns/startOfWeek';
import endOfWeek from 'date-fns/endOfWeek';
import addDays from 'date-fns/addDays';
import startOfMonth from 'date-fns/startOfMonth';
import endOfMonth from 'date-fns/endOfMonth';
import subDays from 'date-fns/subDays';
import isAfter from 'date-fns/isAfter';
import addMonths from 'date-fns/addMonths';

export default function Page() {
return (
<DefaultPage
dependencies={{ DateRangePicker, Button, Divider, addDays, subDays, isAfter }}
dependencies={{
Stack,
DateRangePicker,
Button,
Divider,
addDays,
subDays,
isAfter,
startOfWeek,
endOfWeek,
startOfMonth,
endOfMonth,
addMonths
}}
sandboxDependencies={{ 'date-fns': '^2.13.0' }}
/>
);
Expand Down
67 changes: 67 additions & 0 deletions src/DatePicker/PredefinedRanges.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import React, { useCallback, useState } from 'react';
import Button from '../Button';
import Stack, { StackProps } from '../Stack';
import { useUpdateEffect } from '../utils';
import { getDefaultRanges, getRanges } from './utils';
import { InnerRange, RangeType } from './types';
import { CalendarLocale } from '../locales';

export interface PredefinedRangesProps<T = any, Shortcut = T> extends StackProps {
ranges?: RangeType<Shortcut>[];
calendarDate: T;
locale: CalendarLocale;
disabledShortcut?: (value: T) => boolean;
onClickShortcut?: (value: Shortcut, closeOverlay: boolean, event: React.MouseEvent) => void;
}

const PredefinedRanges = React.forwardRef<HTMLDivElement, PredefinedRangesProps>((props, ref) => {
const {
className,
disabledShortcut,
onClickShortcut,
calendarDate,
ranges: rangesProp,
locale,
...rest
} = props;
const [ranges, setRanges] = useState<InnerRange<any>[]>(getRanges(props));

useUpdateEffect(() => {
setRanges(getRanges({ ranges: rangesProp, calendarDate }));
}, [calendarDate, rangesProp]);

const hasLocaleKey = useCallback(
(key: React.ReactNode) => getDefaultRanges(calendarDate).some(item => item.label === key),
[calendarDate]
);

return (
<Stack className={className} ref={ref} alignItems="flex-start" spacing={4} {...rest}>
{ranges.map(({ value, closeOverlay, label, ...rest }, index: number) => {
const disabled = disabledShortcut?.(value);

const handleClickShortcut = (event: React.MouseEvent) => {
if (disabled) {
return;
}
onClickShortcut?.(value, closeOverlay !== false ? true : false, event);
};

return (
<Button
appearance="link"
size="sm"
key={index}
disabled={disabled}
onClick={handleClickShortcut}
{...rest}
>
{hasLocaleKey(label) && typeof label === 'string' ? locale?.[label] : label}
</Button>
);
})}
</Stack>
);
});

export default PredefinedRanges;

1 comment on commit 8df4a61

@vercel
Copy link

@vercel vercel bot commented on 8df4a61 Aug 26, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.