Просмотр исходного кода

feat: [Calendar] add allDayEventsRender and renderDateDisplay

ShuangYa 2 лет назад
Родитель
Сommit
cd8f544fa8

+ 23 - 1
content/show/calendar/index-en-US.md

@@ -273,7 +273,7 @@ import { Calendar } from '@douyinfe/semi-ui';
 ```
 
 #### Customized Date Cell Style
-You could alos use `dateGridRender` to customize date cell style, e.g. backgroundColor. Please notice that in Month View, the date text on the right corner has a z-index of 3. Use a larger z-index if you would like to cover the text as well.
+You could also use `dateGridRender` to customize date cell style, e.g. backgroundColor. Please notice that in Month View, the date text on the right corner has a z-index of 3. Use a larger z-index if you would like to cover the text as well.
 ```jsx live=true dir="column"
 import React from 'react';
 import { Calendar } from '@douyinfe/semi-ui';
@@ -313,6 +313,26 @@ import { Calendar } from '@douyinfe/semi-ui';
 };
 ```
 
+#### Customized Date Render
+You could use `renderDateDisplay` to customize the display of date
+
+
+```jsx live=true dir="column"
+import React from 'react';
+import { Avatar, Calendar } from '@douyinfe/semi-ui';
+
+() => {
+    const displayValue = new Date(2023, 4, 14);
+
+    const renderDateDisplay = date => {
+        const colors = ["amber", "blue", "cyan", "green", "grey", "indigo", "lime"];
+        return <div><Avatar color={colors[date.getDay()]} size="small">{date.getDate()}</Avatar></div>;
+    };
+
+    return <Calendar height={400} mode="week" displayValue={displayValue} renderDateDisplay={renderDateDisplay} />;
+};
+```
+
 ## API Reference
 
 ### Calendar
@@ -320,6 +340,7 @@ import { Calendar } from '@douyinfe/semi-ui';
 | Properties   | Instructions                                                                                           | type                  | Default      |
 | ------------ | ------------------------------------------------------------------------------------------------------ | --------------------- | ------------ |
 | dateGridRender | Custom render for date cell or column. Use absolute positioning for elements. **v>=1.0.0** | function(dateString: string, date: Date) | - | 
+| allDayEventsRender | Custom render for events area at the top of calendar in day/range/week mode. | function(events: EventObject[]): ReactNode | - | 
 | displayValue | Display date                                                                                           | Date           | current date |
 | events       | Events for rendering, refer to event object                                                            | EventObject[]                 | -            |
 | header       | Header                                                                                                 | React.Node            | -            |
@@ -329,6 +350,7 @@ import { Calendar } from '@douyinfe/semi-ui';
 | onClick      | Callback invoked when clicking on date, basic unit for day and week mode is 0.5h, for month mode is 1d | function(e: Event, date: Date) | -            |
 | onClose | Callback invoked when event display card close in the month mode | function(e: Event) | - |
 | renderTimeDisplay | Customize the display of time in day/week mode | function(time: number): ReactNode | - |
+| renderDateDisplay | Customize the display of date | function(date: Date): ReactNode | - |
 | range | Date range to display in range mode, left-closed and right-open **v>=1.5.0** | Date[] | - |
 | scrollTop    | Scroll height for displayed content in day and week mode                                               | number                | 400          |
 | showCurrTime | Toggle whether to show red line of current time                                                        | boolean               | true         |

+ 24 - 0
content/show/calendar/index.md

@@ -288,6 +288,28 @@ import { Calendar } from '@douyinfe/semi-ui';
 };
 ```
 
+#### 自定义日期文案
+
+可以通过 renderDateDisplay 自定义日期文案。
+
+
+```jsx live=true dir="column"
+import React from 'react';
+import { Avatar, Calendar } from '@douyinfe/semi-ui';
+
+() => {
+    const displayValue = new Date(2023, 4, 14);
+
+    const renderDateDisplay = date => {
+        const colors = ["amber", "blue", "cyan", "green", "grey", "indigo", "lime"];
+        return <div><Avatar color={colors[date.getDay()]} size="small">{date.getDate()}</Avatar></div>;
+    };
+
+    return <Calendar height={400} mode="week" displayValue={displayValue} renderDateDisplay={renderDateDisplay} />;
+};
+```
+
+
 ## API 参考
 
 ### Calendar
@@ -295,6 +317,7 @@ import { Calendar } from '@douyinfe/semi-ui';
 | 属性 | 说明 | 类型 | 默认值 |
 | --- | --- | --- | --- |
 | dateGridRender | 自定义单元格/列渲染,需要绝对定位, **v>=1.0.0** | function(dateString: string, date: Date) | - |
