@octanejs/base-ui 0.1.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 (107) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +33 -0
  3. package/package.json +47 -0
  4. package/src/avatar.ts +308 -0
  5. package/src/checkbox-group.ts +183 -0
  6. package/src/checkbox.ts +578 -0
  7. package/src/field.ts +676 -0
  8. package/src/fieldset.ts +150 -0
  9. package/src/form.ts +183 -0
  10. package/src/index.ts +23 -0
  11. package/src/input.ts +9 -0
  12. package/src/internal.ts +42 -0
  13. package/src/merge-props.ts +2 -0
  14. package/src/meter.ts +204 -0
  15. package/src/number-field.ts +978 -0
  16. package/src/progress.ts +260 -0
  17. package/src/radio-group.ts +285 -0
  18. package/src/radio.ts +401 -0
  19. package/src/separator.ts +43 -0
  20. package/src/slider.ts +1617 -0
  21. package/src/switch.ts +374 -0
  22. package/src/toggle-group.ts +136 -0
  23. package/src/toggle.ts +145 -0
  24. package/src/use-render.ts +40 -0
  25. package/src/utils/CheckboxGroupContext.ts +30 -0
  26. package/src/utils/CompositeRootContext.ts +27 -0
  27. package/src/utils/DirectionContext.ts +15 -0
  28. package/src/utils/RadioGroupContext.ts +27 -0
  29. package/src/utils/ToggleGroupContext.ts +33 -0
  30. package/src/utils/addEventListener.ts +13 -0
  31. package/src/utils/areArraysEqual.ts +11 -0
  32. package/src/utils/clamp.ts +8 -0
  33. package/src/utils/composeRefs.ts +53 -0
  34. package/src/utils/composite/CompositeItem.ts +56 -0
  35. package/src/utils/composite/CompositeList.ts +190 -0
  36. package/src/utils/composite/CompositeListContext.ts +23 -0
  37. package/src/utils/composite/CompositeRoot.ts +132 -0
  38. package/src/utils/composite/keys.ts +179 -0
  39. package/src/utils/composite/list-utils.ts +78 -0
  40. package/src/utils/composite/useCompositeItem.ts +58 -0
  41. package/src/utils/composite/useCompositeListItem.ts +108 -0
  42. package/src/utils/composite/useCompositeRoot.ts +346 -0
  43. package/src/utils/contains.ts +22 -0
  44. package/src/utils/createChangeEventDetails.ts +81 -0
  45. package/src/utils/field/FieldItemContext.ts +13 -0
  46. package/src/utils/field/FieldRootContext.ts +98 -0
  47. package/src/utils/field/FormContext.ts +37 -0
  48. package/src/utils/field/LabelableContext.ts +32 -0
  49. package/src/utils/field/LabelableProvider.ts +108 -0
  50. package/src/utils/field/constants.ts +80 -0
  51. package/src/utils/field/getCombinedFieldValidityData.ts +16 -0
  52. package/src/utils/field/useAriaLabelledBy.ts +86 -0
  53. package/src/utils/field/useFieldControlRegistration.ts +179 -0
  54. package/src/utils/field/useFieldValidation.ts +311 -0
  55. package/src/utils/field/useLabel.ts +95 -0
  56. package/src/utils/field/useLabelableId.ts +99 -0
  57. package/src/utils/field/useRegisterFieldControl.ts +41 -0
  58. package/src/utils/formatNumber.ts +44 -0
  59. package/src/utils/getDefaultFormSubmitter.ts +19 -0
  60. package/src/utils/getElementRef.ts +13 -0
  61. package/src/utils/getStateAttributesProps.ts +34 -0
  62. package/src/utils/matchesFocusVisible.ts +16 -0
  63. package/src/utils/mergeObjects.ts +18 -0
  64. package/src/utils/mergeProps.ts +206 -0
  65. package/src/utils/noop.ts +4 -0
  66. package/src/utils/number/constants.ts +4 -0
  67. package/src/utils/number/parse.ts +227 -0
  68. package/src/utils/number/types.ts +25 -0
  69. package/src/utils/number/validate.ts +129 -0
  70. package/src/utils/owner.ts +9 -0
  71. package/src/utils/platform.ts +15 -0
  72. package/src/utils/resolveAriaLabelledBy.ts +11 -0
  73. package/src/utils/resolveClassName.ts +8 -0
  74. package/src/utils/resolveRef.ts +10 -0
  75. package/src/utils/resolveStyle.ts +10 -0
  76. package/src/utils/serializeValue.ts +15 -0
  77. package/src/utils/slider/asc.ts +4 -0
  78. package/src/utils/slider/getMidpoint.ts +14 -0
  79. package/src/utils/slider/getPushedThumbValues.ts +73 -0
  80. package/src/utils/slider/getSliderValue.ts +27 -0
  81. package/src/utils/slider/replaceArrayItemAtIndex.ts +8 -0
  82. package/src/utils/slider/resolveThumbCollision.ts +178 -0
  83. package/src/utils/slider/roundValueToStep.ts +22 -0
  84. package/src/utils/slider/validateMinimumDistance.ts +22 -0
  85. package/src/utils/slider/valueArrayToPercentages.ts +11 -0
  86. package/src/utils/stringifyLocale.ts +10 -0
  87. package/src/utils/useAnimationFrame.ts +59 -0
  88. package/src/utils/useAnimationsFinished.ts +100 -0
  89. package/src/utils/useBaseUiId.ts +19 -0
  90. package/src/utils/useButton.ts +217 -0
  91. package/src/utils/useCheckboxGroupParent.ts +113 -0
  92. package/src/utils/useControlled.ts +42 -0
  93. package/src/utils/useFocusableWhenDisabled.ts +84 -0
  94. package/src/utils/useForcedRerendering.ts +11 -0
  95. package/src/utils/useIsHydrating.ts +9 -0
  96. package/src/utils/useOpenChangeComplete.ts +46 -0
  97. package/src/utils/usePressAndHold.ts +35 -0
  98. package/src/utils/useRefWithInit.ts +23 -0
  99. package/src/utils/useRegisteredLabelId.ts +32 -0
  100. package/src/utils/useRenderElement.ts +165 -0
  101. package/src/utils/useStableCallback.ts +26 -0
  102. package/src/utils/useTimeout.ts +51 -0
  103. package/src/utils/useTransitionStatus.ts +115 -0
  104. package/src/utils/useValueAsRef.ts +25 -0
  105. package/src/utils/useValueChanged.ts +37 -0
  106. package/src/utils/valueToPercent.ts +4 -0
  107. package/src/utils/visuallyHidden.ts +24 -0
