UNPKG

@awsui/components-react

Version:

On July 19th, 2022, we launched [Cloudscape Design System](https://cloudscape.design). Cloudscape is an evolution of AWS-UI. It consists of user interface guidelines, front-end components, design resources, and development tools for building intuitive, en

185 lines 12.4 kB
import { __rest } from "tslib"; // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import React, { useEffect, useRef, useState } from 'react'; import clsx from 'clsx'; import { warnOnce } from '@awsui/component-toolkit/internal'; import { useInternalI18n } from '../i18n/context'; import InternalIcon from '../icon/internal'; import { getBaseProps } from '../internal/base-component'; import ButtonTrigger from '../internal/components/button-trigger'; import Dropdown from '../internal/components/dropdown'; import { useFormFieldContext } from '../internal/context/form-field-context'; import ResetContextsForModal from '../internal/context/reset-contexts-for-modal.js'; import { fireNonCancelableEvent } from '../internal/events'; import checkControlled from '../internal/hooks/check-controlled'; import useForwardFocus from '../internal/hooks/forward-focus'; import useBaseComponent from '../internal/hooks/use-base-component'; import { useFocusTracker } from '../internal/hooks/use-focus-tracker'; import { useMergeRefs } from '../internal/hooks/use-merge-refs'; import { useMobile } from '../internal/hooks/use-mobile'; import { usePrevious } from '../internal/hooks/use-previous'; import { useUniqueId } from '../internal/hooks/use-unique-id'; import { isDevelopment } from '../internal/is-development.js'; import { KeyCode } from '../internal/keycode'; import { applyDisplayName } from '../internal/utils/apply-display-name'; import { formatDateTimeWithOffset } from '../internal/utils/date-time/format-date-time-with-offset'; import { normalizeLocale } from '../internal/utils/locale'; import { joinStrings } from '../internal/utils/strings/join-strings'; import { DateRangePickerDropdown } from './dropdown'; import { normalizeTimeOffset } from './time-offset'; import { formatInitialValue, formatValue } from './utils'; import styles from './styles.css.js'; import testutilStyles from './test-classes/styles.css.js'; function renderDateRange({ locale, range, placeholder = '', formatRelativeRange, absoluteFormat, hideTimeOffset, timeOffset, }) { var _a; const firstPart = range ? range.type === 'relative' ? (_a = formatRelativeRange === null || formatRelativeRange === void 0 ? void 0 : formatRelativeRange(range)) !== null && _a !== void 0 ? _a : '' : formatDateTimeWithOffset({ date: range.startDate, timeOffset: timeOffset.startDate, hideTimeOffset, format: absoluteFormat, locale, }) : placeholder; const secondPart = (range === null || range === void 0 ? void 0 : range.type) === 'absolute' ? formatDateTimeWithOffset({ date: range.endDate, timeOffset: timeOffset.endDate, hideTimeOffset, format: absoluteFormat, locale, }) : ''; return (React.createElement("span", { className: (!range && styles['label-text']) || undefined, "aria-disabled": !range }, React.createElement("span", { className: (range === null || range === void 0 ? void 0 : range.type) === 'absolute' ? styles['label-token-nowrap'] : undefined }, firstPart), React.createElement("span", null, secondPart && ' — '), React.createElement("span", { className: styles['label-token-nowrap'] }, secondPart))); } const DateRangePicker = React.forwardRef((_a, ref) => { var _b, _c; var { locale = '', startOfWeek, isDateEnabled = () => true, dateDisabledReason, value, placeholder, readOnly = false, disabled = false, onChange, onBlur, onFocus, relativeOptions = [], i18nStrings, isValidRange = () => ({ valid: true }), showClearButton = true, dateOnly = false, timeOffset, getTimeOffset, timeInputFormat = 'hh:mm:ss', expandToViewport = false, rangeSelectorMode = 'default', customAbsoluteRangeControl, absoluteFormat = 'iso', hideTimeOffset, customRelativeRangeUnits, granularity = 'day' } = _a, rest = __rest(_a, ["locale", "startOfWeek", "isDateEnabled", "dateDisabledReason", "value", "placeholder", "readOnly", "disabled", "onChange", "onBlur", "onFocus", "relativeOptions", "i18nStrings", "isValidRange", "showClearButton", "dateOnly", "timeOffset", "getTimeOffset", "timeInputFormat", "expandToViewport", "rangeSelectorMode", "customAbsoluteRangeControl", "absoluteFormat", "hideTimeOffset", "customRelativeRangeUnits", "granularity"]); const { __internalRootRef } = useBaseComponent('DateRangePicker', { props: { absoluteFormat, dateOnly, expandToViewport, rangeSelectorMode, readOnly, showClearButton, timeInputFormat, hideTimeOffset, granularity, }, metadata: { hasDisabledReasons: Boolean(dateDisabledReason) }, }); const isMonthOnly = granularity === 'month'; const hideTime = dateOnly || isMonthOnly; checkControlled('DateRangePicker', 'value', value, 'onChange', onChange); const normalizedTimeOffset = hideTime ? { startDate: undefined, endDate: undefined } : normalizeTimeOffset(value, getTimeOffset, timeOffset); value = formatInitialValue(value, dateOnly, isMonthOnly, normalizedTimeOffset); const baseProps = getBaseProps(rest); const { invalid, warning, controlId, ariaDescribedby, ariaLabelledby } = useFormFieldContext(Object.assign({ ariaLabelledby: (_b = rest.ariaLabelledby) !== null && _b !== void 0 ? _b : i18nStrings === null || i18nStrings === void 0 ? void 0 : i18nStrings.ariaLabelledby, ariaDescribedby: (_c = rest.ariaDescribedby) !== null && _c !== void 0 ? _c : i18nStrings === null || i18nStrings === void 0 ? void 0 : i18nStrings.ariaDescribedby }, rest)); const isSingleGrid = useMobile(); const triggerRef = useRef(null); useForwardFocus(ref, triggerRef); const rootRef = useRef(null); const dropdownId = useUniqueId('date-range-picker-dropdown'); const triggerContentId = useUniqueId('date-range-picker-trigger'); useFocusTracker({ rootRef, onBlur, onFocus }); const [isDropDownOpen, setIsDropDownOpen] = useState(false); const normalizedLocale = normalizeLocale('DateRangePicker', locale); const closeDropdown = (focusTrigger = false) => { var _a; setIsDropDownOpen(false); if (focusTrigger) { (_a = triggerRef.current) === null || _a === void 0 ? void 0 : _a.focus(); } }; const onWrapperKeyDownHandler = (event) => { if (event.keyCode === KeyCode.escape) { if (isDropDownOpen) { event.stopPropagation(); } closeDropdown(true); } }; const onClear = () => { fireNonCancelableEvent(onChange, { value: null }); }; const onApply = (newValue) => { const formattedValue = formatValue(newValue, { dateOnly, monthOnly: isMonthOnly, timeOffset: hideTime ? { startDate: undefined, endDate: undefined } : normalizeTimeOffset(newValue, getTimeOffset, timeOffset), }); const validationResult = isValidRange(formattedValue); if ((validationResult === null || validationResult === void 0 ? void 0 : validationResult.valid) === false) { return validationResult; } if (isDevelopment) { if ((newValue === null || newValue === void 0 ? void 0 : newValue.type) === 'absolute') { const [startDateWithoutTime] = newValue.startDate.split('T'); const [endDateWithoutTime] = newValue.endDate.split('T'); if (!startDateWithoutTime || !endDateWithoutTime) { warnOnce('DateRangePicker', 'You have provided an `isValidRange` prop that did not catch a missing start or end date.'); } } } fireNonCancelableEvent(onChange, { value: formattedValue }); return validationResult || { valid: true }; }; const prevDateOnly = usePrevious(dateOnly); useEffect(() => { if (prevDateOnly !== undefined && prevDateOnly !== dateOnly) { warnOnce('DateRangePicker', `The provided \`dateOnly\` flag has been changed from "${prevDateOnly}" to "${dateOnly}" which can lead to unexpected value format. Consider using separate components.`); } }, [prevDateOnly, dateOnly]); if (value && value.type !== 'absolute' && value.type !== 'relative') { warnOnce('DateRangePicker', 'You provided an invalid value. Reverting back to default.'); value = null; } if (((value === null || value === void 0 ? void 0 : value.type) === 'absolute' && rangeSelectorMode === 'relative-only') || ((value === null || value === void 0 ? void 0 : value.type) === 'relative' && rangeSelectorMode === 'absolute-only')) { warnOnce('DateRangePicker', 'The provided value does not correspond to the current range selector mode. Reverting back to default.'); value = null; } const i18n = useInternalI18n('date-range-picker'); const formatRelativeRange = i18n('i18nStrings.formatRelativeRange', i18nStrings === null || i18nStrings === void 0 ? void 0 : i18nStrings.formatRelativeRange, format => ({ amount, unit }) => format({ amount, unit })); if (isDevelopment) { if (!formatRelativeRange && rangeSelectorMode !== 'absolute-only') { warnOnce('DateRangePicker', 'A function for i18nStrings.formatRelativeRange was not provided. Relative ranges will not be correctly rendered.'); } } const formattedDate = renderDateRange({ locale: normalizedLocale, range: value, placeholder, formatRelativeRange, absoluteFormat, hideTimeOffset: hideTime || hideTimeOffset, timeOffset: normalizedTimeOffset, }); const trigger = (React.createElement(ButtonTrigger, { ref: triggerRef, id: controlId, invalid: invalid, warning: warning, ariaLabelledby: joinStrings(ariaLabelledby, triggerContentId), ariaLabel: i18nStrings === null || i18nStrings === void 0 ? void 0 : i18nStrings.ariaLabel, ariaDescribedby: ariaDescribedby, className: clsx(testutilStyles.label, styles.label, { [styles['label-enabled']]: !readOnly && !disabled, }), hideCaret: true, onClick: () => { setIsDropDownOpen(true); }, disabled: disabled, readOnly: readOnly, ariaHasPopup: "dialog" }, React.createElement("span", { className: styles['trigger-flexbox'] }, React.createElement("span", { className: styles['icon-wrapper'] }, React.createElement(InternalIcon, { name: "calendar", variant: disabled || readOnly ? 'disabled' : 'normal' })), React.createElement("span", { id: triggerContentId }, formattedDate)))); const mergedRef = useMergeRefs(rootRef, __internalRootRef); return (React.createElement("div", Object.assign({}, baseProps, { ref: mergedRef, className: clsx(baseProps.className, styles.root, testutilStyles.root, absoluteFormat === 'long-localized' && !dateOnly && !isMonthOnly && styles.wide), onKeyDown: onWrapperKeyDownHandler }), React.createElement(Dropdown, { stretchWidth: true, stretchHeight: true, open: isDropDownOpen, onDropdownClose: () => closeDropdown(), trigger: trigger, stretchToTriggerWidth: false, expandToViewport: expandToViewport, dropdownId: dropdownId }, React.createElement(ResetContextsForModal, null, isDropDownOpen && (React.createElement(DateRangePickerDropdown, { startOfWeek: startOfWeek, locale: normalizedLocale, isSingleGrid: isSingleGrid, onDropdownClose: () => closeDropdown(true), value: value, showClearButton: showClearButton, isDateEnabled: isDateEnabled, dateDisabledReason: dateDisabledReason, i18nStrings: i18nStrings, onClear: onClear, onApply: onApply, getTimeOffset: getTimeOffset, timeOffset: timeOffset, relativeOptions: relativeOptions, isValidRange: isValidRange, dateOnly: dateOnly, timeInputFormat: timeInputFormat, rangeSelectorMode: rangeSelectorMode, ariaLabelledby: ariaLabelledby, ariaDescribedby: ariaDescribedby, customAbsoluteRangeControl: customAbsoluteRangeControl, customRelativeRangeUnits: customRelativeRangeUnits, granularity: granularity })))))); }); applyDisplayName(DateRangePicker, 'DateRangePicker'); export default DateRangePicker; //# sourceMappingURL=index.js.map