@octanejs/base-ui 0.1.1 → 0.1.2

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/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # @octanejs/base-ui
2
2
 
3
3
  [Base UI](https://base-ui.com) (`@base-ui/react`) ported to the
4
- [octane](https://github.com/octanejs/octane) renderer — headless, accessible, unstyled
4
+ [octane](https://github.com/octanejs/octane) UI framework — headless, accessible, unstyled
5
5
  UI primitives.
6
6
 
7
7
  Alpha, in progress. Ported at full fidelity from the pinned `mui/base-ui` source
@@ -31,3 +31,9 @@ Every component takes Base UI's universal composition props — `render` (a JSX
31
31
  - **ref-as-prop.** No `forwardRef`; `ref` is a normal prop (React-19 shape).
32
32
  - **`className` composition** follows octane's `normalizeClass` at the apply site; the
33
33
  `render`-prop merge concatenates strings exactly like Base UI.
34
+
35
+ ## Status
36
+
37
+ Current scope, known divergences, and verification status are tracked in the
38
+ generated [bindings status table](../../docs/bindings-status.md), sourced from
39
+ this package's [`status.json`](./status.json).
package/package.json CHANGED
@@ -1,8 +1,15 @@
1
1
  {
2
2
  "name": "@octanejs/base-ui",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
+ "octane": {
7
+ "hookSlots": {
8
+ "manual": [
9
+ "src"
10
+ ]
11
+ }
12
+ },
6
13
  "description": "Base UI (@base-ui/react) for the octane renderer — headless, accessible, unstyled UI primitives ported from base-ui.com at full fidelity.",
7
14
  "author": {
8
15
  "name": "Dominic Gannaway",
@@ -28,14 +35,15 @@
28
35
  "./*": "./src/*.ts"
29
36
  },
30
37
  "dependencies": {
38
+ "@floating-ui/utils": "^0.2.11",
31
39
  "aria-hidden": "^1.2.6",
32
40
  "tabbable": "^6.5.0",
33
- "@octanejs/floating-ui": "0.1.2",
34
- "octane": "0.1.3"
41
+ "@octanejs/floating-ui": "0.1.3",
42
+ "octane": "0.1.4"
35
43
  },
36
44
  "devDependencies": {
37
45
  "@base-ui/react": "^1.6.0",
38
- "@tsrx/react": "^0.2.33",
46
+ "@tsrx/react": "^0.2.37",
39
47
  "esbuild": "^0.28.1",
40
48
  "react": "^19.2.0",
41
49
  "react-dom": "^19.2.0",
@@ -0,0 +1,47 @@
1
+ // Ported from .base-ui/packages/react/src/alert-dialog/ (v1.6.0). AlertDialog is a thin variant of
2
+ // Dialog: the Root delegates to Dialog's `useRenderDialogRoot` with the `'alert-dialog'` mode (which
3
+ // forces `modal: true`, `disablePointerDismissal: true`, `role: 'alertdialog'`), and every other part
4
+ // — Trigger, Portal, Backdrop, Popup, Title, Description, Close — is literally Dialog's part reused.
5
+ // The handle extends `DialogHandle`, enforcing the alert-dialog invariants on its store.
6
+ import { useRenderDialogRoot, DialogHandle, DialogStore, Dialog } from './dialog';
7
+
8
+ const alertDialogState = {
9
+ modal: true,
10
+ disablePointerDismissal: true,
11
+ role: 'alertdialog',
12
+ } as const;
13
+
14
+ /**
15
+ * A handle to control an Alert Dialog imperatively and to associate detached triggers with it.
16
+ */
17
+ export class AlertDialogHandle<Payload> extends DialogHandle<Payload> {
18
+ constructor(store?: DialogStore<Payload>) {
19
+ const alertDialogStore = store ?? new DialogStore<Payload>(alertDialogState as any);
20
+ super(alertDialogStore);
21
+
22
+ if (store) {
23
+ // Supplied stores may have been created as plain dialogs; enforce alert-dialog state.
24
+ this.store.update(alertDialogState as any);
25
+ }
26
+ }
27
+ }
28
+
29
+ export function createAlertDialogHandle<Payload>(): AlertDialogHandle<Payload> {
30
+ return new AlertDialogHandle<Payload>();
31
+ }
32
+
33
+ function AlertDialogRoot<Payload>(props: any): any {
34
+ return useRenderDialogRoot<Payload>(props, 'alert-dialog');
35
+ }
36
+
37
+ export const AlertDialog = {
38
+ Root: AlertDialogRoot,
39
+ Trigger: Dialog.Trigger,
40
+ Portal: Dialog.Portal,
41
+ Backdrop: Dialog.Backdrop,
42
+ Popup: Dialog.Popup,
43
+ Title: Dialog.Title,
44
+ Description: Dialog.Description,
45
+ Close: Dialog.Close,
46
+ createHandle: createAlertDialogHandle,
47
+ };
package/src/checkbox.ts CHANGED
@@ -4,10 +4,11 @@
4
4
  //
5
5
  // A checkbox renders a `<span role="checkbox">` + a hidden `<input type="checkbox">`, with an
6
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).
7
+ // native events (no `.nativeEvent`); the hidden input takes the live `checked` prop (octane
8
+ // inputs are CONTROLLED exactly like React's) with native click dispatch;
9
+ // `input.indeterminate` set imperatively as a property (it has no attribute). The
10
+ // CheckboxGroup / parent-checkbox branches stay dormant until CheckboxGroup lands
11
+ // (groupContext undefined).
11
12
  import {
12
13
  createContext,
13
14
  createElement,
@@ -225,9 +226,6 @@ function CheckboxRoot(props: CheckboxRootProps): any {
225
226
  subSlot(slot, 'checked'),
226
227
  );
227
228
 
228
- // octane: reflect the INITIAL checked as the `checked` ATTRIBUTE (see Switch).
229
- const initialCheckedRef = useRef(checked, subSlot(slot, 'initialChecked'));
230
-
231
229
  const computedChecked = isGroupedWithParent ? Boolean(groupChecked) : checked;
232
230
  const computedIndeterminate = isGroupedWithParent
233
231
  ? groupIndeterminate || indeterminate
@@ -289,19 +287,13 @@ function CheckboxRoot(props: CheckboxRootProps): any {
289
287
  subSlot(slot, 'ariaLabelledBy'),
290
288
  );
291
289
 
292
- // octane: set `checked` (property) + `indeterminate` (property) imperatively; matches
293
- // React's controlled input (property-only, never a `checked` attribute beyond the initial).
290
+ // `indeterminate` has no attribute — set the property imperatively (per Base UI).
294
291
  useLayoutEffect(
295
292
  () => {
296
293
  const input = inputRef.current;
297
294
  if (!input) {
298
295
  return;
299
296
  }
300
- const setNativeChecked = Object.getOwnPropertyDescriptor(
301
- ownerWindow(input).HTMLInputElement.prototype,
302
- 'checked',
303
- )?.set;
304
- setNativeChecked?.call(input, checked);
305
297
  input.indeterminate = computedIndeterminate;
306
298
  if (checked) {
307
299
  setFilled(true);
@@ -327,8 +319,7 @@ function CheckboxRoot(props: CheckboxRootProps): any {
327
319
 
328
320
  const inputProps: Record<string, any> = mergeProps(
329
321
  {
330
- // octane: initial checked → attribute; live value driven via the property (above).
331
- checked: initialCheckedRef.current || undefined,
322
+ checked,
332
323
  disabled,
333
324
  form,
334
325
  name: parent ? undefined : name,