@jobber/components 8.19.0 → 8.20.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.
@@ -1,2 +1,2 @@
1
1
  export { InputNumberExperimental } from "./InputNumberExperimental";
2
- export type { InputNumberExperimentalAffix, InputNumberExperimentalProps, InputNumberExperimentalRef, } from "./types";
2
+ export type { InputNumberExperimentalAffix, InputNumberExperimentalAutoComplete, InputNumberExperimentalDescriptionProps, InputNumberExperimentalErrorProps, InputNumberExperimentalFooterProps, InputNumberExperimentalGroupProps, InputNumberExperimentalInputProps, InputNumberExperimentalLabelProps, InputNumberExperimentalLoadingProps, InputNumberExperimentalProps, InputNumberExperimentalRef, InputNumberExperimentalStepButtonProps, InputNumberExperimentalStepperProps, InputNumberExperimentalSuffix, InputNumberExperimentalSuffixProp, InputNumberExperimentalWrapperProps, } from "./types";
@@ -1,16 +1,16 @@
1
1
  export { I as InputNumberExperimental } from '../../InputNumberExperimental-es.js';
2
+ import '../../tslib.es6-es.js';
2
3
  import 'react';
3
4
  import 'classnames';
4
5
  import '../../ActivityIndicator-es.js';
5
6
  import '../../Button-es.js';
6
- import '../../tslib.es6-es.js';
7
7
  import 'react-router-dom';
8
8
  import '../../Icon-es.js';
9
9
  import '@jobber/design';
10
10
  import '../../Typography-es.js';
11
- import '../../clamp-es.js';
12
11
  import '../../useRenderElement-es.js';
13
- import 'react-dom';
12
+ import '../../clamp-es.js';
14
13
  import '../../floating-ui.utils.dom-es.js';
14
+ import 'react-dom';
15
15
  import '../../NumberFieldInput-es.js';
16
16
  import 'react/jsx-runtime';
@@ -9,16 +9,11 @@ export interface InputNumberExperimentalStyleProps {
9
9
  readonly style?: CSSProperties;
10
10
  }
11
11
  /**
12
- * Affix descriptor used by the `prefix` and `suffix` props on
13
- * `<InputNumberExperimental>` for the default (drop-in) render.
14
- *
15
- * At least one of `label` or `icon` should be provided. Icons render outside
16
- * the input area (in the field's outer padding zone), labels render inside the
17
- * input wrapper directly adjacent to the typed value (matching V1).
18
- *
19
- * `onClick` and `ariaLabel` are not allowed in this shape; if you need a
20
- * clickable action, use `InputNumberExperimentalSuffix` instead (only valid
21
- * for `suffix`).
12
+ * Affix descriptor for the top-level `prefix`/`suffix` props. At least one of
13
+ * `label` or `icon` should be provided. Icons render outside the input area (in
14
+ * the field's outer padding zone); labels render inside the input area next to
15
+ * the typed value. `onClick`/`ariaLabel` are not allowed here; use
16
+ * `InputNumberExperimentalSuffix` for a clickable action.
22
17
  */