+| allDayEventsRender | 自定义日/多日/周视图下的顶部事件渲染 | function(events: EventObject[]): ReactNode | - |
 | displayValue | 展示日期 | Date | 当前日期 |
 | events | 渲染事件,具体格式请参考 event object | EventObject[] | - |
 | header | 自定义头部内容 | ReactNode | - |
@@ -305,6 +328,7 @@ import { Calendar } from '@douyinfe/semi-ui';
 | onClose | 月视图下,展示所有 event 的卡片关闭时的回调 | function(e: Event) | - |
 | range | 多日视图模式下展示的日期范围,左闭右开 **v>=1.5.0** | Date[] | - |
 | renderTimeDisplay | 自定义日/周视图下的时间文案 | function(time: number): ReactNode | - |
+| renderDateDisplay | 自定义日期文案 | function(date: Date): ReactNode | - |
 | scrollTop | 日视图和周视图模式下,设置展示内容默认的滚动高度 | number | 400 |
 | showCurrTime | 显示当前时间 | boolean | true |
 | width | 日历宽度 | string\|number | - |

+ 1 - 1
packages/semi-foundation/calendar/calendar.scss

@@ -273,7 +273,7 @@ $module: #{$prefix}-calendar;
                 justify-content: center;
                 align-items: center;
 
-                span {
+                > span {
                     display: flex;
                     align-items: center;
                     justify-content: center;

+ 38 - 0
packages/semi-ui/calendar/__test__/calendar.test.js

@@ -200,4 +200,42 @@ describe('Calendar', () => {
         // 2023-05-06
         expect(lastRow.find('li').last().hasClass(sameMonthClass)).toEqual(false);
     });
+
+    it('Custom renderDateDisplay', () => {
+        const displayValue = new Date(2023, 3, 1, 8, 32, 0);
+        const customPrefix = 'my-custom-render-';
+
+        let monthCalendar = mount(<Calendar
+            mode={'month'}
+            displayValue={displayValue}
+            renderDateDisplay={d => <span className={`${customPrefix}${d.getMonth()}-${d.getDate()}`} />}
+        ></Calendar>);
+
+        // 2023-04-01
+        expect(monthCalendar.find(`.${customPrefix}3-1`).length).toEqual(1);
+
+        let weekCalendar = mount(<Calendar
+            mode={'week'}
+            displayValue={displayValue}
+            renderDateDisplay={d => <span className={`${customPrefix}${d.getMonth()}-${d.getDate()}`} />}
+        ></Calendar>);
+
+        // 2023-04-01
+        expect(weekCalendar.find(`.${customPrefix}3-1`).length).toEqual(1);
+    });
+
+    it('Custom allDayEventsRender', () => {
+        const displayValue = new Date(2023, 3, 1, 8, 32, 0);
+        const customClassName = 'my-custom-render';
+        const defaultElementSelector = '.semi-calendar-event-allday';
+
+        let calendar = mount(<Calendar
+            mode={'week'}
+            displayValue={displayValue}
+            allDayEventsRender={events => <div className={customClassName} />}
+        ></Calendar>);
+
+        expect(calendar.find(`.${customClassName}`).length).toEqual(1);
+        expect(calendar.find(defaultElementSelector).length).toEqual(0);
+    });
 })

+ 4 - 0
packages/semi-ui/calendar/dayCalendar.tsx

@@ -37,6 +37,7 @@ export default class DayCalendar extends BaseComponent<DayCalendarProps, DayCale
         style: PropTypes.object,
         className: PropTypes.string,
         dateGridRender: PropTypes.func,
