@jobber/components 9.12.3 → 9.13.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,6 +1,7 @@
1
1
  import { _ as __rest } from './tslib.es6-es.js';
2
- import React__default, { createContext, useId, useRef, useImperativeHandle, useCallback, useMemo, useContext } from 'react';
2
+ import React__default, { createContext, useContext, useId, useRef, useImperativeHandle, useCallback, useMemo } from 'react';
3
3
  import classnames from 'classnames';
4
+ import { c as createSlotSystem } from './slotSystem-es.js';
4
5
  import { d as defineMessages, u as useAtlantisI18n } from './useAtlantisI18n-es.js';
5
6
  import './AtlantisContext-es.js';
6
7
  import { A as ActivityIndicator } from './ActivityIndicator-es.js';
@@ -11,8 +12,16 @@ import './BottomSheet-es.js';
11
12
  import { H as HelperText } from './HelperText-es.js';
12
13
  import './SelectPrimitive-es.js';
13
14
  import { m as mergeProps } from './useRenderElement-es.js';
14
- import { F as FieldRoot, a as FieldLabel, b as FieldDescription, c as FieldError } from './FieldDescription-es.js';
15
- import { N as NumberFieldRoot, a as NumberFieldGroup, b as NumberFieldInput, c as NumberFieldIncrement, d as NumberFieldDecrement } from './NumberFieldInput-es.js';
15
+ import { N as NumberFieldRoot, a as NumberFieldGroup, b as NumberFieldIncrement, c as NumberFieldDecrement, d as NumberFieldInput } from './NumberFieldInput-es.js';
16
+ import { F as FieldLabel, a as FieldDescription, b as FieldError, c as FieldRoot } from './FieldDescription-es.js';
17
+
18
+ const STRUCTURAL_SLOT_NAMES = ["group", "footer"];
19
+ const inputNumberSlotSystem = createSlotSystem("inputNumberSlot");
20
+ const assignInputNumberSlot = inputNumberSlotSystem.assignSlot;
21
+ const isInputNumberSlotElement = inputNumberSlotSystem.isSlotElement;
22
+ function isStructuralSlot(slot) {
23
+ return STRUCTURAL_SLOT_NAMES.includes(slot);
24
+ }
16
25
 
17
26
  var styles = {"container":"-W4QiocGsR4-","inline":"_9-f2CjSVojs-","wrapper":"_4y342TRP-y8-","disabled":"d-YOq6z4ZxM-","small":"YTL0AyJdbC8-","large":"mUfanIovnVg-","center":"_8MM9keFOeD0-","right":"mTeuCQufdj4-","inputWrapper":"IgE0pfetwVM-","input":"MxShJMNKSB4-","stepper":"SE8DXlYN02U-","loadingIndicator":"VmKH2-NlbDI-","label":"f1OiicY3UzU-","hideLabel":"j9oUhNvNwsE-","affixLabel":"oR6I7xtSbL4-","prefix":"s-WnNh4k-kw-","suffix":"Ep7K8jug-Xc-","affixIcon":"E3w3BEJoBFE-","compoundAffix":"znvaejix3iE-","affixLabelText":"m1ZP5T4QOAQ-","stepperButton":"_22AXoGKVAjc-","belowField":"kkywg49kgrg-","description":"buIzMqBEpSQ-","spinning":"p8ebLlGf8JY-"};
18
27
 
@@ -47,6 +56,116 @@ const DEFAULT_FORMAT = {
47
56
  maximumFractionDigits: 12,
48
57
  };
49
58
  const InputNumberContext = createContext(null);
