Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor datepicker #1372

Merged
merged 10 commits into from
Aug 17, 2023
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
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