23
18
  export interface InputNumberExperimentalAffix {
24
19
  readonly label?: string;
@@ -28,8 +23,7 @@ export interface InputNumberExperimentalAffix {
28
23
  }
29
24
  /**
30
25
  * Suffix variant that turns the suffix icon into a clickable action button.
31
- * Mirrors V1's `Suffix` type. The `?: never` fields make this a discriminated
32
- * union with `InputNumberExperimentalAffix`.
26
+ * The `?: never` fields make this a discriminated union with the plain affix.
33
27
  */
34
28
  export interface InputNumberExperimentalSuffix {
35
29
  readonly label?: string;
@@ -37,46 +31,35 @@ export interface InputNumberExperimentalSuffix {
37
31
  readonly ariaLabel: string;
38
32
  onClick(): void;
39
33
  }
40
- /**
41
- * Tagged union for the `suffix` prop. Discriminated by whether `onClick` is set:
42
- * - omit `onClick` → behaves like `InputNumberExperimentalAffix`
43
- * - set `onClick` → requires `icon` and `ariaLabel` (clickable action button)
44
- */
45
34
  export type InputNumberExperimentalSuffixProp = InputNumberExperimentalAffix | InputNumberExperimentalSuffix;
46
- export type InputNumberExperimentalAutocomplete = boolean | "on" | "off" | "one-time-code" | "address-line1" | "address-line2";
35
+ export type InputNumberExperimentalAutoComplete = boolean | "on" | "off" | "one-time-code" | "address-line1" | "address-line2";
47
36
  export type InputNumberExperimentalSize = "small" | "default" | "large";
48
- export interface InputNumberExperimentalProps {
37
+ /**
38
+ * Field configuration shared by `<InputNumberExperimental.Wrapper>` (the
39
+ * composition entry point) and the top-level `<InputNumberExperimental>` sugar.
40
+ * The Wrapper owns the field state and exposes it to the parts via context.
41
+ */
42
+ export interface InputNumberExperimentalWrapperProps extends InputNumberExperimentalStyleProps {
49
43
  readonly align?: "center" | "right";
50
- readonly autocomplete?: InputNumberExperimentalAutocomplete;
51
- /**
52
- * Optional compound children. When children include any
53
- * `<InputNumberExperimental.Input | .Affix | .Stepper>`, the component renders
54
- * them inside `NumberField.Root`/`NumberField.Group` and ignores `prefix`/`suffix`
55
- * props. Without compound children, the component renders the default
56
- * composition (input + animated mini-label + optional prefix/suffix + stepper).
57
- */
44
+ readonly autoComplete?: InputNumberExperimentalAutoComplete;
45
+ /** Composed parts (`.Group`, `.Footer`, and the parts within them). */
58
46
  readonly children?: ReactNode;
59
- readonly description?: ReactNode;
60
47
  readonly disabled?: boolean;
61
- /** When set, renders a styled error message below the field. */
62
- readonly error?: string;
63
48
  /**
64
- * Number formatting for the displayed value, forwarded verbatim to Base UI
65
- * `NumberField`'s native `format`. Accepts standard `Intl.NumberFormatOptions`
66
- * (e.g. `{ style: "currency", currency: "USD" }` or `{ maximumFractionDigits: 2 }`).
67
- * When omitted, typed decimals are preserved without premature rounding.
49
+ * Number formatting for the displayed value, forwarded to Base UI
50
+ * `NumberField`'s native `format`. When omitted, typed decimals are preserved.
68
51
  */
69
52
  readonly format?: Intl.NumberFormatOptions;
70
53
  readonly id?: string;
71
54
  /** Shrink-wrap the field to its content (auto width). */
72
55
  readonly inline?: boolean;
56
+ readonly inputMode?: "numeric" | "decimal";
73
57
  /** Style the error border without showing an error message. */
74
58
  readonly invalid?: boolean;
75
- readonly keyboard?: "numeric" | "decimal";
76
59
  /**
77
- * Shows a non-blocking loading indicator in the stepper's slot to signal
78
- * background activity. The field stays editable while loading (use `readonly`
79
- * or `disabled` to lock it); the stepper is hidden so the two never overlap.
60
+ * Shows a non-blocking loading indicator in the stepper's slot. The field
61
+ * stays editable (use `readOnly`/`disabled` to lock it); the stepper hides so
62
+ * the two never overlap.
80
63
  */
81
64
  readonly loading?: boolean;
82
65
  readonly max?: number;
@@ -86,29 +69,22 @@ export interface InputNumberExperimentalProps {
86
69
  readonly onBlur?: (event?: FocusEvent<HTMLInputElement>) => void;
87
70
  /**
88
71
  * Fires when the user commits a value (blur, Enter, stepper, arrow step).
89
- * Use for side effects that shouldn't run per keystroke. Emits `undefined`
90
- * when committed empty. For per-keystroke updates, see `onValueChange`.
72
+ * Emits `null` when committed empty. For per-keystroke updates use
73
+ * `onValueChange`.
91
74
  */
92
- readonly onChange?: (newValue: number | undefined) => void;
75
+ readonly onValueCommitted?: (newValue: number | null) => void;
93
76
  /**
94
77
  * Fires on every parsed value change (typing, paste, stepper, arrow step).
95
- * Emits `undefined` when the field is empty. Prefer `onChange` for side
96
- * effects; reach for this only when UI must reflect in-progress input
97
- * (e.g. a live-updating subtotal).
98
- */
99
- readonly onValueChange?: (newValue: number | undefined) => void;
100
- /**
101
- * Derived from `onKeyDown` — fires when the user presses Enter without
102
- * any modifier keys (Shift/Ctrl/Meta).
78
+ * Emits `null` when the field is empty.
103
79
  */
80
+ readonly onValueChange?: (newValue: number | null) => void;
81
+ /** Fires when Enter is pressed without modifier keys (Shift/Ctrl/Meta). */
104
82
  readonly onEnter?: (event: KeyboardEvent<HTMLInputElement>) => void;
105
83
  readonly onFocus?: (event?: FocusEvent<HTMLInputElement>) => void;
106
84
  readonly onKeyDown?: (event: KeyboardEvent<HTMLInputElement>) => void;
107
85
  readonly onKeyUp?: (event: KeyboardEvent<HTMLInputElement>) => void;
108
- readonly placeholder?: string;
109
- readonly prefix?: InputNumberExperimentalAffix;
110
- readonly readonly?: boolean;
111
- /** Default `true`. When `false`, the floating mini-label is hidden. */
86
+ readonly readOnly?: boolean;
87
+ /** Default `true`. When `false`, the floating label is hidden. */
112
88
  readonly showMiniLabel?: boolean;
113
89
  readonly size?: InputNumberExperimentalSize;
114
90
  /**
@@ -116,27 +92,67 @@ export interface InputNumberExperimentalProps {
116
92
  * Default `1`.
117
93
  */
118
94
  readonly step?: number;
119
- readonly suffix?: InputNumberExperimentalSuffixProp;
120
95
  /**
121
- * Value of the field. Controlled-only pair with `onChange` and drive from
122
- * outside state.
123
- *
124
- * - `number` — controlled with that value.
125
- * - `null` (or `undefined`) — controlled with an empty value. Useful for
126
- * controlled forms whose form-state default is empty/cleared.
96
+ * Controlled value. `number` sets the value; `null` (or `undefined`) is an
97
+ * empty field.
127
98
  */
128
99
  readonly value?: number | null;
129
100
  }
101
+ /**
102
+ * Props for the top-level `<InputNumberExperimental>` sugar: the field
103
+ * configuration plus the convenience content props (`label`, `description`,
104
+ * `error`, `prefix`, `suffix`). It composes the parts internally — to compose
105
+ * the field yourself, use `<InputNumberExperimental.Wrapper>` and the parts.
106
+ */
107
+ export interface InputNumberExperimentalProps extends Omit<InputNumberExperimentalWrapperProps, "children"> {
108
+ /** Floating field label. */
109
+ readonly label?: string;
110
+ readonly description?: ReactNode;
111
+ /** Renders a styled error message below the field. */
112
+ readonly error?: string;
113
+ readonly prefix?: InputNumberExperimentalAffix;
114
+ readonly suffix?: InputNumberExperimentalSuffixProp;
115
+ }
130
116
  export interface InputNumberExperimentalRef {
131
117
  blur(): void;
132
118
  focus(): void;
133
119
  }
120
+ export interface InputNumberExperimentalGroupProps extends InputNumberExperimentalStyleProps {
121
+ readonly children?: ReactNode;
122
+ }
123
+ export interface InputNumberExperimentalFooterProps extends InputNumberExperimentalStyleProps {
124
+ readonly children?: ReactNode;
125
+ }
134
126
  export interface InputNumberExperimentalInputProps extends InputNumberExperimentalStyleProps {
135
- /**
136
- * Overrides the floating mini-label text. Falls back to the root component's
137
- * `placeholder` prop when omitted.
138
- */
139
- readonly placeholder?: string;
127
+ /** Content rendered inside the input area (e.g. `.Label`, `.Stepper`). */
128
+ readonly children?: ReactNode;
129
+ }
130
+ export interface InputNumberExperimentalLabelProps extends InputNumberExperimentalStyleProps {
131
+ readonly children?: ReactNode;
132
+ }
133
+ export interface InputNumberExperimentalDescriptionProps extends InputNumberExperimentalStyleProps {
134
+ readonly children?: ReactNode;
135
+ }
136
+ export interface InputNumberExperimentalErrorProps extends InputNumberExperimentalStyleProps {
137
+ readonly children?: ReactNode;
138
+ }
139
+ export interface InputNumberExperimentalLoadingProps extends InputNumberExperimentalStyleProps {
140
+ /** Indicator content. Falls back to the default `ActivityIndicator`. */
141
+ readonly children?: ReactNode;
142
+ }
143
+ export interface InputNumberExperimentalStepperProps extends InputNumberExperimentalStyleProps {
144
+ /** Stepper buttons. Falls back to the default increment/decrement pair. */
145
+ readonly children?: ReactNode;
146
+ /** Accessible label for the increment button. Defaults to `Increase value`. */
147
+ readonly incrementLabel?: string;
148
+ /** Accessible label for the decrement button. Defaults to `Decrease value`. */
149
+ readonly decrementLabel?: string;
150
+ }
151
+ export interface InputNumberExperimentalStepButtonProps extends InputNumberExperimentalStyleProps {
152
+ /** Icon content. Falls back to the default Atlantis stepper icon. */
153
+ readonly children?: ReactNode;
154
+ /** Accessible label for the button. */
155
+ readonly ariaLabel?: string;
140
156
  }
141
157
  export interface InputNumberExperimentalAffixCompound extends InputNumberExperimentalStyleProps {
142
158
  readonly variation: "prefix" | "suffix";
@@ -157,24 +173,8 @@ export interface InputNumberExperimentalAffixCompoundClickable extends InputNumb
157
173
  readonly children?: ReactNode;
158
174
  }
159
175
  /**
160
- * Compound `.Affix` props. Discriminated by whether `onClick` is set:
161
- * - omit `onClick` -> behaves like `InputNumberExperimentalAffixCompound`
176
+ * `.Affix` props. Discriminated by whether `onClick` is set:
177
+ * - omit `onClick` -> behaves like a plain affix
162
178
  * - set `onClick` -> requires `icon` and `ariaLabel` (clickable icon-button)
163
- *
164
- * Unlike the default `prefix`/`suffix` API, the compound version does not
165
- * structurally split label and icon; compose two `.Affix` elements or fall
166
- * back to the default API when you need that.
167
179
  */
168
180
  export type InputNumberExperimentalAffixCompoundProps = InputNumberExperimentalAffixCompound | InputNumberExperimentalAffixCompoundClickable;
169
- export interface InputNumberExperimentalStepperCompoundProps extends InputNumberExperimentalStyleProps {
170
- /**
171
- * Override the increment button's accessible label.
172
- * Defaults to `Increase ${placeholder ?? "value"}`.
173
- */
174
- readonly incrementLabel?: string;
175
- /**
176
- * Override the decrement button's accessible label.
177
- * Defaults to `Decrease ${placeholder ?? "value"}`.
178
- */
179
- readonly decrementLabel?: string;
180
- }
@@ -16,8 +16,8 @@ require('@jobber/design');
16
16
  require('../Typography-cjs.js');
17
17
  require('../ScrollAreaViewport-cjs.js');
18
18
  require('../clamp-cjs.js');
19
- require('react-dom');
20
19
  require('../floating-ui.utils.dom-cjs.js');
20
+ require('react-dom');
21
21
  require('react/jsx-runtime');
22
22
  require('../index.esm-cjs.js');
23
23
  require('../Text-cjs.js');
@@ -14,8 +14,8 @@ import '@jobber/design';
14
14
  import '../Typography-es.js';
15
15
  import '../ScrollAreaViewport-es.js';
16
16
  import '../clamp-es.js';
17
- import 'react-dom';
18
17
  import '../floating-ui.utils.dom-es.js';
18
+ import 'react-dom';
19
19
  import 'react/jsx-runtime';
20
20
  import '../index.esm-es.js';
21
21
  import '../Text-es.js';
@@ -892,7 +892,7 @@ function AriaCombobox(props) {
892
892
  }
893
893
  itemValue = valuesRef.current[activeIndex];
894
894
  }
895
- const targetEl = ScrollAreaViewport.getTarget(event);
895
+ const targetEl = clamp.getTarget(event);
896
896
  const overrideEvent = selectionEventRef.current ?? event;
897
897
  selectionEventRef.current = null;
898
898
  const eventDetails = clamp.createChangeEventDetails(clamp.itemPress, overrideEvent);
@@ -1202,8 +1202,8 @@ function AriaCombobox(props) {
1202
1202
  // Without a popup, let the Escape key bubble the event up to other popups' handlers.
1203
1203
  bubbles: inline ? true : undefined,
1204
1204
  outsidePress(event) {
1205
- const target = ScrollAreaViewport.getTarget(event);
1206
- return !ScrollAreaViewport.contains(triggerElement, target) && !ScrollAreaViewport.contains(clearRef.current, target) && !ScrollAreaViewport.contains(chipsContainerRef.current, target) && !ScrollAreaViewport.contains(inputGroupElement, target);
1205
+ const target = clamp.getTarget(event);
1206
+ return !clamp.contains(triggerElement, target) && !clamp.contains(clearRef.current, target) && !clamp.contains(chipsContainerRef.current, target) && !clamp.contains(inputGroupElement, target);
1207
1207
  }
1208
1208
  });
1209
1209
  const listNavigation = MenuSubmenuTrigger.useListNavigation(floatingRootContext, {
@@ -1671,7 +1671,7 @@ const ComboboxTrigger = /*#__PURE__*/React__namespace.forwardRef(function Combob
1671
1671
  },
1672
1672
  onBlur(event) {
1673
1673
  // If focus is moving into the popup, don't count it as a blur.
1674
- if (ScrollAreaViewport.contains(positionerElement, event.relatedTarget)) {
1674
+ if (clamp.contains(positionerElement, event.relatedTarget)) {
1675
1675
  return;
1676
1676
  }
1677
1677
  setTouched(true);
@@ -1705,10 +1705,10 @@ const ComboboxTrigger = /*#__PURE__*/React__namespace.forwardRef(function Combob
1705
1705
  if (!triggerElement) {
1706
1706
  return;
1707
1707
  }
1708
- const mouseUpTarget = ScrollAreaViewport.getTarget(mouseEvent);
1708
+ const mouseUpTarget = clamp.getTarget(mouseEvent);
1709
1709
  const positioner = store.state.positionerElement;
1710
1710
  const list = store.state.listElement;
1711
- if (ScrollAreaViewport.contains(triggerElement, mouseUpTarget) || ScrollAreaViewport.contains(positioner, mouseUpTarget) || ScrollAreaViewport.contains(list, mouseUpTarget) || mouseUpTarget === triggerElement) {
1711
+ if (clamp.contains(triggerElement, mouseUpTarget) || clamp.contains(positioner, mouseUpTarget) || clamp.contains(list, mouseUpTarget) || mouseUpTarget === triggerElement) {
1712
1712
  return;
1713
1713
  }
1714
1714
  const bounds = MenuSubmenuTrigger.getPseudoElementBounds(triggerElement);
@@ -2722,8 +2722,8 @@ const ComboboxPopup = /*#__PURE__*/React__namespace.forwardRef(function Combobox
2722
2722
  role: inputInsidePopup ? 'dialog' : 'presentation',
2723
2723
  tabIndex: -1,
2724
2724
  onFocus(event) {
2725
- const target = ScrollAreaViewport.getTarget(event.nativeEvent);
2726
- if (openMethod !== 'touch' && (ScrollAreaViewport.contains(store.state.listElement, target) || target === event.currentTarget)) {
2725
+ const target = clamp.getTarget(event.nativeEvent);
2726
+ if (openMethod !== 'touch' && (clamp.contains(store.state.listElement, target) || target === event.currentTarget)) {
2727
2727
  store.state.inputRef.current?.focus();
2728
2728
  }
2729
2729
  }
@@ -3852,7 +3852,7 @@ function useSwipeDismiss(options) {
3852
3852
  function getTargetAtPoint(position, nativeEvent) {
3853
3853
  const doc = clamp.ownerDocument(elementRef.current);
3854
3854
  const elementAtPoint = getElementAtPoint(doc, position.x, position.y);
3855
- const target = elementAtPoint ?? ScrollAreaViewport.getTarget(nativeEvent);
3855
+ const target = elementAtPoint ?? clamp.getTarget(nativeEvent);
3856
3856
  return target;
3857
3857
  }
3858
3858
  function findGestureScrollableTouchTarget(target, root) {
@@ -4043,7 +4043,7 @@ function useSwipeDismiss(options) {
4043
4043
  if (!enabled || !isSwipingRef.current) {
4044
4044
  return;
4045
4045
  }
4046
- const target = ScrollAreaViewport.getTarget(event.nativeEvent);
4046
+ const target = clamp.getTarget(event.nativeEvent);
4047
4047
  if (isTouchLikeEvent(event) && !swipeFromScrollableRef.current) {
4048
4048
  const boundaryElement = event.currentTarget;
4049
4049
  if (findGestureScrollableTouchTarget(target, boundaryElement)) {
@@ -4244,7 +4244,7 @@ function useSwipeDismiss(options) {
4244
4244
  const doc = clamp.ownerDocument(element);
4245
4245
  const body = doc.body;
4246
4246
  const scrollTarget = body ? findGestureScrollableTouchTarget(target, body) : null;
4247
- if (scrollTarget && (ScrollAreaViewport.contains(element, scrollTarget) || ScrollAreaViewport.contains(scrollTarget, element))) {
4247
+ if (scrollTarget && (clamp.contains(element, scrollTarget) || clamp.contains(scrollTarget, element))) {
4248
4248
  const deltaX = currentPos.x - pendingStartPos.x;
4249
4249
  const deltaY = currentPos.y - pendingStartPos.y;
4250
4250
  const canSwipeFromEdge = canSwipeFromScrollEdgeOnPendingMove(scrollTarget, deltaX, deltaY);
@@ -5108,7 +5108,7 @@ const DrawerViewport = /*#__PURE__*/React__namespace.forwardRef(function DrawerV
5108
5108
  }
5109
5109
  const anchorElement = floatingUi_utils_dom.isElement(selection.anchorNode) ? selection.anchorNode : selection.anchorNode?.parentElement;
5110
5110
  const focusElement = floatingUi_utils_dom.isElement(selection.focusNode) ? selection.focusNode : selection.focusNode?.parentElement;
5111
- if (!ScrollAreaViewport.contains(popupElement, anchorElement) && !ScrollAreaViewport.contains(popupElement, focusElement)) {
5111
+ if (!clamp.contains(popupElement, anchorElement) && !clamp.contains(popupElement, focusElement)) {
5112
5112
  return;
5113
5113
  }
5114
5114
  selection.removeAllRanges();
@@ -5133,7 +5133,7 @@ const DrawerViewport = /*#__PURE__*/React__namespace.forwardRef(function DrawerV
5133
5133
  }
5134
5134
  const doc = popupElement.ownerDocument;
5135
5135
  const elementAtPoint = getElementAtPoint(doc, position.x, position.y);
5136
- if (!elementAtPoint || !ScrollAreaViewport.contains(popupElement, elementAtPoint)) {
5136
+ if (!elementAtPoint || !clamp.contains(popupElement, elementAtPoint)) {
5137
5137
  return false;
5138
5138
  }
5139
5139
  const nativeEvent = details.nativeEvent;
@@ -5624,7 +5624,7 @@ const DrawerViewport = /*#__PURE__*/React__namespace.forwardRef(function DrawerV
5624
5624
  const target = floatingUi_utils_dom.isElement(event.target) ? event.target : null;
5625
5625
  let scrollTarget = null;
5626
5626
  let hasCrossAxisScrollableContent = false;
5627
- if (rootElement && target && ScrollAreaViewport.contains(rootElement, target)) {
5627
+ if (rootElement && target && clamp.contains(rootElement, target)) {
5628
5628
  scrollTarget = findScrollableTouchTarget(target, rootElement, scrollAxis);
5629
5629
  hasCrossAxisScrollableContent = findScrollableTouchTarget(target, rootElement, crossScrollAxis) != null;
5630
5630
  }
@@ -5721,11 +5721,11 @@ function isTextSelectionControl(target) {
5721
5721
  function hasExpandedSelectionWithinTarget(selection, target) {
5722
5722
  const anchorElement = floatingUi_utils_dom.isElement(selection.anchorNode) ? selection.anchorNode : selection.anchorNode?.parentElement;
5723
5723
  const focusElement = floatingUi_utils_dom.isElement(selection.focusNode) ? selection.focusNode : selection.focusNode?.parentElement;
5724
- return selection.containsNode(target, true) || ScrollAreaViewport.contains(target, anchorElement) || ScrollAreaViewport.contains(target, focusElement);
5724
+ return selection.containsNode(target, true) || clamp.contains(target, anchorElement) || clamp.contains(target, focusElement);
5725
5725
  }
5726
5726
  function shouldIgnoreSwipeForTextSelection(doc, rootElement) {
5727
5727
  const activeElement = doc.activeElement;
5728
- const activeElementWithinRoot = Boolean(activeElement && ScrollAreaViewport.contains(rootElement, activeElement));
5728
+ const activeElementWithinRoot = Boolean(activeElement && clamp.contains(rootElement, activeElement));
5729
5729
  if (activeElementWithinRoot && isTextSelectionControl(activeElement)) {
5730
5730
  const {
5731
5731
  selectionStart,
@@ -7059,84 +7059,6 @@ function ComboboxRoot(props) {
7059
7059
  });
7060
7060
  }
7061
7061
 
7062
- function useRegisteredLabelId(idProp, setLabelId) {
7063
- const id = clamp.useBaseUiId(idProp);
7064
- clamp.useIsoLayoutEffect(() => {
7065
- setLabelId(id);
7066
- return () => {
7067
- setLabelId(undefined);
7068
- };
7069
- }, [id, setLabelId]);
7070
- return id;
7071
- }
7072
-
7073
- function useLabel(params = {}) {
7074
- const {
7075
- id: idProp,
7076
- fallbackControlId,
7077
- native = false,
7078
- setLabelId: setLabelIdProp,
7079
- focusControl: focusControlProp
7080
- } = params;
7081
- const {
7082
- controlId: contextControlId,
7083
- setLabelId: setContextLabelId
7084
- } = NumberFieldInput.useLabelableContext();
7085
- const syncLabelId = clamp.useStableCallback(nextLabelId => {
7086
- setContextLabelId(nextLabelId);
7087
- setLabelIdProp?.(nextLabelId);
7088
- });
7089
- const id = useRegisteredLabelId(idProp, syncLabelId);
7090
- const resolvedControlId = contextControlId ?? fallbackControlId;
7091
- function focusControl(event) {
7092
- if (focusControlProp) {
7093
- focusControlProp(event, resolvedControlId);
7094
- return;
7095
- }
7096
- if (!resolvedControlId) {
7097
- return;
7098
- }
7099
- const controlElement = clamp.ownerDocument(event.currentTarget).getElementById(resolvedControlId);
7100
- if (floatingUi_utils_dom.isHTMLElement(controlElement)) {
7101
- focusElementWithVisible(controlElement);
7102
- }
7103
- }
7104
- function handleInteraction(event) {
7105
- const target = ScrollAreaViewport.getTarget(event.nativeEvent);
7106
- if (target?.closest('button,input,select,textarea')) {
7107
- return;
7108
- }
7109
-
7110
- // Prevent text selection when double clicking label.
7111
- if (!event.defaultPrevented && event.detail > 1) {
7112
- event.preventDefault();
7113
- }
7114
- if (native) {
7115
- return;
7116
- }
7117
- focusControl(event);
7118
- }
7119
- return native ? {
7120
- id,
7121
- htmlFor: resolvedControlId ?? undefined,
7122
- onMouseDown: handleInteraction
7123
- } : {
7124
- id,
7125
- onClick: handleInteraction,
7126
- onPointerDown(event) {
7127
- event.preventDefault();
7128
- }
7129
- };
7130
- }
7131
- function focusElementWithVisible(element) {
7132
- element.focus({
7133
- // Available from Chrome 144+ (January 2026).
7134
- // Safari and Firefox already support it.
7135
- // @ts-expect-error not available in types yet
7136
- focusVisible: true
7137
- });
7138
- }
7139
-
7140
7062
  /**
7141
7063
  * An accessible label that is automatically associated with the combobox trigger.
7142
7064
  * Renders a `<div>` element.
@@ -7171,7 +7093,7 @@ const ComboboxLabel = /*#__PURE__*/React__namespace.forwardRef(function Combobox
7171
7093
  clamp.error(`${message}${ownerStackMessage}`);
7172
7094
  }, [inputElement, inputInsidePopup]);
7173
7095
  }
7174
- const labelProps = useLabel({
7096
+ const labelProps = NumberFieldInput.useLabel({
7175
7097
  id: defaultLabelId,
7176
7098
  fallbackControlId: localControlId,
7177
7099
  setLabelId(nextLabelId) {
@@ -1,9 +1,9 @@
1
1
  import * as React from 'react';
2
- import { h as useControlled, a as useStableCallback, i as useValueAsRef, c as useTransitionStatus, d as useIsoLayoutEffect, r as createGenericEventDetails, o as inputClear, m as inputChange, k as createChangeEventDetails, L as focusOut, Q as outsidePress, P as itemPress, e as useOpenChangeComplete, z as useValueChanged, n as none, a5 as inputPress, v as visuallyHiddenInput, l as visuallyHidden, a6 as pointer, b as useTimeout, y as useButton, s as ownerDocument, A as stopEvent, K as listNavigation, O as closePress, u as useBaseUiId, a3 as isAndroid, a7 as isFirefox, I as escapeKey, a8 as clearPress, t as transitionStatusMapping, U as imperativeAction, g as clamp, a9 as swipe, Y as useAnimationFrame, a1 as TransitionStatusDataAttributes, J as triggerHover, R as useAnimationsFinished, aa as SafeReact, ab as error, ac as chipRemovePress, ad as scrub, _ as isWebKit } from '../clamp-es.js';
3
- import { Z as createSelector, aq as useStore, a4 as useOpenInteractionType, h as getTarget, ag as useClick, a9 as useDismiss, ac as useInteractions, a2 as useOnFirstRender, ar as Store, n as contains, al as pressableTriggerOpenStateMapping, J as useDirection, ap as triggerOpenStateMapping, Q as FloatingPortal, M as popupStateMapping, a7 as useScrollLock, U as InternalBackdrop, V as inertValue, X as DROPDOWN_COLLISION_AVOIDANCE, O as FloatingFocusManager, L as useDialogRootContext, as as useDrawerProviderContext, at as DrawerBackdropCssVars, au as DrawerPopupCssVars, av as DrawerProviderContext, aw as CommonPopupDataAttributes, ax as useDialogPortalContext, ay as DialogStore, az as useDrawerRootContext, ao as useTriggerRegistration, aA as DrawerPopupDataAttributes, aB as useDrawerSnapPoints, aC as DrawerViewportContext, aD as BASE_UI_SWIPE_IGNORE_SELECTOR, aE as DRAWER_CONTENT_ATTRIBUTE, c as DrawerBackdrop, f as DrawerContent, d as DrawerPopup, b as DrawerPortal, a as DrawerRoot, D as DrawerTrigger, aF as useScrollAreaRootContext, aG as getOffset, aH as ScrollAreaRootCssVars, aI as scrollAreaStateAttributesMapping, aJ as useScrollAreaViewportContext, S as ScrollAreaRoot, e as ScrollAreaViewport } from '../ScrollAreaViewport-es.js';
2
+ import { j as useControlled, a as useStableCallback, k as useValueAsRef, c as useTransitionStatus, d as useIsoLayoutEffect, x as createGenericEventDetails, r as inputClear, q as inputChange, m as createChangeEventDetails, _ as focusOut, a4 as outsidePress, g as getTarget, a3 as itemPress, f as useOpenChangeComplete, B as useValueChanged, n as none, ao as inputPress, v as visuallyHiddenInput, p as visuallyHidden, L as contains, ap as pointer, b as useTimeout, A as useButton, o as ownerDocument, C as stopEvent, W as listNavigation, a2 as closePress, u as useBaseUiId, am as isAndroid, aq as isFirefox, P as escapeKey, ar as clearPress, t as transitionStatusMapping, a7 as imperativeAction, i as clamp, as as swipe, aa as useAnimationFrame, ak as TransitionStatusDataAttributes, R as triggerHover, a5 as useAnimationsFinished, S as SafeReact, e as error, at as chipRemovePress, au as scrub, ac as isWebKit } from '../clamp-es.js';
3
+ import { K as createSelector, ad as useStore, T as useOpenInteractionType, a3 as useClick, Y as useDismiss, $ as useInteractions, O as useOnFirstRender, ae as Store, a8 as pressableTriggerOpenStateMapping, v as useDirection, ac as triggerOpenStateMapping, A as FloatingPortal, y as popupStateMapping, W as useScrollLock, I as InternalBackdrop, E as inertValue, H as DROPDOWN_COLLISION_AVOIDANCE, z as FloatingFocusManager, x as useDialogRootContext, af as useDrawerProviderContext, ag as DrawerBackdropCssVars, ah as DrawerPopupCssVars, ai as DrawerProviderContext, aj as CommonPopupDataAttributes, ak as useDialogPortalContext, al as DialogStore, am as useDrawerRootContext, ab as useTriggerRegistration, an as DrawerPopupDataAttributes, ao as useDrawerSnapPoints, ap as DrawerViewportContext, aq as BASE_UI_SWIPE_IGNORE_SELECTOR, ar as DRAWER_CONTENT_ATTRIBUTE, c as DrawerBackdrop, f as DrawerContent, d as DrawerPopup, b as DrawerPortal, a as DrawerRoot, D as DrawerTrigger, as as useScrollAreaRootContext, at as getOffset, au as ScrollAreaRootCssVars, av as scrollAreaStateAttributesMapping, aw as useScrollAreaViewportContext, S as ScrollAreaRoot, e as ScrollAreaViewport } from '../ScrollAreaViewport-es.js';
4
4
  import { f as formatErrorMessage, d as EMPTY_ARRAY, a as useRefWithInit, b as useMergedRefs, N as NOOP, E as EMPTY_OBJECT, u as useRenderElement, m as mergeProps } from '../useRenderElement-es.js';
5
5
  import { jsx, jsxs } from 'react/jsx-runtime';
6
- import { a as useFormContext, b as useFieldRootContext, i as useLabelableId, j as useField, f as fieldValidityMapping, u as useLabelableContext, k as DEFAULT_FIELD_STATE_ATTRIBUTES, l as useNumberFieldRootContext, m as DEFAULT_STEP, s as stateAttributesMapping$9, h as NumberFieldDecrement, c as NumberFieldGroup, e as NumberFieldIncrement, d as NumberFieldInput, N as NumberFieldRoot } from '../NumberFieldInput-es.js';
6
+ import { a as useFormContext, b as useFieldRootContext, j as useLabelableId, k as useField, f as fieldValidityMapping, u as useLabelableContext, l as DEFAULT_FIELD_STATE_ATTRIBUTES, c as useLabel, m as useNumberFieldRootContext, n as DEFAULT_STEP, s as stateAttributesMapping$9, i as NumberFieldDecrement, d as NumberFieldGroup, h as NumberFieldIncrement, e as NumberFieldInput, N as NumberFieldRoot } from '../NumberFieldInput-es.js';
7
7
  import { u as useFloatingRootContext, o as useListNavigation, p as useTypeahead, q as getPseudoElementBounds, C as CompositeList, r as useAnchorPositioning, s as getDisabledMountTransitionStyles, t as useCompositeListItem, I as IndexGuessBehavior, a as DrawerClose, D as DrawerTitle, v as useMenuRootContext, w as useMenuPositionerContext, x as useContextMenuRootContext, y as useMenuItem, R as REGULAR_ITEM, z as itemMapping, A as MenuStore, l as MenuGroup, n as MenuGroupLabel, c as MenuItem, b as MenuLinkItem, j as MenuPopup, h as MenuPortal, i as MenuPositioner, k as MenuRadioGroup, d as MenuRadioItem, e as MenuRadioItemIndicator, g as MenuRoot, m as MenuSubmenuRoot, f as MenuSubmenuTrigger, M as MenuTrigger } from '../MenuSubmenuTrigger-es.js';
8
8
  import * as ReactDOM from 'react-dom';
9
9
  import { S as Separator } from '../Separator-es.js';
@@ -7037,84 +7037,6 @@ function ComboboxRoot(props) {
7037
7037
  });
7038
7038
  }
7039
7039
 
7040
- function useRegisteredLabelId(idProp, setLabelId) {
7041
- const id = useBaseUiId(idProp);
7042
- useIsoLayoutEffect(() => {
7043
- setLabelId(id);
7044
- return () => {
7045
- setLabelId(undefined);
7046
- };
7047
- }, [id, setLabelId]);
7048
- return id;
7049
- }
7050
-
7051
- function useLabel(params = {}) {
7052
- const {
7053
- id: idProp,
7054
- fallbackControlId,
7055
- native = false,
7056
- setLabelId: setLabelIdProp,
7057
- focusControl: focusControlProp
7058
- } = params;
7059
- const {
7060
- controlId: contextControlId,
7061
- setLabelId: setContextLabelId
7062
- } = useLabelableContext();
7063
- const syncLabelId = useStableCallback(nextLabelId => {
7064
- setContextLabelId(nextLabelId);
7065
- setLabelIdProp?.(nextLabelId);
7066
- });
7067
- const id = useRegisteredLabelId(idProp, syncLabelId);
7068
- const resolvedControlId = contextControlId ?? fallbackControlId;
7069
- function focusControl(event) {
7070
- if (focusControlProp) {
7071
- focusControlProp(event, resolvedControlId);
7072
- return;
7073
- }
7074
- if (!resolvedControlId) {
7075
- return;
7076
- }
7077
- const controlElement = ownerDocument(event.currentTarget).getElementById(resolvedControlId);
7078
- if (isHTMLElement(controlElement)) {
7079
- focusElementWithVisible(controlElement);
7080
- }
7081
- }
7082
- function handleInteraction(event) {
7083
- const target = getTarget(event.nativeEvent);
7084
- if (target?.closest('button,input,select,textarea')) {
7085
- return;
7086
- }
7087
-
7088
- // Prevent text selection when double clicking label.
7089
- if (!event.defaultPrevented && event.detail > 1) {
7090
- event.preventDefault();
7091
- }
7092
- if (native) {
7093
- return;
7094
- }
7095
- focusControl(event);
7096
- }
7097
- return native ? {
7098
- id,
7099
- htmlFor: resolvedControlId ?? undefined,
7100
- onMouseDown: handleInteraction
7101
- } : {
7102
- id,
7103
- onClick: handleInteraction,
7104
- onPointerDown(event) {
7105
- event.preventDefault();
7106
- }
7107
- };
7108
- }
7109
- function focusElementWithVisible(element) {
7110
- element.focus({
7111
- // Available from Chrome 144+ (January 2026).
7112
- // Safari and Firefox already support it.
7113
- // @ts-expect-error not available in types yet
7114
- focusVisible: true
7115
- });
7116
- }
7117
-
7118
7040
  /**
7119
7041
  * An accessible label that is automatically associated with the combobox trigger.
7120
7042
  * Renders a `<div>` element.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jobber/components",
3
- "version": "8.19.0",
3
+ "version": "8.20.1",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -487,7 +487,7 @@
487
487
  "devDependencies": {
488
488
  "@apollo/client": "^3.7.10",
489
489
  "@csstools/postcss-global-data": "^1.0.3",
490
- "@jobber/design": "0.105.0",
490
+ "@jobber/design": "0.106.0",
491
491
  "@jobber/hooks": "2.21.0",
492
492
  "@rollup/plugin-alias": "^5.1.0",
493
493
  "@rollup/plugin-commonjs": "^25.0.7",
@@ -541,5 +541,5 @@
541
541
  "> 1%",
542
542
  "IE 10"
543
543
  ],
544
- "gitHead": "5e02a8ecc5f282d152c92e18dde04a27d401ecf5"
544
+ "gitHead": "c5da7c5d3db964c4803ccf359bc4e59ed63f668d"
545
545
  }