59
+ function isFragmentElement(child) {
60
+ return React__default.isValidElement(child) && child.type === React__default.Fragment;
61
+ }
62
+ /** Conditional JSX often wraps parts in a fragment; unwrap so the parser sees them. */
63
+ function flattenFragments(children) {
64
+ const flattened = [];
65
+ React__default.Children.forEach(children, child => {
66
+ if (isFragmentElement(child)) {
67
+ flattened.push(...flattenFragments(child.props.children));
68
+ }
69
+ else {
70
+ flattened.push(child);
71
+ }
72
+ });
73
+ return flattened;
74
+ }
75
+ /** An `Error` with no message renders nothing, so it must not suppress the description. */
76
+ function hasRenderableChildren(child) {
77
+ return React__default.isValidElement(child)
78
+ ? Boolean(child.props.children)
79
+ : false;
80
+ }
81
+ /** Placement policy; the shared slot system only tags and detects. */
82
+ function parseInputNumberChildren(children) {
83
+ const labels = [];
84
+ const indicators = [];
85
+ const descriptions = [];
86
+ const errors = [];
87
+ const parsed = {
88
+ prefixAffixes: [],
89
+ suffixAffixes: [],
90
+ insideInput: [],
91
+ footer: [],
92
+ structural: [],
93
+ unknown: [],
94
+ };
95
+ flattenFragments(children).forEach(child => {
96
+ if (!isInputNumberSlotElement(child)) {
97
+ parsed.unknown.push(child);
98
+ return;
99
+ }
100
+ const slot = child.type.inputNumberSlot;
101
+ if (isStructuralSlot(slot)) {
102
+ parsed.structural.push(child);
103
+ return;
104
+ }
105
+ switch (slot) {
106
+ case "label":
107
+ labels.push(child);
108
+ break;
109
+ case "affix":
110
+ if (child.props.variation === "suffix") {
111
+ parsed.suffixAffixes.push(child);
112
+ }
113
+ else {
114
+ parsed.prefixAffixes.push(child);
115
+ }
116
+ break;
117
+ case "stepper":
118
+ case "loading":
119
+ indicators.push(child);
120
+ break;
121
+ case "description":
122
+ descriptions.push(child);
123
+ break;
124
+ case "error":
125
+ errors.push(child);
126
+ break;
127
+ }
128
+ });
129
+ parsed.insideInput.push(...labels, ...indicators);
130
+ parsed.footer.push(...(errors.some(hasRenderableChildren) ? [] : descriptions), ...errors);
131
+ return parsed;
132
+ }
133
+ function hasContentParts(parsed) {
134
+ return (parsed.prefixAffixes.length > 0 ||
135
+ parsed.suffixAffixes.length > 0 ||
136
+ parsed.insideInput.length > 0 ||
137
+ parsed.footer.length > 0);
138
+ }
139
+ function resolveWrapperContent(children) {
140
+ const parsed = parseInputNumberChildren(children);
141
+ const hasContent = hasContentParts(parsed);
142
+ if (parsed.structural.length > 0) {
143
+ if (hasContent) {
144
+ throw new Error("<InputNumber.Wrapper> received both structural parts (Group/Footer) " +
145
+ "and loose content parts (Label/Affix/Stepper/Loading/Description/Error). " +
146
+ "Pass content parts alone to have Wrapper build the field, or nest them " +
147
+ "inside the structural parts to compose the tree yourself.");
148
+ }
149
+ return children;
150
+ }
151
+ // A consumer component may render `Group` itself; synthesizing would give it a second field.
152
+ if (!hasContent && parsed.unknown.length > 0)
153
+ return children;
154
+ return renderSynthesizedField(parsed);
155
+ }
156
+ function renderSynthesizedField(parsed) {
157
+ return (React__default.createElement(React__default.Fragment, null,
158
+ React__default.createElement(InputNumberGroup, null,
159
+ keyed(parsed.prefixAffixes),
160
+ React__default.createElement(InputNumberInput, null, keyed(parsed.insideInput)),
161
+ keyed(parsed.suffixAffixes)),
162
+ parsed.footer.length > 0 && (React__default.createElement(InputNumberFooter, null, keyed(parsed.footer))),
163
+ keyed(parsed.unknown)));
164
+ }
165
+ /** Flattening fragments drops the keys `React.Children.toArray` would have assigned. */
166
+ function keyed(children) {
167
+ return React__default.Children.toArray(children);
168
+ }
50
169
  function useInputNumberContext(consumer) {
51
170
  const context = useContext(InputNumberContext);
52
171
  if (context === null) {
@@ -133,7 +252,7 @@ function InputNumberWrapper(_a) {
133
252
  ]);
134
253
  const rootProps = mergeProps({ className: classnames(styles.container, inline && styles.inline) }, { className, style });
135
254
  return (React__default.createElement(FieldRoot, Object.assign({}, rootProps, { disabled: disabled, invalid: invalid || undefined, name: name }),
136
- React__default.createElement(InputNumberContext.Provider, { value: contextValue }, children)));
255
+ React__default.createElement(InputNumberContext.Provider, { value: contextValue }, resolveWrapperContent(children))));
137
256
  }
138
257
  function InputNumberGroup({ children, className, style, }) {
139
258
  var _a, _b;
@@ -220,6 +339,11 @@ function InputNumberAffixCompound({ variation, label, icon, onClick, ariaLabel,
220
339
  label && React__default.createElement("span", { className: styles.affixLabelText }, label),
221
340
  children));
222
341
  }
