@jobber/components 6.104.1 → 6.105.1

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.
Files changed (38) hide show
  1. package/dist/Autocomplete/Autocomplete.types.d.ts +2 -0
  2. package/dist/Autocomplete/components/MenuList.d.ts +2 -1
  3. package/dist/Autocomplete/components/PersistentRegion.d.ts +2 -1
  4. package/dist/Autocomplete/hooks/useAutocompleteListNav.d.ts +2 -1
  5. package/dist/Autocomplete/index.cjs +86 -32
  6. package/dist/Autocomplete/index.mjs +87 -33
  7. package/dist/Autocomplete/tests/Autocomplete.setup.d.ts +13 -0
  8. package/dist/Autocomplete/useAutocomplete.d.ts +1 -0
  9. package/dist/Autocomplete/utils/interactionUtils.d.ts +14 -0
  10. package/dist/Checkbox/Checkbox.rebuilt.d.ts +2 -1
  11. package/dist/Checkbox/Checkbox.types.d.ts +40 -3
  12. package/dist/Checkbox/index.cjs +2 -2
  13. package/dist/Checkbox/index.mjs +2 -2
  14. package/dist/DatePicker-es.js +1 -1
  15. package/dist/InputEmail/InputEmail.types.d.ts +2 -2
  16. package/dist/InputEmail/hooks/useInputEmailActions.d.ts +8 -3
  17. package/dist/InputEmail/index.cjs +28 -3
  18. package/dist/InputEmail/index.mjs +28 -3
  19. package/dist/InputNumber/InputNumber.rebuilt.types.d.ts +2 -2
  20. package/dist/InputNumber/index.cjs +1 -1
  21. package/dist/InputNumber/index.mjs +1 -1
  22. package/dist/InputPhoneNumber/InputPhoneNumber.types.d.ts +2 -2
  23. package/dist/InputPhoneNumber/hooks/useInputPhoneActions.d.ts +8 -3
  24. package/dist/InputPhoneNumber/index.cjs +29 -3
  25. package/dist/InputPhoneNumber/index.mjs +29 -3
  26. package/dist/InputText/InputText.types.d.ts +2 -2
  27. package/dist/InputText/index.cjs +28 -3
  28. package/dist/InputText/index.mjs +28 -3
  29. package/dist/InputText/useInputTextActions.d.ts +8 -3
  30. package/dist/InputTime/InputTime.types.d.ts +2 -2
  31. package/dist/InputTime/hooks/useInputTimeActions.d.ts +8 -3
  32. package/dist/InputTime/index.cjs +28 -3
  33. package/dist/InputTime/index.mjs +28 -3
  34. package/dist/floating-ui.react-cjs.js +115 -0
  35. package/dist/floating-ui.react-es.js +115 -1
  36. package/dist/sharedHelpers/types.d.ts +26 -0
  37. package/dist/styles.css +3 -0
  38. package/package.json +2 -2
@@ -1,5 +1,5 @@
1
1
  import React__default, { useState, useRef, useEffect, useCallback, useMemo, forwardRef } from 'react';
2
- import { u as useFloating, b as autoUpdate, o as offset, f as flip, c as size, e as useListNavigation, d as useDismiss, g as useInteractions, r as useTransitionStyles, F as FloatingPortal, p as FloatingFocusManager } from '../floating-ui.react-es.js';
2
+ import { u as useFloating, b as autoUpdate, o as offset, f as flip, c as size, r as useClick, e as useListNavigation, d as useDismiss, g as useInteractions, t as useTransitionStyles, F as FloatingPortal, p as FloatingFocusManager } from '../floating-ui.react-es.js';
3
3
  import classnames from 'classnames';
4
4
  import { tokens } from '@jobber/design';
5
5
  import { useCallbackRef, useDebounce, useSafeLayoutEffect, useIsMounted, useOnKeyDown } from '@jobber/hooks';
@@ -127,7 +127,7 @@ function invokeActiveItemOnEnter(event, activeIndex, renderable, onSelect, onAct
127
127
 
128
128
  const MENU_OFFSET = tokens["space-small"];
129
129
  const AUTOCOMPLETE_MAX_HEIGHT$1 = 300;
130
- function useAutocompleteListNav({ navigableCount, shouldResetActiveIndexOnClose, onMenuClose, selectedIndex, }) {
130
+ function useAutocompleteListNav({ navigableCount, shouldResetActiveIndexOnClose, onMenuClose, selectedIndex, readOnly = false, }) {
131
131
  const [open, setOpen] = useState(false);
132
132
  const [activeIndex, setActiveIndex] = useState(null);
133
133
  const listRef = useRef([]);
@@ -159,6 +159,10 @@ function useAutocompleteListNav({ navigableCount, shouldResetActiveIndexOnClose,
159
159
  }),
160
160
  ],
161
161
  });