@@ -0,0 +1,578 @@
1
+ // Ported from .base-ui/packages/react/src/checkbox/ (v1.6.0): root/CheckboxRoot,
2
+ // root/CheckboxRootContext, utils/useStateAttributesMapping, indicator/CheckboxIndicator —
3
+ // plus its `index.parts` (the `Checkbox` namespace).
4
+ //
5
+ // A checkbox renders a `<span role="checkbox">` + a hidden `<input type="checkbox">`, with an
6
+ // optional `<Checkbox.Indicator>` (transition-mounted). octane adaptations mirror Switch:
7
+ // native events (no `.nativeEvent`); the uncontrolled-input pattern (initial checked →
8
+ // attribute, live `input.checked` PROPERTY via the native setter, native click/change
9
+ // dispatch); `input.indeterminate` set imperatively as a property. The CheckboxGroup /
10
+ // parent-checkbox branches stay dormant until CheckboxGroup lands (groupContext undefined).
11
+ import {
12
+ createContext,
13
+ createElement,
14
+ useContext,
15
+ useEffect,
16
+ useLayoutEffect,
17
+ useMemo,
18
+ useRef,
19
+ } from 'octane';
20
+
21
+ import { S, subSlot } from './internal';
22
+ import { useRenderElement, type RenderProp } from './utils/useRenderElement';
23
+ import type { StateAttributesMapping } from './utils/getStateAttributesProps';
24
+ import { mergeProps, makeEventPreventable } from './utils/mergeProps';
25
+ import { useBaseUiId } from './utils/useBaseUiId';
26
+ import { useButton } from './utils/useButton';
27
+ import { useControlled } from './utils/useControlled';
28
+ import { useComposedRefs } from './utils/composeRefs';
29
+ import { useRefWithInit } from './utils/useRefWithInit';
30
+ import { visuallyHidden, visuallyHiddenInput } from './utils/visuallyHidden';
31
+ import { ownerWindow } from './utils/owner';
32
+ import { getDefaultFormSubmitter } from './utils/getDefaultFormSubmitter';
33
+ import { NOOP } from './utils/noop';
34
+ import { createChangeEventDetails, REASONS } from './utils/createChangeEventDetails';
35
+ import { fieldValidityMapping, type FieldRootState } from './utils/field/constants';
36
+ import { useFieldRootContext } from './utils/field/FieldRootContext';
37
+ import { useFieldItemContext } from './utils/field/FieldItemContext';
38
+ import { useFormContext } from './utils/field/FormContext';
39
+ import { useLabelableContext } from './utils/field/LabelableContext';
40
+ import { useRegisterFieldControl } from './utils/field/useRegisterFieldControl';
41
+ import { useAriaLabelledBy } from './utils/field/useAriaLabelledBy';
42
+ import { useCheckboxGroupContext } from './utils/CheckboxGroupContext';
43
+ import { useValueChanged } from './utils/useValueChanged';
44
+ import {
45
+ useTransitionStatus,
46
+ transitionStatusMapping,
47
+ type TransitionStatus,
48
+ } from './utils/useTransitionStatus';
49
+ import { useOpenChangeComplete } from './utils/useOpenChangeComplete';
50
+
51
+ export const PARENT_CHECKBOX = 'data-parent';
52
+
53
+ export interface CheckboxRootState extends FieldRootState {
54
+ checked: boolean;
55
+ readOnly: boolean;
56
+ required: boolean;
57
+ indeterminate: boolean;
58
+ }
59
+
60
+ // state → data-* (checked/unchecked, but nothing when indeterminate — `data-indeterminate`
61
+ // comes from the default mapping of the `indeterminate` state key). Recomputed when
62
+ // `indeterminate` flips.
63
+ function useStateAttributesMapping(state: CheckboxRootState): StateAttributesMapping<any> {
64
+ return useMemo<StateAttributesMapping<any>>(
65
+ () => ({
66
+ checked(value: boolean): Record<string, string> {
67
+ if (state.indeterminate) {
68
+ return {};
69
+ }
70
+ if (value) {
71
+ return { 'data-checked': '' };
72
+ }
73
+ return { 'data-unchecked': '' };
74
+ },
75
+ ...(fieldValidityMapping as StateAttributesMapping<any>),
76
+ }),
77
+ [state.indeterminate],
78
+ S('Checkbox.mapping'),
79
+ );
80
+ }
81
+
82
+ // --- Context -----------------------------------------------------------------
83
+
84
+ const CheckboxRootContext = createContext<CheckboxRootState | undefined>(undefined);
85
+
86
+ function useCheckboxRootContext(): CheckboxRootState {
87
+ const context = useContext(CheckboxRootContext);
88
+ if (context === undefined) {
89
+ throw new Error(
90
+ 'Base UI: CheckboxRootContext is missing. Checkbox parts must be placed within <Checkbox.Root>.',
91
+ );
92
+ }
93
+ return context;
94
+ }
95
+
96
+ // --- Root --------------------------------------------------------------------
97
+
98
+ export interface CheckboxRootProps {
99
+ checked?: boolean;
100
+ defaultChecked?: boolean;
101
+ disabled?: boolean;
102
+ readOnly?: boolean;
103
+ required?: boolean;
104
+ indeterminate?: boolean;
105
+ name?: string;
106
+ form?: string;
107
+ id?: string;
108
+ inputRef?: any;
109
+ parent?: boolean;
110
+ nativeButton?: boolean;
111
+ value?: string;
112
+ uncheckedValue?: string;
113
+ onCheckedChange?: (checked: boolean, eventDetails: any) => void;
114
+ 'aria-labelledby'?: string;
115
+ render?: RenderProp<CheckboxRootState>;
116
+ className?: string | ((state: CheckboxRootState) => string | undefined);
117
+ style?: Record<string, any> | ((state: CheckboxRootState) => Record<string, any> | undefined);
118
+ ref?: any;
119
+ [key: string]: any;
120
+ }
121
+
122
+ function CheckboxRoot(props: CheckboxRootProps): any {
123
+ const slot = S('CheckboxRoot');
124
+ const {
125
+ checked: checkedProp,
126
+ className,
127
+ defaultChecked = false,
128
+ 'aria-labelledby': ariaLabelledByProp,
129
+ disabled: disabledProp = false,
130
+ form,
131
+ id: idProp,
132
+ indeterminate = false,
133
+ inputRef: inputRefProp,
134
+ name: nameProp,
135
+ onCheckedChange,
136
+ parent = false,
137
+ readOnly = false,
138
+ render,
139
+ required = false,
140
+ uncheckedValue,
141
+ value: valueProp,
142
+ nativeButton = false,
143
+ style,
144
+ ref,
145
+ ...elementProps
146
+ } = props;
147
+
148
+ const { clearErrors } = useFormContext();
149
+ const {
150
+ disabled: rootDisabled,
151
+ name: fieldName,
152
+ setDirty,
153
+ setFilled,
154
+ setFocused,
155
+ setTouched,
156
+ state: fieldState,
157
+ validationMode,
158
+ validityData,
159
+ validation: localValidation,
160
+ } = useFieldRootContext();
161
+ const fieldItemContext = useFieldItemContext();
162
+ const { labelId, controlId, registerControlId, getDescriptionProps } = useLabelableContext();
163
+
164
+ const groupContext = useCheckboxGroupContext();
165
+ const parentContext = groupContext?.parent;
166
+ const isGroupedWithParent = parentContext && groupContext!.allValues;
167
+
168
+ const disabled =
169
+ rootDisabled || fieldItemContext.disabled || groupContext?.disabled || disabledProp;
170
+ const name = fieldName ?? nameProp;
171
+ const value = valueProp ?? name;
172
+
173
+ const id = useBaseUiId(undefined, subSlot(slot, 'id'));
174
+ const parentId = useBaseUiId(undefined, subSlot(slot, 'parentId'));
175
+
176
+ let inputId = controlId;
177
+ if (isGroupedWithParent) {
178
+ inputId = parent ? parentId : `${parentContext.id}-${value}`;
179
+ } else if (idProp) {
180
+ inputId = idProp;
181
+ }
182
+
183
+ let groupProps: Record<string, any> = {};
184
+ if (isGroupedWithParent) {
185
+ if (parent) {
186
+ groupProps = groupContext!.parent.getParentProps();
187
+ } else if (value) {
188
+ groupProps = groupContext!.parent.getChildProps(value);
189
+ }
190
+ }
191
+
192
+ const {
193
+ checked: groupChecked = checkedProp,
194
+ indeterminate: groupIndeterminate = indeterminate,
195
+ onCheckedChange: groupOnChange,
196
+ ...otherGroupProps
197
+ } = groupProps;
198
+
199
+ const groupValue = groupContext?.value;
200
+ const setGroupValue = groupContext?.setValue;
201
+ const defaultGroupValue = groupContext?.defaultValue;
202
+
203
+ const controlRef = useRef<HTMLButtonElement | null>(null, subSlot(slot, 'controlRef'));
204
+ const controlSourceRef = useRefWithInit<symbol>(
205
+ () => Symbol('checkbox-control'),
206
+ subSlot(slot, 'controlSrc'),
207
+ );
208
+ const hasRegisteredRef = useRef(false, subSlot(slot, 'hasReg'));
209
+
210
+ const { getButtonProps, buttonRef } = useButton(
211
+ { disabled, native: nativeButton },
212
+ subSlot(slot, 'button'),
213
+ );
214
+
215
+ const validation = groupContext?.validation ?? localValidation;
216
+
217
+ const [checked, setCheckedState] = useControlled<boolean>(
218
+ {
219
+ controlled: value && groupValue && !parent ? groupValue.includes(value) : groupChecked,
220
+ default:
221
+ value && defaultGroupValue && !parent ? defaultGroupValue.includes(value) : defaultChecked,
222
+ name: 'Checkbox',
223
+ state: 'checked',
224
+ },
225
+ subSlot(slot, 'checked'),
226
+ );
227
+
228
+ // octane: reflect the INITIAL checked as the `checked` ATTRIBUTE (see Switch).
229
+ const initialCheckedRef = useRef(checked, subSlot(slot, 'initialChecked'));
230
+
231
+ const computedChecked = isGroupedWithParent ? Boolean(groupChecked) : checked;
232
+ const computedIndeterminate = isGroupedWithParent
233
+ ? groupIndeterminate || indeterminate
234
+ : indeterminate;
235
+
236
+ // Field.Item / group control-id registration (inert standalone).
237
+ useLayoutEffect(
238
+ () => {
239
+ if (registerControlId === NOOP) {
240
+ return undefined;
241
+ }
242
+ hasRegisteredRef.current = true;
243
+ registerControlId(controlSourceRef.current, inputId);
244
+ return undefined;
245
+ },
246
+ [inputId, registerControlId, controlSourceRef],
247
+ subSlot(slot, 'e:regId'),
248
+ );
249
+
250
+ useEffect(
251
+ () => {
252
+ const controlSource = controlSourceRef.current;
253
+ return () => {
254
+ if (!hasRegisteredRef.current || registerControlId === NOOP) {
255
+ return;
256
+ }
257
+ hasRegisteredRef.current = false;
258
+ registerControlId(controlSource, undefined);
259
+ };
260
+ },
261
+ [registerControlId, controlSourceRef],
262
+ subSlot(slot, 'e:unregId'),
263
+ );
264
+
265
+ useRegisterFieldControl(
266
+ controlRef,
267
+ id,
268
+ checked,
269
+ undefined,
270
+ !groupContext && !disabled,
271
+ nameProp,
272
+ subSlot(slot, 'register'),
273
+ );
274
+
275
+ const inputRef = useRef<HTMLInputElement | null>(null, subSlot(slot, 'inputRef'));
276
+ const mergedInputRef = useComposedRefs(
277
+ inputRefProp,
278
+ inputRef,
279
+ validation.inputRef,
280
+ validation.registerInput,
281
+ subSlot(slot, 'mergedInputRef'),
282
+ );
283
+ const ariaLabelledBy = useAriaLabelledBy(
284
+ ariaLabelledByProp,
285
+ labelId,
286
+ inputRef,
287
+ !nativeButton,
288
+ inputId ?? undefined,
289
+ subSlot(slot, 'ariaLabelledBy'),
290
+ );
291
+
292
+ // octane: set `checked` (property) + `indeterminate` (property) imperatively; matches
293
+ // React's controlled input (property-only, never a `checked` attribute beyond the initial).
294
+ useLayoutEffect(
295
+ () => {
296
+ const input = inputRef.current;
297
+ if (!input) {
298
+ return;
299
+ }
300
+ const setNativeChecked = Object.getOwnPropertyDescriptor(
301
+ ownerWindow(input).HTMLInputElement.prototype,
302
+ 'checked',
303
+ )?.set;
304
+ setNativeChecked?.call(input, checked);
305
+ input.indeterminate = computedIndeterminate;
306
+ if (checked) {
307
+ setFilled(true);
308
+ }
309
+ },
310
+ [checked, computedIndeterminate, setFilled],
311
+ subSlot(slot, 'e:sync'),
312
+ );
313
+
314
+ useValueChanged(
315
+ checked,
316
+ () => {
317
+ if (groupContext) {
318
+ return;
319
+ }
320
+ clearErrors(name);
321
+ setFilled(checked);
322
+ setDirty(checked !== validityData.initialValue);
323
+ validation.change(checked);
324
+ },
325
+ subSlot(slot, 'valueChanged'),
326
+ );
327
+
328
+ const inputProps: Record<string, any> = mergeProps(
329
+ {
330
+ // octane: initial checked → attribute; live value driven via the property (above).
331
+ checked: initialCheckedRef.current || undefined,
332
+ disabled,
333
+ form,
334
+ name: parent ? undefined : name,
335
+ id: nativeButton ? undefined : (inputId ?? undefined),
336
+ required,
337
+ ref: mergedInputRef,
338
+ style: name ? visuallyHiddenInput : visuallyHidden,
339
+ tabIndex: -1,
340
+ type: 'checkbox',
341
+ 'aria-hidden': true,
342
+ onChange(event: any) {
343
+ if (event.defaultPrevented) {
344
+ return;
345
+ }
346
+ if (readOnly) {
347
+ event.preventDefault();
348
+ return;
349
+ }
350
+ const nextChecked = event.currentTarget.checked;
351
+ const details = createChangeEventDetails(REASONS.none, event);
352
+ onCheckedChange?.(nextChecked, details);
353
+ if (details.isCanceled) {
354
+ return;
355
+ }
356
+ groupOnChange?.(nextChecked, details);
357
+ if (details.isCanceled) {
358
+ return;
359
+ }
360
+ setCheckedState(nextChecked);
361
+ if (value && groupValue && setGroupValue && !parent && !isGroupedWithParent) {
362
+ const nextGroupValue = nextChecked
363
+ ? [...groupValue, value]
364
+ : groupValue.filter((item) => item !== value);
365
+ setGroupValue(nextGroupValue, details);
366
+ }
367
+ },
368
+ onFocus() {
369
+ controlRef.current?.focus();
370
+ },
371
+ },
372
+ valueProp !== undefined
373
+ ? { value: (groupContext ? checked && valueProp : valueProp) || '' }
374
+ : {},
375
+ getDescriptionProps,
376
+ (p: any) => validation.getValidationProps(disabled, p),
377
+ );
378
+
379
+ useEffect(
380
+ () => {
381
+ if (!parentContext || !value) {
382
+ return undefined;
383
+ }
384
+ const disabledStates = parentContext.disabledStatesRef.current;
385
+ disabledStates.set(value, disabled);
386
+ return () => {
387
+ disabledStates.delete(value);
388
+ };
389
+ },
390
+ [parentContext, disabled, value],
391
+ subSlot(slot, 'e:parentDisabled'),
392
+ );
393
+
394
+ const state: CheckboxRootState = useMemo(
395
+ () => ({
396
+ ...fieldState,
397
+ checked: computedChecked,
398
+ disabled,
399
+ readOnly,
400
+ required,
401
+ indeterminate: computedIndeterminate,
402
+ }),
403
+ [fieldState, computedChecked, disabled, readOnly, required, computedIndeterminate],
404
+ subSlot(slot, 'state'),
405
+ );
406
+
407
+ const stateAttributesMapping = useStateAttributesMapping(state);
408
+
409
+ const element = useRenderElement(
410
+ 'span',
411
+ { render, className, style },
412
+ {
413
+ state,
414
+ ref: [buttonRef, controlRef, ref, groupContext?.registerControlRef],
415
+ props: [
416
+ {
417
+ id: nativeButton ? (inputId ?? undefined) : id,
418
+ role: 'checkbox',
419
+ 'aria-checked': computedIndeterminate ? 'mixed' : computedChecked,
420
+ 'aria-readonly': readOnly || undefined,
421
+ 'aria-required': required || undefined,
422
+ 'aria-labelledby': ariaLabelledBy,
423
+ [PARENT_CHECKBOX]: parent ? '' : undefined,
424
+ onFocus() {
425
+ if (!disabled) {
426
+ setFocused(true);
427
+ }
428
+ },
429
+ onBlur() {
430
+ const inputEl = inputRef.current;
431
+ if (!inputEl) {
432
+ return;
433
+ }
434
+ setTouched(true);
435
+ setFocused(false);
436
+ if (validationMode === 'onBlur') {
437
+ validation.commit(groupContext ? groupValue : inputEl.checked);
438
+ }
439
+ },
440
+ onKeyDown(event: any) {
441
+ if (event.key !== 'Enter') {
442
+ return;
443
+ }
444
+ // octane: `event` IS the native event.
445
+ makeEventPreventable(event);
446
+ event.preventBaseUIHandler?.();
447
+ if (event.defaultPrevented) {
448
+ return;
449
+ }
450
+ const formToSubmit = inputRef.current?.form ?? null;
451
+ const currentTarget = event.currentTarget;
452
+ const originalPreventDefault = event.preventDefault.bind(event);
453
+ let preventDefaultCalledAfterPropagation = false;
454
+ event.preventDefault = () => {
455
+ preventDefaultCalledAfterPropagation = true;
456
+ originalPreventDefault();
457
+ };
458
+ // Cancel the native button behavior without flagging defaultPrevented.
459
+ originalPreventDefault();
460
+ ownerWindow(currentTarget).queueMicrotask(() => {
461
+ event.preventDefault = originalPreventDefault;
462
+ if (!preventDefaultCalledAfterPropagation) {
463
+ getDefaultFormSubmitter(formToSubmit)?.click();
464
+ }
465
+ });
466
+ },
467
+ onClick(event: any) {
468
+ if (readOnly || disabled) {
469
+ return;
470
+ }
471
+ event.preventDefault();
472
+ const input = inputRef.current;
473
+ if (!input) {
474
+ return;
475
+ }
476
+ input.dispatchEvent(
477
+ new (ownerWindow(input).PointerEvent)('click', {
478
+ bubbles: true,
479
+ shiftKey: event.shiftKey,
480
+ ctrlKey: event.ctrlKey,
481
+ altKey: event.altKey,
482
+ metaKey: event.metaKey,
483
+ }),
484
+ );
485
+ },
486
+ },
487
+ elementProps,
488
+ otherGroupProps,
489
+ getButtonProps,
490
+ getDescriptionProps,
491
+ (p: any) => validation.getValidationProps(disabled, p),
492
+ ],
493
+ stateAttributesMapping,
494
+ },
495
+ subSlot(slot, 're'),
496
+ );
497
+
498
+ const hiddenValueInput =
499
+ !checked && !groupContext && name && !parent && uncheckedValue !== undefined
500
+ ? createElement('input', {
501
+ type: 'hidden',
502
+ form,
503
+ name,
504
+ value: uncheckedValue,
505
+ disabled,
506
+ })
507
+ : null;
508
+
509
+ return createElement(CheckboxRootContext.Provider, {
510
+ value: state,
511
+ children: [element, hiddenValueInput, createElement('input', inputProps)],
512
+ });
513
+ }
514
+
515
+ // --- Indicator ---------------------------------------------------------------
516
+
517
+ export interface CheckboxIndicatorState extends CheckboxRootState {
518
+ transitionStatus: TransitionStatus;
519
+ }
520
+
521
+ function CheckboxIndicator(props: any): any {
522
+ const slot = S('CheckboxIndicator');
523
+ const { render, className, style, keepMounted = false, ref, ...elementProps } = props;
524
+
525
+ const rootState = useCheckboxRootContext();
526
+ const rendered = rootState.checked || rootState.indeterminate;
527
+
528
+ const { mounted, transitionStatus, setMounted } = useTransitionStatus(
529
+ rendered,
530
+ subSlot(slot, 'ts'),
531
+ );
532
+
533
+ const indicatorRef = useRef<HTMLSpanElement | null>(null, subSlot(slot, 'indRef'));
534
+
535
+ const state: CheckboxIndicatorState = { ...rootState, transitionStatus };
536
+
537
+ useOpenChangeComplete(
538
+ {
539
+ open: rendered,
540
+ ref: indicatorRef,
541
+ onComplete() {
542
+ if (!rendered) {
543
+ setMounted(false);
544
+ }
545
+ },
546
+ },
547
+ subSlot(slot, 'occ'),
548
+ );
549
+
550
+ const baseStateAttributesMapping = useStateAttributesMapping(rootState);
551
+ const stateAttributesMapping: StateAttributesMapping<any> = {
552
+ ...baseStateAttributesMapping,
553
+ ...transitionStatusMapping,
554
+ ...(fieldValidityMapping as StateAttributesMapping<any>),
555
+ };
556
+
557
+ const shouldRender = keepMounted || mounted;
558
+
559
+ const element = useRenderElement(
560
+ 'span',
561
+ { render, className, style },
562
+ { ref: [ref, indicatorRef], state, stateAttributesMapping, props: elementProps },
563
+ subSlot(slot, 're'),
564
+ );
565
+
566
+ if (!shouldRender) {
567
+ return null;
568
+ }
569
+
570
+ return element;
571
+ }
572
+
573
+ // --- Namespace (mirrors `export * as Checkbox`) ------------------------------
574
+
575
+ export const Checkbox = {
576
+ Root: CheckboxRoot,
577
+ Indicator: CheckboxIndicator,
578
+ };