UNPKG

@atlaskit/datetime-picker

Version:

A date time picker allows the user to select an associated date and time.

353 lines (348 loc) 15.7 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); var _typeof = require("@babel/runtime/helpers/typeof"); Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends")); var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties")); var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray")); var _react = _interopRequireWildcard(require("react")); var _dateFns = require("date-fns"); var _usePlatformLeafEventHandler = require("@atlaskit/analytics-next/usePlatformLeafEventHandler"); var _noop = _interopRequireDefault(require("@atlaskit/ds-lib/noop")); var _localizationProvider = require("@atlaskit/locale/localization-provider"); var _fg = require("@atlaskit/platform-feature-flags/fg"); var _default2 = _interopRequireDefault(require("@atlaskit/select/default")); var _creatableSelect = _interopRequireDefault(require("@atlaskit/select/creatable-select")); var _styles = require("@atlaskit/react-select/styles"); var _defaultTimes = require("../internal/default-times"); var _emptyComponent = require("../internal/empty-component"); var _fixedLayerMenu = require("../internal/fixed-layer-menu"); var _fixedLayerMenuTopLayer = require("../internal/fixed-layer-menu-top-layer"); var _parseTime = _interopRequireDefault(require("../internal/parse-time")); var _parseTokens = require("../internal/parse-tokens"); var _placeholderDateTime = require("../internal/placeholder-date-time"); var _singleValue = require("../internal/single-value"); var _excluded = ["styles"]; function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); } function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } // oxlint-disable-next-line @atlassian/no-restricted-imports var packageName = "@atlaskit/datetime-picker"; var packageVersion = "18.7.2"; var defaultTimeFormat = 'h:mma'; var menuStyles = { /* Need to remove default absolute positioning as that causes issues with position fixed */ position: 'static', /* Need to add overflow to the element with max-height, otherwise causes overflow issues in IE11 */ overflowY: 'auto', /* React-Popper has already offset the menu so we need to reset the margin, otherwise the offset value is doubled */ margin: 0 }; var analyticsAttributes = { componentName: 'timePicker', packageName: packageName, packageVersion: packageVersion }; /** * __Time picker__ * * A time picker allows the user to select a specific time. * * - [Examples](https://atlassian.design/components/datetime-picker/time-picker/examples) * - [Code](https://atlassian.design/components/datetime-picker/time-picker/code) * - [Usage](https://atlassian.design/components/datetime-picker/time-picker/usage) */ var TimePicker = /*#__PURE__*/(0, _react.forwardRef)(function (_ref2, _ref) { var ariaDescribedBy = _ref2['aria-describedby'], _ref2$appearance = _ref2.appearance, appearance = _ref2$appearance === void 0 ? 'default' : _ref2$appearance, _ref2$autoFocus = _ref2.autoFocus, autoFocus = _ref2$autoFocus === void 0 ? false : _ref2$autoFocus, _ref2$clearControlLab = _ref2.clearControlLabel, clearControlLabel = _ref2$clearControlLab === void 0 ? 'clear timepicker' : _ref2$clearControlLab, _ref2$defaultIsOpen = _ref2.defaultIsOpen, defaultIsOpen = _ref2$defaultIsOpen === void 0 ? false : _ref2$defaultIsOpen, _ref2$defaultValue = _ref2.defaultValue, defaultValue = _ref2$defaultValue === void 0 ? '' : _ref2$defaultValue, formatDisplayLabel = _ref2.formatDisplayLabel, _ref2$hideIcon = _ref2.hideIcon, hideIcon = _ref2$hideIcon === void 0 ? false : _ref2$hideIcon, _ref2$id = _ref2.id, id = _ref2$id === void 0 ? '' : _ref2$id, _ref2$innerProps = _ref2.innerProps, innerProps = _ref2$innerProps === void 0 ? {} : _ref2$innerProps, _ref2$isDisabled = _ref2.isDisabled, isDisabled = _ref2$isDisabled === void 0 ? false : _ref2$isDisabled, _ref2$isInvalid = _ref2.isInvalid, isInvalid = _ref2$isInvalid === void 0 ? false : _ref2$isInvalid, _ref2$isRequired = _ref2.isRequired, isRequired = _ref2$isRequired === void 0 ? false : _ref2$isRequired, providedIsOpen = _ref2.isOpen, _ref2$label = _ref2.label, label = _ref2$label === void 0 ? '' : _ref2$label, _ref2$locale = _ref2.locale, locale = _ref2$locale === void 0 ? 'en-US' : _ref2$locale, _ref2$name = _ref2.name, name = _ref2$name === void 0 ? '' : _ref2$name, _ref2$onBlur = _ref2.onBlur, providedOnBlur = _ref2$onBlur === void 0 ? _noop.default : _ref2$onBlur, _ref2$onChange = _ref2.onChange, providedOnChange = _ref2$onChange === void 0 ? _noop.default : _ref2$onChange, _ref2$onFocus = _ref2.onFocus, providedOnFocus = _ref2$onFocus === void 0 ? _noop.default : _ref2$onFocus, _ref2$parseInputValue = _ref2.parseInputValue, parseInputValue = _ref2$parseInputValue === void 0 ? function (time, _timeFormat) { return (0, _parseTime.default)(time); } : _ref2$parseInputValue, placeholder = _ref2.placeholder, _ref2$selectProps = _ref2.selectProps, selectProps = _ref2$selectProps === void 0 ? {} : _ref2$selectProps, _ref2$spacing = _ref2.spacing, spacing = _ref2$spacing === void 0 ? 'default' : _ref2$spacing, testId = _ref2.testId, timeFormat = _ref2.timeFormat, _ref2$timeIsEditable = _ref2.timeIsEditable, timeIsEditable = _ref2$timeIsEditable === void 0 ? false : _ref2$timeIsEditable, _ref2$times = _ref2.times, times = _ref2$times === void 0 ? _defaultTimes.defaultTimes : _ref2$times, providedValue = _ref2.value; var _useState = (0, _react.useState)(null), _useState2 = (0, _slicedToArray2.default)(_useState, 2), containerRef = _useState2[0], setContainerRef = _useState2[1]; /** * When being cleared from the icon the TimePicker is blurred. * This variable defines whether the default onMenuOpen or onMenuClose * events should behave as normal */ var _useState3 = (0, _react.useState)(false), _useState4 = (0, _slicedToArray2.default)(_useState3, 2), clearingFromIcon = _useState4[0], setClearingFromIcon = _useState4[1]; // TODO: Remove isFocused? Does it do anything? var _useState5 = (0, _react.useState)(false), _useState6 = (0, _slicedToArray2.default)(_useState5, 2), _ = _useState6[0], setIsFocused = _useState6[1]; var _useState7 = (0, _react.useState)(defaultIsOpen), _useState8 = (0, _slicedToArray2.default)(_useState7, 2), isOpen = _useState8[0], setIsOpen = _useState8[1]; var _useState9 = (0, _react.useState)(defaultValue), _useState0 = (0, _slicedToArray2.default)(_useState9, 2), value = _useState0[0], setValue = _useState0[1]; // Hack to force update: https://legacy.reactjs.org/docs/hooks-faq.html#is-there-something-like-forceupdate var _useReducer = (0, _react.useReducer)(function (x) { return x + 1; }, 0), _useReducer2 = (0, _slicedToArray2.default)(_useReducer, 2), forceUpdate = _useReducer2[1]; var providedOnChangeWithAnalytics = (0, _usePlatformLeafEventHandler.usePlatformLeafEventHandler)(_objectSpread({ fn: providedOnChange, action: 'selectedTime' }, analyticsAttributes)); (0, _react.useEffect)(function () { if (providedValue) { setValue(providedValue); } }, [providedValue]); (0, _react.useEffect)(function () { if (providedIsOpen) { setIsOpen(providedIsOpen); } }, [providedIsOpen]); var onChange = (0, _react.useCallback)(function (newValue, action) { var rawValue = newValue ? newValue.value || newValue : ''; var finalValue = rawValue.toString(); setValue(finalValue); if (action && action.action === 'clear') { setClearingFromIcon(true); } providedOnChangeWithAnalytics(finalValue); }, [providedOnChangeWithAnalytics]); /** * Only allow custom times if timeIsEditable prop is true */ var onCreateOption = function onCreateOption(inputValue) { if (timeIsEditable) { var sanitizedInput; try { sanitizedInput = parseInputValue(inputValue, timeFormat || defaultTimeFormat); } catch (_unused) { return; // do nothing, the main validation should happen in the form } var includesSeconds = !!(timeFormat && /[:.]?(s|ss)/.test(timeFormat)); var formatFormat = includesSeconds ? 'HH:mm:ss' : 'HH:mm'; var formattedValue = (0, _dateFns.format)(sanitizedInput, formatFormat) || ''; setValue(formattedValue); providedOnChangeWithAnalytics(formattedValue); } else { providedOnChangeWithAnalytics(inputValue); } }; var onMenuOpen = function onMenuOpen() { // Don't open menu after the user has clicked clear if (clearingFromIcon) { setClearingFromIcon(false); } else { setIsOpen(true); } }; var onMenuClose = function onMenuClose() { // Don't close menu after the user has clicked clear if (clearingFromIcon) { setClearingFromIcon(false); } else { setIsOpen(false); } }; var setInternalContainerRef = function setInternalContainerRef(ref) { var oldRef = containerRef; setContainerRef(ref); // Cause a re-render if we're getting the container ref for the first time // as the layered menu requires it for dimension calculation if (oldRef === null && ref !== null) { forceUpdate(); } }; var onBlur = function onBlur(event) { setIsFocused(false); providedOnBlur(event); }; var onFocus = function onFocus(event) { setIsFocused(true); providedOnFocus(event); }; var onSelectKeyDown = function onSelectKeyDown(event) { var key = event.key; var keyPressed = key.toLowerCase(); if (clearingFromIcon && (keyPressed === 'backspace' || keyPressed === 'delete')) { // If being cleared from keyboard, don't change behaviour setClearingFromIcon(false); } }; var ICON_PADDING = 2; var GRID_SIZE = 8; var l10n = (0, _localizationProvider.createLocalizationProvider)(locale); var _selectProps$styles = selectProps.styles, selectStyles = _selectProps$styles === void 0 ? {} : _selectProps$styles, otherSelectProps = (0, _objectWithoutProperties2.default)(selectProps, _excluded); var SelectComponent = timeIsEditable ? _creatableSelect.default : _default2.default; /** * There are multiple props that can change how the time is formatted. * The priority of props used is: * 1. formatDisplayLabel * 2. timeFormat * 3. locale */ var formatTime = function formatTime(time) { if (formatDisplayLabel) { return formatDisplayLabel(time, timeFormat || defaultTimeFormat); } var date = (0, _parseTime.default)(time); if (!(date instanceof Date)) { return ''; } if (!(0, _dateFns.isValid)(date)) { return time; } if (timeFormat) { return (0, _dateFns.format)(date, (0, _parseTokens.convertTokens)(timeFormat)); } return l10n.formatTime(date); }; var options = times.map(function (time) { return { label: formatTime(time), value: time }; }); var initialValue; if (providedValue !== null && providedValue !== undefined && providedValue !== '') { initialValue = { label: formatTime(providedValue), value: providedValue }; } else if (providedValue !== '' && value) { initialValue = { label: formatTime(value), value: value }; } else { initialValue = null; } var SingleValue = (0, _singleValue.makeSingleValue)({ lang: locale }); var selectComponents = _objectSpread({ DropdownIndicator: _emptyComponent.EmptyComponent, Menu: (0, _fg.fg)('platform-dst-top-layer') ? _fixedLayerMenuTopLayer.FixedLayerMenuTopLayer : _fixedLayerMenu.FixedLayerMenu, SingleValue: SingleValue }, hideIcon && { ClearIndicator: _emptyComponent.EmptyComponent }); var renderIconContainer = Boolean(!hideIcon && value); var mergedStyles = (0, _styles.mergeStyles)(selectStyles, { control: function control(base) { return _objectSpread({}, base); }, menu: function menu(base) { return _objectSpread(_objectSpread(_objectSpread({}, base), menuStyles), {}, { // Fixed positioned elements no longer inherit width from their parent, so we must explicitly set the // menu width to the width of our container width: containerRef ? containerRef.getBoundingClientRect().width : 'auto' }); }, indicatorsContainer: function indicatorsContainer(base) { return _objectSpread(_objectSpread({}, base), {}, { paddingLeft: renderIconContainer ? ICON_PADDING : 0, paddingRight: renderIconContainer ? GRID_SIZE - ICON_PADDING : 0 }); } }); return /*#__PURE__*/_react.default.createElement("div", (0, _extends2.default)({}, innerProps, { ref: setInternalContainerRef, "data-testid": testId && "".concat(testId, "--container") }), /*#__PURE__*/_react.default.createElement("input", { name: name, type: "hidden", value: value, "data-testid": testId && "".concat(testId, "--input"), onKeyDown: onSelectKeyDown }), /*#__PURE__*/_react.default.createElement(SelectComponent, (0, _extends2.default)({ "aria-describedby": ariaDescribedBy, "aria-label": label || undefined, appearance: appearance, autoFocus: autoFocus, clearControlLabel: clearControlLabel, components: selectComponents, inputId: id, isClearable: true, isDisabled: isDisabled, isRequired: isRequired, menuIsOpen: isOpen && !isDisabled, menuPlacement: "auto", openMenuOnFocus: true, onBlur: onBlur, onCreateOption: onCreateOption, onChange: onChange, options: options, onFocus: onFocus, onMenuOpen: onMenuOpen, onMenuClose: onMenuClose, placeholder: placeholder || l10n.formatTime(_placeholderDateTime.placeholderDatetime), styles: mergedStyles, value: initialValue, spacing: spacing // We need this to get things to work, even though it's not supported. , fixedLayerRef: containerRef, isInvalid: isInvalid, testId: testId }, otherSelectProps))); }); var _default = exports.default = TimePicker;