1 | 'use strict';
|
2 |
|
3 | Object.defineProperty(exports, '__esModule', { value: true });
|
4 |
|
5 | var _objectSpread = require('@babel/runtime/helpers/objectSpread2');
|
6 | var _slicedToArray = require('@babel/runtime/helpers/slicedToArray');
|
7 | var _objectWithoutProperties = require('@babel/runtime/helpers/objectWithoutProperties');
|
8 | var stack = require('@spark-web/stack');
|
9 | var react = require('react');
|
10 | var reactPopper = require('react-popper');
|
11 | var icon = require('@spark-web/icon');
|
12 | var reactDayPicker = require('react-day-picker');
|
13 | var FocusLock = require('react-focus-lock');
|
14 | var css = require('@emotion/css');
|
15 | var a11y = require('@spark-web/a11y');
|
16 | var box = require('@spark-web/box');
|
17 | var button = require('@spark-web/button');
|
18 | var heading = require('@spark-web/heading');
|
19 | var text = require('@spark-web/text');
|
20 | var theme = require('@spark-web/theme');
|
21 | var jsxRuntime = require('react/jsx-runtime');
|
22 | var field = require('@spark-web/field');
|
23 | var textInput = require('@spark-web/text-input');
|
24 | var dateFns = require('date-fns');
|
25 |
|
26 | function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
|
27 |
|
28 | var FocusLock__default = _interopDefault(FocusLock);
|
29 |
|
30 | function CalendarContainer(_ref) {
|
31 | var children = _ref.children;
|
32 | var dayPickerStyles = useDayPickerStyles();
|
33 | return jsxRuntime.jsx(box.Box, {
|
34 | background: "surface",
|
35 | border: "standard",
|
36 | borderRadius: "medium",
|
37 | display: "inline-block",
|
38 | padding: "small",
|
39 | position: "relative",
|
40 | shadow: "medium",
|
41 | className: css.css(dayPickerStyles),
|
42 | children: children
|
43 | });
|
44 | }
|
45 | function useDayPickerStyles() {
|
46 | var theme$1 = theme.useTheme();
|
47 | var cellSize = theme$1.sizing.medium;
|
48 | var _useHeading = heading.useHeading({
|
49 | level: '3',
|
50 | align: 'left'
|
51 | }),
|
52 | _useHeading2 = _slicedToArray(_useHeading, 2),
|
53 | typographyHeadingStyles = _useHeading2[0],
|
54 | responsiveHeadingStyles = _useHeading2[1];
|
55 | var _useText = text.useText({
|
56 | baseline: true,
|
57 | tone: 'neutral',
|
58 | size: 'small',
|
59 | weight: 'regular'
|
60 | }),
|
61 | _useText2 = _slicedToArray(_useText, 2),
|
62 | typographyTextStyles = _useText2[0],
|
63 | responsiveTextStyles = _useText2[1];
|
64 | var _useButtonStyles = button.useButtonStyles({
|
65 | iconOnly: false,
|
66 | prominence: 'none',
|
67 | size: 'medium',
|
68 | tone: 'primary'
|
69 | }),
|
70 | _useButtonStyles2 = _slicedToArray(_useButtonStyles, 2),
|
71 | buttonStyles = _useButtonStyles2[1];
|
72 | var focusStyles = a11y.useFocusRing({
|
73 | always: true
|
74 | });
|
75 | return {
|
76 | '.rdp-vhidden': a11y.visuallyHiddenStyles,
|
77 |
|
78 | '.rdp-button_reset': {
|
79 | appearance: 'none',
|
80 | background: 'none',
|
81 | border: 'none',
|
82 | margin: 0,
|
83 | padding: 0,
|
84 | cursor: 'pointer',
|
85 | color: 'inherit',
|
86 | font: 'inherit'
|
87 | },
|
88 |
|
89 | '.rdp-caption': {
|
90 | display: 'flex',
|
91 | alignItems: 'center',
|
92 | justifyContent: 'center',
|
93 | height: theme$1.sizing.medium,
|
94 | position: 'relative'
|
95 | },
|
96 | '.rdp-caption_label': _objectSpread(_objectSpread(_objectSpread({}, typographyHeadingStyles), responsiveHeadingStyles), {}, {
|
97 | margin: 0,
|
98 | whiteSpace: 'nowrap'
|
99 | }),
|
100 |
|
101 | '.rdp-nav': {
|
102 | position: 'absolute',
|
103 | top: 0,
|
104 | bottom: 0,
|
105 | left: 0,
|
106 | right: 0,
|
107 | display: 'flex',
|
108 | alignItems: 'center',
|
109 | justifyContent: 'space-between',
|
110 | paddingLeft: theme$1.spacing.medium - theme$1.spacing.small,
|
111 | paddingRight: theme$1.spacing.medium - theme$1.spacing.small
|
112 | },
|
113 | '.rdp-nav_button': _objectSpread(_objectSpread({}, buttonStyles), {}, {
|
114 | display: 'inline-flex',
|
115 | alignItems: 'center',
|
116 | justifyContent: 'center',
|
117 | cursor: 'pointer',
|
118 | height: theme$1.sizing.small,
|
119 | width: theme$1.sizing.small,
|
120 | borderRadius: theme$1.border.radius.full
|
121 | }),
|
122 | '.rdp-nav_button:focus': _objectSpread(_objectSpread({}, focusStyles), {}, {
|
123 | position: 'relative',
|
124 | backgroundColor: theme$1.backgroundInteractions.primaryLowHover
|
125 | }),
|
126 |
|
127 | '.rdp-head_cell': _objectSpread(_objectSpread(_objectSpread({}, typographyTextStyles), responsiveTextStyles), {}, {
|
128 | fontWeight: theme$1.typography.fontWeight.semibold,
|
129 | margin: 0,
|
130 | padding: 0,
|
131 | textAlign: 'center',
|
132 | verticalAlign: 'middle',
|
133 | height: cellSize,
|
134 | width: cellSize
|
135 | }),
|
136 |
|
137 | '.rdp-day': _objectSpread(_objectSpread(_objectSpread(_objectSpread({}, typographyTextStyles), responsiveTextStyles), buttonStyles), {}, {
|
138 | borderRadius: theme$1.border.radius.small
|
139 | }),
|
140 | '.rdp-day:focus': _objectSpread(_objectSpread({}, focusStyles), {}, {
|
141 | position: 'relative',
|
142 | backgroundColor: theme$1.backgroundInteractions.primaryLowHover
|
143 | }),
|
144 | ".rdp-button:disabled, .rdp-button[aria-disabled='true']": {
|
145 | color: theme$1.color.foreground.disabled,
|
146 | pointerEvents: 'none',
|
147 | userSelect: 'none'
|
148 | },
|
149 | '.rdp-weeknumber, .rdp-day': {
|
150 | display: 'flex',
|
151 | justifyContent: 'center',
|
152 | alignItems: 'center',
|
153 | width: cellSize,
|
154 | height: cellSize
|
155 | },
|
156 |
|
157 | '.rdp-months': {
|
158 | display: 'flex'
|
159 | },
|
160 | '.rdp-month:first-of-type': {
|
161 | marginLeft: 0
|
162 | },
|
163 | '.rdp-month:last-of-type': {
|
164 | marginRight: 0
|
165 | },
|
166 | '.rdp-table': {
|
167 | margin: 0,
|
168 | maxWidth: "calc(".concat(cellSize, " * 7)"),
|
169 | borderCollapse: 'collapse'
|
170 | },
|
171 | '.rdp-tbody': {
|
172 | border: 0
|
173 | },
|
174 | '.rdp-cell': {
|
175 | width: cellSize,
|
176 | height: cellSize,
|
177 | padding: 0,
|
178 | textAlign: 'center'
|
179 | },
|
180 | ".rdp-day_selected:not([aria-disabled='true']), .rdp-day_selected:focus:not([aria-disabled='true']), .rdp-day_selected:active:not([aria-disabled='true']), .rdp-day_selected:hover:not([aria-disabled='true']), .rdp-day_selected:hover:not([aria-disabled='true'])": {
|
181 | backgroundColor: theme$1.color.background.primary,
|
182 | color: theme$1.color.foreground.neutralInverted
|
183 | }
|
184 | };
|
185 | }
|
186 |
|
187 | function CalendarSingle(props) {
|
188 | return jsxRuntime.jsx(FocusLock__default["default"], {
|
189 | autoFocus: false,
|
190 | returnFocus: true,
|
191 | children: jsxRuntime.jsx(CalendarContainer, {
|
192 | children: jsxRuntime.jsx(reactDayPicker.DayPicker, _objectSpread(_objectSpread({}, props), {}, {
|
193 | mode: "single",
|
194 | components: calendarComponents
|
195 | }))
|
196 | })
|
197 | });
|
198 | }
|
199 | var calendarComponents = {
|
200 | IconRight: function IconRight() {
|
201 | return jsxRuntime.jsx(icon.ChevronRightIcon, {
|
202 | size: "xsmall"
|
203 | });
|
204 | },
|
205 | IconLeft: function IconLeft() {
|
206 | return jsxRuntime.jsx(icon.ChevronLeftIcon, {
|
207 | size: "xsmall"
|
208 | });
|
209 | }
|
210 | };
|
211 |
|
212 |
|
213 | var dateFormat = 'dd/MM/yyyy';
|
214 |
|
215 |
|
216 | function formatDate(date) {
|
217 | return dateFns.format(new Date(date), dateFormat);
|
218 | }
|
219 |
|
220 |
|
221 | function formatHumanReadableDate(date) {
|
222 | return dateFns.format(date, 'eeee MMMM do, yyyy');
|
223 | }
|
224 |
|
225 |
|
226 | function isDate(value) {
|
227 | return dateFns.isDate(value);
|
228 | }
|
229 |
|
230 |
|
231 |
|
232 |
|
233 |
|
234 |
|
235 | function parseDate(value) {
|
236 | if (value.length !== dateFormat.length) {
|
237 | return undefined;
|
238 | }
|
239 | var parsedDate = dateFns.parse(value, dateFormat, new Date());
|
240 | if (isDate(parsedDate) && dateFns.isValid(parsedDate)) {
|
241 | return parsedDate;
|
242 | }
|
243 | return undefined;
|
244 | }
|
245 |
|
246 |
|
247 |
|
248 |
|
249 |
|
250 |
|
251 |
|
252 |
|
253 | function constrainDate(date, minDate, maxDate) {
|
254 | if (!date) {
|
255 | return date;
|
256 | }
|
257 | if (minDate && dateFns.isBefore(date, minDate)) {
|
258 | return minDate;
|
259 | }
|
260 | if (maxDate && dateFns.isAfter(date, maxDate)) {
|
261 | return maxDate;
|
262 | }
|
263 | return date;
|
264 | }
|
265 |
|
266 |
|
267 |
|
268 |
|
269 | function getSeparatorIndexes() {
|
270 | var indexes = [];
|
271 | for (var i = 0; i < dateFormat.length; i++) {
|
272 | if (dateFormat[i] === '/') {
|
273 | indexes.push(i);
|
274 | }
|
275 | }
|
276 | return indexes;
|
277 | }
|
278 |
|
279 |
|
280 |
|
281 |
|
282 |
|
283 |
|
284 |
|
285 | function formatDateOnChange(date, inputValue, cursorPosition) {
|
286 | var indexes = getSeparatorIndexes();
|
287 | var format = dateFormat.toUpperCase();
|
288 | var dateFormatParts = format.split('/');
|
289 | var dateParts = date.split('/');
|
290 | var newDate = [];
|
291 |
|
292 |
|
293 | if (!inputValue || !date) return format;
|
294 | if (indexes.includes(cursorPosition)) {
|
295 | return date.slice(0, cursorPosition) + inputValue.slice(cursorPosition);
|
296 | }
|
297 | dateFormatParts.forEach(function (part, index) {
|
298 | var cleanValue = part;
|
299 | var cleanDate = dateParts[index].replace(/\D/g, '');
|
300 | var length = part.length;
|
301 | if (Boolean(cleanDate)) {
|
302 | var _char = part.charAt(0);
|
303 | var formattedDate = cleanDate.padEnd(length, _char).slice(0, length);
|
304 | cleanValue = formattedDate;
|
305 | }
|
306 | newDate.push(cleanValue);
|
307 | });
|
308 | return newDate.join('/');
|
309 | }
|
310 |
|
311 | var _excluded$1 = ["buttonRef", "buttonOnClick", "value"];
|
312 | var DateInput = react.forwardRef(function DateInput(_ref, forwardedRef) {
|
313 | var buttonRef = _ref.buttonRef,
|
314 | buttonOnClick = _ref.buttonOnClick,
|
315 | value = _ref.value,
|
316 | consumerProps = _objectWithoutProperties(_ref, _excluded$1);
|
317 | var _useIconButtonStyles = useIconButtonStyles(),
|
318 | _useIconButtonStyles2 = _slicedToArray(_useIconButtonStyles, 2),
|
319 | boxProps = _useIconButtonStyles2[0],
|
320 | buttonStyles = _useIconButtonStyles2[1];
|
321 | var _useFieldContext = field.useFieldContext(),
|
322 | _useFieldContext2 = _slicedToArray(_useFieldContext, 1),
|
323 | disabled = _useFieldContext2[0].disabled;
|
324 | var buttonLabel = react.useMemo(function () {
|
325 | if (typeof value !== 'string') {
|
326 | return 'Choose date';
|
327 | }
|
328 | var parsed = parseDate(value);
|
329 | if (!parsed) {
|
330 | return 'Choose date';
|
331 | }
|
332 | return "Change Date, ".concat(formatHumanReadableDate(parsed));
|
333 | }, [value]);
|
334 | return jsxRuntime.jsx(textInput.TextInput, _objectSpread(_objectSpread({}, consumerProps), {}, {
|
335 | ref: forwardedRef,
|
336 | value: value,
|
337 | children: jsxRuntime.jsx(textInput.InputAdornment, {
|
338 | placement: "end",
|
339 | children: jsxRuntime.jsx(button.BaseButton, _objectSpread(_objectSpread({}, boxProps), {}, {
|
340 | "aria-label": buttonLabel,
|
341 | onClick: buttonOnClick,
|
342 | ref: buttonRef,
|
343 | disabled: disabled,
|
344 | className: css.css(buttonStyles)
|
345 |
|
346 |
|
347 |
|
348 | ,
|
349 | tabIndex: disabled ? -1 : undefined,
|
350 | children: jsxRuntime.jsx(icon.CalendarIcon, {
|
351 | tone: disabled ? 'disabled' : 'neutral'
|
352 | })
|
353 | }))
|
354 | })
|
355 | }));
|
356 | });
|
357 | function useIconButtonStyles() {
|
358 | var _useButtonStyles = button.useButtonStyles({
|
359 | iconOnly: false,
|
360 | prominence: 'none',
|
361 | size: 'medium',
|
362 | tone: 'neutral'
|
363 | }),
|
364 | _useButtonStyles2 = _slicedToArray(_useButtonStyles, 2),
|
365 | buttonStyles = _useButtonStyles2[1];
|
366 | return [{
|
367 | alignItems: 'center',
|
368 | borderRadius: 'full',
|
369 | cursor: 'pointer',
|
370 | display: 'inline-flex',
|
371 | gap: 'small',
|
372 | height: 'small',
|
373 | justifyContent: 'center',
|
374 | paddingX: 'xsmall',
|
375 | position: 'relative',
|
376 | width: 'small'
|
377 | }, buttonStyles];
|
378 | }
|
379 |
|
380 |
|
381 |
|
382 |
|
383 |
|
384 |
|
385 |
|
386 | function useTernaryState(initialValue) {
|
387 | var _useState = react.useState(initialValue),
|
388 | _useState2 = _slicedToArray(_useState, 2),
|
389 | state = _useState2[0],
|
390 | setState = _useState2[1];
|
391 | var setTrue = react.useCallback(function () {
|
392 | return setState(true);
|
393 | }, []);
|
394 | var setFalse = react.useCallback(function () {
|
395 | return setState(false);
|
396 | }, []);
|
397 | return [state, setTrue, setFalse];
|
398 | }
|
399 |
|
400 |
|
401 |
|
402 |
|
403 |
|
404 |
|
405 | function useClickOutside(ref, handler) {
|
406 | react.useEffect(function () {
|
407 | function listener(event) {
|
408 | var element = ref === null || ref === void 0 ? void 0 : ref.current;
|
409 |
|
410 |
|
411 | if (!element || element.contains(event.target)) {
|
412 | return;
|
413 | }
|
414 | handler(event);
|
415 | }
|
416 | window.addEventListener('mousedown', listener);
|
417 | return function () {
|
418 | return window.removeEventListener('mousedown', listener);
|
419 | };
|
420 | }, [handler, ref]);
|
421 | }
|
422 |
|
423 | var _excluded = ["data", "initialMonth", "maxDate", "minDate", "onChange", "value"];
|
424 | var DatePicker = react.forwardRef(function DatePicker(_ref, forwardedRef) {
|
425 | var data = _ref.data,
|
426 | initialMonth = _ref.initialMonth,
|
427 | maxDate = _ref.maxDate,
|
428 | minDate = _ref.minDate,
|
429 | onChange = _ref.onChange,
|
430 | value = _ref.value,
|
431 | consumerProps = _objectWithoutProperties(_ref, _excluded);
|
432 | var _useTernaryState = useTernaryState(false),
|
433 | _useTernaryState2 = _slicedToArray(_useTernaryState, 3),
|
434 | isCalendarOpen = _useTernaryState2[0],
|
435 | openCalendar = _useTernaryState2[1],
|
436 | closeCalendar = _useTernaryState2[2];
|
437 |
|
438 |
|
439 | var triggerRef = react.useRef(null);
|
440 | var _useState = react.useState(null),
|
441 | _useState2 = _slicedToArray(_useState, 2),
|
442 | refEl = _useState2[0],
|
443 | setRefEl = _useState2[1];
|
444 | var _useState3 = react.useState(null),
|
445 | _useState4 = _slicedToArray(_useState3, 2),
|
446 | popperEl = _useState4[0],
|
447 | setPopperEl = _useState4[1];
|
448 | var _usePopper = reactPopper.usePopper(refEl, popperEl, {
|
449 | placement: 'bottom-start',
|
450 | modifiers: [{
|
451 | name: 'offset',
|
452 | options: {
|
453 | offset: [0, 8]
|
454 | }
|
455 | }]
|
456 | }),
|
457 | styles = _usePopper.styles,
|
458 | attributes = _usePopper.attributes;
|
459 | var defaultValue = dateFormat.toUpperCase();
|
460 | var _useState5 = react.useState(''),
|
461 | _useState6 = _slicedToArray(_useState5, 2),
|
462 | inputValue = _useState6[0],
|
463 | setInputValue = _useState6[1];
|
464 | var onSelect = react.useCallback(function (_, selectedDay, modifiers) {
|
465 |
|
466 | if (modifiers.disabled) {
|
467 | return;
|
468 | }
|
469 |
|
470 | setInputValue(formatDate(selectedDay));
|
471 |
|
472 | onChange(selectedDay);
|
473 |
|
474 | closeCalendar();
|
475 | }, [onChange, closeCalendar]);
|
476 | var onInputChange = react.useCallback(function (event) {
|
477 | var _event$target$selecti;
|
478 | var indexes = getSeparatorIndexes();
|
479 | var eventValue = event.target.value;
|
480 | var startPos = (_event$target$selecti = event.target.selectionStart) !== null && _event$target$selecti !== void 0 ? _event$target$selecti : 0;
|
481 | var formattedDate = formatDateOnChange(eventValue, inputValue, startPos);
|
482 | var nextPos = startPos;
|
483 |
|
484 |
|
485 | if (indexes.includes(startPos) && eventValue.length > inputValue.length) {
|
486 | nextPos = startPos + 1;
|
487 | }
|
488 | setInputValue(formattedDate);
|
489 | setCursorPosition(event, nextPos);
|
490 | var parsedDate = parseDate(formattedDate);
|
491 | var constrainedDate = constrainDate(parsedDate, minDate, maxDate);
|
492 | onChange(constrainedDate);
|
493 | }, [maxDate, minDate, onChange, inputValue]);
|
494 |
|
495 |
|
496 | react.useEffect(function () {
|
497 | if (value) {
|
498 | setInputValue(formatDate(value));
|
499 | }
|
500 | }, [value]);
|
501 |
|
502 |
|
503 | var clickOutsideRef = react.useRef(popperEl);
|
504 | clickOutsideRef.current = popperEl;
|
505 | var handleClickOutside = react.useCallback(function () {
|
506 | if (isCalendarOpen) {
|
507 | closeCalendar();
|
508 | }
|
509 | }, [isCalendarOpen, closeCalendar]);
|
510 | useClickOutside(clickOutsideRef, handleClickOutside);
|
511 |
|
512 |
|
513 | var handleEscape = react.useCallback(function (event) {
|
514 | if (isCalendarOpen && event.code === 'Escape') {
|
515 | event.preventDefault();
|
516 | event.stopPropagation();
|
517 |
|
518 | closeCalendar();
|
519 | }
|
520 | }, [isCalendarOpen, closeCalendar]);
|
521 | var disabledCalendarDays = react.useMemo(function () {
|
522 | if (!(minDate || maxDate)) {
|
523 | return;
|
524 | }
|
525 | return [minDate ? {
|
526 | before: minDate
|
527 | } : undefined, maxDate ? {
|
528 | after: maxDate
|
529 | } : undefined].filter(function (x) {
|
530 | return Boolean(x);
|
531 | });
|
532 | }, [minDate, maxDate]);
|
533 |
|
534 |
|
535 | var setCursorPosition = function setCursorPosition(event, position) {
|
536 | setTimeout(function () {
|
537 | event.target.setSelectionRange(position, position);
|
538 | }, 0);
|
539 | };
|
540 | return jsxRuntime.jsxs(stack.Stack, {
|
541 | ref: setRefEl,
|
542 | onKeyDown: handleEscape,
|
543 | data: data,
|
544 | width: "full",
|
545 | children: [jsxRuntime.jsx(DateInput, _objectSpread(_objectSpread({}, consumerProps), {}, {
|
546 | buttonOnClick: openCalendar,
|
547 | buttonRef: triggerRef,
|
548 | onChange: onInputChange,
|
549 | ref: forwardedRef,
|
550 | value: inputValue,
|
551 | placeholder: defaultValue,
|
552 | onFocus: function onFocus(e) {
|
553 | if (!inputValue) setInputValue(defaultValue);
|
554 | setCursorPosition(e, 0);
|
555 | },
|
556 | onBlur: function onBlur() {
|
557 | if (inputValue === defaultValue) setInputValue('');
|
558 | }
|
559 | })), isCalendarOpen && jsxRuntime.jsx("div", _objectSpread(_objectSpread({}, attributes.popper), {}, {
|
560 | ref: setPopperEl,
|
561 | style: _objectSpread(_objectSpread({}, styles.popper), {}, {
|
562 | zIndex: 1
|
563 | }),
|
564 | children: jsxRuntime.jsx(CalendarSingle, {
|
565 | defaultMonth: value || initialMonth,
|
566 | disabled: disabledCalendarDays,
|
567 | initialFocus: true,
|
568 | numberOfMonths: 1,
|
569 | onSelect: onSelect,
|
570 | selected: value
|
571 | })
|
572 | }))]
|
573 | });
|
574 | });
|
575 |
|
576 | exports.DatePicker = DatePicker;
|