@homebound/beam 2.90.3 → 2.91.2

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.
@@ -79,20 +79,18 @@ function SuperDrawer() {
79
79
  // Preventing clicks from triggering parent onClick
80
80
  onClick: (e) => e.stopPropagation() }, { children: [(0, jsx_runtime_1.jsxs)("header", Object.assign({ css: Css_1.Css.df.p3.bb.bGray200.df.aic.jcsb.$ }, { children: [(0, jsx_runtime_1.jsxs)("div", Object.assign({ css: Css_1.Css.df.aic.$ }, { children: [(0, jsx_runtime_1.jsx)("div", Object.assign({ css: Css_1.Css.xl2Em.gray900.mr2.$ }, testId.title, { ref: drawerHeaderRef }, { children: !modalState.current && (title || null) }), void 0), !modalState.current && (titleLeftContent || null)] }), void 0), !modalState.current && (
81
81
  // Forcing height to 32px to match title height
82
- (0, jsx_runtime_1.jsxs)("div", Object.assign({ css: Css_1.Css.df.childGap3.aic.hPx(32).$ }, { children: [titleRightContent || null, (0, jsx_runtime_1.jsx)(components_1.ButtonGroup, { buttons: [
82
+ (0, jsx_runtime_1.jsxs)("div", Object.assign({ css: Css_1.Css.df.childGap3.aic.hPx(32).$ }, { children: [titleRightContent || null, (0, jsx_runtime_1.jsx)(components_1.ButtonGroup, Object.assign({ buttons: [
83
83
  {
84
84
  icon: "chevronLeft",
85
85
  onClick: () => onPrevClick && onPrevClick(),
86
86
  disabled: !onPrevClick || isDetail,
87
- ...testId.prev,
88
87
  },
89
88
  {
90
89
  icon: "chevronRight",
91
90
  onClick: () => onNextClick && onNextClick(),
92
91
  disabled: !onNextClick || isDetail,
93
- ...testId.next,
94
92
  },
95
- ] }, void 0), (0, jsx_runtime_1.jsx)(components_1.IconButton, Object.assign({ icon: "x", onClick: closeDrawer }, testId.close), void 0)] }), void 0))] }), void 0), content, modalState.current && (
93
+ ] }, testId.headerActions), void 0), (0, jsx_runtime_1.jsx)(components_1.IconButton, Object.assign({ icon: "x", onClick: closeDrawer }, testId.close), void 0)] }), void 0))] }), void 0), content, modalState.current && (
96
94
  // Forcing some design constraints on the modal component
97
95
  (0, jsx_runtime_1.jsxs)("div", Object.assign({ css:
98
96
  // topPX(81) is the offset from the header
@@ -12,7 +12,7 @@ export interface ChipSelectFieldProps<O, V extends Value> {
12
12
  onBlur?: () => void;
13
13
  onFocus?: () => void;
14
14
  clearable?: boolean;
15
- onCreateNew?: (value: string) => Promise<O>;
15
+ onCreateNew?: (value: string) => Promise<void>;
16
16
  disabled?: boolean | ReactNode;
17
17
  }
18
18
  export declare function ChipSelectField<O, V extends Value>(props: ChipSelectFieldProps<O, V>): JSX.Element;
@@ -25,7 +25,8 @@ function ChipSelectField(props) {
25
25
  const isDisabled = !!disabled;
26
26
  const showClearButton = !disabled && clearable && !!value;
27
27
  const chipStyles = Css_1.Css[typeScale].tl.bgGray300.gray900.br16.pxPx(10).pyPx(2).$;
28
- const [isFocused, setIsFocused] = (0, react_1.useState)(false);
28
+ // Controls showing the focus border styles.
29
+ const [visualFocus, setVisualFocus] = (0, react_1.useState)(false);
29
30
  const [isClearFocused, setIsClearFocused] = (0, react_1.useState)(false);
30
31
  const { focusProps } = (0, react_aria_1.useFocus)({
31
32
  onFocus: (e) => {
@@ -35,10 +36,15 @@ function ChipSelectField(props) {
35
36
  }
36
37
  (0, utils_1.maybeCall)(onFocus);
37
38
  },
38
- // Do not call onBlur if we just opened the menu
39
- onBlur: () => !state.isOpen && (0, utils_1.maybeCall)(onBlur),
40
- // Do not change focus state if select menu is opened
41
- onFocusChange: (isFocused) => !state.isOpen && setIsFocused(isFocused),
39
+ onBlur: (e) => {
40
+ // Do not call onBlur if focus moved to within the Popover
41
+ if (popoverRef.current && popoverRef.current.contains(e.relatedTarget)) {
42
+ return;
43
+ }
44
+ (0, utils_1.maybeCall)(onBlur);
45
+ },
46
+ // Do not change visual focus state if select menu is opened
47
+ onFocusChange: (isFocused) => !state.isOpen && setVisualFocus(isFocused),
42
48
  });
43
49
  const { focusProps: clearFocusProps } = (0, react_aria_1.useFocus)({ onFocusChange: setIsClearFocused });
44
50
  const buttonRef = (0, react_1.useRef)(null);
@@ -72,12 +78,12 @@ function ChipSelectField(props) {
72
78
  isDisabled,
73
79
  items: listData.items,
74
80
  children: selectChildren,
81
+ autoFocus: true,
75
82
  };
76
83
  const state = (0, react_stately_1.useSelectState)({
77
84
  ...selectHookProps,
78
- autoFocus: false,
79
85
  selectedKey: (0, Value_1.valueToKey)(value),
80
- disallowEmptySelection: true,
86
+ disallowEmptySelection: false,
81
87
  onSelectionChange: (key) => {
82
88
  if (key === createNewOpt.id) {
83
89
  setShowInput(true);
@@ -87,11 +93,21 @@ function ChipSelectField(props) {
87
93
  if (selectedItem) {
88
94
  onSelect(key, selectedItem);
89
95
  }
96
+ // Per UX, when an option is selected then we want to call our `onBlur` callback and remove the focus styles. The field _is_ still in focus but that is only to retain tab position in the DOM.
97
+ // We cannot simply call `buttonRef.current.blur()` here because `state.isOpen === true` and we keep the visualFocus shown when the menu is open.
98
+ setVisualFocus(false);
99
+ (0, utils_1.maybeCall)(onBlur);
90
100
  },
91
101
  onOpenChange: (isOpen) => {
92
- if (!isOpen && buttonRef.current) {
93
- // When closing reset the focus to the button element.
94
- buttonRef.current.focus();
102
+ var _a;
103
+ if (!isOpen) {
104
+ // When closing, reset the focus to the button element. This is to retain "tab position" in the document, allowing hte user to hit "Tab" and move to the next tabbable element.
105
+ // If the menu closed due to a user selecting an option, then the field will not visually appear focused.
106
+ (_a = buttonRef.current) === null || _a === void 0 ? void 0 : _a.focus();
107
+ }
108
+ else {
109
+ // If opened, set visual focus to true. It is possible to be in a state where the browser focus is on this element, but we are not "visually" focused (see `onSelectionChange`). If the user opens the menu again, we should trigger the visual focus.
110
+ setVisualFocus(true);
95
111
  }
96
112
  },
97
113
  });
@@ -122,29 +138,26 @@ function ChipSelectField(props) {
122
138
  (_a = buttonRef.current) === null || _a === void 0 ? void 0 : _a.focus();
123
139
  }, [setShowInput, setInputValue]);
124
140
  const field = ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [showInput && onCreateNew && ((0, jsx_runtime_1.jsx)(ChipTextField_1.ChipTextField, Object.assign({ autoFocus: true, label: "Add new", value: inputValue, onChange: setInputValue, onEnter: async () => {
125
- const newOption = await onCreateNew(inputValue);
126
- listData.insertBefore(createNewOpt.id, newOption);
141
+ await onCreateNew(inputValue);
127
142
  removeCreateNewField();
128
143
  }, onBlur: removeCreateNewField }, tid.createNewField), void 0)), (0, jsx_runtime_1.jsxs)("div", Object.assign({ ref: wrapperRef, css: {
129
144
  ...chipStyles,
130
145
  ...Css_1.Css.dif.relative.p0.mwPx(32).if(!value).bgGray200.$,
131
- ...(isFocused ? Css_1.Css.bshFocus.$ : {}),
146
+ ...(visualFocus ? Css_1.Css.bshFocus.$ : {}),
132
147
  ...(showInput ? Css_1.Css.dn.$ : {}),
133
148
  } }, { children: [(0, jsx_runtime_1.jsx)(Label_1.Label, Object.assign({ label: label, labelProps: labelProps, hidden: true }, tid.label), void 0), (0, jsx_runtime_1.jsx)("button", Object.assign({}, (0, react_aria_1.mergeProps)(focusProps, buttonProps), { ref: buttonRef, css: {
134
149
  ...Css_1.Css.tl.br16.pxPx(10).pyPx(2).outline0.if(showClearButton).prPx(4).borderRadius("16px 0 0 16px").$,
135
150
  ...(isDisabled ? Css_1.Css.cursorNotAllowed.gray700.$ : {}),
136
151
  "&:hover:not(:disabled)": Css_1.Css.bgGray400.if(!value).bgGray300.$,
137
- }, title: state.selectedItem ? state.selectedItem.textValue : placeholder }, tid, { children: (0, jsx_runtime_1.jsx)("span", Object.assign({}, valueProps, { css: Css_1.Css.lineClamp1.breakAll.$ }, { children: state.selectedItem ? state.selectedItem.textValue : placeholder }), void 0) }), void 0), showClearButton && (
138
- // Apply a tabIndex=-1 to remove need for addresses this focus behavior separately from the rest of the button.
139
- // This will require the user to click on the button if they want to remove it.
140
- (0, jsx_runtime_1.jsx)("button", Object.assign({}, clearFocusProps, { css: {
152
+ }, title: state.selectedItem ? state.selectedItem.textValue : placeholder }, tid, { children: (0, jsx_runtime_1.jsx)("span", Object.assign({}, valueProps, { css: Css_1.Css.lineClamp1.breakAll.$ }, { children: state.selectedItem ? state.selectedItem.textValue : placeholder }), void 0) }), void 0), showClearButton && ((0, jsx_runtime_1.jsx)("button", Object.assign({}, clearFocusProps, { css: {
141
153
  ...Css_1.Css.prPx(4).borderRadius("0 16px 16px 0").outline0.$,
142
154
  "&:hover": Css_1.Css.bgGray400.$,
143
155
  ...(isClearFocused ? Css_1.Css.boxShadow(`0px 0px 0px 2px rgba(3,105,161,1)`).$ : {}),
144
156
  }, onClick: () => {
145
157
  onSelect(undefined, undefined);
158
+ (0, utils_1.maybeCall)(onBlur);
146
159
  setIsClearFocused(false);
147
- }, "aria-label": "Remove" }, tid.clearButton, { children: (0, jsx_runtime_1.jsx)(components_1.Icon, { icon: "x", inc: typeScale === "xs" ? 2 : undefined }, void 0) }), void 0))] }), void 0), state.isOpen && ((0, jsx_runtime_1.jsx)(internal_1.Popover, Object.assign({ triggerRef: buttonRef, popoverRef: popoverRef, positionProps: overlayProps, onClose: state.close, isOpen: state.isOpen, shouldCloseOnBlur: true }, { children: (0, jsx_runtime_1.jsx)(ListBox_1.ListBox, Object.assign({}, menuProps, { listBoxRef: listBoxRef, state: state, getOptionLabel: getOptionLabel, getOptionValue: getOptionValue, positionProps: overlayProps }), void 0) }), void 0))] }, void 0));
160
+ }, "aria-label": "Remove" }, tid.clearButton, { children: (0, jsx_runtime_1.jsx)(components_1.Icon, { icon: "x", inc: typeScale === "xs" ? 2 : undefined }, void 0) }), void 0))] }), void 0), state.isOpen && ((0, jsx_runtime_1.jsx)(internal_1.Popover, Object.assign({ triggerRef: buttonRef, popoverRef: popoverRef, positionProps: overlayProps, onClose: state.close, isOpen: state.isOpen, shouldCloseOnBlur: true }, { children: (0, jsx_runtime_1.jsx)(ListBox_1.ListBox, Object.assign({}, menuProps, { listBoxRef: listBoxRef, state: state, getOptionLabel: getOptionLabel, getOptionValue: getOptionValue, positionProps: overlayProps, positionOffset: 8 }), void 0) }), void 0))] }, void 0));
148
161
  const tooltipText = selectHookProps.isDisabled && typeof disabled !== "boolean" ? disabled : undefined;
