@octanejs/base-ui 0.1.1 → 0.1.3

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 (67) hide show
  1. package/README.md +7 -1
  2. package/package.json +12 -4
  3. package/src/alert-dialog.ts +47 -0
  4. package/src/checkbox.ts +7 -16
  5. package/src/dialog.ts +859 -0
  6. package/src/field.ts +4 -30
  7. package/src/index.ts +3 -0
  8. package/src/number-field.ts +8 -45
  9. package/src/popover.ts +1205 -0
  10. package/src/radio.ts +3 -24
  11. package/src/slider.ts +4 -5
  12. package/src/switch.ts +4 -30
  13. package/src/utils/InternalBackdrop.ts +28 -0
  14. package/src/utils/adaptiveOriginMiddleware.ts +82 -0
  15. package/src/utils/closePart.ts +61 -0
  16. package/src/utils/constants.ts +9 -0
  17. package/src/utils/createChangeEventDetails.ts +7 -0
  18. package/src/utils/dom.ts +9 -0
  19. package/src/utils/empty.ts +7 -0
  20. package/src/utils/floating/FloatingFocusManager.ts +833 -0
  21. package/src/utils/floating/FloatingPortal.ts +268 -0
  22. package/src/utils/floating/FloatingRootStore.ts +127 -0
  23. package/src/utils/floating/FloatingTree.ts +70 -0
  24. package/src/utils/floating/FloatingTreeStore.ts +22 -0
  25. package/src/utils/floating/FocusGuard.ts +34 -0
  26. package/src/utils/floating/composite.ts +20 -0
  27. package/src/utils/floating/constants.ts +8 -0
  28. package/src/utils/floating/createAttribute.ts +4 -0
  29. package/src/utils/floating/createEventEmitter.ts +20 -0
  30. package/src/utils/floating/element.ts +54 -0
  31. package/src/utils/floating/enqueueFocus.ts +38 -0
  32. package/src/utils/floating/event.ts +53 -0
  33. package/src/utils/floating/getEmptyRootContext.ts +19 -0
  34. package/src/utils/floating/markOthers.ts +197 -0
  35. package/src/utils/floating/nodes.ts +31 -0
  36. package/src/utils/floating/tabbable.ts +260 -0
  37. package/src/utils/floating/types.ts +57 -0
  38. package/src/utils/floating/useClick.ts +174 -0
  39. package/src/utils/floating/useDismiss.ts +641 -0
  40. package/src/utils/floating/useFloating.ts +198 -0
  41. package/src/utils/floating/useFloatingRootContext.ts +81 -0
  42. package/src/utils/floating/useHoverFloatingInteraction.ts +12 -0
  43. package/src/utils/floating/useHoverReferenceInteraction.ts +17 -0
  44. package/src/utils/floating/useSyncedFloatingRootContext.ts +93 -0
  45. package/src/utils/getDisabledMountTransitionStyles.ts +12 -0
  46. package/src/utils/hideMiddleware.ts +22 -0
  47. package/src/utils/inertValue.ts +5 -0
  48. package/src/utils/mergeCleanups.ts +13 -0
  49. package/src/utils/platform.ts +46 -2
  50. package/src/utils/popupStateMapping.ts +48 -0
  51. package/src/utils/popups/index.ts +4 -0
  52. package/src/utils/popups/popupStoreUtils.ts +484 -0
  53. package/src/utils/popups/popupTriggerMap.ts +62 -0
  54. package/src/utils/popups/store.ts +147 -0
  55. package/src/utils/popups/useTriggerFocusGuards.ts +73 -0
  56. package/src/utils/store/ReactStore.ts +168 -0
  57. package/src/utils/store/Store.ts +84 -0
  58. package/src/utils/store/createSelector.ts +66 -0
  59. package/src/utils/store/useStore.ts +49 -0
  60. package/src/utils/useAnchorPositioning.ts +581 -0
  61. package/src/utils/useAnchoredPopupScrollLock.ts +50 -0
  62. package/src/utils/useAnimationFrame.ts +28 -5
  63. package/src/utils/useEnhancedClickHandler.ts +47 -0
  64. package/src/utils/useOnFirstRender.ts +11 -0
  65. package/src/utils/useOpenInteractionType.ts +32 -0
  66. package/src/utils/usePositioner.ts +48 -0
  67. package/src/utils/useScrollLock.ts +253 -0
package/src/field.ts CHANGED
@@ -4,8 +4,8 @@
4
4
  //
5
5
  // Field groups a control with its label/description/error and runs validation. The default
6
6
  // (inert) field contexts are overridden here by the real providers. octane adaptations:
