UNPKG

@atlaskit/datetime-picker

Version:

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

352 lines (349 loc) 14.9 kB
import _classCallCheck from "@babel/runtime/helpers/classCallCheck"; import _createClass from "@babel/runtime/helpers/createClass"; import _assertThisInitialized from "@babel/runtime/helpers/assertThisInitialized"; import _inherits from "@babel/runtime/helpers/inherits"; import _possibleConstructorReturn from "@babel/runtime/helpers/possibleConstructorReturn"; import _getPrototypeOf from "@babel/runtime/helpers/getPrototypeOf"; import _defineProperty from "@babel/runtime/helpers/defineProperty"; import _extends from "@babel/runtime/helpers/extends"; import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties"; var _excluded = ["selectProps"], _excluded2 = ["styles"]; 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) { _defineProperty(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; } function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } import React from 'react'; // eslint-disable-next-line no-restricted-imports import { format, isValid } from 'date-fns'; import pick from 'lodash/pick'; import { createAndFireEvent, withAnalyticsContext, withAnalyticsEvents } from '@atlaskit/analytics-next'; import { createLocalizationProvider } from '@atlaskit/locale'; import Select, { components, CreatableSelect, mergeStyles } from '@atlaskit/select'; // eslint-disable-next-line @atlaskit/design-system/no-deprecated-imports import { gridSize } from '@atlaskit/theme/constants'; import { defaultTimeFormat, defaultTimes, EmptyComponent, placeholderDatetime } from '../internal'; import FixedLayer from '../internal/fixed-layer'; import parseTime from '../internal/parse-time'; import { makeSingleValue } from '../internal/single-value'; import { convertTokens } from './utils'; var packageName = "@atlaskit/datetime-picker"; var packageVersion = "13.0.0"; 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 FixedLayerMenu = function FixedLayerMenu(_ref) { var selectProps = _ref.selectProps, rest = _objectWithoutProperties(_ref, _excluded); return /*#__PURE__*/React.createElement(FixedLayer, { inputValue: selectProps.inputValue, containerRef: selectProps.fixedLayerRef, content: /*#__PURE__*/React.createElement(components.Menu, _extends({}, rest, { menuShouldScrollIntoView: false })), testId: selectProps.testId }); }; var timePickerDefaultProps = { appearance: 'default', autoFocus: false, defaultIsOpen: false, defaultValue: '', hideIcon: false, id: '', innerProps: {}, isDisabled: false, isInvalid: false, name: '', // These disables are here for proper typing when used as defaults. They // should *not* use the `noop` function. /* eslint-disable @repo/internal/react/use-noop */ onBlur: function onBlur(_event) {}, onChange: function onChange(_value) {}, onFocus: function onFocus(_event) {}, /* eslint-enable @repo/internal/react/use-noop */ parseInputValue: function parseInputValue(time, _timeFormat) { return parseTime(time); }, selectProps: {}, spacing: 'default', times: defaultTimes, timeIsEditable: false, locale: 'en-US' // Not including a default prop for value as it will // Make the component a controlled component }; var TimePicker = /*#__PURE__*/function (_React$Component) { _inherits(TimePicker, _React$Component); var _super = _createSuper(TimePicker); function TimePicker() { var _this; _classCallCheck(this, TimePicker); for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } _this = _super.call.apply(_super, [this].concat(args)); _defineProperty(_assertThisInitialized(_this), "containerRef", null); _defineProperty(_assertThisInitialized(_this), "state", { isOpen: _this.props.defaultIsOpen, clearingFromIcon: false, value: _this.props.defaultValue, isFocused: false }); // All state needs to be accessed via this function so that the state is mapped from props // correctly to allow controlled/uncontrolled usage. _defineProperty(_assertThisInitialized(_this), "getSafeState", function () { return _objectSpread(_objectSpread({}, _this.state), pick(_this.props, ['value', 'isOpen'])); }); _defineProperty(_assertThisInitialized(_this), "onChange", function (newValue, action) { var rawValue = newValue ? newValue.value || newValue : ''; var value = rawValue.toString(); var changedState = { value: value }; if (action && action.action === 'clear') { changedState = _objectSpread(_objectSpread({}, changedState), {}, { clearingFromIcon: true }); } _this.setState(changedState); _this.props.onChange(value); }); /** * Only allow custom times if timeIsEditable prop is true */ _defineProperty(_assertThisInitialized(_this), "onCreateOption", function (inputValue) { if (_this.props.timeIsEditable) { var _this$props = _this.props, parseInputValue = _this$props.parseInputValue, _timeFormat2 = _this$props.timeFormat; var sanitizedInput; try { sanitizedInput = parseInputValue(inputValue, _timeFormat2 || defaultTimeFormat); } catch (e) { return; // do nothing, the main validation should happen in the form } var includesSeconds = !!(_timeFormat2 && /[:.]?(s|ss)/.test(_timeFormat2)); var formatFormat = includesSeconds ? 'HH:mm:ss' : 'HH:mm'; var formattedValue = format(sanitizedInput, formatFormat) || ''; _this.setState({ value: formattedValue }); _this.props.onChange(formattedValue); } else { _this.onChange(inputValue); } }); _defineProperty(_assertThisInitialized(_this), "onMenuOpen", function () { // Don't open menu after the user has clicked clear if (_this.getSafeState().clearingFromIcon) { _this.setState({ clearingFromIcon: false }); } else { _this.setState({ isOpen: true }); } }); _defineProperty(_assertThisInitialized(_this), "onMenuClose", function () { // Don't close menu after the user has clicked clear if (_this.getSafeState().clearingFromIcon) { _this.setState({ clearingFromIcon: false }); } else { _this.setState({ isOpen: false }); } }); _defineProperty(_assertThisInitialized(_this), "setContainerRef", function (ref) { var oldRef = _this.containerRef; _this.containerRef = 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) { _this.forceUpdate(); } }); _defineProperty(_assertThisInitialized(_this), "onBlur", function (event) { _this.setState({ isFocused: false }); _this.props.onBlur(event); }); _defineProperty(_assertThisInitialized(_this), "onFocus", function (event) { _this.setState({ isFocused: true }); _this.props.onFocus(event); }); _defineProperty(_assertThisInitialized(_this), "onSelectKeyDown", function (event) { var key = event.key; var keyPressed = key.toLowerCase(); if (_this.getSafeState().clearingFromIcon && (keyPressed === 'backspace' || keyPressed === 'delete')) { // If being cleared from keyboard, don't change behaviour _this.setState({ clearingFromIcon: false }); } }); return _this; } _createClass(TimePicker, [{ key: "render", value: function render() { var _this2 = this; var _this$props2 = this.props, appearance = _this$props2.appearance, autoFocus = _this$props2.autoFocus, formatDisplayLabel = _this$props2.formatDisplayLabel, hideIcon = _this$props2.hideIcon, id = _this$props2.id, innerProps = _this$props2.innerProps, isDisabled = _this$props2.isDisabled, locale = _this$props2.locale, name = _this$props2.name, placeholder = _this$props2.placeholder, selectProps = _this$props2.selectProps, spacing = _this$props2.spacing, testId = _this$props2.testId, isInvalid = _this$props2.isInvalid, timeIsEditable = _this$props2.timeIsEditable, timeFormat = _this$props2.timeFormat, times = _this$props2.times; var ICON_PADDING = 2; var l10n = createLocalizationProvider(locale); var _this$getSafeState = this.getSafeState(), _this$getSafeState$va = _this$getSafeState.value, value = _this$getSafeState$va === void 0 ? '' : _this$getSafeState$va, isOpen = _this$getSafeState.isOpen; var _selectProps$styles = selectProps.styles, selectStyles = _selectProps$styles === void 0 ? {} : _selectProps$styles, otherSelectProps = _objectWithoutProperties(selectProps, _excluded2); var SelectComponent = timeIsEditable ? CreatableSelect : Select; /** * 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 = parseTime(time); if (!(date instanceof Date)) { return ''; } if (!isValid(date)) { return time; } if (timeFormat) { return format(date, convertTokens(timeFormat)); } return l10n.formatTime(date); }; var options = times.map(function (time) { return { label: formatTime(time), value: time }; }); var labelAndValue = value && { label: formatTime(value), value: value }; var SingleValue = makeSingleValue({ lang: this.props.locale }); var selectComponents = _objectSpread({ DropdownIndicator: EmptyComponent, Menu: FixedLayerMenu, SingleValue: SingleValue }, hideIcon && { ClearIndicator: EmptyComponent }); var renderIconContainer = Boolean(!hideIcon && value); var mergedStyles = 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: _this2.containerRef ? _this2.containerRef.getBoundingClientRect().width : 'auto' }); }, indicatorsContainer: function indicatorsContainer(base) { return _objectSpread(_objectSpread({}, base), {}, { paddingLeft: renderIconContainer ? ICON_PADDING : 0, paddingRight: renderIconContainer ? gridSize() - ICON_PADDING : 0 }); } }); return /*#__PURE__*/React.createElement("div", _extends({}, innerProps, { ref: this.setContainerRef, "data-testid": testId && "".concat(testId, "--container") }), /*#__PURE__*/React.createElement("input", { name: name, type: "hidden", value: value, "data-testid": testId && "".concat(testId, "--input"), onKeyDown: this.onSelectKeyDown }), /*#__PURE__*/React.createElement(SelectComponent, _extends({ appearance: appearance, autoFocus: autoFocus, components: selectComponents, instanceId: id, isClearable: true, isDisabled: isDisabled, menuIsOpen: isOpen && !isDisabled, menuPlacement: "auto", openMenuOnFocus: true, onBlur: this.onBlur, onCreateOption: this.onCreateOption, onChange: this.onChange, options: options, onFocus: this.onFocus, onMenuOpen: this.onMenuOpen, onMenuClose: this.onMenuClose, placeholder: placeholder || l10n.formatTime(placeholderDatetime), styles: mergedStyles, value: labelAndValue, spacing: spacing // @ts-ignore caused by prop not part of @atlaskit/select , fixedLayerRef: this.containerRef, isInvalid: isInvalid, testId: testId }, otherSelectProps))); } }]); return TimePicker; }(React.Component); _defineProperty(TimePicker, "defaultProps", timePickerDefaultProps); export { TimePicker as TimePickerWithoutAnalytics }; export default withAnalyticsContext({ componentName: 'timePicker', packageName: packageName, packageVersion: packageVersion })(withAnalyticsEvents({ onChange: createAndFireEvent('atlaskit')({ action: 'selectedTime', actionSubject: 'timePicker', attributes: { componentName: 'timePicker', packageName: packageName, packageVersion: packageVersion } }) })(TimePicker));