342
+ /** Carries the sugar's `prefix`/`suffix` through routing, adding no element of its own. */
343
+ function InputNumberPropAffix({ variation, affix }) {
344
+ const ctx = useInputNumberContext("Affix");
345
+ return renderDefaultAffix(variation, affix, ctx.size);
346
+ }
223
347
  /**
224
348
  * Renders the prop-driven `prefix`/`suffix` affixes for the sugar component.
225
349
  * Icons sit outside the input area; labels render next to the typed value
@@ -257,31 +381,22 @@ function resolveAutoComplete(autoComplete) {
257
381
  return undefined;
258
382
  return autoComplete;
259
383
  }
260
- /**
261
- * Sugar layer: composes the parts from props. This is the same composition a
262
- * consumer would write by hand with `<InputNumber.Wrapper>` and the
263
- * parts — to customize a single piece, copy this tree and swap that part.
264
- */
384
+ /** Sugar layer: hands the parts its props describe to `Wrapper`, the same path a consumer takes. */
265
385
  function InputNumber(_a) {
266
- var _b;
267
386
  var { ref } = _a, props = __rest(_a, ["ref"]);
268
387
  const { t } = useAtlantisI18n(inputNumberMessages);
269
388
  const { label, description, error, prefix, suffix } = props, config = __rest(props, ["label", "description", "error", "prefix", "suffix"]);
270
- const size = (_b = config.size) !== null && _b !== void 0 ? _b : "default";
271
389
  return (React__default.createElement(InputNumberWrapper, Object.assign({}, config, { invalid: config.invalid || Boolean(error), ref: ref }),
272
- React__default.createElement(InputNumberGroup, null,
273
- prefix && renderDefaultAffix("prefix", prefix, size),
274
- React__default.createElement(InputNumberInput, null,
275
- React__default.createElement(InputNumberLabel, null, label),
276
- config.loading ? (React__default.createElement(InputNumberLoading, null)) : (React__default.createElement(InputNumberStepper, { incrementLabel: t("Increase {label}", {
277
- label: label !== null && label !== void 0 ? label : t("value"),
278
- }), decrementLabel: t("Decrease {label}", {
279
- label: label !== null && label !== void 0 ? label : t("value"),
280
- }) }))),
281
- suffix && renderDefaultAffix("suffix", suffix, size)),
282
- (description || error) && (React__default.createElement(InputNumberFooter, null,
283
- React__default.createElement(InputNumberDescription, null, description),
284
- React__default.createElement(InputNumberError, null, error)))));
390
+ prefix ? (React__default.createElement(InputNumberPropAffix, { affix: prefix, variation: "prefix" })) : null,
391
+ React__default.createElement(InputNumberLabel, null, label),
392
+ config.loading ? (React__default.createElement(InputNumberLoading, null)) : (React__default.createElement(InputNumberStepper, { incrementLabel: t("Increase {label}", {
393
+ label: label !== null && label !== void 0 ? label : t("value"),
394
+ }), decrementLabel: t("Decrease {label}", {
395
+ label: label !== null && label !== void 0 ? label : t("value"),
396
+ }) })),
397
+ suffix ? (React__default.createElement(InputNumberPropAffix, { affix: suffix, variation: "suffix" })) : null,
398
+ description ? (React__default.createElement(InputNumberDescription, null, description)) : null,
399
+ error ? React__default.createElement(InputNumberError, null, error) : null));
285
400
  }
286
401
  InputNumberWrapper.displayName = "InputNumber.Wrapper";
287
402
  InputNumber.displayName = "InputNumber";
@@ -296,6 +411,16 @@ InputNumberStepper.displayName = "InputNumber.Stepper";
296
411
  InputNumberIncrement.displayName = "InputNumber.Increment";
297
412
  InputNumberDecrement.displayName = "InputNumber.Decrement";
298
413
  InputNumberAffixCompound.displayName = "InputNumber.Affix";
414
+ // Tagged here so the parts stay plain declarations, which the docs prop-table generator needs.
415
+ assignInputNumberSlot(InputNumberGroup, "group");
416
+ assignInputNumberSlot(InputNumberFooter, "footer");
417
+ assignInputNumberSlot(InputNumberLabel, "label");
418
+ assignInputNumberSlot(InputNumberDescription, "description");
419
+ assignInputNumberSlot(InputNumberError, "error");
420
+ assignInputNumberSlot(InputNumberLoading, "loading");
421
+ assignInputNumberSlot(InputNumberStepper, "stepper");
422
+ assignInputNumberSlot(InputNumberAffixCompound, "affix");
423
+ assignInputNumberSlot(InputNumberPropAffix, "affix");
299
424
  InputNumber.Wrapper = InputNumberWrapper;
300
425
  InputNumber.Group = InputNumberGroup;
301
426
  InputNumber.Footer = InputNumberFooter;
@@ -16,7 +16,7 @@ var Tooltip = require('./Tooltip-cjs.js');
16
16
  var AtlantisThemeContext = require('./AtlantisThemeContext-cjs.js');
17
17
  var Menu = require('./Menu-cjs.js');
18
18
 
