UNPKG

8.85 kBJavaScriptView Raw
1import React, { forwardRef, useState, useImperativeHandle, useMemo, useEffect } from 'react';
2import { withNativeProps } from '../../utils/native-props';
3import dayjs from 'dayjs';
4import classNames from 'classnames';
5import { mergeProps } from '../../utils/with-default-props';
6import { ArrowLeft } from './arrow-left';
7import { ArrowLeftDouble } from './arrow-left-double';
8import { useConfig } from '../config-provider';
9import isoWeek from 'dayjs/plugin/isoWeek';
10import { useUpdateEffect } from 'ahooks';
11import { usePropsValue } from '../../utils/use-props-value';
12import { replaceMessage } from '../../utils/replace-message';
13import { devWarning } from '../../utils/dev-log';
14import { convertValueToRange, convertPageToDayjs } from './convert';
15dayjs.extend(isoWeek);
16const classPrefix = 'adm-calendar';
17const defaultProps = {
18 weekStartsOn: 'Sunday',
19 defaultValue: null,
20 allowClear: true,
21 prevMonthButton: React.createElement(ArrowLeft, null),
22 prevYearButton: React.createElement(ArrowLeftDouble, null),
23 nextMonthButton: React.createElement(ArrowLeft, null),
24 nextYearButton: React.createElement(ArrowLeftDouble, null)
25};
26export const Calendar = forwardRef((p, ref) => {
27 const today = dayjs();
28 const props = mergeProps(defaultProps, p);
29 const {
30 locale
31 } = useConfig();
32 const markItems = [...locale.Calendar.markItems];
33 if (props.weekStartsOn === 'Sunday') {
34 const item = markItems.pop();
35 if (item) markItems.unshift(item);
36 }
37 const [dateRange, setDateRange] = usePropsValue({
38 value: props.value === undefined ? undefined : convertValueToRange(props.selectionMode, props.value),
39 defaultValue: convertValueToRange(props.selectionMode, props.defaultValue),
40 onChange: v => {
41 var _a, _b;
42 if (props.selectionMode === 'single') {
43 (_a = props.onChange) === null || _a === void 0 ? void 0 : _a.call(props, v ? v[0] : null);
44 } else if (props.selectionMode === 'range') {
45 (_b = props.onChange) === null || _b === void 0 ? void 0 : _b.call(props, v);
46 }
47 }
48 });
49 const [intermediate, setIntermediate] = useState(false);
50 const [current, setCurrent] = useState(() => dayjs(dateRange ? dateRange[0] : today).date(1));
51 useUpdateEffect(() => {
52 var _a;
53 (_a = props.onPageChange) === null || _a === void 0 ? void 0 : _a.call(props, current.year(), current.month() + 1);
54 }, [current]);
55 useImperativeHandle(ref, () => ({
56 jumpTo: pageOrPageGenerator => {
57 let page;
58 if (typeof pageOrPageGenerator === 'function') {
59 page = pageOrPageGenerator({
60 year: current.year(),
61 month: current.month() + 1
62 });
63 } else {
64 page = pageOrPageGenerator;
65 }
66 setCurrent(convertPageToDayjs(page));
67 },
68 jumpToToday: () => {
69 setCurrent(dayjs().date(1));
70 }
71 }));
72 const handlePageChange = (action, num, type) => {
73 const nxtCurrent = current[action](num, type);
74 if (action === 'subtract' && props.minPage) {
75 const minPage = convertPageToDayjs(props.minPage);
76 if (nxtCurrent.isBefore(minPage, type)) {
77 return;
78 }
79 }
80 if (action === 'add' && props.maxPage) {
81 const maxPage = convertPageToDayjs(props.maxPage);
82 if (nxtCurrent.isAfter(maxPage, type)) {
83 return;
84 }
85 }
86 setCurrent(nxtCurrent);
87 };
88 const header = React.createElement("div", {
89 className: `${classPrefix}-header`
90 }, React.createElement("a", {
91 className: `${classPrefix}-arrow-button ${classPrefix}-arrow-button-year`,
92 onClick: () => {
93 handlePageChange('subtract', 1, 'year');
94 }
95 }, props.prevYearButton), React.createElement("a", {
96 className: `${classPrefix}-arrow-button ${classPrefix}-arrow-button-month`,
97 onClick: () => {
98 handlePageChange('subtract', 1, 'month');
99 }
100 }, props.prevMonthButton), React.createElement("div", {
101 className: `${classPrefix}-title`
102 }, replaceMessage(locale.Calendar.yearAndMonth, {
103 year: current.year().toString(),
104 month: (current.month() + 1).toString()
105 })), React.createElement("a", {
106 className: classNames(`${classPrefix}-arrow-button`, `${classPrefix}-arrow-button-right`, `${classPrefix}-arrow-button-right-month`),
107 onClick: () => {
108 handlePageChange('add', 1, 'month');
109 }
110 }, props.nextMonthButton), React.createElement("a", {
111 className: classNames(`${classPrefix}-arrow-button`, `${classPrefix}-arrow-button-right`, `${classPrefix}-arrow-button-right-year`),
112 onClick: () => {
113 handlePageChange('add', 1, 'year');
114 }
115 }, props.nextYearButton));
116 const maxDay = useMemo(() => props.max && dayjs(props.max), [props.max]);
117 const minDay = useMemo(() => props.min && dayjs(props.min), [props.min]);
118 function renderCells() {
119 var _a;
120 const cells = [];
121 let iterator = current.subtract(current.isoWeekday(), 'day');
122 if (props.weekStartsOn === 'Monday') {
123 iterator = iterator.add(1, 'day');
124 }
125 while (cells.length < 6 * 7) {
126 const d = iterator;
127 let isSelect = false;
128 let isBegin = false;
129 let isEnd = false;
130 let isSelectRowBegin = false;
131 let isSelectRowEnd = false;
132 if (dateRange) {
133 const [begin, end] = dateRange;
134 isBegin = d.isSame(begin, 'day');
135 isEnd = d.isSame(end, 'day');
136 isSelect = isBegin || isEnd || d.isAfter(begin, 'day') && d.isBefore(end, 'day');
137 if (isSelect) {
138 isSelectRowBegin = (cells.length % 7 === 0 || d.isSame(d.startOf('month'), 'day')) && !isBegin;
139 isSelectRowEnd = (cells.length % 7 === 6 || d.isSame(d.endOf('month'), 'day')) && !isEnd;
140 }
141 }
142 const inThisMonth = d.month() === current.month();
143 const disabled = props.shouldDisableDate ? props.shouldDisableDate(d.toDate()) : maxDay && d.isAfter(maxDay, 'day') || minDay && d.isBefore(minDay, 'day');
144 cells.push(React.createElement("div", {
145 key: d.valueOf(),
146 className: classNames(`${classPrefix}-cell`, (disabled || !inThisMonth) && `${classPrefix}-cell-disabled`, inThisMonth && {
147 [`${classPrefix}-cell-today`]: d.isSame(today, 'day'),
148 [`${classPrefix}-cell-selected`]: isSelect,
149 [`${classPrefix}-cell-selected-begin`]: isBegin,
150 [`${classPrefix}-cell-selected-end`]: isEnd,
151 [`${classPrefix}-cell-selected-row-begin`]: isSelectRowBegin,
152 [`${classPrefix}-cell-selected-row-end`]: isSelectRowEnd
153 }),
154 onClick: () => {
155 if (!props.selectionMode) return;
156 if (disabled) return;
157 const date = d.toDate();
158 if (!inThisMonth) {
159 setCurrent(d.clone().date(1));
160 }
161 function shouldClear() {
162 if (!props.allowClear) return false;
163 if (!dateRange) return false;
164 const [begin, end] = dateRange;
165 return d.isSame(begin, 'date') && d.isSame(end, 'day');
166 }
167 if (props.selectionMode === 'single') {
168 if (props.allowClear && shouldClear()) {
169 setDateRange(null);
170 return;
171 }
172 setDateRange([date, date]);
173 } else if (props.selectionMode === 'range') {
174 if (!dateRange) {
175 setDateRange([date, date]);
176 setIntermediate(true);
177 return;
178 }
179 if (shouldClear()) {
180 setDateRange(null);
181 setIntermediate(false);
182 return;
183 }
184 if (intermediate) {
185 const another = dateRange[0];
186 setDateRange(another > date ? [date, another] : [another, date]);
187 setIntermediate(false);
188 } else {
189 setDateRange([date, date]);
190 setIntermediate(true);
191 }
192 }
193 }
194 }, React.createElement("div", {
195 className: `${classPrefix}-cell-top`
196 }, props.renderDate ? props.renderDate(d.toDate()) : d.date()), React.createElement("div", {
197 className: `${classPrefix}-cell-bottom`
198 }, (_a = props.renderLabel) === null || _a === void 0 ? void 0 : _a.call(props, d.toDate()))));
199 iterator = iterator.add(1, 'day');
200 }
201 return cells;
202 }
203 const body = React.createElement("div", {
204 className: `${classPrefix}-cells`
205 }, renderCells());
206 const mark = React.createElement("div", {
207 className: `${classPrefix}-mark`
208 }, markItems.map((item, index) => React.createElement("div", {
209 key: index,
210 className: `${classPrefix}-mark-cell`
211 }, item)));
212 // Dev only warning
213 if (process.env.NODE_ENV !== 'production') {
214 useEffect(() => {
215 devWarning('Calendar', 'Calendar will be removed in the future, please use CalendarPickerView instead.');
216 }, []);
217 }
218 return withNativeProps(props, React.createElement("div", {
219 className: classPrefix
220 }, header, mark, body));
221});
\No newline at end of file