Skip to content

Commit

Permalink
refactor: state manager
Browse files Browse the repository at this point in the history
  • Loading branch information
fikyair committed Sep 5, 2021
1 parent b2f6fa8 commit 3ebd890
Show file tree
Hide file tree
Showing 16 changed files with 209 additions and 54 deletions.
2 changes: 1 addition & 1 deletion src/components/Button/button.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const BaseButton = (props: BaseButtonProps) => {
disabled={disabled}
className={className}
btnType={btnType}
onClick={action('clicked')}
onClick={action('onClick')}
> button </Button>
)
}
Expand Down
24 changes: 21 additions & 3 deletions src/components/InputDatePicker/calendar.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
import React, { useEffect, useRef, useState } from "react";
import React, { MouseEvent, useEffect, useRef, useState } from "react";
import DateView from "./dateView";
import MonthYearView from "./monthYearView";
import getYear from "date-fns/get_year";
import getMonth from "date-fns/get_month";
import startOfDay from "date-fns/start_of_day";

function Calendar() {
export interface CalendarProps {
selectedDate: Date;
onSelectDate: (
e: MouseEvent<HTMLElement>,
date: Date | string | number
) => void;
}

function Calendar(props: CalendarProps) {
const { selectedDate, onSelectDate } = props;
const [isDateView, setDateView] = useState(true);
const calendarRef = useRef(null);

Expand All @@ -14,7 +24,7 @@ function Calendar() {
monthIndex: getMonth(today),
};
const [calendar, setCalendar] = useState(initialCalendar);

function onSelectMonth(selectedMonthIndex: number) {
setCalendar({ ...calendar, monthIndex: selectedMonthIndex });
}
Expand All @@ -31,20 +41,28 @@ function Calendar() {
}
}, [isDateView]);

const onClickToday = (e: MouseEvent<HTMLElement>) => {
onSelectDate(e, startOfDay(new Date()));
};

return (
<div className="chocolate-picker" ref={calendarRef}>
{isDateView ? (
<DateView
calendar={calendar}
onSelectMonthYear={setCalendar}
onTitleClick={onSetMonthYearView}
selectedDate={selectedDate}
onSelectDate={onSelectDate}
onClickToday={onClickToday}
/>
) : (
<MonthYearView
calendar={calendar}
onSelectMonth={onSelectMonth}
onBackClick={onSetDateView}
onSelectYear={onSelectYear}
onClickToday={onClickToday}
/>
)}
</div>
Expand Down
8 changes: 8 additions & 0 deletions src/components/InputDatePicker/dateContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { createContext } from "react";
import DateManagerState from './dateManager'


interface Provider {
defaultValue: (value: DateManagerState, onSelectDate: ) => void
}
export default createContext<Provider>(Cell);
54 changes: 54 additions & 0 deletions src/components/InputDatePicker/dateManager.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import React, { ChangeEvent, createContext, useState } from "react";
import { dateToStr } from "./utils/date-extraction";

interface DateManagerState {
date: Date;
textInput: string;
}

interface DateManagerProps {
onChange?: (
e: ChangeEvent<HTMLInputElement>,
value: DateManagerState
) => void;
children: React.ReactNode;
}

export interface IPickerContext {
value: DateManagerState;
onSelectDate: (e: ChangeEvent<HTMLInputElement>, date: Date) => void;
}
export const DateContext = createContext<IPickerContext>({
value: { date: new Date(), textInput: "" },
onSelectDate: () => {},
});

function DateManager(props: DateManagerProps) {
const { onChange, children } = props;
const [state, setState] = useState<DateManagerState>({
date: new Date(),
textInput: "",
});

function onSelectDate(e: ChangeEvent<HTMLInputElement>, date: Date) {
const nextState: DateManagerState = {
date,
textInput: dateToStr(date),
};
setState(nextState);
onChange && onChange(e, nextState);
}

const passedContext: IPickerContext = {
value: state,
onSelectDate,
};

return (
<DateContext.Provider value={passedContext}>
{children}
</DateContext.Provider>
);
}

export default DateManager;
30 changes: 19 additions & 11 deletions src/components/InputDatePicker/datePicker.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,35 @@
import React from 'react'
import { Story, Meta } from '@storybook/react'
import InputDatePicker from './inputDatePicker'
const BaseInputDatePicker = () => <div style={{ width: 300 }}> <InputDatePicker /> </div>
import React from "react";
import { Story, Meta } from "@storybook/react";
import InputDatePicker, { InputDatePickerProps } from "./inputDatePicker";
import { actions } from "@storybook/addon-actions";

const BaseInputDatePicker = (props: InputDatePickerProps) => {
return (
<div style={{ width: 300 }}>
<InputDatePicker onChange={actions("onChange")} />
</div>
);
};

export default {
component: InputDatePicker,
title: 'InputDatePicker',
title: "InputDatePicker",
argTypes: {
defaultValue: {
// options: [],
// control: { type: 'date' }
}
},
},
parameters: {
docs: {
source: {
type: 'code'
}
type: "code",
},
},
controls: {
controls: {
include: [],
hideNoControlsWarning: true
}
hideNoControlsWarning: true,
},
},
} as Meta;

Expand Down
35 changes: 29 additions & 6 deletions src/components/InputDatePicker/datePicker.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import React, { FC, ChangeEvent, useMemo, useState } from "react";
import React, { FC, ChangeEvent, useMemo } from "react";
import classNames from "classnames";
import { scopedClass } from "../../utils/scopedClass";
import buildWeeks, { buildDayNames } from "./generator";
import buildWeeks, { buildDayNames } from "./utils/generator";
import getDate from "date-fns/get_date";
import getMonth from "date-fns/get_month";
import isSameDay from "date-fns/is_same_day";
import dateFnsIsToday from "date-fns/is_today";
import { Button } from "../Button/button";
import { CalendarProps } from "./calendar";

