Skip to content

Commit

Permalink
Refactor datepicker (#1372)
Browse files Browse the repository at this point in the history
  • Loading branch information
takurinton authored Aug 17, 2023
1 parent 128c401 commit 827ca39
Show file tree
Hide file tree
Showing 14 changed files with 91 additions and 98 deletions.
5 changes: 5 additions & 0 deletions .changeset/serious-roses-roll.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"ingred-ui": patch
---

refactor datepicker and daterangepicker
22 changes: 11 additions & 11 deletions src/components/Calendar/Calendar/internal/Day/Day.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ type Props = {
};

export const Day: FC<Props> = memo(
({ selected, value, onClickDate, children }) => (
<DayContainer
selected={selected}
// eslint-disable-next-line react/jsx-handler-names
onClick={() => {
onClickDate?.(value);
}}
>
{children}
</DayContainer>
),
({ selected, value, onClickDate, children }) => {
const handleClick = () => {
onClickDate?.(value);
};

return (
<DayContainer selected={selected} onClick={handleClick}>
{children}
</DayContainer>
);
},
);
24 changes: 11 additions & 13 deletions src/components/Calendar/CalendarRange/CalendarRange.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import {
import { useScroll } from "../hooks/useScroll";
import { getDayState } from "./utils";
import { Action, Actions } from "../internal/Actions";
import { ClickState, ClickStateType } from "./constants";
import { DateRange } from "./types";

export type CalendarRangeProps = React.HTMLAttributes<HTMLDivElement> & {
startDate: Dayjs;
Expand All @@ -22,19 +24,13 @@ export type CalendarRangeProps = React.HTMLAttributes<HTMLDivElement> & {
/**
* 親コンポーネントで calendar を任意のタイミングで閉じたい場合に使用する
*/
onClose?: (clickState: "start" | "end") => void;
onClose?: (clickState: ClickStateType) => void;
/**
* 閉じるボタンを押したときの振る舞い
* この関数が渡されてないときは、閉じるボタンが表示されない
*/
onClickCloseButton?: () => void;
onDatesChange: ({
startDate,
endDate,
}: {
startDate: Dayjs;
endDate: Dayjs;
}) => void;
onDatesChange: ({ startDate, endDate }: DateRange) => void;
};

/**
Expand All @@ -57,25 +53,27 @@ export const CalendarRange = forwardRef<HTMLDivElement, CalendarRangeProps>(
) {
const scrollRef = useRef<HTMLDivElement>(null);
const { monthList } = useScroll(startDate ?? dayjs(), scrollRef);
const [clickState, setClickState] = useState<"start" | "end">("start");
const [clickState, setClickState] = useState<ClickStateType>(
ClickState.START,
);

const handleDateChange = useCallback(
(value: Dayjs) => {
onClose && onClose(clickState);
switch (clickState) {
case "start":
case ClickState.START:
onDatesChange?.({
startDate: value,
endDate,
});
setClickState("end");
setClickState(ClickState.END);
break;
case "end":
case ClickState.END:
onDatesChange?.({
startDate,
endDate: value,
});
setClickState("start");
setClickState(ClickState.START);
break;
// Maybe, I will add other state.
default:
Expand Down
15 changes: 15 additions & 0 deletions src/components/Calendar/CalendarRange/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export const DayState = {
NONE: "none",
START: "start",
END: "end",
BETWEEN: "between",
} as const;

export type DayStateType = (typeof DayState)[keyof typeof DayState];

export const ClickState = {
START: "start",
END: "end",
} as const;

export type ClickStateType = (typeof ClickState)[keyof typeof ClickState];
9 changes: 5 additions & 4 deletions src/components/Calendar/CalendarRange/internal/Day/Day.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { Dayjs } from "dayjs";
import React, { FC, memo, ReactNode } from "react";
import { DayStyle, DayBetween, DayEnd, DayStart } from "./styled";
import { DayState, DayStateType } from "../../constants";

type Props = {
state: "start" | "end" | "between" | "none";
state: DayStateType;
value: Dayjs;
onClickDate?: (value: Dayjs) => void;
children: ReactNode;
Expand All @@ -16,11 +17,11 @@ export const Day: FC<Props> = memo(
};

switch (state) {
case "start":
case DayState.START:
return <DayStart onClick={onClick}>{children}</DayStart>;
case "end":
case DayState.END:
return <DayEnd onClick={onClick}>{children}</DayEnd>;
case "between":
case DayState.BETWEEN:
return <DayBetween onClick={onClick}>{children}</DayBetween>;
default:
return <DayStyle onClick={onClick}>{children}</DayStyle>;
Expand Down
2 changes: 0 additions & 2 deletions src/components/Calendar/CalendarRange/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,3 @@ export type DateRange = {
startDate: Dayjs;
endDate: Dayjs;
};

export type ClickState = "start" | "end";
19 changes: 10 additions & 9 deletions src/components/Calendar/CalendarRange/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import dayjs, { Dayjs } from "dayjs";
import { DayState } from "./constants";

const isStart = (startDate: Dayjs | null, month: Dayjs, day: number) =>
startDate?.format("YYYY-MM-DD") ===
Expand Down Expand Up @@ -32,14 +33,14 @@ export const getDayState = (
month: Dayjs,
day: number,
) => {
if (isStart(startDate, month, day)) {
return "start";
switch (true) {
case isStart(startDate, month, day):
return DayState.START;
case isEnd(endDate, month, day):
return DayState.END;
case isBetween(startDate, endDate, month, day):
return DayState.BETWEEN;
default:
return DayState.NONE;
}
if (isEnd(endDate, month, day)) {
return "end";
}
if (isBetween(startDate, endDate, month, day)) {
return "between";
}
return "none";
};
20 changes: 10 additions & 10 deletions src/components/Calendar/internal/Actions/Actions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,25 @@ export type Action = {
onClick: () => void;
};

// CalendarRange が実装されたら移動する
export const Actions = memo(({ actions }: { actions?: Action[] }) => {
const theme = useTheme();
const [clickedAction, setClickedAction] = useState<string | null>(null);

const handleClick = (action: Action) => () => {
setClickedAction(action.text);
action.onClick();
};

return actions ? (
<Flex display="flex">
<ActionsContainer>
{actions.map(({ text, onClick }, i) => (
{actions.map((action, i) => (
<Action
key={`${text}-${i.toString()}`}
clicked={clickedAction === text}
// eslint-disable-next-line react/jsx-handler-names
onClick={() => {
setClickedAction(text);
onClick();
}}
key={`${action.text}-${i.toString()}`}
clicked={clickedAction === action.text}
onClick={handleClick(action)}
>
{text}
{action.text}
</Action>
))}
</ActionsContainer>
Expand Down
1 change: 0 additions & 1 deletion src/components/Calendar/styled.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import styled from "styled-components";
import { Flex } from "..";

// TODO: CalendarRange を実装時に移動する
export const Container = styled.div`
padding: 0 ${({ theme }) => theme.spacing * 3}px;
position: relative;
Expand Down
2 changes: 1 addition & 1 deletion src/components/DateField/DateField/DateField.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { forwardRef, memo } from "react";
import { Icon, Input } from "../..";
import { useDateField } from "../useDateField";
import { useMergeRefs } from "../utils";
import { useMergeRefs } from "../../../hooks/useMergeRefs";
import { CalendarIcon, InputContainer } from "../styled";
import { DateFieldProps } from "../types";

Expand Down
14 changes: 9 additions & 5 deletions src/components/DateField/DateRangeField/DateRangeField.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import React, { forwardRef, memo, useMemo } from "react";
import { Icon, Input } from "../..";
import { useMergeRefs } from "../utils";
import { useMergeRefs } from "../../../hooks/useMergeRefs";
import { CalendarIcon, InputContainer } from "./styled";
import { Dayjs } from "dayjs";
import { useDateField } from "../useDateField";
import {
ClickState,
ClickStateType,
} from "../../Calendar/CalendarRange/constants";

type Range = {
startDate: Dayjs;
Expand All @@ -24,9 +28,9 @@ const DateRangeField = forwardRef<HTMLInputElement, DateRangeFieldProps>(
) {
const width = useMemo(() => format.length * 12, [format]);

const handleChange = (t: "start" | "end") => (date: Dayjs) => {
const handleChange = (t: ClickStateType) => (date: Dayjs) => {
const { startDate, endDate } = rest.date;
if (t === "start") {
if (t === ClickState.START) {
rest.onDatesChange?.({
endDate,
startDate: date,
Expand All @@ -43,14 +47,14 @@ const DateRangeField = forwardRef<HTMLInputElement, DateRangeFieldProps>(
...rest,
format,
date: rest.date.startDate,
onDateChange: handleChange("start"),
onDateChange: handleChange(ClickState.START),
});

const { ref: endInputRef, ...endProps } = useDateField({
...rest,
format,
date: rest.date.endDate,
onDateChange: handleChange("end"),
onDateChange: handleChange(ClickState.END),
});

const startRef = useMergeRefs<HTMLInputElement>(propRef, startInputRef);
Expand Down
32 changes: 2 additions & 30 deletions src/components/DateField/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { useMemo } from "react";

type Sections = {
start: number;
end: number;
Expand Down Expand Up @@ -57,31 +55,5 @@ export const getSections = (formattedDate?: string | null) => {
/**
* 開始位置と終了位置と値を持つセクションをフォーマットされた日付に変換する
*/
export const formatString = (sectionsWithCharactor: Sections[]) =>
sectionsWithCharactor.map((section) => section.value).join("");

type ReactRef<T> =
| React.RefCallback<T>
| React.MutableRefObject<T>
| React.ForwardedRef<T>
| string
| null
| undefined;

// from: https://github.com/voyagegroup/ingred-ui/blob/master/src/hooks/useMergeRefs.ts
export function useMergeRefs<T>(...refs: ReactRef<T>[]): React.Ref<T> {
return useMemo(() => {
if (refs.every((ref) => ref === null)) {
return null;
}
return (refValue: T) => {
for (const ref of refs) {
if (typeof ref === "function") {
ref(refValue);
} else if (ref && typeof ref !== "string") {
ref.current = refValue;
}
}
};
}, [refs]);
}
export const formatString = (sectionsWithCharacter: Sections[]) =>
sectionsWithCharacter.map((section) => section.value).join("");
10 changes: 1 addition & 9 deletions src/components/NewDatePicker/NewDatePicker.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React, { forwardRef, memo, useCallback, useState } from "react";
import { Flex, Calendar, DateField } from "..";
// 後で export しておく
import { Action } from "../Calendar/internal/Actions";
import { Dayjs } from "dayjs";
import {
Expand All @@ -20,14 +19,7 @@ export type NewDatePickerProps = {
};

/**
* @todo HTMLInputElementにする
* @example
* ```tsx
* <DatePicker
* date={dayjs()}
* onDateChange={handleDateChange}
* />
* ```
* @memo 次のメジャーリリースで DatePicker に変更。現行の DatePicker は削除。
*/
export const NewDatePicker = forwardRef<HTMLDivElement, NewDatePickerProps>(
function DatePicker(
Expand Down
14 changes: 11 additions & 3 deletions src/components/NewDateRangePicker/NewDateRangePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,22 @@ import {
} from "@floating-ui/react";
import { Dayjs } from "dayjs";
import { Action } from "../Calendar/internal/Actions";
import {
ClickState,
ClickStateType,
} from "../Calendar/CalendarRange/constants";
import { DateRange } from "../Calendar/CalendarRange/types";

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

/**
* @memo 次のメジャーリリースで DateRangePicker に変更。現行の DateRangePicker は削除。
*/
export const DateRangePicker = forwardRef<
HTMLDivElement,
NewDateRangePickerProps
Expand All @@ -43,8 +51,8 @@ export const DateRangePicker = forwardRef<
setOpen((prev) => !prev);
};

const handleClose = (clickState: "start" | "end") => {
if (clickState === "end") {
const handleClose = (clickState: ClickStateType) => {
if (clickState === ClickState.END) {
setOpen(false);
}
};
Expand Down

0 comments on commit 827ca39

Please sign in to comment.