149
162
  return tooltipText ? ((0, jsx_runtime_1.jsx)(components_1.Tooltip, Object.assign({ title: tooltipText, placement: "top" }, { children: field }), void 0)) : (field);
150
163
  }
@@ -75,6 +75,13 @@ function DateField(props) {
75
75
  state.close();
76
76
  (0, utils_1.maybeCall)(onBlur);
77
77
  },
78
+ onKeyDown: (e) => {
79
+ var _a;
80
+ if (e.key === "Enter") {
81
+ // Blur the field when the user hits the enter key - as if they are "committing" the value and done with the field
82
+ (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.blur();
83
+ }
84
+ },
78
85
  }, inputRef);
79
86
  const { triggerProps, overlayProps } = (0, react_aria_1.useOverlayTrigger)({ type: "dialog" }, state, buttonRef);
80
87
  const { buttonProps } = (0, react_aria_1.useButton)({
@@ -47,6 +47,13 @@ function NumberField(props) {
47
47
  onBlur: () => {
48
48
  valueRef.current = { wip: false };
49
49
  },
50
+ onKeyDown: (e) => {
51
+ var _a;
52
+ if (e.key === "Enter") {
53
+ // Blur the field when the user hits the enter key - as if they are "committing" the value and done with the field
54
+ (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.blur();
55
+ }
56
+ },
50
57
  validationState: errorMsg !== undefined ? "invalid" : "valid",
51
58
  label: label,
52
59
  isDisabled: disabled,
@@ -43,9 +43,12 @@ function TextAreaField(props) {
43
43
  ...(preventNewLines
44
44
  ? {
45
45
  onKeyDown: (e) => {
46
+ var _a;
46
47
  // Prevent user from typing the new line character
47
- if (e.keyCode === 13) {
48
+ if (e.key === "Enter") {
48
49
  e.preventDefault();
50
+ // And then leave the field
51
+ (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.blur();
49
52
  }
50
53
  },
51
54
  onInput: (e) => {
@@ -16,7 +16,16 @@ function TextField(props) {
16
16
  value,
17
17
  };
18
18
  const inputRef = (0, react_1.useRef)(null);
19
- const { labelProps, inputProps } = (0, react_aria_1.useTextField)(textFieldProps, inputRef);
19
+ const { labelProps, inputProps } = (0, react_aria_1.useTextField)({
20
+ ...textFieldProps,
21
+ onKeyDown: (e) => {
22
+ var _a;
23
+ if (e.key === "Enter") {
24
+ // Blur the field when the user hits the enter key - as if they are "committing" the value and done with the field
25
+ (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.blur();
26
+ }
27
+ },
28
+ }, inputRef);
20
29
  // Construct our TextFieldApi to give access to some imperative methods
21
30
  if (api) {
22
31
  api.current = {
@@ -24,6 +24,7 @@ function SelectFieldBase(props) {
24
24
  const { compact, disabled: isDisabled = false, errorMsg, helperText, label, hideLabel, required, inlineLabel, readOnly: isReadOnly = false, onSelect, fieldDecoration, options, onBlur, onFocus, multiselect = false, getOptionLabel, getOptionValue, getOptionMenuLabel = getOptionLabel, sizeToContent = false, values, nothingSelectedText = "", contrast, disabledOptions, borderless, ...otherProps } = props;
25
25
  const { contains } = (0, react_aria_1.useFilter)({ sensitivity: "base" });
26
26
  function onSelectionChange(keys) {
27
+ var _a;
27
28
  // Close menu upon selection change only for Single selection mode
28
29
  if (!multiselect) {
29
30
  state.close();
@@ -72,6 +73,10 @@ function SelectFieldBase(props) {
72
73
  });
73
74
  }
74
75
  selectionChanged && onSelect([...keys.values()].map(Value_1.keyToValue));
76
+ if (!multiselect) {
77
+ // When a single select menu item changes, then blur the field AFTER `onSelect` has been called
78
+ (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.blur();
79
+ }
75
80
  }
76
81
  function onInputChange(value) {
77
82
  setFieldState((prevState) => ({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@homebound/beam",
3
- "version": "2.90.3",
3
+ "version": "2.91.2",
4
4
  "author": "Homebound",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",