Skip to content

Commit

Permalink
feature(date-picker): 日期选择组件支持农历 (#2036)
Browse files Browse the repository at this point in the history
  • Loading branch information
orime authored Oct 20, 2023
1 parent 2ebfcc5 commit 2760f02
Show file tree
Hide file tree
Showing 13 changed files with 168 additions and 21 deletions.
14 changes: 14 additions & 0 deletions packages/zent/assets/date-picker.scss
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,10 @@
width: 280px;
cursor: pointer;

&_lunar {
width: 330px;
}

&-header {
@include font-large;
display: flex;
Expand Down Expand Up @@ -223,6 +227,16 @@
box-sizing: border-box;
}

.zent-datepicker-lunar-cell {
display: block;
width: 32px;
height: 38px;

&_text {
font-size: $font-size-small;
}
}

&_current {
.zent-datepicker-cell-inner {
@include border-with-radius;
Expand Down
1 change: 1 addition & 0 deletions packages/zent/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"big.js": "^6.1.1",
"classnames": "^2.2.6",
"date-fns": "^2.7.0",
"lunar-typescript": "^1.6.10",
"observable-hooks": "^4.1.0",
"react-is": "^17.0.1",
"react-transition-group": "^4.4.1",
Expand Down
2 changes: 2 additions & 0 deletions packages/zent/src/date-picker/DatePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ export interface IDatePickerProps<T extends IValueType = 'string'>
showTime?: IShowTime;
disabledTime?: IDisabledTime;
hideFooter?: boolean;
showLunarDate?: boolean;
lunarValueFormatter?: (date: Date) => string;
}
const defaultDatePickerProps = {
format: DATE_FORMAT,
Expand Down
3 changes: 3 additions & 0 deletions packages/zent/src/date-picker/README_en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,15 @@ interface IDisableDateMap {
| hideFooter | Whether to show footer | `boolean` | `false` | No |
| showTime | To provide an additional time selection | `boolean` \| `object` | `false` | No |
| disabledTime | To specify the time that cannot be selected | `(date?: Date) => IDisabledTimeOption` | - | No |
| showLunarDate | Whether to display lunar date | `boolean` | `false` | No |
| lunarValueFormatter | Format lunar date backfill value | `(date: Date) => string` | - | No |

**Additional**

- When return value of `showTime` is an object, to provide an additional time selection, there are some properties within this object: `format``hourStep``minuteStep``secondStep`, but redefines the type of `defaultTime` to be `string | (date: Date) => string`
- `disabledTime` only works with `showTime`, see the details in `TimePicker`
- `format` should be `'YYYY-MM-DD HH:mm:ss'` when `showTime` is `true`
- `lunarValueFormatter` only works with `showLunarDate`

#### WeekPicker API

Expand Down
4 changes: 4 additions & 0 deletions packages/zent/src/date-picker/README_zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,16 @@ interface IDisableDateMap {
| hideFooter | 隐藏面板底部 | `boolean` | `false` ||
| showTime | 是否支持时间选择功能 | `boolean` \| `object` | `false` ||
| disabledTime | 时间禁用判断 | `(date?: Date) => IDisabledTimeOption` | - ||
| showLunarDate | 是否显示农历日期 | `boolean` | `false` ||
| lunarValueFormatter | 格式化农历日期回填值 | `(date: Date) => string` | - ||

**注意:**

- `showTime` 为对象时,表示支持时间选择,具体属性可参考 `TimePicker`,包括 `format``hourStep``minuteStep``secondStep`,其中 `defaultTime` 类型定义为 `string | (date: Date) => string`
- `disabledTime``showTime` 开启时生效,可参考 `TimePicker``IDisabledTimeOption`
- `showTime``format` 值应为`'YYYY-MM-DD HH:mm:ss'`,使用时注意 `format` 参数
- `showTime``format` 值应为`'YYYY-MM-DD HH:mm:ss'`,使用时注意 `format` 参数
- `lunarValueFormatter``showLunarDate` 开启时生效

### WeekPicker 其他 API

Expand Down
27 changes: 19 additions & 8 deletions packages/zent/src/date-picker/components/PanelCell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,21 +57,32 @@ const PanelCell: React.FC<IPanelCellProps> = ({
const rows = useMemo(() => {
const uls: React.ReactNode[] = [];
let rowCells: React.ReactNode[] = [];
cells.map(({ value, text, ...classNameProps }, index) => {
cells.map(({ value, text, lunarText, ...classNameProps }, index) => {
const { isSelected, isDisabled } = classNameProps;
const cellNode = (
<li
key={value.getTime()}
className={getCellClassName(classNameProps)}
onClick={() => onCellClick({ isDisabled, value })}
>
<div
className="zent-datepicker-cell-inner"
onMouseEnter={() => onCellMouseOver({ isDisabled, value })}
onMouseLeave={() => onCellMouseOver({ isDisabled, value: null })}
>
{text}
</div>
{lunarText ? (
<div
className="zent-datepicker-cell-inner zent-datepicker-lunar-cell"
onMouseEnter={() => onCellMouseOver({ isDisabled, value })}
onMouseLeave={() => onCellMouseOver({ isDisabled, value: null })}
>
<div>{text}</div>
<div className="zent-datepicker-lunar-cell_text">{lunarText}</div>
</div>
) : (
<div
className="zent-datepicker-cell-inner"
onMouseEnter={() => onCellMouseOver({ isDisabled, value })}
onMouseLeave={() => onCellMouseOver({ isDisabled, value: null })}
>
{text}
</div>
)}
</li>
);
// 单元格支持Tooltip
Expand Down
33 changes: 25 additions & 8 deletions packages/zent/src/date-picker/components/SinglePickerBase.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ export function SinglePicker({
PanelComponent,
...restPanelProps
} = restPropsRef.current;
const { showLunarDate, lunarValueFormatter } =
restPanelProps as ISinglePanelProps;

const { getSelectedValue, getCallbackValue, getInputText } =
useContext(PickerContext);
// props onChangeRef
Expand Down Expand Up @@ -114,10 +117,19 @@ export function SinglePicker({
);

// trigger-input text
const text = useMemo(
() => getInputText?.(selected),
[selected, getInputText]
);
const text = useMemo(() => {
if (!selected) return '';

if (
showLunarDate &&
lunarValueFormatter &&
typeof lunarValueFormatter === 'function'
) {
return lunarValueFormatter(selected);
}

return getInputText?.(selected);
}, [selected, showLunarDate, getInputText, lunarValueFormatter]);

const trigger = useMemo(() => {
const triggerProps = pick(restPropsRef.current, triggerCommonProps);
Expand All @@ -137,7 +149,11 @@ export function SinglePicker({

const content = useMemo(() => {
return (
<div className="zent-datepicker-panel">
<div
className={cx('zent-datepicker-panel', {
['zent-datepicker-panel_lunar']: !!showLunarDate,
})}
>
<PanelComponent
{...restPanelProps}
selected={selected}
Expand All @@ -149,13 +165,14 @@ export function SinglePicker({
</div>
);
}, [
showLunarDate,
PanelComponent,
restPanelProps,
selected,
hoverDate,
defaultPanelDate,
restPanelProps,
disabledPanelDate,
onSelected,
PanelComponent,
disabledPanelDate,
]);

return (
Expand Down
47 changes: 47 additions & 0 deletions packages/zent/src/date-picker/demos/lunar.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
---
order: 10
zh-CN:
title: 农历日期选择
en-US:
title: Lunar date picker
---

```jsx
import { DatePicker } from 'zent';
import { useState } from 'react';

import { Lunar } from 'lunar-typescript';

class LunarDatePickerDemo extends Component {
state = {};

lunarValueFormatter = date => {
const d = Lunar.fromDate(date);
return d.toString();
};

handleDateChange = val => {
this.setState({
date: val,
});
};

render() {
const { date } = this.state;
return (
<DatePicker
showLunarDate
lunarValueFormatter={this.lunarValueFormatter}
value={date}
onChange={this.handleDateChange}
/>
);
}
}

ReactDOM.render(<LunarDatePickerDemo />, mountNode);
```

<style>

</style>
3 changes: 3 additions & 0 deletions packages/zent/src/date-picker/panels/date-panel/DateBody.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ const DatePickerBody: FC<IDatePickerBodyProps> = props => {
showTimeOption,
onSelected,
disabledPanelDate,
showLunarDate,
} = props;

const startDateOfMonth = useMemo(
Expand All @@ -62,6 +63,7 @@ const DatePickerBody: FC<IDatePickerBodyProps> = props => {
dateConfig: dateConfig.date,
inView: isSameMonth,
disableRangeOverView,
showLunarDate,
}),
[
disableRangeOverView,
Expand All @@ -72,6 +74,7 @@ const DatePickerBody: FC<IDatePickerBodyProps> = props => {
col,
startDateOfMonth,
disabledPanelDate,
showLunarDate,
]
);

Expand Down
26 changes: 21 additions & 5 deletions packages/zent/src/date-picker/panels/date-panel/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import usePanelDate from '../../hooks/usePanelDate';
import { ISinglePanelProps, IDisabledTime, IShowTime } from '../../types';
import { useEventCallbackRef } from '../../../utils/hooks/useEventCallbackRef';

import { Lunar } from 'lunar-typescript';

export interface IDatePickerPanelProps extends ISinglePanelProps {
disableRangeOverView?: boolean;
popText?: string;
Expand Down Expand Up @@ -44,6 +46,23 @@ const DatePickerPanel: React.FC<IDatePickerPanelProps> = props => {
const showTimeOption = useShowTimeOption(showTime);
const onPanelDateChangeRef = useEventCallbackRef(onPanelDateChange);

const { showLunarDate } = resetBodyProps;

const monthLabel = useMemo(() => {
const solarMonth = panelDate.getMonth();

const solarMonthLabel = i18n.panel.monthNames[solarMonth];
if (!showLunarDate) {
return solarMonthLabel;
}

const d = Lunar.fromDate(new Date(panelDate.getFullYear(), solarMonth));

const lunar = d.getMonthInChinese();

return `${solarMonthLabel}${lunar}月)`;
}, [showLunarDate, i18n.panel.monthNames, panelDate]);

const titleNode = useMemo(
() => (
<>
Expand All @@ -52,13 +71,10 @@ const DatePickerPanel: React.FC<IDatePickerPanelProps> = props => {
unit={i18n.panel.year}
onClick={() => setShowYear(true)}
/>
<Title
text={i18n.panel.monthNames[panelDate.getMonth()]}
onClick={() => setShowMonth(true)}
/>
<Title text={monthLabel} onClick={() => setShowMonth(true)} />
</>
),
[panelDate, i18n]
[panelDate, i18n.panel.year, monthLabel]
);

const modifyPanelDate = useCallback(
Expand Down
3 changes: 3 additions & 0 deletions packages/zent/src/date-picker/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ interface ICommonProps<DateValue = SingleDate> {
export interface IDateCellBase {
value: Date;
text: string | number;
lunarText?: string | number;
isSelected?: boolean;
isCurrent?: boolean;
isDisabled?: boolean;
Expand Down Expand Up @@ -146,6 +147,8 @@ export interface ISinglePanelProps {
onSelected: (val: Date, status?: boolean) => void;
disabledPanelDate: (val: Date) => boolean;
onChangePanel?: (type: IPickerType) => void;
showLunarDate?: boolean;
lunarValueFormatter?: (date: Date) => string;
}

export type ISingleDateBodyProps = Omit<ISinglePanelProps, 'onChangePanel'>;
Expand Down
21 changes: 21 additions & 0 deletions packages/zent/src/date-picker/utils/getPanelCellsData.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { eachDayOfInterval, isAfter, isBefore, isSameDay } from 'date-fns';
import { IDateCellBase, IGenerateDateConfig, DateTuple } from '../types';
import { Lunar } from 'lunar-typescript';

interface ICellDateParams {
selected: Date | null;
Expand All @@ -14,6 +15,8 @@ interface ICellDateParams {
offset?: number;
inView?: (val1: Date, val2: Date) => boolean;
disableRangeOverView?: boolean;
showLunarDate?: boolean;
lunarValueFormatter?: (date: Date) => string;
}

/**
Expand All @@ -33,6 +36,7 @@ export default function getPanelCellsData({
offset = 0,
inView,
disableRangeOverView,
showLunarDate,
}: ICellDateParams) {
const { isSame, startDate, endDate, offsetDate } = dateConfig;

Expand All @@ -43,6 +47,22 @@ export default function getPanelCellsData({
const currentDate = offsetDate(defaultPanelDate, index - offset);
const text = texts ? texts[index] : currentDate.getDate();

let lunarText = '';

if (showLunarDate) {
const date = currentDate as any;

const d = Lunar.fromDate(date);
const lunarDay = d.getDayInChinese();
const solarTerm = d.getJieQi();

if (1 === d.getDay()) {
lunarText = d.getMonthInChinese() + '月';
} else {
lunarText = solarTerm || lunarDay;
}
}

const isCurrent = isSame(new Date(), currentDate);

const isInView = inView ? inView(currentDate, defaultPanelDate) : true;
Expand Down Expand Up @@ -92,6 +112,7 @@ export default function getPanelCellsData({
cells[index] = {
value: currentDate,
text,
lunarText,
isCurrent,
isSelected,
isInView,
Expand Down
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -7398,6 +7398,11 @@ lru-cache@^6.0.0:
dependencies:
yallist "^4.0.0"

lunar-typescript@^1.6.10:
version "1.6.10"
resolved "https://registry.npmmirror.com/lunar-typescript/-/lunar-typescript-1.6.10.tgz#607b5d6821efb48c0ff751a79859b1dfeb39c7a6"
integrity sha512-NpimyKWfxaYbUPJIdHcDX5iaXSMbQVob2yk4Baox9KMwCNLrLsjNk/sS92fAlqTkziKkvM/39rPxk4qXX7gWaA==

lunr@^2.3.9:
version "2.3.9"
resolved "https://registry.yarnpkg.com/lunr/-/lunr-2.3.9.tgz#18b123142832337dd6e964df1a5a7707b25d35e1"
Expand Down

0 comments on commit 2760f02

Please sign in to comment.