@box/blueprint-web 17.13.0 → 17.14.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.
@@ -92,6 +92,7 @@ const RootInner = ({
92
92
  idForLabel,
93
93
  displaySingleSelectionAsChip = multiselect,
94
94
  autoComplete,
95
+ virtualFocus = false,
95
96
  ...comboboxProps
96
97
  } = rest;
97
98
  const isInput = useMemo(() => as === 'input', [as]);
@@ -119,7 +120,7 @@ const RootInner = ({
119
120
  open: open && !disabled,
120
121
  setOpen: onOpenChange,
121
122
  includesBaseElement: true,
122
- virtualFocus: false,
123
+ virtualFocus,
123
124
  focusLoop
124
125
  });
125
126
  const {
@@ -209,6 +209,13 @@ export interface ComboboxBaseProps<Multiple extends boolean, FreeInput extends b
209
209
  * @default true
210
210
  */
211
211
  focusLoop?: boolean;
212
+ /**
213
+ * When set to `true`, arrow keys highlight options with `aria-activedescendant` and keyboard focus
214
+ * stays on the input, so navigating the list never blurs the field. Useful when the input validates
215
+ * or reacts to losing focus. When `false`, DOM focus moves onto the options themselves.
216
+ * @default false
217
+ */
218
+ virtualFocus?: boolean;
212
219
  /**
213
220
  * Callback used when combobox input/textarea is focused
214
221
  */
@@ -70,6 +70,7 @@ const DatePicker = /*#__PURE__*/forwardRef((props, forwardedRef) => {
70
70
  });
71
71
  const dateInputRef = useRef(null);
72
72
  const calendarButtonRef = useRef(null);
73
+ const groupRef = useRef(null);
73
74
  const shouldFocusStayInInput = useRef(autoFocus && isCalendarOpen);
74
75
  const focusFirstDateSegment = useCallback(() => {
75
76
  dateInputRef.current?.querySelector('[role="spinbutton"]')?.focus();
@@ -108,6 +109,23 @@ const DatePicker = /*#__PURE__*/forwardRef((props, forwardedRef) => {
108
109
  setIsCalendarOpen(true);
109
110
  shouldFocusStayInInput.current = true;
110
111
  }, [setIsCalendarOpen]);
112
+ const onDateInputKeyDownCapture = useCallback(event => {
113
+ if (event.key !== 'Enter' || !isCalendarOpen) {
114
+ return;
115
+ }
116
+ const {
117
+ target
118
+ } = event;
119
+ if (!(target instanceof HTMLElement) || target.getAttribute('role') !== 'spinbutton') {
120
+ return;
121
+ }
122
+ event.preventDefault();
123
+ shouldFocusStayInInput.current = true;
124
+ setIsCalendarOpen(false);
125
+ }, [isCalendarOpen, setIsCalendarOpen]);
126
+ const onGroupFocusCapture = useCallback(() => {
127
+ shouldFocusStayInInput.current = true;
128
+ }, []);
111
129
  const hasError = !!error && !isDisabled;
112
130
  const calendarKey = `${internalValue?.day}/${internalValue?.month}/${internalValue?.year}${internalValue?.era}`;
113
131
  const onOpenAutoFocus = useCallback(e => {
@@ -135,11 +153,8 @@ const DatePicker = /*#__PURE__*/forwardRef((props, forwardedRef) => {
135
153
  calendarButtonRef.current?.focus();
136
154
  }
137
155
  }, [focusFirstDateSegment]);
138
- const onFocusOutside = useCallback(e => {
139
- const wasCalendarButtonPressed = calendarButtonRef.current && calendarButtonRef.current.contains(e.target);
140
- if (!wasCalendarButtonPressed) {
141
- e.preventDefault();
142
- }
156
+ const onFocusOutside = useCallback(event => {
157
+ event.preventDefault();
143
158
  }, []);
144
159
  const displayValue = value !== undefined ? value : internalValue;
145
160
  const Label = useLabelable(label, '');
@@ -171,8 +186,10 @@ const DatePicker = /*#__PURE__*/forwardRef((props, forwardedRef) => {
171
186
  }), jsx(RadixPopover.Anchor, {
172
187
  asChild: true,
173
188
  children: jsxs(Group, {
189
+ ref: groupRef,
174
190
  className: clsx(styles.group),
175
191
  "data-target-id": rest.dataTargetId,
192
+ onFocusCapture: onGroupFocusCapture,
176
193
  children: [jsx("div", {
177
194
  ref: dateInputRef,
178
195
  className: clsx(styles.groupContainer, {
@@ -181,6 +198,7 @@ const DatePicker = /*#__PURE__*/forwardRef((props, forwardedRef) => {
181
198
  "data-bp-animated": isAnimationEnabled ? 'true' : 'false',
182
199
  "data-testid": "date-picker-input",
183
200
  onClickCapture: onDateInputClick,
201
+ onKeyDownCapture: onDateInputKeyDownCapture,
184
202
  children: jsx(DateInput, {
185
203
  className: clsx(styles.groupContainerInput),
186
204
  children: segment => jsx(DateSegment, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@box/blueprint-web",
3
- "version": "17.13.0",
3
+ "version": "17.14.0",
4
4
  "type": "module",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "publishConfig": {
@@ -55,7 +55,7 @@
55
55
  "dependencies": {
56
56
  "@ariakit/react": "0.4.21",
57
57
  "@ariakit/react-core": "0.4.21",
58
- "@box/blueprint-web-assets": "^5.10.17",
58
+ "@box/blueprint-web-assets": "^5.10.19",
59
59
  "@internationalized/date": "^3.12.0",
60
60
  "@radix-ui/react-accordion": "1.1.2",
61
61
  "@radix-ui/react-checkbox": "1.0.4",
@@ -86,7 +86,7 @@
86
86
  "devDependencies": {
87
87
  "@box/box-test-client": "^3.2.7",
88
88
  "@box/playwright-utils": "^2.8.5",
89
- "@box/storybook-utils": "^1.2.61",
89
+ "@box/storybook-utils": "^1.2.63",
90
90
  "@figma/code-connect": "1.4.4",
91
91
  "@playwright/test": "1.46.1",
92
92
  "@types/js-yaml": "^4.0.9",