@homebound/beam 2.298.1 → 2.299.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,9 +5,6 @@ const jsx_runtime_1 = require("@emotion/react/jsx-runtime");
5
5
  const Css_1 = require("../../../Css");
6
6
  // Small wrapper around DatePicker to provide necessary styling and state handling when displayed as an overlay.
7
7
  function DatePickerOverlay({ overlayProps, children }) {
8
- return (
9
- // Adds `tabIndex` so clicking within the DatePicker will provide a `e.relatedTarget` on blur and focus events.
10
- // This allows for components such as the DateField to conditionally trigger their 'onBlur' prop. E.g. If the user leaves the field to interact with the DatePicker, then don't call onBlur
11
- (0, jsx_runtime_1.jsx)("div", { css: Css_1.Css.br4.bshModal.$, ...overlayProps, tabIndex: 0, children: children }));
8
+ return ((0, jsx_runtime_1.jsx)("div", { css: Css_1.Css.br4.bshModal.$, ...overlayProps, children: children }));
12
9
  }
13
10
  exports.DatePickerOverlay = DatePickerOverlay;
@@ -24,12 +24,13 @@ function DateFieldBase(props) {
24
24
  const inputWrapRef = (0, react_1.useRef)(null);
25
25
  const buttonRef = (0, react_1.useRef)(null);
26
26
  const overlayRef = (0, react_1.useRef)(null);
27
- // Local focus ref to conditionally call onBlur when the date picker closes. Using a ref instead of a state to have a reliable value between renders
28
- // E.g. If the picker closes due to focus going back to the input field then don't call onBlur. Also used to avoid updating WIP values
27
+ // Local focus ref used to avoid updating WIP values
29
28
  const isFocused = (0, react_1.useRef)(false);
29
+ // Ref helper to identify when focus is returned to the TextField due to the DatePicker closing.
30
+ const closingDatePicker = (0, react_1.useRef)(false);
30
31
  const dateFormat = (0, utils_1.getDateFormat)(format);
31
32
  // The `wipValue` allows the "range" mode to set the value to `undefined`, even if the `onChange` response cannot be undefined.
32
- // This makes working within the DateRangePicker much more user friendly.
33
+ // This makes working within the DateRangePicker much more user-friendly.
33
34
  const [wipValue, setWipValue] = (0, react_1.useState)(value);
34
35
  const [inputValue, setInputValue] = (0, react_1.useState)((_a = (isRangeMode ? (0, utils_1.formatDateRange)(props.value, dateFormat) : (0, utils_1.formatDate)(props.value, dateFormat))) !== null && _a !== void 0 ? _a : "");
35
36
  const tid = (0, utils_2.useTestIds)(props, (0, defaultTestId_1.defaultTestId)(label));
@@ -43,23 +44,24 @@ function DateFieldBase(props) {
43
44
  "aria-haspopup": "dialog",
44
45
  value: inputValue,
45
46
  };
46
- const state = (0, react_stately_1.useOverlayTriggerState)({
47
- onOpenChange: (isOpen) => {
48
- // Handles avoiding calling `onBlur` for the case where the user closes the overlay by changing the DateField input value (TextFieldBase.onChange calls state.close()).
49
- // Calls `onBlur` for the case where the user interacts with the overlay (!isFocused) and eventually closes the overlay (whether clicking away, or selecting a date) as focus is not returned to the field.
50
- if (!isOpen && !isFocused.current) {
51
- (0, utils_2.maybeCall)(onBlur);
52
- }
53
- },
54
- isOpen: defaultOpen,
55
- });
47
+ const state = (0, react_stately_1.useOverlayTriggerState)({ isOpen: defaultOpen });
48
+ const onPickerClose = (0, react_1.useCallback)(() => {
49
+ closingDatePicker.current = true;
50
+ state.close();
51
+ }, [state]);
56
52
  const { labelProps, inputProps } = (0, react_aria_1.useTextField)({
57
53
  ...textFieldProps,
54
+ // Setting `inputMode` to none. This disables the virtual keyboard from being triggered on touch devices
55
+ inputMode: "none",
58
56
  onFocus: () => {
59
57
  var _a;
60
- // Open overlay on focus of the input.
61
58
  isFocused.current = true;
62
- state.open();
59
+ // Open overlay on focus of the input, only if the focus is not triggered due to the overlay being closed.
60
+ if (!closingDatePicker.current) {
61
+ state.open();
62
+ }
63
+ // Reset the closingDatePicker ref to false, so that the overlay can be opened again on the next focus event
64
+ closingDatePicker.current = false;
63
65
  (0, utils_2.maybeCall)(onFocus);
64
66
  if (wipValue && dateFormat !== utils_1.dateFormats.short) {
65
67
  // When focused, change to use the "short" date format, as it is simpler to update by hand and parse.
@@ -70,13 +72,15 @@ function DateFieldBase(props) {
70
72
  },
71
73
  onBlur: (e) => {
72
74
  var _a, _b;
75
+ // Resets the ref variables when the input loses focus.
73
76
  isFocused.current = false;
74
- // If we are interacting any other part of `inputWrap` ref (such as the calendar button) return early as clicking anywhere within there will push focus to the input field.
75
- // Or if interacting with the DatePicker then also return early. The overlay will handle calling `onBlur` once it closes.
77
+ closingDatePicker.current = false;
78
+ // If interacting with the overlay or the input, then assume the user is still working within the DatePicker and return early to not trigger onBlur functionality.
76
79
  if ((inputWrapRef.current && inputWrapRef.current.contains(e.relatedTarget)) ||
77
80
  (overlayRef.current && overlayRef.current.contains(e.relatedTarget))) {
78
81
  return;
79
82
  }
83
+ // Otherwise, if we are actually leaving the DatePicker component, then
80
84
  const parsedDate = isRangeMode
81
85
  ? (0, utils_1.parseDateRange)(inputValue, utils_1.dateFormats.short)
82
86
  : (0, utils_1.parseDate)(inputValue, utils_1.dateFormats.short);
@@ -89,8 +93,7 @@ function DateFieldBase(props) {
89
93
  // Or if we need to reset the dateFormat back from `short` to whatever the user specified
90
94
  setInputValue((_b = (isRangeMode ? (0, utils_1.formatDateRange)(props.value, dateFormat) : (0, utils_1.formatDate)(props.value, dateFormat))) !== null && _b !== void 0 ? _b : "");
91
95
  }
92
- state.close();
93
- // Only call `onBlur` if the overlay is closed. In other cases, the overlay's `onOpenChange` will handling calling `onBlur` when closing and the focus is not still on the input.
96
+ // Only call `onBlur` if the DatePicker is closed, meaning the user has actually left the DateField component.
94
97
  if (!state.isOpen) {
95
98
  (0, utils_2.maybeCall)(onBlur);
96
99
  }
@@ -107,16 +110,15 @@ function DateFieldBase(props) {
107
110
  const { buttonProps } = (0, react_aria_1.useButton)({
108
111
  ...triggerProps,
109
112
  isDisabled: isDisabled || isReadOnly,
110
- // When pressed or focused then move focus the input, which will select the text and trigger the DatePicker to open
113
+ // When pressed then move focus the input, which will select the text and trigger the DatePicker to open
111
114
  onPress: () => { var _a; return (_a = inputRef === null || inputRef === void 0 ? void 0 : inputRef.current) === null || _a === void 0 ? void 0 : _a.focus(); },
112
- onFocus: () => { var _a; return (_a = inputRef === null || inputRef === void 0 ? void 0 : inputRef.current) === null || _a === void 0 ? void 0 : _a.focus(); },
113
115
  }, buttonRef);
114
116
  const { overlayProps: positionProps } = (0, react_aria_1.useOverlayPosition)({
115
117
  targetRef: inputWrapRef,
116
118
  overlayRef,
117
119
  shouldFlip: true,
118
120
  isOpen: state.isOpen,
119
- onClose: state.close,
121
+ onClose: onPickerClose,
120
122
  placement: "bottom left",
121
123
  shouldUpdatePosition: true,
122
124
  offset: 4,
@@ -164,9 +166,9 @@ function DateFieldBase(props) {
164
166
  } })) }));
165
167
  const calendarButton = ((0, jsx_runtime_1.jsx)("button", { ref: buttonRef, ...buttonProps, disabled: isDisabled, css: Css_1.Css.if(isDisabled).cursorNotAllowed.$, tabIndex: -1, ...tid.calendarButton, children: (0, jsx_runtime_1.jsx)(components_1.Icon, { icon: "calendar", color: isDisabled ? Css_1.Palette.Gray400 : Css_1.Palette.Gray700 }) }));
166
168
  const EndFieldButtons = ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [isRangeFilterField && clearButton, !hideCalendarIcon && calendarButton] }));