+        allDayEventsRender: PropTypes.func,
     };
 
     static defaultProps = {
@@ -109,6 +110,9 @@ export default class DayCalendar extends BaseComponent<DayCalendarProps, DayCale
     checkWeekend = (val: Date) => this.foundation.checkWeekend(val);
 
     renderAllDayEvents = (events: ParsedEventsWithArray['allDay']) => {
+        if (this.props.allDayEventsRender) {
+            return this.props.allDayEventsRender(this.props.events);
+        }
         const list = events.map((event, ind) => {
             const { children, key } = event;
             return (

+ 1 - 0
packages/semi-ui/calendar/index.tsx

@@ -29,6 +29,7 @@ class Calendar extends BaseComponent<CalendarProps, {}> {
         scrollTop: PropTypes.number,
         onClick: PropTypes.func,
         renderTimeDisplay: PropTypes.func,
+        renderDateDisplay: PropTypes.func,
         markWeekend: PropTypes.bool,
         width: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
         height: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

+ 3 - 1
packages/semi-ui/calendar/interface.ts

@@ -18,7 +18,9 @@ export interface CalendarProps extends BaseProps {
     markWeekend?: boolean;
     width?: number | string;
     height?: number | string;
-    dateGridRender?: (dateString?: string, date?: Date) => React.ReactNode
+    renderDateDisplay?: (date: Date) => React.ReactNode;
+    dateGridRender?: (dateString?: string, date?: Date) => React.ReactNode;
+    allDayEventsRender?: (events: EventObject[]) => React.ReactNode
 }
 
 export type DayCalendarProps = Omit<CalendarProps, 'mode'>;

+ 7 - 3
packages/semi-ui/calendar/monthCalendar.tsx

@@ -290,7 +290,7 @@ export default class monthCalendar extends BaseComponent<MonthCalendarProps, Mon
                 ref={ref => this.cardRef.set(key, ref)}
             >
                 <li key={date as any} className={listCls} onClick={e => this.handleClick(e, [date])}>
-                    {this.formatDayString(month, dayString)}
+                    {this.formatDayString(date, month, dayString)}
                     {shouldRenderCard ? text : null}
                     {this.renderCusDateGrid(date)}
                 </li>
@@ -298,7 +298,11 @@ export default class monthCalendar extends BaseComponent<MonthCalendarProps, Mon
         );
     };
 
-    formatDayString = (month: string, date: string) => {
+    formatDayString = (dateObj: Date, month: string, date: string) => {
+        const { renderDateDisplay } = this.props;
+        if (renderDateDisplay) {
+            return renderDateDisplay(dateObj);
+        }
         if (date === '1') {
             return (
                 <LocaleConsumer componentName="Calendar">
@@ -343,7 +347,7 @@ export default class monthCalendar extends BaseComponent<MonthCalendarProps, Mon
                         const shouldRenderCollapsed = Boolean(day && day[ind] && day[ind].length > itemLimit);
                         const inner = (
                             <li role="gridcell" aria-label={date.toLocaleDateString()} aria-current={isToday ? "date" : false} key={`${date}-weeksk`} className={listCls} onClick={e => this.handleClick(e, [date])}>
-                                {this.formatDayString(month, dayString)}
+                                {this.formatDayString(date, month, dayString)}
                                 {this.renderCusDateGrid(date)}
                             </li>
                         );

+ 20 - 8
packages/semi-ui/calendar/rangeCalendar.tsx

@@ -1,8 +1,8 @@
-import React from 'react';
+import React, { Fragment } from 'react';
 import cls from 'classnames';
 import PropTypes from 'prop-types';
 // eslint-disable-next-line max-len
-import CalendarFoundation, { ParsedEvents, CalendarAdapter, RangeData, ParsedRangeEvent, ParsedEventsType } from '@douyinfe/semi-foundation/calendar/foundation';
+import CalendarFoundation, { ParsedEvents, CalendarAdapter, RangeData, ParsedRangeEvent, ParsedEventsType, EventObject } from '@douyinfe/semi-foundation/calendar/foundation';
 import LocaleConsumer from '../locale/localeConsumer';
 import localeContext from '../locale/context';
 import { cssClasses } from '@douyinfe/semi-foundation/calendar/constants';
@@ -41,7 +41,9 @@ export default class RangeCalendar extends BaseComponent<RangeCalendarProps, Ran
         markWeekend: PropTypes.bool,
         scrollTop: PropTypes.number,
         renderTimeDisplay: PropTypes.func,
+        renderDateDisplay: PropTypes.func,
         dateGridRender: PropTypes.func,
+        allDayEventsRender: PropTypes.func,
         width: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
         height: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
         style: PropTypes.object,
@@ -151,7 +153,7 @@ export default class RangeCalendar extends BaseComponent<RangeCalendarProps, Ran
     };
 
     renderHeader = (dateFnsLocale: Locale['dateFnsLocale']) => {
-        const { markWeekend, range } = this.props;
+        const { markWeekend, range, renderDateDisplay } = this.props;
         const { month, week } = this.foundation.getRangeData(range[0], dateFnsLocale);
         return (
             <div className={`${prefixCls}-header`}>
@@ -166,10 +168,17 @@ export default class RangeCalendar extends BaseComponent<RangeCalendarProps, Ran
                                 [`${cssClasses.PREFIX}-today`]: isToday,
                                 [`${cssClasses.PREFIX}-weekend`]: markWeekend && day.isWeekend,
                             });
-                            return (
-                                <li key={`${date.toString()}-weekheader`} className={listCls}>
+                            const dateContent = renderDateDisplay ? (
+                                renderDateDisplay(date)
+                            ) : (
+                                <Fragment>
                                     <span className={`${cssClasses.PREFIX}-today-date`}>{dayString}</span>
                                     <span>{weekday}</span>
+                                </Fragment>
+                            );
+                            return (
+                                <li key={`${date.toString()}-weekheader`} className={listCls}>
+                                    {dateContent}
                                 </li>
                             );
                         })}
@@ -180,6 +189,9 @@ export default class RangeCalendar extends BaseComponent<RangeCalendarProps, Ran
     };
 
     renderAllDayEvents = (events: Array<ParsedRangeEvent>) => {
+        if (this.props.allDayEventsRender) {
+            return this.props.allDayEventsRender(this.props.events);
+        }
         const list = events.map((event, ind) => {
             const { leftPos, width, topInd, children } = event;
             const top = `${topInd}em`;
@@ -202,11 +214,11 @@ export default class RangeCalendar extends BaseComponent<RangeCalendarProps, Ran
     };
 
     renderAllDay = (locale: Locale['Calendar']) => {
+        const { allDayEventsRender } = this.props;
         const { allDay } = this.state.parsedEvents;
         const parsed = this.foundation.parseRangeAllDayEvents(allDay);
-        const maxRowHeight = calcRowHeight(parsed);
-        const style = {
-            height: `${maxRowHeight}em`
+        const style = allDayEventsRender ? null : {
+            height: `${calcRowHeight(parsed) }em`
         };
         const { markWeekend } = this.props;
         const { week } = this.RangeData;

+ 19 - 7
packages/semi-ui/calendar/weekCalendar.tsx

@@ -1,4 +1,4 @@
-import React from 'react';
+import React, { Fragment } from 'react';
 import cls from 'classnames';
 import PropTypes from 'prop-types';
 import CalendarFoundation, { CalendarAdapter, EventObject, ParsedEvents, ParsedEventsType, ParsedRangeEvent, WeeklyData } from '@douyinfe/semi-foundation/calendar/foundation';
@@ -39,7 +39,9 @@ export default class WeekCalendar extends BaseComponent<WeekCalendarProps, WeekC
         markWeekend: PropTypes.bool,
         scrollTop: PropTypes.number,
         renderTimeDisplay: PropTypes.func,
+        renderDateDisplay: PropTypes.func,
         dateGridRender: PropTypes.func,
+        allDayEventsRender: PropTypes.func,
         width: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
         height: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
         style: PropTypes.object,
@@ -152,7 +154,7 @@ export default class WeekCalendar extends BaseComponent<WeekCalendarProps, WeekC
     };
 
     renderHeader = (dateFnsLocale: any) => {
-        const { markWeekend, displayValue } = this.props;
+        const { markWeekend, displayValue, renderDateDisplay } = this.props;
         const { month, week } = this.foundation.getWeeklyData(displayValue, dateFnsLocale);
         return (
             <div className={`${prefixCls}-header`}>
@@ -167,10 +169,17 @@ export default class WeekCalendar extends BaseComponent<WeekCalendarProps, WeekC
                                 [`${cssClasses.PREFIX}-today`]: isToday,
                                 [`${cssClasses.PREFIX}-weekend`]: markWeekend && day.isWeekend,
                             });
-                            return (
-                                <li key={`${date.toString()}-weekheader`} className={listCls}>
+                            const dateContent = renderDateDisplay ? (
+                                renderDateDisplay(date)
+                            ) : (
+                                <Fragment>
                                     <span className={`${cssClasses.PREFIX}-today-date`}>{dayString}</span>
                                     <span>{weekday}</span>
+                                </Fragment>
+                            );
+                            return (
+                                <li key={`${date.toString()}-weekheader`} className={listCls}>
+                                    {dateContent}
                                 </li>
                             );
                         })}
@@ -181,6 +190,9 @@ export default class WeekCalendar extends BaseComponent<WeekCalendarProps, WeekC
     };
 
     renderAllDayEvents = (events: ParsedRangeEvent[]) => {
+        if (this.props.allDayEventsRender) {
+            return this.props.allDayEventsRender(this.props.events);
+        }
         const list = events.map((event, ind) => {
             const { leftPos, width, topInd, children, key } = event;
             const top = `${topInd}em`;
@@ -203,11 +215,11 @@ export default class WeekCalendar extends BaseComponent<WeekCalendarProps, WeekC
     };
 
     renderAllDay = (locale: Locale['Calendar']) => {
+        const { allDayEventsRender } = this.props;
         const { allDay } = this.state.parsedEvents;
         const parsed = this.foundation.parseWeeklyAllDayEvents(allDay);
-        const maxRowHeight = calcRowHeight(parsed);
-        const style = {
-            height: `${maxRowHeight}em`
+        const style = allDayEventsRender ? null : {
+            height: `${calcRowHeight(parsed)}em`
         };
         const { markWeekend } = this.props;
         const { week } = this.weeklyData;