19
- var styles = {"backgroundImage":"i9Tw1T65W-k-","next":"Q8amcRaTGf0-","prev":"W9FVb24yJrk-","buttonHidden":"nsN0TPWsBXI-","buttonVisible":"dkLYp7AD2jE-","lightboxWrapper":"_5p2iAj4JfoE-","toolbar":"rMK4cKdOxFw-","closeButton":"_0m6vb11DgiA-","slideNumber":"kCc68gGuTgg-","leftActions":"OunDmtPpa9g-","downloadOptionContent":"TPSJq-8p0vo-","downloadOptionLabel":"vZU6ZSl8QLI-","downloadOptionDetail":"vubHTSF4aYA-","image":"yYFVVScosfQ-","imageArea":"UskuwLHR6fg-","captionWrapper":"OGjhge-r-U4-","title":"tZU2g-NYdIs-","blurOverlay":"GKIdLTmvcvQ-","thumbnailBar":"_3TfQLQEE3GQ-","thumbnailImage":"eBMzUOlcfQ4-","thumbnail":"eapm2zruLn8-","selected":"PeLn2u-QB0k-","spinning":"_8tDoqjgfLcw-"};
19
+ var styles = {"lightboxWrapper":"_5p2iAj4JfoE-","thumbnailBar":"_3TfQLQEE3GQ-","backgroundImage":"i9Tw1T65W-k-","next":"Q8amcRaTGf0-","prev":"W9FVb24yJrk-","buttonHidden":"nsN0TPWsBXI-","buttonVisible":"dkLYp7AD2jE-","toolbar":"rMK4cKdOxFw-","closeButton":"_0m6vb11DgiA-","slideNumber":"kCc68gGuTgg-","leftActions":"OunDmtPpa9g-","downloadOptionContent":"TPSJq-8p0vo-","downloadOptionLabel":"vZU6ZSl8QLI-","downloadOptionDetail":"vubHTSF4aYA-","image":"yYFVVScosfQ-","imageArea":"UskuwLHR6fg-","captionWrapper":"OGjhge-r-U4-","title":"tZU2g-NYdIs-","blurOverlay":"GKIdLTmvcvQ-","thumbnailImage":"eBMzUOlcfQ4-","thumbnail":"eapm2zruLn8-","selected":"PeLn2u-QB0k-","spinning":"_8tDoqjgfLcw-"};
20
20
 
21
21
  // A little bit more than the transition's duration
22
22
  // We're doing this to prevent a bug from framer-motion
