UNPKG

18.1 kBJavaScriptView Raw
1"use strict";
2
3exports.__esModule = true;
4exports.default = void 0;
5
6var _classnames = _interopRequireDefault(require("classnames"));
7
8var _propTypes = _interopRequireDefault(require("prop-types"));
9
10var _react = _interopRequireWildcard(require("react"));
11
12var _uncontrollable = require("uncontrollable");
13
14var _CalendarHeader = _interopRequireDefault(require("./CalendarHeader"));
15
16var _Century = _interopRequireDefault(require("./Century"));
17
18var _Decade = _interopRequireDefault(require("./Decade"));
19
20var _Localization = require("./Localization");
21
22var _Month = _interopRequireDefault(require("./Month"));
23
24var _SlideTransitionGroup = _interopRequireDefault(require("./SlideTransitionGroup"));
25
26var _Widget = _interopRequireDefault(require("./Widget"));
27
28var _Year = _interopRequireDefault(require("./Year"));
29
30var _dates = _interopRequireDefault(require("./dates"));
31
32var _useAutoFocus = _interopRequireDefault(require("./useAutoFocus"));
33
34var _useFocusManager = _interopRequireDefault(require("./useFocusManager"));
35
36var _WidgetHelpers = require("./WidgetHelpers");
37
38const _excluded = ["id", "autoFocus", "bordered", "views", "tabIndex", "disabled", "readOnly", "className", "value", "defaultValue", "onChange", "currentDate", "defaultCurrentDate", "onCurrentDateChange", "min", "max", "view", "defaultView", "onViewChange", "onKeyDown", "onNavigate", "renderDay", "messages", "formats"];
39
40function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
41
42function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
43
44function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
45
46function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
47
48function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
49
50let last = a => a[a.length - 1];
51
52const CELL_CLASSNAME = 'rw-cell';
53const FOCUSED_CELL_SELECTOR = `.${CELL_CLASSNAME}[tabindex]`;
54const MIN = new Date(1900, 0, 1);
55const MAX = new Date(2099, 11, 31);
56const VIEW_OPTIONS = ['month', 'year', 'decade', 'century'];
57const VIEW_UNIT = {
58 month: 'day',
59 year: 'month',
60 decade: 'year',
61 century: 'decade'
62};
63const VIEW = {
64 month: _Month.default,
65 year: _Year.default,
66 decade: _Decade.default,
67 century: _Century.default
68};
69const ARROWS_TO_DIRECTION = {
70 ArrowDown: 'DOWN',
71 ArrowUp: 'UP',
72 ArrowRight: 'RIGHT',
73 ArrowLeft: 'LEFT'
74};
75const OPPOSITE_DIRECTION = {
76 LEFT: 'RIGHT',
77 RIGHT: 'LEFT'
78};
79const MULTIPLIER = {
80 year: 1,
81 decade: 10,
82 century: 100
83};
84
85function inRangeValue(_value, min, max) {
86 let value = dateOrNull(_value);
87 if (value === null) return value;
88 return _dates.default.max(_dates.default.min(value, max), min);
89}
90
91const propTypes = {
92 /**
93 * @example ['disabled', ['new Date()']]
94 */
95 disabled: _propTypes.default.bool,
96
97 /**
98 * @example ['readOnly', ['new Date()']]
99 */
100 readOnly: _propTypes.default.bool,
101
102 /**
103 * @example ['onChangePicker', [ ['new Date()'] ]]
104 */
105 onChange: _propTypes.default.func,
106
107 /**
108 * The selected Date.
109 *
110 * ```tsx live
111 * import { Calendar } from 'react-widgets';
112 *
113 * <Calendar value={new Date()} />
114 * ```
115 * @example false
116 */
117 value: _propTypes.default.instanceOf(Date),
118
119 /**
120 * The minimum date that the Calendar can navigate from.
121 *
122 * @example ['prop', ['min', 'new Date()']]
123 */
124 min: _propTypes.default.instanceOf(Date),
125
126 /**
127 * The maximum date that the Calendar can navigate to.
128 *
129 * @example ['prop', ['max', 'new Date()']]
130 */
131 max: _propTypes.default.instanceOf(Date),
132
133 /**
134 * Default current date at which the calendar opens. If none is provided, opens at today's date or the `value` date (if any).
135 */
136 currentDate: _propTypes.default.instanceOf(Date),
137
138 /**
139 * Change event Handler that is called when the currentDate is changed. The handler is called with the currentDate object.
140 */
141 onCurrentDateChange: _propTypes.default.func,
142
143 /** Specify the navigate into the past header icon */
144 navigatePrevIcon: _propTypes.default.node,
145
146 /** Specify the navigate into the future header icon */
147 navigateNextIcon: _propTypes.default.node,
148
149 /**
150 * Controls the currently displayed calendar view. Use `defaultView` to set a unique starting view.
151 *
152 * @type {("month"|"year"|"decade"|"century")}
153 * @controllable onViewChange
154 */
155 view(props, ...args) {
156 // @ts-ignore
157 return _propTypes.default.oneOf(props.views || VIEW_OPTIONS)(props, ...args);
158 },
159
160 /**
161 * Defines a list of views the Calendar can traverse through, starting with the
162 * first in the list to the last.
163 *
164 * @type array<"month"|"year"|"decade"|"century">
165 */
166 views: _propTypes.default.arrayOf(_propTypes.default.oneOf(VIEW_OPTIONS)),
167
168 /**
169 * A callback fired when the `view` changes.
170 *
171 * @controllable view
172 */
173 onViewChange: _propTypes.default.func,
174
175 /**
176 * Callback fired when the Calendar navigates between views, or forward and backwards in time.
177 *
178 * @type function(date: ?Date, direction: string, view: string)
179 */
180 onNavigate: _propTypes.default.func,
181 culture: _propTypes.default.string,
182 autoFocus: _propTypes.default.bool,
183
184 /**
185 * Show or hide the Calendar footer.
186 *
187 * @example ['prop', ['footer', true]]
188 */
189 footer: _propTypes.default.bool,
190
191 /**
192 * Provide a custom component to render the days of the month. The Component is provided the following props
193 *
194 * - `date`: a `Date` object for the day of the month to render
195 * - `label`: a formatted `string` of the date to render. To adjust the format of the `label` string use the `dateFormat` prop, listed below.
196 */
197 renderDay: _propTypes.default.func,
198 formats: _propTypes.default.shape({
199 /**
200 * A formatter for the header button of the month view.
201 *
202 * @example ['dateFormat', ['headerFormat', "{ date: 'medium' }"]]
203 */
204 header: _propTypes.default.any,
205
206 /**
207 * A formatter for the Calendar footer, formats today's Date as a string.
208 *
209 * @example ['dateFormat', ['footerFormat', "{ date: 'medium' }", "date => 'Today is: ' + formatter(date)"]]
210 */
211 footer: _propTypes.default.any,
212
213 /**
214 * A formatter calendar days of the week, the default formats each day as a Narrow name: "Mo", "Tu", etc.
215 *
216 * @example ['prop', { day: "day => \n['🎉', 'M', 'T','W','Th', 'F', '🎉'][day.getDay()]" }]
217 */
218 day: _propTypes.default.any,
219
220 /**
221 * A formatter for day of the month
222 *
223 * @example ['prop', { date: "dt => String(dt.getDate())" }]
224 */
225 date: _propTypes.default.any,
226
227 /**
228 * A formatter for month name.
229 *
230 * @example ['dateFormat', ['monthFormat', "{ raw: 'MMMM' }", null, { defaultView: '"year"' }]]
231 */
232 month: _propTypes.default.any,
233
234 /**
235 * A formatter for month name.
236 *
237 * @example ['dateFormat', ['yearFormat', "{ raw: 'yy' }", null, { defaultView: '"decade"' }]]
238 */
239 year: _propTypes.default.any,
240
241 /**
242 * A formatter for decade, the default formats the first and last year of the decade like: 2000 - 2009.
243 */
244 decade: _propTypes.default.any,
245
246 /**
247 * A formatter for century, the default formats the first and last year of the century like: 1900 - 1999.
248 */
249 century: _propTypes.default.any
250 }),
251 messages: _propTypes.default.shape({
252 moveBack: _propTypes.default.string,
253 moveForward: _propTypes.default.string
254 }),
255 onKeyDown: _propTypes.default.func,
256
257 /** @ignore */
258 tabIndex: _propTypes.default.any
259};
260
261const useViewState = (views, view = views[0], currentDate) => {
262 const lastView = (0, _react.useRef)(view);
263 const lastDate = (0, _react.useRef)(currentDate);
264 let slideDirection;
265
266 if (view !== lastView.current) {
267 slideDirection = views.indexOf(lastView.current) > views.indexOf(view) ? 'top' : 'bottom';
268 } else if (lastDate.current !== currentDate) {
269 slideDirection = _dates.default.gt(currentDate, lastDate.current) ? 'left' : 'right';
270 }
271
272 (0, _react.useEffect)(() => {
273 lastDate.current = currentDate;
274 lastView.current = view;
275 });
276 return slideDirection;
277};
278
279/**
280 * @public
281 */
282function Calendar(_ref) {
283 let {
284 id,
285 autoFocus,
286 bordered = true,
287 views = VIEW_OPTIONS,
288 tabIndex = 0,
289 disabled,
290 readOnly,
291 className,
292 value,
293 defaultValue,
294 onChange,
295 currentDate: pCurrentDate,
296 defaultCurrentDate,
297 onCurrentDateChange,
298 min = MIN,
299 max = MAX,
300 view,
301 defaultView = views[0],
302 onViewChange,
303 onKeyDown,
304 onNavigate,
305 renderDay,
306 messages,
307 formats
308 } = _ref,
309 elementProps = _objectWithoutPropertiesLoose(_ref, _excluded);
310
311 const [currentValue, handleChange] = (0, _uncontrollable.useUncontrolledProp)(value, defaultValue, onChange);
312 const [currentDate, handleCurrentDateChange] = (0, _uncontrollable.useUncontrolledProp)(pCurrentDate, defaultCurrentDate || currentValue || new Date(), onCurrentDateChange);
313 const [currentView, handleViewChange] = (0, _uncontrollable.useUncontrolledProp)(view, defaultView, onViewChange);
314 const localizer = (0, _Localization.useLocalizer)(messages, formats);
315 const ref = (0, _react.useRef)(null);
316 const viewId = (0, _WidgetHelpers.useInstanceId)(id, '_calendar');
317 const labelId = (0, _WidgetHelpers.useInstanceId)(id, '_calendar_label');
318 (0, _useAutoFocus.default)(!!autoFocus, ref);
319 const slideDirection = useViewState(views, currentView, currentDate);
320 const [, focused] = (0, _useFocusManager.default)(ref, {
321 disabled
322 }, {
323 willHandle() {
324 if (tabIndex == -1) return false;
325 }
326
327 });
328 const lastValue = (0, _react.useRef)(currentValue);
329 (0, _react.useEffect)(() => {
330 const inValue = inRangeValue(currentValue, min, max);
331 const last = lastValue.current;
332 lastValue.current = currentValue;
333 if (!_dates.default.eq(inValue, dateOrNull(last), VIEW_UNIT[currentView])) maybeSetCurrentDate(inValue);
334 });
335 const isDisabled = disabled || readOnly;
336 /**
337 * Handlers
338 */
339
340 const handleViewChangeImpl = () => {
341 navigate('UP');
342 };
343
344 const handleMoveBack = () => {
345 navigate('LEFT');
346 };
347
348 const handleMoveForward = () => {
349 navigate('RIGHT');
350 };
351
352 const handleDateChange = date => {
353 if (views[0] === currentView) {
354 maybeSetCurrentDate(date);
355 (0, _WidgetHelpers.notify)(handleChange, [date]);
356 focus();
357 return;
358 }
359
360 navigate('DOWN', date);
361 };
362
363 const handleMoveToday = () => {
364 let date = new Date();
365 let firstView = views[0];
366 (0, _WidgetHelpers.notify)(onChange, [date]);
367
368 if (_dates.default.inRange(date, min, max, firstView)) {
369 focus();
370 maybeSetCurrentDate(date);
371 (0, _WidgetHelpers.notify)(handleViewChange, [firstView]);
372 }
373 };
374
375 const handleKeyDown = e => {
376 let ctrl = e.ctrlKey || e.metaKey;
377 let key = e.key;
378 let direction = ARROWS_TO_DIRECTION[key];
379 let unit = VIEW_UNIT[currentView];
380
381 if (key === 'Enter') {
382 e.preventDefault();
383 return handleDateChange(currentDate);
384 }
385
386 if (direction) {
387 if (ctrl) {
388 e.preventDefault();
389 navigate(direction);
390 } else {
391 const isRTL = getComputedStyle(e.currentTarget).getPropertyValue('direction') === 'rtl';
392 if (isRTL && direction in OPPOSITE_DIRECTION) direction = OPPOSITE_DIRECTION[direction];
393 let nextDate = Calendar.move(currentDate, min, max, currentView, direction);
394
395 if (!_dates.default.eq(currentDate, nextDate, unit)) {
396 e.preventDefault();
397 if (_dates.default.gt(nextDate, currentDate, currentView)) navigate('RIGHT', nextDate);else if (_dates.default.lt(nextDate, currentDate, currentView)) navigate('LEFT', nextDate);else maybeSetCurrentDate(nextDate);
398 }
399 }
400 }
401
402 (0, _WidgetHelpers.notify)(onKeyDown, [e]);
403 };
404
405 function navigate(direction, date) {
406 let nextView = currentView;
407 let slideDir = direction === 'LEFT' || direction === 'UP' ? 'right' : 'left';
408 if (direction === 'UP') nextView = views[views.indexOf(currentView) + 1] || nextView;
409 if (direction === 'DOWN') nextView = views[views.indexOf(currentView) - 1] || nextView;
410 if (!date) date = ['LEFT', 'RIGHT'].indexOf(direction) !== -1 ? nextDate(direction) : currentDate;
411
412 if (_dates.default.inRange(date, min, max, nextView)) {
413 (0, _WidgetHelpers.notify)(onNavigate, [date, slideDir, nextView]); //this.focus()
414
415 maybeSetCurrentDate(date);
416 (0, _WidgetHelpers.notify)(handleViewChange, [nextView]);
417 }
418 }
419
420 const focus = () => {
421 var _ref$current;
422
423 const node = (_ref$current = ref.current) == null ? void 0 : _ref$current.querySelector(FOCUSED_CELL_SELECTOR);
424 node == null ? void 0 : node.focus();
425 };
426
427 const moveFocus = (node, hadFocus) => {
428 let current = document.activeElement;
429
430 if (hadFocus && (!current || !node.contains(current))) {
431 node.focus();
432 }
433 };
434
435 function maybeSetCurrentDate(date) {
436 let inRangeDate = inRangeValue(date ? new Date(date) : currentDate, min, max);
437 if (date === currentDate || _dates.default.eq(inRangeDate, dateOrNull(currentDate), VIEW_UNIT[currentView])) return;
438 (0, _WidgetHelpers.notify)(handleCurrentDateChange, [inRangeDate]);
439 }
440
441 function nextDate(direction) {
442 let method = direction === 'LEFT' ? 'subtract' : 'add';
443 let unit = currentView === 'month' ? currentView : 'year';
444 let multi = MULTIPLIER[currentView] || 1;
445 return _dates.default[method](currentDate, 1 * multi, unit);
446 }
447
448 function getHeaderLabel() {
449 switch (currentView) {
450 case 'month':
451 return localizer.formatDate(currentDate, 'header');
452
453 case 'year':
454 return localizer.formatDate(currentDate, 'year');
455
456 case 'decade':
457 return localizer.formatDate(_dates.default.startOf(currentDate, 'decade'), 'decade');
458
459 case 'century':
460 return localizer.formatDate(_dates.default.startOf(currentDate, 'century'), 'century');
461 }
462 }
463
464 let View = VIEW[currentView];
465 let todayNotInRange = !_dates.default.inRange(new Date(), min, max, currentView);
466
467 let key = currentView + '_' + _dates.default[currentView](currentDate); // let elementProps = Props.pickElementProps(this),
468 // let viewProps = pick(uncontrolledProps, View)
469
470
471 const prevDisabled = isDisabled || !_dates.default.inRange(nextDate('LEFT'), min, max, currentView);
472 const nextDisabled = isDisabled || !_dates.default.inRange(nextDate('RIGHT'), min, max, currentView);
473 return /*#__PURE__*/_react.default.createElement(_Widget.default, _extends({}, elementProps, {
474 role: "group",
475 ref: ref,
476 focused: focused,
477 disabled: disabled,
478 readOnly: readOnly,
479 tabIndex: tabIndex,
480 className: (0, _classnames.default)(className, 'rw-calendar', bordered && 'rw-calendar-contained')
481 }), /*#__PURE__*/_react.default.createElement(_CalendarHeader.default, {
482 label: getHeaderLabel(),
483 labelId: labelId,
484 localizer: localizer,
485 upDisabled: isDisabled || currentView === last(views),
486 prevDisabled: prevDisabled,
487 todayDisabled: isDisabled || todayNotInRange,
488 nextDisabled: nextDisabled,
489 onViewChange: handleViewChangeImpl,
490 onMoveLeft: handleMoveBack,
491 onMoveRight: handleMoveForward,
492 onMoveToday: handleMoveToday
493 }), /*#__PURE__*/_react.default.createElement(Calendar.Transition, {
494 direction: slideDirection,
495 onTransitionEnd: moveFocus
496 }, /*#__PURE__*/_react.default.createElement(View, {
497 key: key,
498 min: min,
499 max: max,
500 id: viewId,
501 value: currentValue,
502 localizer: localizer,
503 disabled: isDisabled,
504 focusedItem: currentDate,
505 onChange: handleDateChange,
506 onKeyDown: handleKeyDown,
507 "aria-labelledby": labelId,
508 renderDay: renderDay
509 })));
510}
511
512function dateOrNull(dt) {
513 if (dt && !isNaN(dt.getTime())) return dt;
514 return null;
515}
516
517Calendar.displayName = 'Calendar';
518Calendar.propTypes = propTypes; // Calendar.defaultProps = {
519// min: new Date(1900, 0, 1),
520// max: new Date(2099, 11, 31),
521// views: VIEW_OPTIONS,
522// tabIndex: '0',
523// }
524
525Calendar.Transition = _SlideTransitionGroup.default;
526
527Calendar.move = (date, min, max, view, direction) => {
528 let isMonth = view === 'month';
529 let isUpOrDown = direction === 'UP' || direction === 'DOWN';
530 let rangeUnit = view && VIEW_UNIT[view];
531 let addUnit = isMonth && isUpOrDown ? 'week' : VIEW_UNIT[view];
532 let amount = isMonth || !isUpOrDown ? 1 : 4;
533 let newDate;
534 if (direction === 'UP' || direction === 'LEFT') amount *= -1;
535 newDate = _dates.default.add(date, amount, addUnit);
536 return _dates.default.inRange(newDate, min, max, rangeUnit) ? newDate : date;
537};
538
539var _default = Calendar;
540exports.default = _default;
\No newline at end of file