1 | import React, { forwardRef, useState, useImperativeHandle, useMemo, useEffect } from 'react';
|
2 | import { withNativeProps } from '../../utils/native-props';
|
3 | import dayjs from 'dayjs';
|
4 | import classNames from 'classnames';
|
5 | import { mergeProps } from '../../utils/with-default-props';
|
6 | import { ArrowLeft } from './arrow-left';
|
7 | import { ArrowLeftDouble } from './arrow-left-double';
|
8 | import { useConfig } from '../config-provider';
|
9 | import isoWeek from 'dayjs/plugin/isoWeek';
|
10 | import { useUpdateEffect } from 'ahooks';
|
11 | import { usePropsValue } from '../../utils/use-props-value';
|
12 | import { replaceMessage } from '../../utils/replace-message';
|
13 | import { devWarning } from '../../utils/dev-log';
|
14 | import { convertValueToRange, convertPageToDayjs } from './convert';
|
15 | dayjs.extend(isoWeek);
|
16 | const classPrefix = 'adm-calendar';
|
17 | const 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 | };
|
26 | export 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 |
|
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 |