@@ -69,7 +69,6 @@ function LightBoxProvider({ open = true, images, imageIndex = 0, onRequestClose
69
69
  const debouncedHandleNext = jobberHooks.useDebounce(handleMoveNext, BUTTON_DEBOUNCE_DELAY);
70
70
  const debouncedHandlePrevious = jobberHooks.useDebounce(handleMovePrevious, BUTTON_DEBOUNCE_DELAY);
71
71
  const mounted = jobberHooks.useIsMounted();
72
- const prevOpen = React.useRef(open);
73
72
  jobberHooks.useRefocusOnActivator(open);
74
73
  const handleMouseMovement = jobberHooks.useDebounce(() => {
75
74
  setMouseIsStationary(true);
@@ -85,10 +84,6 @@ function LightBoxProvider({ open = true, images, imageIndex = 0, onRequestClose
85
84
  setCurrentImageIndex(imageIndex);
86
85
  onImageChange(imageIndex);
87
86
  }, [imageIndex, open]);
88
- if (prevOpen.current !== open) {
89
- prevOpen.current = open;
90
- togglePrintStyles(open);
91
- }
92
87
  React.useEffect(() => {
93
88
  var _a;
94
89
  (_a = selectedThumbnailRef === null || selectedThumbnailRef === void 0 ? void 0 : selectedThumbnailRef.current) === null || _a === void 0 ? void 0 : _a.scrollIntoView({
@@ -158,19 +153,6 @@ function LightBoxProvider({ open = true, images, imageIndex = 0, onRequestClose
158
153
  function useLightBoxContext() {
159
154
  return React.useContext(LightBoxContext);
160
155
  }
161
- function togglePrintStyles(open) {
162
- try {
163
- if (open) {
164
- document.documentElement.classList.add("atlantisLightBoxActive");
165
- }
166
- else {
167
- document.documentElement.classList.remove("atlantisLightBoxActive");
168
- }
169
- }
170
- catch (error) {
171
- console.error(error);
172
- }
173
- }
174
156
 
175
157
  const lightBoxMessages = useAtlantisI18n.defineMessages({
176
158
  close: {
@@ -202,7 +184,6 @@ const lightBoxMessages = useAtlantisI18n.defineMessages({
202
184
  function LightBoxContent() {
203
185
  const { open, lightboxRef, handleMouseMove } = useLightBoxContext();
204
186
  const { t } = useAtlantisI18n.useAtlantisI18n(lightBoxMessages);
205
- const mounted = jobberHooks.useIsMounted();
206
187
  const template = (React.createElement(React.Fragment, null, open && (React.createElement("div", { className: styles.lightboxWrapper, tabIndex: 0, "aria-label": t("lightbox"), key: "Lightbox", ref: lightboxRef, onMouseMove: handleMouseMove },
207
188
  React.createElement(LightBoxBackground, null),
208
189
  React.createElement(LightBoxOverlay, null),
@@ -211,7 +192,7 @@ function LightBoxContent() {
211
192
  React.createElement(LightBoxNavigation, null),
212
193
  React.createElement(LightBoxCaption, null),
213
194
  React.createElement(LightBoxThumbnails, null)))));
214
- return mounted.current
195
+ return (globalThis === null || globalThis === void 0 ? void 0 : globalThis.document)
215
196
  ? ReactDOM.createPortal(template, document.body)
216
197
  : template;
217
198
  }
@@ -14,7 +14,7 @@ import { T as Tooltip } from './Tooltip-es.js';
14
14
  import { A as AtlantisThemeContextProvider } from './AtlantisThemeContext-es.js';
15
15
  import { M as Menu } from './Menu-es.js';
16
16
 
17
- var styles = {"backgroundImage":"i9Tw1T65W-k-","next":"Q8amcRaTGf0-","prev":"W9FVb24yJrk-","buttonHidden":"nsN0TPWsBXI-","buttonVisible":"dkLYp7AD2jE-","lightboxWrapper":"_5p2iAj4JfoE-","toolbar":"rMK4cKdOxFw-","closeButton":"_0m6vb11DgiA-","slideNumber":"kCc68gGuTgg-","leftActions":"OunDmtPpa9g-","downloadOptionContent":"TPSJq-8p0vo-","downloadOptionLabel":"vZU6ZSl8QLI-","downloadOptionDetail":"vubHTSF4aYA-","image":"yYFVVScosfQ-","imageArea":"UskuwLHR6fg-","captionWrapper":"OGjhge-r-U4-","title":"tZU2g-NYdIs-","blurOverlay":"GKIdLTmvcvQ-","thumbnailBar":"_3TfQLQEE3GQ-","thumbnailImage":"eBMzUOlcfQ4-","thumbnail":"eapm2zruLn8-","selected":"PeLn2u-QB0k-","spinning":"_8tDoqjgfLcw-"};
17
+ var styles = {"lightboxWrapper":"_5p2iAj4JfoE-","thumbnailBar":"_3TfQLQEE3GQ-","backgroundImage":"i9Tw1T65W-k-","next":"Q8amcRaTGf0-","prev":"W9FVb24yJrk-","buttonHidden":"nsN0TPWsBXI-","buttonVisible":"dkLYp7AD2jE-","toolbar":"rMK4cKdOxFw-","closeButton":"_0m6vb11DgiA-","slideNumber":"kCc68gGuTgg-","leftActions":"OunDmtPpa9g-","downloadOptionContent":"TPSJq-8p0vo-","downloadOptionLabel":"vZU6ZSl8QLI-","downloadOptionDetail":"vubHTSF4aYA-","image":"yYFVVScosfQ-","imageArea":"UskuwLHR6fg-","captionWrapper":"OGjhge-r-U4-","title":"tZU2g-NYdIs-","blurOverlay":"GKIdLTmvcvQ-","thumbnailImage":"eBMzUOlcfQ4-","thumbnail":"eapm2zruLn8-","selected":"PeLn2u-QB0k-","spinning":"_8tDoqjgfLcw-"};
18
18
 
19
19
  // A little bit more than the transition's duration
20
20
  // We're doing this to prevent a bug from framer-motion
@@ -67,7 +67,6 @@ function LightBoxProvider({ open = true, images, imageIndex = 0, onRequestClose
67
67
  const debouncedHandleNext = useDebounce(handleMoveNext, BUTTON_DEBOUNCE_DELAY);
68
68
  const debouncedHandlePrevious = useDebounce(handleMovePrevious, BUTTON_DEBOUNCE_DELAY);
69
69
  const mounted = useIsMounted();
70
- const prevOpen = useRef(open);
71
70
  useRefocusOnActivator(open);
72
71
  const handleMouseMovement = useDebounce(() => {
73
72
  setMouseIsStationary(true);
@@ -83,10 +82,6 @@ function LightBoxProvider({ open = true, images, imageIndex = 0, onRequestClose
83
82
  setCurrentImageIndex(imageIndex);
84
83
  onImageChange(imageIndex);
85
84
  }, [imageIndex, open]);
86
- if (prevOpen.current !== open) {
87
- prevOpen.current = open;
88
- togglePrintStyles(open);
89
- }
90
85
  useEffect(() => {
91
86
  var _a;
92
87
  (_a = selectedThumbnailRef === null || selectedThumbnailRef === void 0 ? void 0 : selectedThumbnailRef.current) === null || _a === void 0 ? void 0 : _a.scrollIntoView({
@@ -156,19 +151,6 @@ function LightBoxProvider({ open = true, images, imageIndex = 0, onRequestClose
156
151
  function useLightBoxContext() {
157
152
  return useContext(LightBoxContext);
158
153
  }
159
- function togglePrintStyles(open) {
160
- try {
161
- if (open) {
162
- document.documentElement.classList.add("atlantisLightBoxActive");
163
- }
164
- else {
165
- document.documentElement.classList.remove("atlantisLightBoxActive");
166
- }
167
- }
168
- catch (error) {
169
- console.error(error);
170
- }
171
- }
172
154
 
173
155
  const lightBoxMessages = defineMessages({
174
156
  close: {
@@ -200,7 +182,6 @@ const lightBoxMessages = defineMessages({
200
182
  function LightBoxContent() {
201
183
  const { open, lightboxRef, handleMouseMove } = useLightBoxContext();
202
184
  const { t } = useAtlantisI18n(lightBoxMessages);
203
- const mounted = useIsMounted();
204
185
  const template = (React__default.createElement(React__default.Fragment, null, open && (React__default.createElement("div", { className: styles.lightboxWrapper, tabIndex: 0, "aria-label": t("lightbox"), key: "Lightbox", ref: lightboxRef, onMouseMove: handleMouseMove },
205
186
  React__default.createElement(LightBoxBackground, null),
206
187
  React__default.createElement(LightBoxOverlay, null),
@@ -209,7 +190,7 @@ function LightBoxContent() {
209
190
  React__default.createElement(LightBoxNavigation, null),
210
191
  React__default.createElement(LightBoxCaption, null),
211
192
  React__default.createElement(LightBoxThumbnails, null)))));
212
- return mounted.current
193
+ return (globalThis === null || globalThis === void 0 ? void 0 : globalThis.document)
213
194
  ? ReactDOM__default.createPortal(template, document.body)
214
195
  : template;
215
196
  }
@@ -1485,4 +1485,4 @@ const NumberFieldInput = /*#__PURE__*/React.forwardRef(function NumberFieldInput
1485
1485
  });
1486
1486
  if (process.env.NODE_ENV !== "production") NumberFieldInput.displayName = "NumberFieldInput";
1487
1487
 
1488
- export { NumberFieldRoot as N, NumberFieldGroup as a, NumberFieldInput as b, NumberFieldIncrement as c, NumberFieldDecrement as d, stateAttributesMapping as s, useNumberFieldRootContext as u };
1488
+ export { NumberFieldRoot as N, NumberFieldGroup as a, NumberFieldIncrement as b, NumberFieldDecrement as c, NumberFieldInput as d, stateAttributesMapping as s, useNumberFieldRootContext as u };
package/dist/Select-es.js CHANGED
@@ -5,7 +5,7 @@ import { S as SelectPrimitive } from './SelectPrimitive-es.js';
5
5
  import { a as BottomSheet } from './BottomSheet-es.js';
6
6
  import { I as Icon } from './Icon-es.js';
7
7
  import { H as HelperText } from './HelperText-es.js';
8
- import { F as FieldRoot, a as FieldLabel, b as FieldDescription, c as FieldError } from './FieldDescription-es.js';
8
+ import { c as FieldRoot, F as FieldLabel, a as FieldDescription, b as FieldError } from './FieldDescription-es.js';
9
9
 
10
10
  var styles = {"field":"cWI-uFeNWWw-","inline":"ddkBixixBz4-","control":"_-1-lIAkgLuo-","trigger":"_7rwBeCqTgRY-","small":"_5QeBX5VxX6o-","large":"Pp5nj1nxejw-","label":"M1IjAW17-OE-","hasLabel":"_1rx-nSAJT0U-","sheetListbox":"AVkCcDISAio-","sheetOption":"_35H33txs5QU-","sheetOptionLabel":"uL9j4OZn-3c-","sheetOptionIndicator":"_7NHtDHZnkG8-","sheetGroup":"UQ1tsDd1NFs-","sheetGroupLabel":"_0kBfkWoRI5c-","sheetSeparator":"RPsN9vqlzRw-","spinning":"oOACJmrVDf0-"};
11
11
 
@@ -339,17 +339,17 @@ the **Design** tab.
339
339
  | `Description` | Helper text below the field |
340
340
  | `Error` | Styled error message below the field |
341
341
 
342
- ## Composition
343
-
344
- The prop-driven component is sugar: it renders exactly the tree you would write
345
- by hand with `<InputNumber.Wrapper>` and the parts. To customize a single piece,
346
- compose the tree yourself and swap that one part — the other parts keep their
347
- defaults. The sugar does not merge consumer-provided parts into its render, so
348
- there is no per-slot precedence to reason about.
349
-
350
342
  `Wrapper` owns the field state and shares it with the parts through context, so
351
343
  every part must be rendered inside a `Wrapper` (a part used outside one throws).
352
344
 
345
+ ## Customize one part
346
+
347
+ Pass the content parts you want — `Label`, `Affix`, `Stepper`, `Loading`,
348
+ `Description`, `Error` — straight to `Wrapper`. It builds `Group`, `Input`, and
349
+ `Footer` around them and places each part at the right depth, so you never
350
+ reproduce the skeleton to change one piece. `Wrapper` renders only the parts you
351
+ gave it: leave one out and it is not there.
352
+
353
353
  The example below replaces the default stepper icons with `+` / `−` and leaves
354
354
  everything else as the default:
355
355
 
@@ -361,25 +361,139 @@ export function InputNumberCompositionExample() {
361
361
  const [value, setValue] = useState<number | null>(3);
362
362
 
363
363
  return (
364
- <InputNumber.Wrapper value={value} onValueCommitted={setValue}>
364
+ <InputNumber.Wrapper onValueCommitted={setValue} value={value}>
365
+ <InputNumber.Label>Quantity</InputNumber.Label>
366
+ <InputNumber.Stepper>
367
+ <InputNumber.Increment ariaLabel="Increase Quantity">
368
+ +
369
+ </InputNumber.Increment>
370
+ <InputNumber.Decrement ariaLabel="Decrease Quantity">
371
+
372
+ </InputNumber.Decrement>
373
+ </InputNumber.Stepper>
374
+ </InputNumber.Wrapper>
375
+ );
376
+ }
377
+ ```
378
+
379
+ ### Omit a part
380
+
381
+ A field with no stepper is the same composition minus the `Stepper`:
382
+
383
+ ```tsx
384
+ import React, { useState } from "react";
385
+ import { InputNumber } from "@jobber/components";
386
+
387
+ export function InputNumberCompositionStepperlessExample() {
388
+ const [value, setValue] = useState<number | null>(15);
389
+
390
+ return (
391
+ <InputNumber.Wrapper
392
+ format={{ style: "unit", unit: "percent" }}
393
+ onValueCommitted={setValue}
394
+ value={value}
395
+ >
396
+ <InputNumber.Label>Tax rate</InputNumber.Label>
397
+ </InputNumber.Wrapper>
398
+ );
399
+ }
400
+ ```
401
+
402
+ ### Show a part conditionally
403
+
404
+ Presence is ordinary conditional JSX; there is no `show*` prop to reach for:
405
+
406
+ ```tsx
407
+ import React, { useState } from "react";
408
+ import { InputNumber } from "@jobber/components";
409
+
410
+ export function InputNumberCompositionConditionalStepperExample() {
411
+ const [unitCost, setUnitCost] = useState<number | null>(0);
412
+ const [markup, setMarkup] = useState<number | null>(20);
413
+
414
+ return (
415
+ <>
416
+ <InputNumber
417
+ label="Unit cost"
418
+ onValueCommitted={setUnitCost}
419
+ prefix={{ label: "$" }}
420
+ value={unitCost}
421
+ />
422
+ <InputNumber.Wrapper onValueCommitted={setMarkup} value={markup}>
423
+ <InputNumber.Label>Markup</InputNumber.Label>
424
+ <InputNumber.Affix label="%" variation="suffix" />
425
+ {Boolean(unitCost) && <InputNumber.Stepper />}
426
+ </InputNumber.Wrapper>
427
+ </>
428
+ );
429
+ }
430
+ ```
431
+
432
+ ### Below-field content
433
+
434
+ `Description` and `Error` route into the footer row, which exists only while one
435
+ of them is rendered. The two are never shown together: when both are present the
436
+ error replaces the description, since a description hints at what the field
437
+ wants and the error already restates it.
438
+
439
+ ```tsx
440
+ import React, { useState } from "react";
441
+ import { InputNumber } from "@jobber/components";
442
+
443
+ export function InputNumberCompositionFooterExample() {
444
+ const [value, setValue] = useState<number | null>(50);
445
+ const error = (value ?? 0) > 99 ? "Enter a value between 1 and 99" : "";
446
+
447
+ return (
448
+ <InputNumber.Wrapper
449
+ invalid={Boolean(error)}
450
+ max={99}
451
+ min={1}
452
+ onValueChange={setValue}
453
+ value={value}
454
+ >
455
+ <InputNumber.Label>Quantity</InputNumber.Label>
456
+ <InputNumber.Description>Per visit</InputNumber.Description>
457
+ {error && <InputNumber.Error>{error}</InputNumber.Error>}
458
+ </InputNumber.Wrapper>
459
+ );
460
+ }
461
+ ```
462
+
463
+ ## Custom layouts
464
+
465
+ For an arrangement the standard skeleton does not produce, compose the
466
+ structural parts (`Group`, `Input`, `Footer`) yourself. `Wrapper` then renders
467
+ your tree exactly as written and adds nothing:
468
+
469
+ ```tsx
470
+ import React, { useState } from "react";
471
+ import { InputNumber } from "@jobber/components";
472
+
473
+ export function InputNumberCompositionCustomLayoutExample() {
474
+ const [value, setValue] = useState<number | null>(3);
475
+
476
+ return (
477
+ <InputNumber.Wrapper inline onValueCommitted={setValue} value={value}>
365
478
  <InputNumber.Group>
479
+ <InputNumber.Decrement ariaLabel="Decrease quantity" />
366
480
  <InputNumber.Input>
367
481
  <InputNumber.Label>Quantity</InputNumber.Label>
368
- <InputNumber.Stepper>
369
- <InputNumber.Increment ariaLabel="Increase Quantity">
370
- +
371
- </InputNumber.Increment>
372
- <InputNumber.Decrement ariaLabel="Decrease Quantity">
373
-
374
- </InputNumber.Decrement>
375
- </InputNumber.Stepper>
376
482
  </InputNumber.Input>
483
+ <InputNumber.Increment ariaLabel="Increase quantity" />
377
484
  </InputNumber.Group>
378
485
  </InputNumber.Wrapper>
379
486
  );
380
487
  }
381
488
  ```
382
489
 
490
+ Pick one path per `Wrapper`. Mixing structural parts with loose content parts
491
+ throws, rather than guessing where the loose parts belong.
492
+
493
+ The sugar does not merge consumer-provided parts into its render, so there is no
494
+ per-slot precedence to reason about — children passed to `<InputNumber>` are
495
+ ignored. Compose on `Wrapper` instead.
496
+
383
497
  ## Controlled usage
384
498
 
385
499
  The field is controlled: pass `value` (a `number`, or `null` for empty) and read
package/dist/index.cjs CHANGED
@@ -113,6 +113,7 @@ require('color');
113
113
  require('./tslib.es6-cjs.js');
114
114
  require('react-router-dom');
115
115
  require('./getMappedAtlantisSpaceToken-cjs.js');
116
+ require('./slotSystem-cjs.js');
116
117
  require('./buttonRenderAdapter-cjs.js');
117
118
  require('./useRenderElement-cjs.js');
118
119
  require('./ComboboxPrimitive-cjs.js');
package/dist/index.mjs CHANGED
@@ -111,6 +111,7 @@ import 'color';
111
111
  import './tslib.es6-es.js';
112
112
  import 'react-router-dom';
113
113
  import './getMappedAtlantisSpaceToken-es.js';
114
+ import './slotSystem-es.js';
114
115
  import './buttonRenderAdapter-es.js';
115
116
  import './useRenderElement-es.js';
116
117
  import './ComboboxPrimitive-es.js';
@@ -0,0 +1,30 @@
1
+ 'use strict';
2
+
3
+ var React = require('react');
4
+
5
+ function createSlotSystem(markerKey) {
6
+ function assignSlot(component, slot) {
7
+ return Object.assign(component, {
8
+ [markerKey]: slot,
9
+ });
10
+ }
11
+ function createSlot(slot) {
12
+ function SlotMarker(props) {
13
+ return null;
14
+ }
15
+ return assignSlot(SlotMarker, slot);
16
+ }
17
+ function isSlotElement(child) {
18
+ if (!React.isValidElement(child))
19
+ return false;
20
+ // `in` throws on a primitive, and an element's type is a string for a host
21
+ // element ("div") or a symbol for a fragment.
22
+ const type = child.type;
23
+ return ((typeof type === "object" || typeof type === "function") &&
24
+ type !== null &&
25
+ markerKey in type);
26
+ }
27
+ return { assignSlot, createSlot, isSlotElement };
28
+ }
29
+
30
+ exports.createSlotSystem = createSlotSystem;
@@ -0,0 +1,28 @@
1
+ import React__default from 'react';
2
+
3
+ function createSlotSystem(markerKey) {
4
+ function assignSlot(component, slot) {
5
+ return Object.assign(component, {
6
+ [markerKey]: slot,
7
+ });
8
+ }
9
+ function createSlot(slot) {
10
+ function SlotMarker(props) {
11
+ return null;
12
+ }
13
+ return assignSlot(SlotMarker, slot);
14
+ }
15
+ function isSlotElement(child) {
16
+ if (!React__default.isValidElement(child))
17
+ return false;
18
+ // `in` throws on a primitive, and an element's type is a string for a host
19
+ // element ("div") or a symbol for a fragment.
20
+ const type = child.type;
21
+ return ((typeof type === "object" || typeof type === "function") &&
22
+ type !== null &&
23
+ markerKey in type);
24
+ }
25
+ return { assignSlot, createSlot, isSlotElement };
26
+ }
27
+
28
+ export { createSlotSystem as c };