167
- return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(TextFieldBase_1.TextFieldBase, { ...textFieldProps, errorMsg: errorMsg, helperText: helperText, required: required, labelProps: labelProps, inputProps: { ...inputProps, size: inputSize }, inputRef: inputRef, inputWrapRef: inputWrapRef, onChange: (v) => {
169
+ return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(TextFieldBase_1.TextFieldBase, { ...textFieldProps, ...{ internalProps: { forceFocus: state.isOpen } }, errorMsg: errorMsg, helperText: helperText, required: required, labelProps: labelProps, inputProps: { ...inputProps, size: inputSize }, inputRef: inputRef, inputWrapRef: inputWrapRef, onChange: (v) => {
168
170
  // hide the calendar if the user is manually entering the date
169
- state.close();
171
+ onPickerClose();
170
172
  if (v) {
171
173
  setInputValue(v);
172
174
  // If changing the value directly (vs using the DatePicker), then we always use the short format
@@ -177,17 +179,17 @@ function DateFieldBase(props) {
177
179
  else if (v === undefined) {
178
180
  setInputValue("");
179
181
  }
180
- }, endAdornment: !iconLeft && EndFieldButtons, startAdornment: !hideCalendarIcon && iconLeft && calendarButton, tooltip: (0, components_1.resolveTooltip)(disabled, undefined, readOnly), ...others }), state.isOpen && ((0, jsx_runtime_1.jsx)(internal_1.Popover, { triggerRef: inputWrapRef, popoverRef: overlayRef, positionProps: positionProps, onClose: state.close, isOpen: state.isOpen, children: (0, jsx_runtime_1.jsx)(DatePickerOverlay_1.DatePickerOverlay, { overlayProps: overlayProps, children: isRangeMode ? ((0, jsx_runtime_1.jsx)(internal_1.DateRangePicker, { range: wipValue, disabledDays: disabledDays, onSelect: (dr) => {
181
- var _a;
182
- // Note: Do not close date range picker on select to allow the user to select multiple dates at a time
183
- setInputValue((_a = (0, utils_1.formatDateRange)(dr, utils_1.dateFormats.short)) !== null && _a !== void 0 ? _a : "");
184
- onChange(dr);
185
- }, useYearPicker: isRangeFilterField, ...tid.datePicker })) : ((0, jsx_runtime_1.jsx)(internal_1.DatePicker, { value: wipValue, disabledDays: disabledDays, onSelect: (d) => {
186
- var _a;
187
- setInputValue((_a = (0, utils_1.formatDate)(d, utils_1.dateFormats.short)) !== null && _a !== void 0 ? _a : "");
188
- onChange(d);
189
- state.close();
190
- }, ...tid.datePicker })) }) }))] }));
182
+ }, endAdornment: !iconLeft && EndFieldButtons, startAdornment: !hideCalendarIcon && iconLeft && calendarButton, tooltip: (0, components_1.resolveTooltip)(disabled, undefined, readOnly), ...others }), state.isOpen && ((0, jsx_runtime_1.jsx)(internal_1.Popover, { triggerRef: inputWrapRef, popoverRef: overlayRef, positionProps: positionProps, onClose: onPickerClose, isOpen: state.isOpen, children: (0, jsx_runtime_1.jsx)(react_aria_1.FocusScope, { autoFocus: true, restoreFocus: true, children: (0, jsx_runtime_1.jsx)(DatePickerOverlay_1.DatePickerOverlay, { overlayProps: overlayProps, children: isRangeMode ? ((0, jsx_runtime_1.jsx)(internal_1.DateRangePicker, { range: wipValue, disabledDays: disabledDays, onSelect: (dr) => {
183
+ var _a;
184
+ // Note: Do not close date range picker on select to allow the user to select multiple dates at a time
185
+ setInputValue((_a = (0, utils_1.formatDateRange)(dr, utils_1.dateFormats.short)) !== null && _a !== void 0 ? _a : "");
186
+ onChange(dr);
187
+ }, useYearPicker: isRangeFilterField, ...tid.datePicker })) : ((0, jsx_runtime_1.jsx)(internal_1.DatePicker, { value: wipValue, disabledDays: disabledDays, onSelect: (d) => {
188
+ var _a;
189
+ setInputValue((_a = (0, utils_1.formatDate)(d, utils_1.dateFormats.short)) !== null && _a !== void 0 ? _a : "");
190
+ onChange(d);
191
+ onPickerClose();
192
+ }, ...tid.datePicker })) }) }) }))] }));
191
193
  }