7
- // native events; forwardRef → ref-as-prop; the text-input value adaptation (initial value →
8
- // the `value` ATTRIBUTE; controlled value driven via the property).
7
+ // native events; forwardRef → ref-as-prop. The control's `value`/`defaultValue` pass straight
8
+ // through octane inputs are CONTROLLED exactly like React's.
9
9
  import {
10
10
  createContext,
11
11
  createElement,
@@ -20,7 +20,7 @@ import {
20
20
 
21
21
  import { S, subSlot } from './internal';
22
22
  import { useRenderElement, type RenderProp } from './utils/useRenderElement';
23
- import { ownerDocument, ownerWindow } from './utils/owner';
23
+ import { ownerDocument } from './utils/owner';
24
24
  import { createChangeEventDetails, REASONS } from './utils/createChangeEventDetails';
25
25
  import { useControlled } from './utils/useControlled';
26
26
  import { useStableCallback } from './utils/useStableCallback';
@@ -341,32 +341,6 @@ function FieldControl(props: any): any {
341
341
  const isControlled = valueProp !== undefined;
342
342
  const value = isControlled ? valueUnwrapped : undefined;
343
343
 
344
- // octane text-input adaptation: the INITIAL value is the `value` ATTRIBUTE; a controlled
345
- // value is driven imperatively via the property.
346
- const initialValueRef = useRef(
347
- isControlled ? value : defaultValue,
348
- subSlot(slot, 'initialValue'),
349
- );
350
-
351
- useLayoutEffect(
352
- () => {
353
- if (!isControlled) {
354
- return;
355
- }
356
- const input = validation.inputRef.current;
357
- if (!input) {
358
- return;
359
- }
360
- const setNativeValue = Object.getOwnPropertyDescriptor(
361
- ownerWindow(input).HTMLInputElement.prototype,
362
- 'value',
363
- )?.set;
364
- setNativeValue?.call(input, value ?? '');
365
- },
366
- [isControlled, value],
367
- subSlot(slot, 'e:syncValue'),
368
- );
369
-
370
344
  const getValueFromInput = useStableCallback(
371
345
  () => validation.inputRef.current?.value,
372
346
  subSlot(slot, 'getValue'),
@@ -396,7 +370,7 @@ function FieldControl(props: any): any {
396
370
  ref: validation.inputRef,
397
371
  'aria-labelledby': labelId,
398
372
  autoFocus: autoFocus || undefined,
399
- value: initialValueRef.current,
373
+ ...(isControlled ? { value } : { defaultValue }),
400
374
  onChange(event: any) {
401
375
  const inputValue = event.currentTarget.value;
402
376
  onValueChange?.(inputValue, createChangeEventDetails(REASONS.none, event));
package/src/index.ts CHANGED
@@ -3,9 +3,11 @@
3
3
  // (`@octanejs/base-ui/separator`, `@octanejs/base-ui/use-render`, …). This barrel
4
4
  // re-exports the public surface for convenience as components land.
5
5
  export { Separator } from './separator';
6
+ export { AlertDialog } from './alert-dialog';
6
7
  export { Avatar } from './avatar';
7
8
  export { Checkbox } from './checkbox';
8
9
  export { CheckboxGroup } from './checkbox-group';
10
+ export { Dialog } from './dialog';
9
11
  export { Field } from './field';
10
12
  export { Fieldset } from './fieldset';
11
13
  export { Form } from './form';
@@ -15,6 +17,7 @@ export { NumberField } from './number-field';
15
17
  export { Progress } from './progress';
16
18
  export { Radio } from './radio';
17
19
  export { RadioGroup } from './radio-group';
20
+ export { Popover } from './popover';
18
21
  export { Slider } from './slider';
19
22
  export { Switch } from './switch';
20
23
  export { Toggle } from './toggle';
@@ -3,10 +3,11 @@
3
3
  // decrement/NumberFieldDecrement, root/useNumberFieldButton + useNumberFieldStepperButton, and
4
4
  // utils/stateAttributesMapping — plus its `index.parts` (the `NumberField` namespace).
5
5
  //
6
- // octane adaptations: native events (no `.nativeEvent`); forwardRef → ref-as-prop; the
7
- // text-input value adaptation (initial value the `value` ATTRIBUTE; the live value driven via
8
- // the `.value` PROPERTY). The scrub area + press-and-hold auto-repeat are deferred (see
9
- // `usePressAndHold` stub). Field/Form integration is inert when standalone.
6
+ // octane adaptations: native events (no `.nativeEvent`); forwardRef → ref-as-prop. The visible
7
+ // and hidden inputs take live controlled `value` props octane inputs are CONTROLLED exactly
8
+ // like React's (property-driven, reasserted on every commit and after discrete events). The
9
+ // scrub area + press-and-hold auto-repeat are deferred (see `usePressAndHold` stub).
10
+ // Field/Form integration is inert when standalone.
10
11
  import {
11
12
  createContext,
12
13
  createElement,
@@ -29,7 +30,7 @@ import { useValueChanged } from './utils/useValueChanged';
29
30
  import { useForcedRerendering } from './utils/useForcedRerendering';
30
31
  import { addEventListener } from './utils/addEventListener';
31
32
  import { platform } from './utils/platform';
32
- import { ownerDocument, ownerWindow } from './utils/owner';
33
+ import { ownerDocument } from './utils/owner';
33
34
  import { visuallyHidden, visuallyHiddenInput } from './utils/visuallyHidden';
34
35
  import { stopEvent } from './utils/composite/list-utils';
35
36
  import { useButton } from './utils/useButton';
@@ -212,9 +213,6 @@ function NumberFieldRoot(props: any): any {
212
213
  );
213
214
  const [inputMode, setInputMode] = useState<InputMode>('numeric', subSlot(slot, 'inputMode'));
214
215
 
215
- // octane: reflect the INITIAL numeric value as the hidden input's `value` ATTRIBUTE.
216
- const initialHiddenValueRef = useRef(value ?? '', subSlot(slot, 'initialHidden'));
217
-
218
216
  const getAllowedNonNumericKeys = useStableCallback(
219
217
  () => {
220
218
  const { decimal, group, currency, literal } = getNumberLocaleDetails(locale, format);
@@ -467,8 +465,7 @@ function NumberFieldRoot(props: any): any {
467
465
  type: 'number',
468
466
  form,
469
467
  name,
470
- // octane: initial value attribute; live value driven via the property (sync effect below).
471
- value: initialHiddenValueRef.current,
468
+ value: value ?? '',
472
469
  min,
473
470
  max,
474
471
  step: stepProp,
@@ -480,21 +477,6 @@ function NumberFieldRoot(props: any): any {
480
477
  style: name ? visuallyHiddenInput : visuallyHidden,
481
478
  });
482
479
 
483
- // octane: drive the hidden input's live `.value` PROPERTY.
484
- useLayoutEffect(
485
- () => {
486
- const el = validation.inputRef.current ?? inputRef.current;
487
- if (!el) return;
488
- const setNativeValue = Object.getOwnPropertyDescriptor(
489
- ownerWindow(el).HTMLInputElement.prototype,
490
- 'value',
491
- )?.set;
492
- setNativeValue?.call(el, value ?? '');
493
- },
494
- [value],
495
- subSlot(slot, 'e:syncHidden'),
496
- );
497
-
498
480
  return createElement(NumberFieldRootContext.Provider, {
499
481
  value: contextValue,
500
482
  children: [element, hiddenInput],
@@ -558,9 +540,6 @@ function NumberFieldInput(props: any): any {
558
540
  const blockRevalidationRef = useRef(false, subSlot(slot, 'blockReval'));
559
541
  const pendingCaretRef = useRef<number | null>(null, subSlot(slot, 'caret'));
560
542
 
561
- // octane: reflect the INITIAL formatted text as the `value` ATTRIBUTE; live via the property.
562
- const initialInputValueRef = useRef(inputValue, subSlot(slot, 'initialInput'));
563
-
564
543
  useRegisterFieldControl(
565
544
  inputRef,
566
545
  id,
@@ -583,21 +562,6 @@ function NumberFieldInput(props: any): any {
583
562
  subSlot(slot, 'e:caret'),
584
563
  );
585
564
 
586
- // octane: drive the visible input's live `.value` PROPERTY (matches React's controlled input).
587
- useLayoutEffect(
588
- () => {
589
- const el = inputRef.current;
590
- if (!el) return;
591
- const setNativeValue = Object.getOwnPropertyDescriptor(
592
- ownerWindow(el).HTMLInputElement.prototype,
593
- 'value',
594
- )?.set;
595
- setNativeValue?.call(el, inputValue);
596
- },
597
- [inputValue],
598
- subSlot(slot, 'e:syncValue'),
599
- );
600
-
601
565
  useValueChanged(
602
566
  value,
603
567
  () => {
@@ -617,8 +581,7 @@ function NumberFieldInput(props: any): any {
617
581
  disabled,
618
582
  readOnly,
619
583
  inputMode,
620
- // octane: initial formatted text → attribute; live value via the property (sync effect above).
621
- value: initialInputValueRef.current,
584
+ value: inputValue,
622
585
  type: 'text',
623
586
  autoComplete: 'off',
624
587
  autoCorrect: 'off',