@razorpay/blade
Version:
The Design System that powers Razorpay
270 lines (262 loc) • 12.3 kB
JavaScript
import _defineProperty from '@babel/runtime/helpers/defineProperty';
import _objectWithoutProperties from '@babel/runtime/helpers/objectWithoutProperties';
import React__default, { useRef } from 'react';
import { FloatingPortal, FloatingFocusManager } from '@floating-ui/react';
import { TimeInput } from './TimeInput.web.js';
import { useTimePickerState } from './useTimePickerState.js';
import { TimePickerContent } from './TimePickerContent.web.js';
import '../BottomSheet/index.js';
import '../Box/BaseBox/index.js';
import '../../utils/assignWithoutSideEffects/index.js';
import '../../utils/makeAccessible/index.js';
import { useId } from '../../utils/useId.js';
import { useIsMobile } from '../../utils/useIsMobile.js';
import '../../utils/index.js';
import { usePopup } from '../DatePicker/usePopup.js';
import '../Box/styledProps/index.js';
import '../../utils/metaAttribute/index.js';
import '../../tokens/global/index.js';
import { componentZIndices } from '../../utils/componentZIndices.js';
import { jsx, jsxs } from 'react/jsx-runtime';
import useTheme from '../BladeProvider/useTheme.js';
import { size } from '../../tokens/global/size.js';
import { BaseBox } from '../Box/BaseBox/BaseBox.web.js';
import { getStyledProps } from '../Box/styledProps/getStyledProps.js';
import { metaAttribute } from '../../utils/metaAttribute/metaAttribute.web.js';
import { MetaConstants } from '../../utils/metaAttribute/metaConstants.js';
import { makeAccessible } from '../../utils/makeAccessible/makeAccessible.web.js';
import { getPopupBoxShadowString } from '../../utils/makePopupBoxShadow/makePopupBoxShadow.js';
import { BottomSheet } from '../BottomSheet/BottomSheet.web.js';
import { BottomSheetHeader } from '../BottomSheet/BottomSheetHeader.web.js';
import { BottomSheetBody } from '../BottomSheet/BottomSheetBody.web.js';
import { assignWithoutSideEffects } from '../../utils/assignWithoutSideEffects/assignWithoutSideEffects.js';
var _excluded = ["value", "defaultValue", "onChange", "onApply", "isOpen", "defaultIsOpen", "onOpenChange", "label", "labelPosition", "accessibilityLabel", "errorText", "helpText", "isDisabled", "isRequired", "successText", "validationState", "size", "autoFocus", "necessityIndicator", "name", "placeholder", "showFooterActions", "timeFormat", "minuteStep", "labelSuffix", "labelTrailing", "testID", "zIndex"];
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; }
var _BaseTimePicker = function _BaseTimePicker(_ref) {
var value = _ref.value,
defaultValue = _ref.defaultValue,
onChange = _ref.onChange,
onApply = _ref.onApply,
isOpen = _ref.isOpen,
defaultIsOpen = _ref.defaultIsOpen,
onOpenChange = _ref.onOpenChange,
label = _ref.label,
_ref$labelPosition = _ref.labelPosition,
labelPosition = _ref$labelPosition === void 0 ? 'top' : _ref$labelPosition,
accessibilityLabel = _ref.accessibilityLabel,
errorText = _ref.errorText,
helpText = _ref.helpText,
isDisabled = _ref.isDisabled,
isRequired = _ref.isRequired,
successText = _ref.successText,
validationState = _ref.validationState,
_ref$size = _ref.size,
size$1 = _ref$size === void 0 ? 'medium' : _ref$size,
autoFocus = _ref.autoFocus,
necessityIndicator = _ref.necessityIndicator,
name = _ref.name,
placeholder = _ref.placeholder,
_ref$showFooterAction = _ref.showFooterActions,
showFooterActions = _ref$showFooterAction === void 0 ? true : _ref$showFooterAction,
_ref$timeFormat = _ref.timeFormat,
timeFormat = _ref$timeFormat === void 0 ? '12h' : _ref$timeFormat,
_ref$minuteStep = _ref.minuteStep,
minuteStep = _ref$minuteStep === void 0 ? 1 : _ref$minuteStep,
labelSuffix = _ref.labelSuffix,
labelTrailing = _ref.labelTrailing,
testID = _ref.testID,
_ref$zIndex = _ref.zIndex,
zIndex = _ref$zIndex === void 0 ? componentZIndices.popover : _ref$zIndex,
props = _objectWithoutProperties(_ref, _excluded);
var referenceRef = useRef(null);
var isMobile = useIsMobile();
var _useTheme = useTheme(),
theme = _useTheme.theme;
var titleId = useId('timepicker-title');
var _useTheme2 = useTheme(),
colorScheme = _useTheme2.colorScheme;
var _useTimePickerState = useTimePickerState({
value: value,
defaultValue: defaultValue,
onChange: onChange,
isOpen: isOpen,
defaultIsOpen: defaultIsOpen,
onOpenChange: onOpenChange,
timeFormat: timeFormat,
showFooterActions: showFooterActions,
onApply: onApply
}),
timeValue = _useTimePickerState.timeValue,
setTimeValue = _useTimePickerState.setTimeValue,
internalTimeValue = _useTimePickerState.internalTimeValue,
setInternalTimeValue = _useTimePickerState.setInternalTimeValue,
selectedHour = _useTimePickerState.selectedHour,
selectedMinute = _useTimePickerState.selectedMinute,
selectedPeriod = _useTimePickerState.selectedPeriod,
isDropdownOpen = _useTimePickerState.isOpen,
setIsDropdownOpen = _useTimePickerState.setIsOpen,
handleApply = _useTimePickerState.onApply,
handleCancel = _useTimePickerState.onCancel,
createCompleteTime = _useTimePickerState.createCompleteTime;
var _usePopup = usePopup({
enabled: !isMobile,
placement: 'bottom-start',
open: isDropdownOpen,
onOpenChange: function onOpenChange(isOpen, _, reason) {
if (isDisabled) return;
setIsDropdownOpen(isOpen);
if (reason === 'escape-key') {
handleCancel();
}
},
referenceRef: referenceRef,
// crossAxisOffset calculation for left label positioning:
// Medium: 120px (label width) + 12px (padding) = 132px
// Large: 176px (label width) + 16px (padding) = 192px
crossAxisOffset: labelPosition === 'left' ? size$1 === 'large' ? size[192] : size[132] : 0
}),
refs = _usePopup.refs,
context = _usePopup.context,
isMounted = _usePopup.isMounted,
floatingStyles = _usePopup.floatingStyles,
animationStyles = _usePopup.animationStyles,
getReferenceProps = _usePopup.getReferenceProps,
getFloatingProps = _usePopup.getFloatingProps;
// Fix for React Aria contentEditable focus issue with Floating UI
// React Aria's contentEditable segments don't properly trigger Floating UI's dismiss
// See: https://github.com/adobe/react-spectrum/issues/3164
React__default.useEffect(function () {
var handleClick = function handleClick(e) {
var _referenceRef$current, _refs$floating$curren, _closest, _ref2;
var target = e.target;
var isClickOnInput = (_referenceRef$current = referenceRef.current) === null || _referenceRef$current === void 0 ? void 0 : _referenceRef$current.contains(target);
var isClickOnDropdown = (_refs$floating$curren = refs.floating.current) === null || _refs$floating$curren === void 0 ? void 0 : _refs$floating$curren.contains(target);
// On mobile, also check if click is inside TimePicker content (BottomSheet)
var isClickOnTimePickerContent = (_closest = (_ref2 = target).closest) === null || _closest === void 0 ? void 0 : _closest.call(_ref2, '.timepicker-content');
// Only close dropdown if click is outside input, dropdown, and TimePicker content
// This ensures clicking different segments within the input keeps dropdown open
if (isDropdownOpen && !isClickOnInput && !isClickOnDropdown) {
if (isClickOnTimePickerContent && showFooterActions) {
// Inside TimePicker with footer actions - don't close
return;
}
handleApply();
}
};
if (isDropdownOpen) {
document.addEventListener('click', handleClick);
return function () {
return document.removeEventListener('click', handleClick);
};
}
return undefined;
}, [isDropdownOpen, showFooterActions, handleCancel, handleApply]);
// Mobile: Blur input when bottom sheet opens
React__default.useEffect(function () {
if (isMobile && isDropdownOpen) {
// Find the currently focused time segment and blur it
var activeElement = document.activeElement;
if (activeElement && 'blur' in activeElement && typeof activeElement.blur === 'function') {
setTimeout(function () {
activeElement.blur();
}, 0);
}
}
}, [isMobile, isDropdownOpen]);
var content = /*#__PURE__*/jsx(TimePickerContent, {
selectedTime: timeValue,
setSelectedTime: setTimeValue,
selectedHour: selectedHour,
selectedMinute: selectedMinute,
selectedPeriod: selectedPeriod,
timeFormat: timeFormat,
minuteStep: minuteStep,
showFooterActions: showFooterActions,
onApply: handleApply,
onCancel: handleCancel
});
return /*#__PURE__*/jsxs(BaseBox, _objectSpread(_objectSpread(_objectSpread({
width: "100%"
}, getStyledProps(props)), metaAttribute({
name: MetaConstants.TimePicker
})), {}, {
children: [/*#__PURE__*/jsx(TimeInput, _objectSpread({
ref: referenceRef,
inputRef: refs.setReference,
referenceProps: getReferenceProps(),
timeValue: timeValue,
internalTimeValue: internalTimeValue,
onChange: setTimeValue,
onTimeValueChange: function onTimeValueChange(timeValue) {
if (timeValue) {
setInternalTimeValue(timeValue);
}
},
createCompleteTime: createCompleteTime,
label: label,
helpText: helpText,
errorText: errorText,
successText: successText,
validationState: validationState,
isDisabled: isDisabled,
isRequired: isRequired,
necessityIndicator: necessityIndicator,
autoFocus: autoFocus // eslint-disable-line jsx-a11y/no-autofocus
,
name: name,
placeholder: placeholder,
size: size$1,
labelPosition: labelPosition,
labelSuffix: labelSuffix,
labelTrailing: labelTrailing,
timeFormat: timeFormat,
isDropdownOpen: isDropdownOpen,
setIsDropdownOpen: setIsDropdownOpen,
testID: testID,
accessibilityLabel: accessibilityLabel
}, props)), !isMobile && isMounted && /*#__PURE__*/jsx(FloatingPortal, {
children: /*#__PURE__*/jsx(FloatingFocusManager, {
initialFocus: -1,
context: context,
guards: true,
order: ['reference', 'content'],
returnFocus: true,
children: /*#__PURE__*/jsx(BaseBox, _objectSpread(_objectSpread(_objectSpread({
ref: refs.setFloating,
style: floatingStyles,
zIndex: zIndex
}, getFloatingProps()), makeAccessible({
labelledBy: titleId
})), {}, {
children: /*#__PURE__*/jsx(BaseBox, {
borderRadius: "medium",
overflow: "hidden",
border: "none",
style: _objectSpread(_objectSpread({}, animationStyles), {}, {
background: theme.colors.popup.background.gray.moderate,
boxShadow: getPopupBoxShadowString(theme, colorScheme),
backdropFilter: "blur(".concat(theme.backdropBlur.high, "px)")
}),
children: content
})
}))
})
}), isMobile && !isDisabled && /*#__PURE__*/jsxs(BottomSheet, {
snapPoints: [0.6, 0.6, 1],
isOpen: isDropdownOpen,
onDismiss: handleCancel,
children: [/*#__PURE__*/jsx(BottomSheetHeader, {
title: "Select Time"
}), /*#__PURE__*/jsx(BottomSheetBody, {
children: content
})]
})]
}));
};
var BaseTimePicker = /*#__PURE__*/assignWithoutSideEffects(_BaseTimePicker, {
displayName: 'BaseTimePicker',
componentId: 'BaseTimePicker'
});
export { BaseTimePicker };
//# sourceMappingURL=BaseTimePicker.web.js.map