Skip to content

Commit

Permalink
Feat week with time selector component (#1439)
Browse files Browse the repository at this point in the history
  • Loading branch information
takurinton committed Sep 28, 2023
1 parent f28dcc1 commit fc2d971
Show file tree
Hide file tree
Showing 11 changed files with 2,658 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .changeset/curvy-mugs-deliver.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"ingred-ui": minor
---

feat WeekTime selector
19 changes: 19 additions & 0 deletions src/components/WeekTimeSelector/WeekTimeSelector.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import * as React from "react";
import { StoryObj } from "@storybook/react";
import WeekTimeSelector, { WeekTimeSelectorProps } from "./WeekTimeSelector";

export default {
title: "Components/Inputs/WeekTime",
component: WeekTimeSelector,
};

export const Example: StoryObj<WeekTimeSelectorProps> = {
args: {
weekTime: "ffffffffffffffffffffffffffffffffffffffffff",
},
render: (args) => {
const [weekTime, setWeekTime] = React.useState(args.weekTime);

return <WeekTimeSelector weekTime={weekTime} onChange={setWeekTime} />;
},
};
135 changes: 135 additions & 0 deletions src/components/WeekTimeSelector/WeekTimeSelector.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import ErrorText from "../ErrorText";
import * as Styled from "./styled";
import React, { Fragment, useEffect, useMemo, useState } from "react";
import { timeList, weekList } from "./constants";
import {
convertTargetSettingToHex,
getNewWeekTimeList,
getTargetSetting,
} from "./utils";

export type WeekTimeSelectorProps = {
weekTime: string;
errorText?: string;
onChange?: (weekTime: string) => void;
};

const WeekTimeSelector: React.FC<WeekTimeSelectorProps> = ({
weekTime,
errorText,
onChange,
}) => {
const weekTimeList = useMemo(() => getTargetSetting(weekTime), [weekTime]);
const [startIndex, setStartIndex] = useState<{
weekIndex: number;
timeIndex: number;
}>({ weekIndex: 0, timeIndex: 0 });
const [endIndex, setEndIndex] = useState<{
weekIndex: number;
timeIndex: number;
}>({ weekIndex: 0, timeIndex: 0 });
const [selectState, setSelectState] = useState<"none" | "choosing">("none");
const [toValue, setToValue] = useState<string>("1");

const handleChangeWeekTime = (
weekIndex: number,
timeIndex: number,
newValue: string,
) => {
if (selectState === "choosing") {
const newWeekTimeList = getNewWeekTimeList(
startIndex.weekIndex,
startIndex.timeIndex,
weekIndex,
timeIndex,
newValue,
[...weekTimeList],
);

setSelectState("none");

onChange?.(convertTargetSettingToHex(newWeekTimeList));
} else if (selectState === "none") {
setStartIndex({ weekIndex, timeIndex });
setEndIndex({ weekIndex, timeIndex });
setSelectState("choosing");
}
};

const isWithinHoverRange = (weekIndex: number, timeIndex: number) => {
if (selectState === "choosing") {
const minWeekIndex = Math.min(startIndex.weekIndex, endIndex.weekIndex);
const maxWeekIndex = Math.max(startIndex.weekIndex, endIndex.weekIndex);
const minTimeIndex = Math.min(startIndex.timeIndex, endIndex.timeIndex);
const maxTimeIndex = Math.max(startIndex.timeIndex, endIndex.timeIndex);

return (
weekIndex >= minWeekIndex &&
weekIndex <= maxWeekIndex &&
timeIndex >= minTimeIndex &&
timeIndex <= maxTimeIndex
);
}
return false;
};

const handleMouseOver = (weekIndex: number, timeIndex: number) => () => {
if (selectState === "none") {
return;
}

setEndIndex({ weekIndex, timeIndex });
};

const handleMouseDown =
(weekIndex: number, timeIndex: number, time: string) => () => {
const newValue = time === "1" ? "0" : "1";
setToValue(newValue);
handleChangeWeekTime(weekIndex, timeIndex, newValue);
};

// MEMO: button 以外の場所で mouseup したときの挙動を制御する
const onMouseUp = () => {
// mousedown の位置が button のときにのみこの条件に到達する想定
if (selectState === "choosing") {
handleChangeWeekTime(endIndex.weekIndex, endIndex.timeIndex, toValue);
}
};

useEffect(() => {
addEventListener("mouseup", onMouseUp);
return () => {
removeEventListener("mouseup", onMouseUp);
};
});

return (
<Styled.Container>
<Styled.WeekTimeContainer>
<Styled.EmptyContainer />
{timeList.map((time) => (
<Styled.TimeContainer key={time}>{time}</Styled.TimeContainer>
))}
{weekTimeList.map((time, weekIndex) => (
// eslint-disable-next-line react/no-array-index-key
<Fragment key={weekIndex}>
<Styled.WeekContainer>{weekList[weekIndex]}</Styled.WeekContainer>
{time.map((t, timeIndex) => (
<Styled.WeekTimeItem
// eslint-disable-next-line react/no-array-index-key
key={`${weekIndex}-${timeIndex}`}
active={t === "1"}
hover={isWithinHoverRange(weekIndex, timeIndex)}
onMouseOver={handleMouseOver(weekIndex, timeIndex)}
onMouseDown={handleMouseDown(weekIndex, timeIndex, t)}
/>
))}
</Fragment>
))}
</Styled.WeekTimeContainer>
{errorText && <ErrorText>{errorText}</ErrorText>}
</Styled.Container>
);
};

export default WeekTimeSelector;
19 changes: 19 additions & 0 deletions src/components/WeekTimeSelector/__tests__/WeekTime.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import * as React from "react";
import "@testing-library/jest-dom";
import { cleanup } from "@testing-library/react";
import { renderWithThemeProvider } from "../../../utils/renderWithThemeProvider";
import WeekTime from "..";

describe("WeekTime component testing", () => {
afterEach(cleanup);

test("WeekTime", () => {
const { asFragment } = renderWithThemeProvider(
<WeekTime
weekTime="ffffffffffffffffffffffffffffffffffffffffff"
onChange={jest.fn()}
/>,
);
expect(asFragment()).toMatchSnapshot();
});
});

0 comments on commit fc2d971

Please sign in to comment.