const sc = scopedClass("chocolate-picker");

Expand All @@ -21,32 +22,53 @@ export interface DatePickerProps {
selectedDate: Date;
}

export const DatePicker: FC<DatePickerProps> = (props) => {
export const DatePicker: FC<DatePickerProps & CalendarProps> = (props) => {
const {
calendar: { year, monthIndex },
className,
selectedDate,
onSelectDate,
} = props;

const weeks = useMemo(() => buildWeeks(year, monthIndex), [monthIndex, year]);
const dayNames = useMemo(() => buildDayNames(0), []);
const exchangeDayNames = (name: string) => {
switch (name) {
case "0":
return "星期日";
case "1":
return "星期一";
case "2":
return "星期二";
case "3":
return "星期三";
case "4":
return "星期四";
case "5":
return "星期五";
case "6":
return "星期六";
default:
break;
}
};
return (
<table className={classNames(sc("wrapper"))}>
<thead className={classNames(sc("header"))}>
{dayNames.map((dayName, i) => (
<th key={i}>{dayName}</th>
<th key={i}>{exchangeDayNames(dayName)}</th>
))}
</thead>

<tbody className={classNames("weeks")}>
{weeks.map((week: [], i: number) => (
<tr key={i} className={classNames(sc("weeks-item"))}>
{week.map((day: number, j: number) => {
{week.map((day: Date | string | number, j: number) => {
// 目前是当前日期
const isToday = dateFnsIsToday(day);
// 当前月日期
const isCurrentMonth = getMonth(day) === monthIndex;
// 选中日期
// 选中日期
const isSelected = isSameDay(day, selectedDate);
return (
<td key={j} className={classNames(sc("day"), {})}>
Expand All @@ -57,6 +79,7 @@ export const DatePicker: FC<DatePickerProps> = (props) => {
[`${sc("is-current-month")}`]: !isCurrentMonth,
})}
btnType="ghost"
onClick={(e) => onSelectDate(e, day)}
>
{getDate(day)}
</Button>
Expand Down
25 changes: 17 additions & 8 deletions src/components/InputDatePicker/dateView.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,32 @@
import React, { Dispatch, SetStateAction } from "react";
import React, {MouseEvent, Dispatch, SetStateAction } from "react";
import { ViewLayout } from "./viewLayout";
import DatePicker, { CalendarType } from "./datePicker";
import { Button } from "../Button/button";
import Icon from "../icons/icon";
import HeaderTitle from "./headerTitle";
import { CalendarProps } from "./calendar";

interface DateViewProps {
calendar: CalendarType;
onSelectMonthYear: Dispatch<
SetStateAction<{ year: number; monthIndex: number }>
>;
onTitleClick: () => void;
onClickToday: (e: MouseEvent<HTMLElement>) => void;
}

function module(m: number, n: number) {
return ((m % n) + n) % n;
}
function DateView(props: DateViewProps) {

function DateView(props: DateViewProps & CalendarProps) {
const {
calendar: { year, monthIndex },
onSelectMonthYear,
onTitleClick
onTitleClick,
selectedDate,
onSelectDate,
onClickToday
} = props;

function incrementMonthIndex(increment: number) {
Expand All @@ -41,23 +47,26 @@ function DateView(props: DateViewProps) {
bodyElement={
<DatePicker
calendar={props.calendar}
selectedDate={new Date(2021, 8, 5)}
selectedDate={selectedDate}
onSelectDate={onSelectDate}
/>
}
header={{
leftElement: <Icon icon="arrow-left" onClick={goToPreviousMonth} />,
middleElement: (
<p>
<HeaderTitle year={year} monthIndex={monthIndex} onTitleClick={onTitleClick}/>
<HeaderTitle
year={year}
monthIndex={monthIndex}
onTitleClick={onTitleClick}
/>
</p>
),
rightElement: <Icon icon="arrow-right" onClick={goToNextMonth} />,
}}
footerElement={<Button btnType="ghost"> today</Button>}
footerElement={<Button btnType="ghost" onClick={onClickToday}> 今天 </Button>}
></ViewLayout>
);
}

DateView.propTypes = {};

export default DateView;
8 changes: 4 additions & 4 deletions src/components/InputDatePicker/headerTitle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,24 @@ const sc = scopedClass("chocolate-picker-header-title");
function HeaderTitle(props: CalendarType) {
const { year, monthIndex, onTitleClick, onSelectYear } = props;
const firstDayOfMonth = new Date(year, monthIndex);
const monthLabel = format(firstDayOfMonth, "MMM");
const monthLabel = format(firstDayOfMonth, "MM");
const yearLabel = format(firstDayOfMonth, "YYYY");

if (onSelectYear) {
return (
<div className={sc("wrapper")}>
<span>{monthLabel}</span>
<span>{monthLabel}</span>
<YearPicker
selectedYear={year}
defaultValue={yearLabel}
defaultValue={`${yearLabel} 年`}
onSelectYear={onSelectYear}
/>
</div>
);
} else {
return (
<Button btnType="ghost" size="sm" onClick={onTitleClick}>
{yearLabel} {monthLabel}
{`${yearLabel} 年`} {monthLabel}
</Button>
);
}
Expand Down
10 changes: 10 additions & 0 deletions src/components/InputDatePicker/input.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import React, { useContext } from "react";
import { DateContext, IPickerContext } from "./dateManager";
import { Input } from "../inputs/input";

function InputComponent() {
const { value } = useContext<IPickerContext>(DateContext);
return <Input size="sm" value={value.textInput} />;
}

export default InputComponent;
Loading

0 comments on commit 3ebd890

Please sign in to comment.