192
194
  exports.DateFieldBase = DateFieldBase;
193
195
  function isParsedDateValid(d) {
@@ -20,7 +20,7 @@ function NumberField(props) {
20
20
  const isDisabled = !!disabled;
21
21
  const isReadOnly = !!readOnly;
22
22
  const factor = type === "percent" || type === "cents" ? 100 : type === "basisPoints" ? 10000 : 1;
23
- const signDisplay = displayDirection ? "exceptZero" : "auto";
23
+ const signDisplay = displayDirection ? "always" : "auto";
24
24
  const defaultFormatOptions = (0, react_1.useMemo)(() => ({
25
25
  [truncate ? "maximumFractionDigits" : "minimumFractionDigits"]: numFractionDigits,
26
26
  ...(numIntegerDigits !== undefined && { minimumIntegerDigits: numIntegerDigits }),
@@ -111,9 +111,7 @@ function TextFieldBase(props) {
111
111
  // Only show error styles if the field is not disabled, following the pattern that the error message is also hidden
112
112
  ...(errorMsg && !inputProps.disabled ? fieldStyles.error : {}),
113
113
  ...Css_1.Css.if(multiline).aifs.px0.mhPx(textAreaMinHeight).$,
114
- }, ...hoverProps, ref: inputWrapRef, children: [!multiline && labelStyle === "inline" && label && ((0, jsx_runtime_1.jsx)(Label_1.InlineLabel, { labelProps: labelProps, label: label, ...tid.label })), !multiline && startAdornment && (0, jsx_runtime_1.jsx)("span", { css: Css_1.Css.df.aic.fs0.br4.pr1.$, children: startAdornment }), (0, jsx_runtime_1.jsx)(ElementType, { ...(0, react_aria_1.mergeProps)(inputProps, { onBlur, onFocus: onFocusChained, onChange: onDomChange }, { "aria-invalid": Boolean(errorMsg), ...(labelStyle === "hidden" ? { "aria-label": label } : {}) }), ...(errorMsg ? { "aria-errormessage": errorMessageId } : {}), ref: fieldRef, rows: multiline ? 1 : undefined,
115
- // Make the input field readOnly if the field explicitly sets it to `true`
116
- readOnly: inputProps.readOnly, css: {
114
+ }, ...hoverProps, ref: inputWrapRef, children: [!multiline && labelStyle === "inline" && label && ((0, jsx_runtime_1.jsx)(Label_1.InlineLabel, { labelProps: labelProps, label: label, ...tid.label })), !multiline && startAdornment && (0, jsx_runtime_1.jsx)("span", { css: Css_1.Css.df.aic.fs0.br4.pr1.$, children: startAdornment }), (0, jsx_runtime_1.jsx)(ElementType, { ...(0, react_aria_1.mergeProps)(inputProps, { onBlur, onFocus: onFocusChained, onChange: onDomChange }, { "aria-invalid": Boolean(errorMsg), ...(labelStyle === "hidden" ? { "aria-label": label } : {}) }), ...(errorMsg ? { "aria-errormessage": errorMessageId } : {}), ref: fieldRef, rows: multiline ? 1 : undefined, css: {
117
115
  ...fieldStyles.input,
118
116
  ...(inputProps.disabled ? fieldStyles.disabled : {}),
119
117
  ...(showHover ? fieldStyles.hover : {}),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@homebound/beam",
3
- "version": "2.298.1",
3
+ "version": "2.299.0",
4
4
  "author": "Homebound",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",