162
+ const click = useClick(context, {
163
+ enabled: !readOnly,
164
+ toggle: false, // Only open, never close on click
165
+ });
162
166
  const listNav = useListNavigation(context, {
163
167
  listRef,
164
168
  activeIndex,
@@ -180,7 +184,7 @@ function useAutocompleteListNav({ navigableCount, shouldResetActiveIndexOnClose,
180
184
  escapeKey: true,
181
185
  outsidePressEvent: "click",
182
186
  });
183
- const { getReferenceProps, getFloatingProps, getItemProps } = useInteractions([listNav, dismiss]);
187
+ const { getReferenceProps, getFloatingProps, getItemProps } = useInteractions([click, listNav, dismiss]);
184
188
  useEffect(() => {
185
189
  listRef.current.length = navigableCount;
186
190
  setActiveIndex(prev => {
@@ -207,11 +211,34 @@ function useAutocompleteListNav({ navigableCount, shouldResetActiveIndexOnClose,
207
211
  };
208
212
  }
209
213
 
214
+ /**
215
+ * Handler that prevents default pointer behavior.
216
+ * Used to prevent blur/focus issues when clicking on non-interactive menu elements.
217
+ */
218
+ function preventDefaultPointerDown(e) {
219
+ e.preventDefault();
220
+ }
221
+ /**
222
+ * Creates a handler for pointer down events on interactive menu items (options/actions).
223
+ * Prevents default to avoid blur and sets flag for focus management.
224
+ *
225
+ * @param isHandlingMenuInteractionRef - Ref to track if a menu interaction is in progress
226
+ * @returns A pointer down event handler
227
+ */
228
+ function createInteractionPointerDownHandler(isHandlingMenuInteractionRef) {
229
+ return (e) => {
230
+ e.preventDefault();
231
+ // Set flag to prevent blur/focus handlers from interfering
232
+ isHandlingMenuInteractionRef.current = true;
233
+ };
234
+ }
235
+
210
236
  // Keeping this hook cohesive improves readability by centralizing related
211
237
  // interactions and state transitions.
212
238
  // eslint-disable-next-line max-statements
213
239
  function useAutocomplete(props) {
214
240
  const { menu, emptyActions, getOptionLabel: getOptionLabelProp, isOptionEqualToValue, inputValue, onInputChange, value, onChange, multiple, openOnFocus = true, readOnly = false, debounce: debounceMs = 300, } = props;
241
+ const isHandlingMenuInteractionRef = useRef(false);
215
242
  // TODO: Clean up the types in these refs by enhancing the type system in useCallbackRef
216
243
  const getOptionLabelPropRef = useCallbackRef((opt) => getOptionLabelProp === null || getOptionLabelProp === void 0 ? void 0 : getOptionLabelProp(opt));
217
244
  const getOptionLabel = useCallback((opt) => {
@@ -254,7 +281,8 @@ function useAutocomplete(props) {
254
281
  const [debouncedInputValue, setDebouncedInputValue] = useState(inputValue);
255
282
  const debouncedSetter = useDebounce(setDebouncedInputValue, debounceMs);
256
283
  useEffect(() => {
257
- if (debounceMs === 0) {
284
+ // Skip debounce when clearing input for immediate feedback, preventing flickering of last selected item
285
+ if (debounceMs === 0 || inputValue === "") {
258
286
  setDebouncedInputValue(inputValue);
259
287
  return;
260
288
  }
@@ -344,6 +372,7 @@ function useAutocomplete(props) {
344
372
  navigableCount: totalNavigableCount,
345
373
  shouldResetActiveIndexOnClose: () => !hasSelection,
346
374
  selectedIndex,
375
+ readOnly,
347
376
  onMenuClose: () => {
348
377
  if (props.allowFreeForm !== true) {
349
378
  const hasText = inputValue.trim().length > 0;
@@ -355,7 +384,6 @@ function useAutocomplete(props) {
355
384
  }
356
385
  },
357
386
  });
358
- const [inputFocused, setInputFocused] = useState(false);
359
387
  // Handles activeIndex reset and, in single-select mode only, clearing selection when input is empty
360
388
  useEffect(() => {
361
389
  const hasText = inputValue.trim().length > 0;
@@ -451,13 +479,24 @@ function useAutocomplete(props) {
451
479
  selectOption(option);
452
480
  // Might not always want to close on selection. Multi for example.
453
481
  setOpen(false);
482
+ if (refs.domReference.current instanceof HTMLElement) {
483
+ refs.domReference.current.focus();
484
+ }
454
485
  }, [selectOption, setOpen]);
455
486
  const onAction = useCallback((action) => {
456
487
  action.run();
457
488
  setActiveIndex(null);
458
489
  if (action.closeOnRun !== false)
459
490
  setOpen(false);
491
+ if (refs.domReference.current instanceof HTMLElement) {
492
+ refs.domReference.current.focus();
493
+ }
460
494
  }, [setOpen, setActiveIndex]);
495
+ /**
496
+ * Handler for mousedown on interactive menu items (options/actions)
497
+ * Prevents default to avoid blur and sets flag for focus management
498
+ */
499
+ const onInteractionPointerDown = useMemo(() => createInteractionPointerDownHandler(isHandlingMenuInteractionRef), []);
461
500
  function commitFromInputText(inputText) {
462
501
  var _a;
463
502
  if (inputText.length === 0)
@@ -499,14 +538,20 @@ function useAutocomplete(props) {
499
538
  ]);
500
539
  const onInputFocus = useCallback((event) => {
501
540
  var _a;
502
- setInputFocused(true);
503
- if (!readOnly && openOnFocus)
541
+ if (!readOnly && openOnFocus && !isHandlingMenuInteractionRef.current) {
504
542
  setOpen(true);
505
- (_a = props.onFocus) === null || _a === void 0 ? void 0 : _a.call(props, event);
543
+ }
544
+ // Only call user's onFocus for genuine focus events, not programmatic restorations
545
+ if (!isHandlingMenuInteractionRef.current) {
546
+ (_a = props.onFocus) === null || _a === void 0 ? void 0 : _a.call(props, event);
547
+ }
548
+ isHandlingMenuInteractionRef.current = false;
506
549
  }, [props.onFocus, readOnly, openOnFocus, setOpen]);
507
550
  const onInputBlur = useCallback((event) => {
508
551
  var _a, _b;
509
- setInputFocused(false);
552
+ if (isHandlingMenuInteractionRef.current) {
553
+ return;
554
+ }
510
555
  if (readOnly) {
511
556
  (_a = props.onBlur) === null || _a === void 0 ? void 0 : _a.call(props, event);
512
557
  return;
@@ -612,11 +657,11 @@ function useAutocomplete(props) {
612
657
  setActiveIndex(null);
613
658
  }
614
659
  // Important: update open state before propagating the change so that downstream effects
615
- // dont see an intermediate state where inputValue changed but open was stale
660
+ // don't see an intermediate state where inputValue changed but open was stale
616
661
  if (!readOnly) {
617
662
  const hasText = val.trim().length > 0;
618
663
  const mustSelectFromOptions = hasText && !props.allowFreeForm;
619
- const keepOpenOnEmpty = openOnFocus && inputFocused;
664
+ const keepOpenOnEmpty = openOnFocus;
620
665
  setOpen(mustSelectFromOptions || keepOpenOnEmpty);
621
666
  }
622
667
  onInputChange === null || onInputChange === void 0 ? void 0 : onInputChange(val);
@@ -627,7 +672,6 @@ function useAutocomplete(props) {
627
672
  readOnly,
628
673
  props.allowFreeForm,
629
674
  openOnFocus,
630
- inputFocused,
631
675
  setOpen,
632
676
  ]);
633
677
  return {
@@ -656,6 +700,7 @@ function useAutocomplete(props) {
656
700
  // actions
657
701
  onSelection,
658
702
  onAction,
703
+ onInteractionPointerDown,
659
704
  // input handlers
660
705
  onInputChangeFromUser,
661
706
  onInputBlur,
@@ -666,7 +711,7 @@ function useAutocomplete(props) {
666
711
  };
667
712
  }
668
713
 
669
- function MenuList({ items, activeIndex, indexOffset = 0, getItemProps, listRef, listboxId, customRenderOption, customRenderSection, customRenderAction, getOptionLabel, onSelect, onAction, isOptionSelected, slotOverrides, }) {
714
+ function MenuList({ items, activeIndex, indexOffset = 0, getItemProps, listRef, listboxId, customRenderOption, customRenderSection, customRenderAction, getOptionLabel, onSelect, onAction, onInteractionPointerDown, isOptionSelected, slotOverrides, }) {
670
715
  let navigableIndex = -1;
671
716
  function renderItemNode(item) {
672
717
  var _a, _b, _c, _d, _e, _f;
@@ -690,6 +735,7 @@ function MenuList({ items, activeIndex, indexOffset = 0, getItemProps, listRef,
690
735
  customRenderOption,
691
736
  getOptionLabel,
692
737
  onSelect,
738
+ onInteractionPointerDown,
693
739
  indexOffset,
694
740
  optionClassName: (_c = slotOverrides === null || slotOverrides === void 0 ? void 0 : slotOverrides.option) === null || _c === void 0 ? void 0 : _c.className,
695
741
  optionStyle: (_d = slotOverrides === null || slotOverrides === void 0 ? void 0 : slotOverrides.option) === null || _d === void 0 ? void 0 : _d.style,
@@ -706,6 +752,7 @@ function MenuList({ items, activeIndex, indexOffset = 0, getItemProps, listRef,
706
752
  listboxId,
707
753
  customRenderAction,
708
754
  onAction,
755
+ onInteractionPointerDown,
709
756
  indexOffset,
710
757
  actionClassName: (_e = slotOverrides === null || slotOverrides === void 0 ? void 0 : slotOverrides.action) === null || _e === void 0 ? void 0 : _e.className,
711
758
  actionStyle: (_f = slotOverrides === null || slotOverrides === void 0 ? void 0 : slotOverrides.action) === null || _f === void 0 ? void 0 : _f.style,
@@ -719,12 +766,12 @@ function MenuList({ items, activeIndex, indexOffset = 0, getItemProps, listRef,
719
766
  function handleSectionRendering({ customRenderSection, section, sectionClassName, sectionStyle, }) {
720
767
  var _a;
721
768
  const headerContent = customRenderSection ? (customRenderSection(section)) : (React__default.createElement(DefaultSectionContent, { section: section }));
722
- return (React__default.createElement("div", { key: `section-${String((_a = section.key) !== null && _a !== void 0 ? _a : section.label)}`, role: "presentation", tabIndex: -1, "data-testid": "ATL-AutocompleteRebuilt-Section", className: classnames(styles$1.section, styles$1.stickyTop, sectionClassName), style: sectionStyle }, headerContent));
769
+ return (React__default.createElement("div", { key: `section-${String((_a = section.key) !== null && _a !== void 0 ? _a : section.label)}`, role: "presentation", tabIndex: -1, "data-testid": "ATL-AutocompleteRebuilt-Section", className: classnames(styles$1.section, styles$1.stickyTop, sectionClassName), style: sectionStyle, onPointerDown: preventDefaultPointerDown }, headerContent));
723
770
  }
724
771
  function DefaultSectionContent({ section, }) {
725
772
  return React__default.createElement(Heading, { level: 5 }, section.label);
726
773
  }
727
- function handleOptionRendering({ option, activeIndex, navigableIndex, getItemProps, listRef, listboxId, isOptionSelected, customRenderOption, getOptionLabel, onSelect, indexOffset = 0, optionClassName, optionStyle, }) {
774
+ function handleOptionRendering({ option, activeIndex, navigableIndex, getItemProps, listRef, listboxId, isOptionSelected, customRenderOption, getOptionLabel, onSelect, onInteractionPointerDown, indexOffset = 0, optionClassName, optionStyle, }) {
728
775
  var _a;
729
776
  const nextNavigableIndex = navigableIndex + 1;
730
777
  const isActive = activeIndex === nextNavigableIndex;
@@ -738,6 +785,7 @@ function handleOptionRendering({ option, activeIndex, navigableIndex, getItemPro
738
785
  listRef.current[idx] = node;
739
786
  },
740
787
  onClick: () => onSelect(option),
788
+ onPointerDown: onInteractionPointerDown,
741
789
  className: classnames(styles$1.option, isActive && styles$1.optionActive, optionClassName),
742
790
  style: optionStyle,
743
791
  }), { role: "option", tabIndex: -1, "aria-selected": isSelected ? true : false, id: `${listboxId}-item-${nextNavigableIndex + indexOffset}`, "data-index": nextNavigableIndex + indexOffset, "data-active": isActive ? true : undefined }), optionContent)),
@@ -749,7 +797,7 @@ function DefaultOptionContent({ isSelected, text, }) {
749
797
  React__default.createElement("div", { className: styles$1.icon }, isSelected && React__default.createElement(Icon, { name: "checkmark", size: "small" })),
750
798
  React__default.createElement(Text, null, text)));
751
799
  }
752
- function handleActionRendering({ action, activeIndex, navigableIndex, getItemProps, listRef, listboxId, customRenderAction, onAction, indexOffset = 0, actionClassName, actionStyle, origin, }) {
800
+ function handleActionRendering({ action, activeIndex, navigableIndex, getItemProps, listRef, listboxId, customRenderAction, onAction, onInteractionPointerDown, indexOffset = 0, actionClassName, actionStyle, origin, }) {
753
801
  var _a;
754
802
  const nextNavigableIndex = navigableIndex + 1;
755
803
  const isActive = activeIndex === nextNavigableIndex;
@@ -767,6 +815,7 @@ function handleActionRendering({ action, activeIndex, navigableIndex, getItemPro
767
815
  closeOnRun: action.shouldClose,
768
816
  });
769
817
  },
818
+ onPointerDown: onInteractionPointerDown,
770
819
  className: classnames(styles$1.action, isActive && styles$1.actionActive, actionClassName),
771
820
  style: actionStyle,
772
821
  });
@@ -784,7 +833,7 @@ function DefaultActionContent({ textContent, }) {
784
833
  } }, textContent));
785
834
  }
786
835
 
787
- function PersistentRegion({ items, position, activeIndex, indexOffset, getItemProps, listRef, customRenderHeader, customRenderFooter, className, style, onAction, }) {
836
+ function PersistentRegion({ items, position, activeIndex, indexOffset, getItemProps, listRef, customRenderHeader, customRenderFooter, className, style, onAction, onInteractionPointerDown, }) {
788
837
  if (!items || items.length === 0)
789
838
  return null;
790
839
  let navigableIndex = -1;
@@ -799,13 +848,14 @@ function PersistentRegion({ items, position, activeIndex, indexOffset, getItemPr
799
848
  customRenderFooter,
800
849
  listRef,
801
850
  onAction,
851
+ onInteractionPointerDown,
802
852
  navigableIndex,
803
853
  });
804
854
  navigableIndex = result.nextNavigableIndex;
805
855
  return result.node;
806
856
  })));
807
857
  }
808
- function handlePersistentRendering({ persistent, position, activeIndex, indexOffset, getItemProps, customRenderHeader, customRenderFooter, listRef, onAction, navigableIndex, }) {
858
+ function handlePersistentRendering({ persistent, position, activeIndex, indexOffset, getItemProps, customRenderHeader, customRenderFooter, listRef, onAction, onInteractionPointerDown, navigableIndex, }) {
809
859
  const interactive = Boolean(persistent.onClick);
810
860
  if (!interactive) {
811
861
  const node = handleTextPersistentRendering({
@@ -826,6 +876,7 @@ function handlePersistentRendering({ persistent, position, activeIndex, indexOff
826
876
  customRenderFooter,
827
877
  listRef,
828
878
  onAction,
879
+ onInteractionPointerDown,
829
880
  navigableIndex,
830
881
  });
831
882
  }
@@ -841,9 +892,9 @@ function handleTextPersistentRendering({ persistent, position, customRenderHeade
841
892
  else {
842
893
  content = React__default.createElement(DefaultTextPersistentContent, { persistent: persistent });
843
894
  }
844
- return (React__default.createElement("div", { key: `persistent-${position}-${String((_a = persistent.key) !== null && _a !== void 0 ? _a : persistent.label)}`, role: "presentation", tabIndex: -1, className: styles$1.textPersistent }, content));
895
+ return (React__default.createElement("div", { key: `persistent-${position}-${String((_a = persistent.key) !== null && _a !== void 0 ? _a : persistent.label)}`, role: "presentation", tabIndex: -1, className: styles$1.textPersistent, onPointerDown: preventDefaultPointerDown }, content));
845
896
  }
846
- function handleActionPersistentRendering({ persistent, position, activeIndex, indexOffset, getItemProps, customRenderHeader, customRenderFooter, listRef, onAction, navigableIndex, }) {
897
+ function handleActionPersistentRendering({ persistent, position, activeIndex, indexOffset, getItemProps, customRenderHeader, customRenderFooter, listRef, onAction, onInteractionPointerDown, navigableIndex, }) {
847
898
  var _a;
848
899
  const nextNavigableIndex = navigableIndex + 1;
849
900
  const isActive = activeIndex === indexOffset + nextNavigableIndex;
@@ -870,13 +921,16 @@ function handleActionPersistentRendering({ persistent, position, activeIndex, in
870
921
  if (persistNode)
871
922
  listRef.current[idx] = persistNode;
872
923
  },
873
- onClick: () => onAction({
874
- run: () => {
875
- var _a;
876
- (_a = persistent.onClick) === null || _a === void 0 ? void 0 : _a.call(persistent);
877
- },
878
- closeOnRun: persistent.shouldClose,
879
- }),
924
+ onClick: () => {
925
+ onAction({
926
+ run: () => {
927
+ var _a;
928
+ (_a = persistent.onClick) === null || _a === void 0 ? void 0 : _a.call(persistent);
929
+ },
930
+ closeOnRun: persistent.shouldClose,
931
+ });
932
+ },
933
+ onPointerDown: onInteractionPointerDown,
880
934
  className: classnames(styles$1.action, isActive && styles$1.actionActive),
881
935
  }), { role: "button", tabIndex: -1 }), content)),
882
936
  nextNavigableIndex,
@@ -891,7 +945,7 @@ const AutocompleteRebuilt = forwardRef(AutocompleteRebuiltInternal);
891
945
  function AutocompleteRebuiltInternal(props, forwardedRef) {
892
946
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
893
947
  const { inputValue, placeholder, disabled, error, invalid, description, size: sizeProp, loading = false, } = props;
894
- const { renderable, optionCount, persistentsHeaders, persistentsFooters, headerInteractiveCount, middleNavigableCount, getOptionLabel, isOptionSelected, refs, floatingStyles, context, getReferenceProps, getFloatingProps, getItemProps, activeIndex, open, listRef, onSelection, onAction, onInputChangeFromUser, onInputBlur, onInputFocus, onInputKeyDown, setReferenceElement, } = useAutocomplete(props);
948
+ const { renderable, optionCount, persistentsHeaders, persistentsFooters, headerInteractiveCount, middleNavigableCount, getOptionLabel, isOptionSelected, refs, floatingStyles, context, getReferenceProps, getFloatingProps, getItemProps, activeIndex, open, listRef, onSelection, onAction, onInteractionPointerDown, onInputChangeFromUser, onInputBlur, onInputFocus, onInputKeyDown, setReferenceElement, } = useAutocomplete(props);
895
949
  const listboxId = React__default.useId();
896
950
  // Provides mount/unmount-aware transition styles for the floating element
897
951
  const { isMounted, styles: transitionStyles } = useTransitionStyles(context, {
@@ -951,10 +1005,10 @@ function AutocompleteRebuiltInternal(props, forwardedRef) {
951
1005
  ? { width: menuWidth, maxWidth: menuWidth }
952
1006
  : {})),
953
1007
  })),
954
- React__default.createElement(PersistentRegion, { items: persistentsHeaders, position: "header", activeIndex: activeIndex, indexOffset: 0, listboxId: listboxId, getItemProps: getItemProps, listRef: listRef, customRenderHeader: props.customRenderHeader, customRenderFooter: props.customRenderFooter, onAction: onAction, className: classnames(styles$1.persistentHeader, (_c = props.UNSAFE_className) === null || _c === void 0 ? void 0 : _c.header), style: (_d = props.UNSAFE_styles) === null || _d === void 0 ? void 0 : _d.header }),
1008
+ React__default.createElement(PersistentRegion, { items: persistentsHeaders, position: "header", activeIndex: activeIndex, indexOffset: 0, listboxId: listboxId, getItemProps: getItemProps, listRef: listRef, customRenderHeader: props.customRenderHeader, customRenderFooter: props.customRenderFooter, onAction: onAction, onInteractionPointerDown: onInteractionPointerDown, className: classnames(styles$1.persistentHeader, (_c = props.UNSAFE_className) === null || _c === void 0 ? void 0 : _c.header), style: (_d = props.UNSAFE_styles) === null || _d === void 0 ? void 0 : _d.header }),
955
1009
  React__default.createElement("div", { className: styles$1.scrollRegion }, loading ? ((_e = props.customRenderLoading) !== null && _e !== void 0 ? _e : React__default.createElement(LoadingContent, null)) : (React__default.createElement(React__default.Fragment, null,
956
1010
  showEmptyStateMessage && (React__default.createElement(EmptyStateMessage, { emptyState: props.emptyStateMessage })),
957
- renderable.length > 0 && (React__default.createElement(MenuList, { items: renderable, activeIndex: activeIndexForMiddle, indexOffset: headerInteractiveCount, listboxId: listboxId, getItemProps: getItemProps, listRef: listRef, customRenderOption: props.customRenderOption, customRenderSection: props.customRenderSection, customRenderAction: props.customRenderAction, getOptionLabel: getOptionLabel, onSelect: onSelection, onAction: onAction, isOptionSelected: isOptionSelected, slotOverrides: {
1011
+ renderable.length > 0 && (React__default.createElement(MenuList, { items: renderable, activeIndex: activeIndexForMiddle, indexOffset: headerInteractiveCount, listboxId: listboxId, getItemProps: getItemProps, listRef: listRef, customRenderOption: props.customRenderOption, customRenderSection: props.customRenderSection, customRenderAction: props.customRenderAction, getOptionLabel: getOptionLabel, onSelect: onSelection, onAction: onAction, onInteractionPointerDown: onInteractionPointerDown, isOptionSelected: isOptionSelected, slotOverrides: {
958
1012
  option: {
959
1013
  className: (_f = props.UNSAFE_className) === null || _f === void 0 ? void 0 : _f.option,
960
1014
  style: (_g = props.UNSAFE_styles) === null || _g === void 0 ? void 0 : _g.option,
@@ -968,10 +1022,10 @@ function AutocompleteRebuiltInternal(props, forwardedRef) {
968
1022
  style: (_l = props.UNSAFE_styles) === null || _l === void 0 ? void 0 : _l.section,
969
1023
  },
970
1024
  } }))))),
971
- React__default.createElement(PersistentRegion, { items: persistentsFooters, position: "footer", activeIndex: activeIndex, indexOffset: headerInteractiveCount + middleNavigableCount, listboxId: listboxId, getItemProps: getItemProps, listRef: listRef, customRenderHeader: props.customRenderHeader, customRenderFooter: props.customRenderFooter, onAction: onAction, className: classnames(styles$1.persistentFooter, (_m = props.UNSAFE_className) === null || _m === void 0 ? void 0 : _m.footer), style: (_o = props.UNSAFE_styles) === null || _o === void 0 ? void 0 : _o.footer })))))));
1025
+ React__default.createElement(PersistentRegion, { items: persistentsFooters, position: "footer", activeIndex: activeIndex, indexOffset: headerInteractiveCount + middleNavigableCount, listboxId: listboxId, getItemProps: getItemProps, listRef: listRef, customRenderHeader: props.customRenderHeader, customRenderFooter: props.customRenderFooter, onAction: onAction, onInteractionPointerDown: onInteractionPointerDown, className: classnames(styles$1.persistentFooter, (_m = props.UNSAFE_className) === null || _m === void 0 ? void 0 : _m.footer), style: (_o = props.UNSAFE_styles) === null || _o === void 0 ? void 0 : _o.footer })))))));
972
1026
  }
973
1027
  function LoadingContent() {
974
- return (React__default.createElement("div", { className: styles$1.loadingList },
1028
+ return (React__default.createElement("div", { className: styles$1.loadingList, onPointerDown: preventDefaultPointerDown },
975
1029
  React__default.createElement(Glimmer, { shape: "rectangle", size: "base" }),
976
1030
  React__default.createElement(Glimmer, { shape: "rectangle", size: "base" }),
977
1031
  React__default.createElement(Glimmer, { shape: "rectangle", size: "base" })));
@@ -979,7 +1033,7 @@ function LoadingContent() {
979
1033
  function EmptyStateMessage({ emptyState, }) {
980
1034
  const emptyStateDefault = "No options";
981
1035
  const emptyStateContent = emptyState !== null && emptyState !== void 0 ? emptyState : emptyStateDefault;
982
- return React__default.createElement("div", { className: styles$1.emptyStateMessage }, emptyStateContent);
1036
+ return (React__default.createElement("div", { className: styles$1.emptyStateMessage, onPointerDown: preventDefaultPointerDown }, emptyStateContent));
983
1037
  }
984
1038
 
985
1039
  var styles = {"autocomplete":"_7mObJiwfPh4-","options":"dL5JShAJlKM-","heading":"PWZL-94hH7k-","visible":"_2RzcnTdaPyc-","option":"y9zhi8Wr8QA-","active":"_3Xg49dtL1Q8-","separator":"LIeh390F3W8-","icon":"K2phy6IC3TY-","text":"a6-LbUm5WnY-","label":"tQNbuxcE9nU-","details":"qacStG9-XbE-","spinning":"P9cQDL4MZ-s-"};
@@ -36,3 +36,16 @@ export declare function FreeFormWrapper({ initialValue, initialInputValue, onCha
36
36
  readonly inputEqualsOption?: (input: string, option: OptionLike) => boolean;
37
37
  readonly debounce?: number;
38
38
  }): React.JSX.Element;
39
+ /**
40
+ * Wrapper for testing focus and blur behavior with tabbable siblings
41
+ * Includes tabbable elements before and after the autocomplete
42
+ * so tests can use tab navigation to focus without clicking
43
+ */
44
+ export declare function FocusableSiblingsWrapper<T extends OptionLike>({ onFocus, onChange, onInputChange, menu, readOnly, openOnFocus, }: {
45
+ readonly onChange?: (v: T | undefined) => void;
46
+ readonly onInputChange?: (v: string) => void;
47
+ readonly menu?: MenuItem<T>[];
48
+ readonly readOnly?: boolean;
49
+ readonly onFocus?: () => void;
50
+ readonly openOnFocus?: boolean;
51
+ }): React.JSX.Element;
@@ -58,6 +58,7 @@ export declare function useAutocomplete<Value extends OptionLike, Multiple exten
58
58
  listRef: React.RefObject<(HTMLElement | null)[]>;
59
59
  onSelection: (option: Value) => void;
60
60
  onAction: (action: ActionConfig) => void;
61
+ onInteractionPointerDown: (e: React.PointerEvent) => void;
61
62
  onInputChangeFromUser: (val: string) => void;
62
63
  onInputBlur: (event: React.FocusEvent<HTMLInputElement | HTMLTextAreaElement>) => void;
63
64
  onInputFocus: (event: React.FocusEvent<HTMLInputElement | HTMLTextAreaElement>) => void;
@@ -0,0 +1,14 @@
1
+ import type React from "react";
2
+ /**
3
+ * Handler that prevents default pointer behavior.
4
+ * Used to prevent blur/focus issues when clicking on non-interactive menu elements.
5
+ */
6
+ export declare function preventDefaultPointerDown(e: React.PointerEvent): void;
7
+ /**
8
+ * Creates a handler for pointer down events on interactive menu items (options/actions).
9
+ * Prevents default to avoid blur and sets flag for focus management.
10
+ *
11
+ * @param isHandlingMenuInteractionRef - Ref to track if a menu interaction is in progress
12
+ * @returns A pointer down event handler
13
+ */
14
+ export declare function createInteractionPointerDownHandler(isHandlingMenuInteractionRef: React.RefObject<boolean>): (e: React.PointerEvent) => void;
@@ -1,6 +1,7 @@
1
1
  import React from "react";
2
- export declare const CheckboxRebuilt: React.ForwardRefExoticComponent<Omit<import("./Checkbox.types").BaseCheckboxProps, "children" | "onChange" | "label" | "description"> & Pick<import("../sharedHelpers/types").RebuiltInputCommonProps, "version"> & {
2
+ export declare const CheckboxRebuilt: React.ForwardRefExoticComponent<import("./Checkbox.types").CheckboxCoreProps & import("../sharedHelpers/types").AriaInputProps & import("../sharedHelpers/types").FocusEvents<HTMLInputElement> & import("../sharedHelpers/types").MouseEvents<HTMLInputElement> & Pick<import("../sharedHelpers/types").HTMLInputBaseProps, "id" | "name" | "disabled"> & Pick<import("../sharedHelpers/types").RebuiltInputCommonProps, "version"> & {
3
3
  label?: string | React.ReactElement;
4
4
  description?: React.ReactNode;
5
+ invalid?: boolean;
5
6
  onChange?(newValue: boolean, event: React.ChangeEvent<HTMLInputElement>): void;
6
7
  } & React.RefAttributes<HTMLInputElement>>;
@@ -1,7 +1,10 @@
1
1
  import type { ReactElement, ReactNode } from "react";
2
2
  import type { XOR } from "ts-xor";
3
- import type { AriaInputProps, FocusEvents, HTMLInputBaseProps, RebuiltInputCommonProps } from "../sharedHelpers/types";
4
- export interface BaseCheckboxProps extends AriaInputProps, FocusEvents<HTMLInputElement>, Pick<HTMLInputBaseProps, "id" | "name" | "disabled">, Pick<RebuiltInputCommonProps, "description" | "invalid"> {
3
+ import type { AriaInputProps, FocusEvents, HTMLInputBaseProps, MouseEvents, RebuiltInputCommonProps } from "../sharedHelpers/types";
4
+ /**
5
+ * Shared checkbox-specific props used by both legacy and rebuilt versions
6
+ */
7
+ export interface CheckboxCoreProps {
5
8
  /**
6
9
  * Determines if the checkbox is checked or not.
7
10
  */
@@ -22,10 +25,39 @@ export interface BaseCheckboxProps extends AriaInputProps, FocusEvents<HTMLInput
22
25
  * Value of the checkbox.
23
26
  */
24
27
  readonly value?: string;
28
+ }
29
+ /**
30
+ * Base props for legacy checkbox
31
+ */
32
+ export interface BaseCheckboxProps extends CheckboxCoreProps {
33
+ /**
34
+ * Disables the checkbox.
35
+ */
36
+ readonly disabled?: boolean;
37
+ /**
38
+ * Checkbox input name
39
+ */
40
+ readonly name?: string;
41
+ /**
42
+ * Further description of the label
43
+ */
44
+ readonly description?: ReactNode;
45
+ /**
46
+ * ID for the checkbox input
47
+ */
48
+ readonly id?: string;
25
49
  /**
26
50
  * Called when the checkbox value changes
27
51
  */
28
52
  onChange?(newValue: boolean): void;
53
+ /**
54
+ * Called when the checkbox is focused
55
+ */
56
+ onFocus?(event: React.FocusEvent<HTMLInputElement>): void;
57
+ /**
58
+ * Called when the checkbox loses focus
59
+ */
60
+ onBlur?(event: React.FocusEvent<HTMLInputElement>): void;
29
61
  }
30
62
  interface CheckboxLabelProps extends BaseCheckboxProps {
31
63
  /**
@@ -39,7 +71,7 @@ interface CheckboxChildrenProps extends BaseCheckboxProps {
39
71
  */
40
72
  readonly children?: ReactElement;
41
73
  }
42
- export type CheckboxRebuiltProps = Omit<BaseCheckboxProps, "label" | "description" | "children" | "onChange"> & Pick<RebuiltInputCommonProps, "version"> & {
74
+ export type CheckboxRebuiltProps = CheckboxCoreProps & AriaInputProps & FocusEvents<HTMLInputElement> & MouseEvents<HTMLInputElement> & Pick<HTMLInputBaseProps, "id" | "name" | "disabled"> & Pick<RebuiltInputCommonProps, "version"> & {
43
75
  /**
44
76
  * Label that shows up beside the checkbox.
45
77
  * String will be rendered with the default markup.
@@ -52,9 +84,14 @@ export type CheckboxRebuiltProps = Omit<BaseCheckboxProps, "label" | "descriptio
52
84
  * ReactElement will be rendered with provided positioning.
53
85
  */
54
86
  description?: ReactNode;
87
+ /**
88
+ * Whether the checkbox is invalid
89
+ */
90
+ invalid?: boolean;
55
91
  /**
56
92
  * Called when the checkbox value changes.
57
93
  * Includes the change event as a second argument.
94
+ * This is the recommended event handler to access the new value.
58
95
  */
59
96
  onChange?(newValue: boolean, event: React.ChangeEvent<HTMLInputElement>): void;
60
97
  };
@@ -56,7 +56,7 @@ function CheckboxLegacy({ checked, defaultChecked, disabled, label, name, value,
56
56
  }
57
57
 
58
58
  const CheckboxRebuilt = React.forwardRef(function CheckboxRebuiltInternal(props, ref) {
59
- const { checked, defaultChecked, disabled, label, name, value, indeterminate = false, description, id, onBlur, onChange, onFocus, invalid, } = props;
59
+ const { checked, defaultChecked, disabled, label, name, value, indeterminate = false, description, id, onBlur, onChange, onFocus, onClick, onMouseDown, onMouseUp, onPointerDown, onPointerUp, invalid, } = props;
60
60
  const descriptionIdentifier = React.useId();
61
61
  const descriptionVisible = Boolean(description);
62
62
  const wrapperClassName = classnames(styles.wrapper, disabled && styles.disabled, invalid && styles.invalid);
@@ -77,7 +77,7 @@ const CheckboxRebuilt = React.forwardRef(function CheckboxRebuiltInternal(props,
77
77
  React.createElement("span", { className: styles.checkHolder },
78
78
  React.createElement("input", Object.assign({ ref: ref, type: "checkbox", id: id, className: inputClassName, name: name, "aria-label": props["aria-label"], "aria-describedby": descriptionVisible
79
79
  ? descriptionIdentifier
80
- : props["aria-describedby"], "aria-invalid": isInvalid ? true : undefined, "aria-required": props["aria-required"], checked: checked, value: value, defaultChecked: defaultChecked, disabled: disabled, onChange: handleChange, onFocus: onFocus, onBlur: onBlur }, dataAttrs)),
80
+ : props["aria-describedby"], "aria-invalid": isInvalid ? true : undefined, "aria-required": props["aria-required"], checked: checked, value: value, defaultChecked: defaultChecked, disabled: disabled, onChange: handleChange, onFocus: onFocus, onBlur: onBlur, onClick: onClick, onMouseDown: onMouseDown, onMouseUp: onMouseUp, onPointerDown: onPointerDown, onPointerUp: onPointerUp }, dataAttrs)),
81
81
  React.createElement("span", { className: styles.checkBox },
82
82
  React.createElement(Icon.Icon, { name: iconName, color: "surface" }))),
83
83
  labelContent && React.createElement("span", { className: styles.label }, labelContent)),
@@ -54,7 +54,7 @@ function CheckboxLegacy({ checked, defaultChecked, disabled, label, name, value,
54
54
  }
55
55
 
56
56
  const CheckboxRebuilt = forwardRef(function CheckboxRebuiltInternal(props, ref) {
57
- const { checked, defaultChecked, disabled, label, name, value, indeterminate = false, description, id, onBlur, onChange, onFocus, invalid, } = props;
57
+ const { checked, defaultChecked, disabled, label, name, value, indeterminate = false, description, id, onBlur, onChange, onFocus, onClick, onMouseDown, onMouseUp, onPointerDown, onPointerUp, invalid, } = props;
58
58
  const descriptionIdentifier = useId();
59
59
  const descriptionVisible = Boolean(description);
60
60
  const wrapperClassName = classnames(styles.wrapper, disabled && styles.disabled, invalid && styles.invalid);
@@ -75,7 +75,7 @@ const CheckboxRebuilt = forwardRef(function CheckboxRebuiltInternal(props, ref)
75
75
  React__default.createElement("span", { className: styles.checkHolder },
76
76
  React__default.createElement("input", Object.assign({ ref: ref, type: "checkbox", id: id, className: inputClassName, name: name, "aria-label": props["aria-label"], "aria-describedby": descriptionVisible
77
77
  ? descriptionIdentifier
78
- : props["aria-describedby"], "aria-invalid": isInvalid ? true : undefined, "aria-required": props["aria-required"], checked: checked, value: value, defaultChecked: defaultChecked, disabled: disabled, onChange: handleChange, onFocus: onFocus, onBlur: onBlur }, dataAttrs)),
78
+ : props["aria-describedby"], "aria-invalid": isInvalid ? true : undefined, "aria-required": props["aria-required"], checked: checked, value: value, defaultChecked: defaultChecked, disabled: disabled, onChange: handleChange, onFocus: onFocus, onBlur: onBlur, onClick: onClick, onMouseDown: onMouseDown, onMouseUp: onMouseUp, onPointerDown: onPointerDown, onPointerUp: onPointerUp }, dataAttrs)),
79
79
  React__default.createElement("span", { className: styles.checkBox },
80
80
  React__default.createElement(Icon, { name: iconName, color: "surface" }))),
81
81
  labelContent && React__default.createElement("span", { className: styles.label }, labelContent)),
@@ -1,7 +1,7 @@
1
1
  import React__default, { cloneElement, Component, createRef, useRef, useCallback, useEffect, createElement, forwardRef, isValidElement, useState } from 'react';
2
2
  import classnames from 'classnames';
3
3
  import { c as clsx } from './clsx-es.js';
4
- import { u as useFloating, b as autoUpdate, f as flip, o as offset, a as arrow, t as FloatingArrow } from './floating-ui.react-es.js';
4
+ import { u as useFloating, b as autoUpdate, f as flip, o as offset, a as arrow, v as FloatingArrow } from './floating-ui.react-es.js';
5
5
  import ReactDOM__default from 'react-dom';
6
6
  import { useRefocusOnActivator } from '@jobber/hooks';
7
7
  import { T as Typography } from './Typography-es.js';
@@ -1,5 +1,5 @@
1
1
  import type { CommonFormFieldProps, FormFieldProps } from "../FormField";
2
- import type { FocusEvents, HTMLInputBaseProps, KeyboardEvents, RebuiltInputCommonProps } from "../sharedHelpers/types";
2
+ import type { FocusEvents, HTMLInputBaseProps, KeyboardEvents, MouseEvents, RebuiltInputCommonProps } from "../sharedHelpers/types";
3
3
  export type InputEmailLegacyProps = CommonFormFieldProps & Pick<FormFieldProps, "maxLength" | "readonly" | "validations" | "defaultValue">;
4
4
  export declare const validationMessage = "Please enter a valid email";
5
5
  export type InputEmailVersion = 1 | 2 | undefined;
@@ -7,7 +7,7 @@ export type InputEmailVersion = 1 | 2 | undefined;
7
7
  * Experimental version 2 of the InputEmail component.
8
8
  * Do not use unless you have talked with Atlantis first.
9
9
  */
10
- export interface InputEmailRebuiltProps extends HTMLInputBaseProps, FocusEvents<HTMLInputElement>, KeyboardEvents<HTMLInputElement>, RebuiltInputCommonProps {
10
+ export interface InputEmailRebuiltProps extends HTMLInputBaseProps, FocusEvents<HTMLInputElement>, KeyboardEvents<HTMLInputElement>, MouseEvents<HTMLInputElement>, RebuiltInputCommonProps {
11
11
  /**
12
12
  * The current value of the input.
13
13
  */
@@ -1,13 +1,18 @@
1
- import type { ChangeEvent, FocusEvent, KeyboardEvent } from "react";
1
+ import type { ChangeEvent, FocusEvent, KeyboardEvent, MouseEvent, PointerEvent } from "react";
2
2
  import type { InputEmailRebuiltProps } from "../InputEmail.types";
3
- export interface UseInputEmailActionsProps extends Pick<InputEmailRebuiltProps, "onChange" | "onEnter" | "onFocus" | "onBlur" | "onKeyDown" | "onKeyUp"> {
3
+ export interface UseInputEmailActionsProps extends Pick<InputEmailRebuiltProps, "onChange" | "onEnter" | "onFocus" | "onBlur" | "onKeyDown" | "onKeyUp" | "onClick" | "onMouseDown" | "onMouseUp" | "onPointerDown" | "onPointerUp"> {
4
4
  inputRef?: React.RefObject<HTMLInputElement>;
5
5
  }
6
- export declare function useInputEmailActions({ onChange, inputRef, onEnter, onFocus, onBlur, onKeyDown, onKeyUp, }: UseInputEmailActionsProps): {
6
+ export declare function useInputEmailActions({ onChange, inputRef, onEnter, onFocus, onBlur, onKeyDown, onKeyUp, onClick, onMouseDown, onMouseUp, onPointerDown, onPointerUp, }: UseInputEmailActionsProps): {
7
7
  handleClear: () => void;
8
8
  handleChange: (event: ChangeEvent<HTMLInputElement>) => void;
9
9
  handleKeyDown: (event: KeyboardEvent<HTMLInputElement>) => void;
10
10
  handleKeyUp: (event: KeyboardEvent<HTMLInputElement>) => void;
11
11
  handleFocus: (event: FocusEvent<HTMLInputElement>) => void;
12
12
  handleBlur: (event: FocusEvent<HTMLInputElement>) => void;
13
+ handleClick: (event: MouseEvent<HTMLInputElement>) => void;
14
+ handleMouseDown: (event: MouseEvent<HTMLInputElement>) => void;
15
+ handleMouseUp: (event: MouseEvent<HTMLInputElement>) => void;
16
+ handlePointerDown: (event: PointerEvent<HTMLInputElement>) => void;
17
+ handlePointerUp: (event: PointerEvent<HTMLInputElement>) => void;
13
18
  };