@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
JavaScript
"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;