Skip to content

Commit

Permalink
feat <DateRangePicker /> (#1369)
Browse files Browse the repository at this point in the history
  • Loading branch information
takurinton committed Aug 16, 2023
1 parent 7302143 commit 8ec9cc7
Show file tree
Hide file tree
Showing 6 changed files with 189 additions and 2 deletions.
5 changes: 5 additions & 0 deletions .changeset/big-sloths-flash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"ingred-ui": minor
---

feat `<DateRangePicker />` component
9 changes: 7 additions & 2 deletions src/components/Calendar/CalendarRange/CalendarRange.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ export type CalendarRangeProps = {
startDate: Dayjs;
endDate: Dayjs;
actions?: Action[];
/**
* 親コンポーネントで calendar を任意のタイミングで閉じたい場合に使用する
*/
onClose?: (clickState: "start" | "end") => void;
onDatesChange: ({
startDate,
endDate,
Expand All @@ -33,13 +37,14 @@ export type CalendarRangeProps = {
* Currently, one year from the currently selected date is displayed.
*/
export const CalendarRange = forwardRef<HTMLDivElement, CalendarRangeProps>(
function ({ startDate, endDate, actions, onDatesChange }, ref) {
function ({ startDate, endDate, actions, onClose, onDatesChange }, ref) {
const scrollRef = useRef<HTMLDivElement>(null);
const { monthList } = useScroll(startDate ?? dayjs(), scrollRef);
const [clickState, setClickState] = useState<"start" | "end">("start");

const handleDateChange = useCallback(
(value: Dayjs) => {
onClose && onClose(clickState);
switch (clickState) {
case "start":
onDatesChange?.({
Expand All @@ -60,7 +65,7 @@ export const CalendarRange = forwardRef<HTMLDivElement, CalendarRangeProps>(
break;
}
},
[clickState, startDate, endDate, onDatesChange],
[onClose, clickState, onDatesChange, endDate, startDate],
);

return (
Expand Down
80 changes: 80 additions & 0 deletions src/components/NewDateRangePicker/NewDateRangePicker.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { StoryObj } from "@storybook/react";
import NewDateRangePicker, {
NewDateRangePickerProps,
} from "./NewDateRangePicker";
import dayjs from "dayjs";
import React, { useState } from "react";

export default {
title: "Components/Inputs/NewDateRangePicker",
component: NewDateRangePicker,
args: {
format: "YYYY-MM-DD",
},
};

export const Example: StoryObj<NewDateRangePickerProps> = {
render: (args) => {
const [date, setDate] = useState({
startDate: dayjs(),
endDate: dayjs().add(1, "week"),
});

return (
<NewDateRangePicker
{...args}
startDate={date.startDate}
endDate={date.endDate}
onDatesChange={setDate}
/>
);
},
};

export const WithActions: StoryObj<NewDateRangePickerProps> = {
render: (args) => {
const [date, setDate] = useState({
startDate: dayjs(),
endDate: dayjs().add(1, "week"),
});
const actions = [
{
text: "今日",
onClick: () => {
setDate({
startDate: dayjs(),
endDate: dayjs(),
});
},
},
{
text: "明日",
onClick: () => {
setDate({
startDate: dayjs(),
endDate: dayjs().add(1, "day"),
});
},
},
{
text: "来週",
onClick: () => {
setDate({
startDate: dayjs(),
endDate: dayjs().add(1, "week"),
});
},
},
];

return (
<NewDateRangePicker
{...args}
actions={actions}
startDate={date.startDate}
endDate={date.endDate}
onDatesChange={setDate}
/>
);
},
};
93 changes: 93 additions & 0 deletions src/components/NewDateRangePicker/NewDateRangePicker.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import * as React from "react";
import { Flex, DateRangeField, CalendarRange, Card } from "..";
import { forwardRef, useRef, useState } from "react";
import {
flip,
offset,
useDismiss,
useFloating,
useInteractions,
useRole,
} from "@floating-ui/react";
import { Dayjs } from "dayjs";
import { Action, Actions } from "../Calendar/internal/Actions";

export type NewDateRangePickerProps = {
startDate: Dayjs;
endDate: Dayjs;
actions?: Action[];
onDatesChange: (date: { startDate: Dayjs; endDate: Dayjs }) => void;
};

export const DateRangePicker = forwardRef<
HTMLDivElement,
NewDateRangePickerProps
>(function DateRangePicker({ startDate, endDate, actions, onDatesChange }) {
const [open, setOpen] = useState(false);
const { context, refs, strategy, x, y } = useFloating({
placement: "right-start",
open,
onOpenChange: setOpen,
middleware: [offset(10), flip()],
});
const ref = useRef<HTMLDivElement>(null);
const dismiss = useDismiss(context);
const role = useRole(context);

const { getReferenceProps, getFloatingProps } = useInteractions([
dismiss,
role,
]);

const handleClickCalendarIcon = () => {
setOpen((prev) => !prev);
};

const handleClose = (clickState: "start" | "end") => {
if (clickState === "end") {
setOpen(false);
}
};

return (
<Flex ref={ref}>
<div
ref={refs.setReference}
{...getReferenceProps()}
style={{ width: "fit-content" }}
>
<DateRangeField
date={{
startDate,
endDate,
}}
onDatesChange={onDatesChange}
onClickCalendarIcon={handleClickCalendarIcon}
/>
</div>
{open && (
<Card
ref={refs.setFloating}
display="flex"
style={{
position: strategy,
top: y ?? 0,
left: x ?? 0,
zIndex: 1,
}}
{...getFloatingProps()}
>
<Actions actions={actions} />
<CalendarRange
startDate={startDate}
endDate={endDate}
onClose={handleClose}
onDatesChange={onDatesChange}
/>
</Card>
)}
</Flex>
);
});

export default React.memo(DateRangePicker);
1 change: 1 addition & 0 deletions src/components/NewDateRangePicker/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from "./NewDateRangePicker";
3 changes: 3 additions & 0 deletions src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,9 @@ export * from "./NavigationRail";
export { default as NewDatePicker } from "./NewDatePicker";
export * from "./NewDatePicker";

export { default as NewDateRangePicker } from "./NewDateRangePicker";
export * from "./NewDateRangePicker";

export { default as NotificationBadge } from "./NotificationBadge";
export * from "./NotificationBadge";

Expand Down

0 comments on commit 8ec9cc7

Please sign in to comment.